1 /*
2  * bmpcmp.c: BMP Comparison - utility for use with htmldiff.pl
3  */
4 
5 #include <stdio.h>
6 #include <stdlib.h>
7 #include <string.h>
8 #include <ctype.h>
9 
10 #define MINX (300)
11 #define MINY (320)
12 #define MAXX (600)
13 #define MAXY (960)
14 
15 typedef struct {
16     int xmin;
17     int ymin;
18     int xmax;
19     int ymax;
20 } BBox;
21 
22 typedef struct ImageReader
23 {
24   FILE *file;
25   void *(*read)(struct ImageReader *, int *w, int *h, int *s, int *bpp);
26 } ImageReader;
27 
Malloc(size_t size)28 static void *Malloc(size_t size) {
29     void *block;
30 
31     block = malloc(size);
32     if (block == NULL) {
33         fprintf(stderr, "Failed to malloc %u bytes\n", size);
34         exit(EXIT_FAILURE);
35     }
36     return block;
37 }
38 
putword(unsigned char * buf,int word)39 static void putword(unsigned char *buf, int word) {
40   buf[0] = word;
41   buf[1] = (word>>8);
42 }
43 
getdword(unsigned char * bmp)44 static int getdword(unsigned char *bmp) {
45 
46   return bmp[0]+(bmp[1]<<8)+(bmp[2]<<16)+(bmp[3]<<24);
47 }
48 
getword(unsigned char * bmp)49 static int getword(unsigned char *bmp) {
50 
51   return bmp[0]+(bmp[1]<<8);
52 }
53 
putdword(unsigned char * bmp,int i)54 static void putdword(unsigned char *bmp, int i) {
55 
56   bmp[0] = i & 0xFF;
57   bmp[1] = (i>>8) & 0xFF;
58   bmp[2] = (i>>16) & 0xFF;
59   bmp[3] = (i>>24) & 0xFF;
60 }
61 
bmp_load_sub(unsigned char * bmp,int * width_ret,int * height_ret,int * span,int * bpp,int image_offset,int filelen)62 static unsigned char *bmp_load_sub(unsigned char *bmp,
63                                    int           *width_ret,
64                                    int           *height_ret,
65                                    int           *span,
66                                    int           *bpp,
67                                    int            image_offset,
68                                    int            filelen)
69 {
70   int size, src_bpp, dst_bpp, comp, xdpi, ydpi, i, x, y, cols;
71   int masks, redmask, greenmask, bluemask, col, col2;
72   int width, byte_width, word_width, height;
73   unsigned char *palette;
74   unsigned char *src, *dst;
75   int           *dst2;
76   short         *dst3;
77   int            pal[256];
78 
79   size = getdword(bmp);
80   if (size == 12) {
81     /* Windows v2 - thats fine */
82   } else if (size == 40) {
83     /* Windows v3 - also fine */
84   } else if (size == 108) {
85     /* Windows v4 - also fine */
86   } else {
87     /* Windows 8004 - god knows. Give up */
88     return NULL;
89   }
90   if (size == 12) {
91     width  = getword(bmp+4);
92     height = getword(bmp+6);
93     i      = getword(bmp+8);
94     if (i != 1) {
95       /* Single plane images only! */
96       return NULL;
97     }
98     src_bpp = getword(bmp+10);
99     comp    = 0;
100     xdpi    = 90;
101     ydpi    = 90;
102     cols    = 1<<src_bpp;
103     masks   = 0;
104     palette = bmp+14;
105   } else {
106     width  = getdword(bmp+4);
107     height = getdword(bmp+8);
108     i      = getword(bmp+12);
109     if (i != 1) {
110       /* Single plane images only! */
111       return NULL;
112     }
113     src_bpp = getword(bmp+14);
114     comp    = getdword(bmp+16);
115     cols    = getdword(bmp+32);
116     if (comp == 3) {
117       /* Windows NT */
118       redmask = getdword(bmp+40);
119       greenmask = getdword(bmp+44);
120       bluemask = getdword(bmp+48);
121       masks = 1;
122       palette = bmp+52;
123     } else {
124       if (size == 108) {
125         /* Windows 95 */
126         redmask   = getdword(bmp+40);
127         greenmask = getdword(bmp+44);
128         bluemask  = getdword(bmp+48);
129         /* ColorSpace rubbish ignored for now */
130         masks = 1;
131         palette = bmp+108;
132       } else {
133         masks = 0;
134         palette = bmp+40;
135       }
136     }
137   }
138 
139   dst_bpp = src_bpp;
140   if ((src_bpp < 8) || (src_bpp == 24))
141       src_bpp = 32;
142 
143   /* Read the palette */
144   if (src_bpp <= 8) {
145     for (i = 0; i < (1<<dst_bpp); i++) {
146       col = getdword(palette+i*4);
147       col2  = (col & 0x0000FF)<<24;
148       col2 |= (col & 0x00FF00)<<8;
149       col2 |= (col & 0xFF0000)>>8;
150       pal[i] = col2;
151     }
152   }
153   if (image_offset <= 0) {
154     src = palette + (1<<src_bpp)*4;
155   } else {
156     src = bmp + image_offset;
157   }
158 
159   byte_width  = (width+7)>>3;
160   word_width  = width * (src_bpp>>3);
161   word_width += 3;
162   word_width &= ~3;
163 
164   dst = Malloc(word_width * height);
165 
166   /* Now we do the actual conversion */
167   if (comp == 0) {
168     switch (src_bpp) {
169       case 1:
170         for (y = height-1; y >= 0; y--) {
171           dst2 = (int *)dst;
172           for (x = 0; x < byte_width; x++) {
173             i  = *src++;
174             *dst2++ = pal[(i   ) & 1];
175             *dst2++ = pal[(i>>1) & 1];
176             *dst2++ = pal[(i>>2) & 1];
177             *dst2++ = pal[(i>>3) & 1];
178             *dst2++ = pal[(i>>4) & 1];
179             *dst2++ = pal[(i>>5) & 1];
180             *dst2++ = pal[(i>>6) & 1];
181             *dst2++ = pal[(i>>7) & 1];
182           }
183           while (x & 3) {
184             src++;
185             x += 1;
186           }
187           dst += word_width;
188         }
189         break;
190       case 4:
191         for (y = height-1; y >= 0; y--) {
192           dst2 = (int *)dst;
193           for (x = 0; x < byte_width; x++) {
194             i  = *src++;
195             *dst2++ = pal[(i   ) & 0xF];
196             *dst2++ = pal[(i>>4) & 0xF];
197           }
198           while (x & 3) {
199             src++;
200             x += 1;
201           }
202           dst += word_width;
203         }
204         break;
205       case 8:
206         for (y = height-1; y >= 0; y--) {
207           dst2 = (int *)dst;
208           for (x = 0; x < byte_width; x++) {
209             *dst2++ = pal[*src++];
210           }
211           while (x & 3) {
212             src++;
213             x += 1;
214           }
215           dst += word_width;
216         }
217         break;
218       case 16:
219         for (y = height-1; y >= 0; y--) {
220           dst3 = (short *)dst;
221           for (x = 0; x < byte_width; x+=2) {
222             i  = (*src++);
223             i |= (*src++)<<8;
224             *dst3++ = i;
225             *dst3++ = i>>8;
226           }
227           while (x & 3) {
228             src++;
229             x += 1;
230           }
231           dst += word_width;
232         }
233         break;
234       case 32:
235         if (masks) {
236           printf("Send this file to Robin, now! (32bpp with colour masks)\n");
237           free(dst);
238           return NULL;
239         } else {
240           for (y = 0; y < height; y++) {
241             dst2 = (int *)dst;
242             for (x = 0; x < width; x++) {
243               i  = (*src++);
244               i |= (*src++)<<8;
245               i |= (*src++)<<16;
246               *dst2++=i;
247             }
248             x=x*3;
249             while (x & 3) {
250               src++;
251               x += 1;
252             }
253             dst += word_width;
254           }
255         }
256         break;
257     }
258   } else {
259     printf("Send this file to Robin, now! (compressed)\n");
260     free(dst);
261     return NULL;
262   }
263 
264   *span       = word_width;
265   *width_ret  = width;
266   *height_ret = height;
267   *bpp        = src_bpp;
268 
269   return dst - word_width*height;
270 }
271 
bmp_read(ImageReader * im,int * width,int * height,int * span,int * bpp)272 static void *bmp_read(ImageReader *im,
273                       int         *width,
274                       int         *height,
275                       int         *span,
276                       int         *bpp)
277 {
278     int            offset;
279     long           filelen, filepos;
280     unsigned char *data;
281     unsigned char *bmp;
282 
283     filepos = ftell(im->file);
284     fseek(im->file, 0, SEEK_END);
285     filelen = ftell(im->file);
286     fseek(im->file, 0, SEEK_SET);
287 
288     /* If we were at the end to start, then we'd read our one and only
289      * image. */
290     if (filepos == filelen)
291         return NULL;
292 
293     /* Load the whole lot into memory */
294     bmp = Malloc(filelen);
295     fread(bmp, 1, filelen, im->file);
296 
297     offset = getdword(bmp+10);
298     data = bmp_load_sub(bmp+14, width, height, span, bpp, offset-14, filelen);
299     free(bmp);
300     return data;
301 }
302 
pbm_read(FILE * file,int width,int height,int maxval,unsigned char * bmp)303 static void pbm_read(FILE          *file,
304                      int            width,
305                      int            height,
306                      int            maxval,
307                      unsigned char *bmp)
308 {
309     int w;
310     int byte, mask, g;
311 
312     for (; height>0; height--) {
313         mask = 0;
314         for (w=width; w>0; w--) {
315             if (mask == 0)
316             {
317                 byte = fgetc(file);
318                 mask = 0x80;
319             }
320             g = byte & mask;
321             if (g != 0)
322                 g = 255;
323             mask >>= 1;
324             *bmp++ = g;
325             *bmp++ = g;
326             *bmp++ = g;
327             *bmp++ = 0;
328         }
329     }
330 }
331 
pgm_read(FILE * file,int width,int height,int maxval,unsigned char * bmp)332 static void pgm_read(FILE          *file,
333                      int            width,
334                      int            height,
335                      int            maxval,
336                      unsigned char *bmp)
337 {
338     int w;
339 
340     if (maxval == 255)
341     {
342         for (; height>0; height--) {
343             for (w=width; w>0; w--) {
344                 int g = fgetc(file);
345                 *bmp++ = g;
346                 *bmp++ = g;
347                 *bmp++ = g;
348                 *bmp++ = 0;
349             }
350         }
351     } else if (maxval < 255) {
352         for (; height>0; height--) {
353             for (w=width; w>0; w--) {
354                 int g = fgetc(file)*255/maxval;
355                 *bmp++ = g;
356                 *bmp++ = g;
357                 *bmp++ = g;
358                 *bmp++ = 0;
359             }
360         }
361     } else {
362         for (; height>0; height--) {
363             for (w=width; w>0; w--) {
364                 int g = ((fgetc(file)<<8) + (fgetc(file)))*255/maxval;
365                 *bmp++ = g;
366                 *bmp++ = g;
367                 *bmp++ = g;
368                 *bmp++ = 0;
369             }
370         }
371     }
372 }
373 
ppm_read(FILE * file,int width,int height,int maxval,unsigned char * bmp)374 static void ppm_read(FILE          *file,
375                      int            width,
376                      int            height,
377                      int            maxval,
378                      unsigned char *bmp)
379 {
380     int w;
381 
382     if (maxval == 255)
383     {
384         for (; height>0; height--) {
385             for (w=width; w>0; w--) {
386                 *bmp++ = fgetc(file);
387                 *bmp++ = fgetc(file);
388                 *bmp++ = fgetc(file);
389                 *bmp++ = 0;
390             }
391         }
392     } else if (maxval < 255) {
393         for (; height>0; height--) {
394             for (w=width; w>0; w--) {
395                 *bmp++ = fgetc(file)*255/maxval;
396                 *bmp++ = fgetc(file)*255/maxval;
397                 *bmp++ = fgetc(file)*255/maxval;
398                 *bmp++ = 0;
399             }
400         }
401     } else {
402         for (; height>0; height--) {
403             for (w=width; w>0; w--) {
404                 *bmp++ = ((fgetc(file)<<8) + (fgetc(file)))*255/maxval;
405                 *bmp++ = ((fgetc(file)<<8) + (fgetc(file)))*255/maxval;
406                 *bmp++ = ((fgetc(file)<<8) + (fgetc(file)))*255/maxval;
407                 *bmp++ = 0;
408             }
409         }
410     }
411 }
412 
get_uncommented_char(FILE * file)413 static int get_uncommented_char(FILE *file)
414 {
415     int c;
416 
417     do
418     {
419         c = fgetc(file);
420         if (c != '#')
421             return c;
422         do {
423             c = fgetc(file);
424         } while ((c != EOF) && (c != '\n') && (c != '\r'));
425     }
426     while (c != EOF);
427 
428     return EOF;
429 }
430 
get_pnm_num(FILE * file)431 static int get_pnm_num(FILE *file)
432 {
433     int c;
434     int val = 0;
435 
436     /* Skip over any whitespace */
437     do {
438         c = get_uncommented_char(file);
439     } while (isspace(c));
440 
441     /* Read the number */
442     while (isdigit(c))
443     {
444         val = val*10 + c - '0';
445         c = get_uncommented_char(file);
446     }
447 
448     /* assume the last c is whitespace */
449     return val;
450 }
451 
pnm_read(ImageReader * im,int * width,int * height,int * span,int * bpp)452 static void *pnm_read(ImageReader *im,
453                       int         *width,
454                       int         *height,
455                       int         *span,
456                       int         *bpp)
457 {
458     unsigned char *bmp;
459     int            c, maxval;
460     void          (*read)(FILE *, int, int, int, unsigned char *);
461 
462     c = fgetc(im->file);
463     /* Skip over any white space before the P */
464     while ((c != 'P') && (c != EOF)) {
465         c = fgetc(im->file);
466     }
467     if (c == EOF)
468         return NULL;
469     switch (get_pnm_num(im->file))
470     {
471         case 1:
472             /* Plain PBM - we don't support that */
473             fprintf(stderr, "Plain PBM unsupported!\n");
474             return NULL;
475         case 2:
476             /* Plain PGM - we don't support that */
477             fprintf(stderr, "Plain PGM unsupported!\n");
478             return NULL;
479         case 3:
480             /* Plain PPM - we don't support that */
481             fprintf(stderr, "Plain PPM unsupported!\n");
482             return NULL;
483         case 4:
484             read = pbm_read;
485             break;
486         case 5:
487             read = pgm_read;
488             break;
489         case 6:
490             read = ppm_read;
491             break;
492         default:
493             /* Eh? */
494             fprintf(stderr, "Unknown PxM format!\n");
495             return NULL;
496     }
497     *width  = get_pnm_num(im->file);
498     *span   = *width * 4;
499     *height = get_pnm_num(im->file);
500     if (read != pbm_read)
501         maxval = get_pnm_num(im->file);
502     else
503         maxval = 1;
504     *bpp = 32; /* We always convert to 32bpp */
505 
506     bmp = Malloc(*width * *height * 4);
507 
508     read(im->file, *width, *height, maxval, bmp);
509     return bmp;
510 }
511 
image_open(ImageReader * im,char * filename)512 static void image_open(ImageReader *im,
513                        char        *filename)
514 {
515     int type;
516 
517     im->file = fopen(filename, "rb");
518     if (im->file == NULL) {
519         fprintf(stderr, "%s failed to open\n", filename);
520         exit(EXIT_FAILURE);
521     }
522 
523     /* Identify the filetype */
524     type  = fgetc(im->file);
525 
526     if (type == 0x50) {
527         /* Starts with a P! Must be a P?M file. */
528         im->read = pnm_read;
529         ungetc(0x50, im->file);
530     } else {
531         type |= (fgetc(im->file)<<8);
532         if (type == 0x4d42) { /* BM */
533             /* BMP format; Win v2 or above */
534             im->read = bmp_read;
535         } else {
536             fprintf(stderr, "%s: Unrecognised image type\n", filename);
537             exit(EXIT_FAILURE);
538         }
539     }
540 }
541 
image_close(ImageReader * im)542 static void image_close(ImageReader *im)
543 {
544     fclose(im->file);
545     im->file = NULL;
546 }
547 
find_changed_bbox(unsigned char * bmp,unsigned char * bmp2,int w,int h,int span,int bpp,BBox * bbox)548 static void find_changed_bbox(unsigned char *bmp,
549                               unsigned char *bmp2,
550                               int            w,
551                               int            h,
552                               int            span,
553                               int            bpp,
554                               BBox          *bbox)
555 {
556     int    x, y;
557     int   *isrc, *isrc2;
558     short *ssrc, *ssrc2;
559 
560     bbox->xmin = w;
561     bbox->ymin = h;
562     bbox->xmax = -1;
563     bbox->ymax = -1;
564 
565     if (bpp == 32)
566     {
567         isrc  = (int *)bmp;
568         isrc2 = (int *)bmp2;
569         span >>= 2;
570         span -= w;
571         for (y = 0; y < h; y++)
572         {
573             for (x = 0; x < w; x++)
574             {
575                 if (*isrc++ != *isrc2++)
576                 {
577                     if (x < bbox->xmin)
578                         bbox->xmin = x;
579                     if (x > bbox->xmax)
580                         bbox->xmax = x;
581                     if (y < bbox->ymin)
582                         bbox->ymin = y;
583                     if (y > bbox->ymax)
584                         bbox->ymax = y;
585                 }
586             }
587             isrc  += span;
588             isrc2 += span;
589         }
590     }
591     else
592     {
593         ssrc  = (short *)bmp;
594         ssrc2 = (short *)bmp2;
595         span >>= 1;
596         span -= w;
597         for (y = 0; y < h; y++)
598         {
599             for (x = 0; x < w; x++)
600             {
601                 if (*ssrc++ != *ssrc2++)
602                 {
603                     if (x < bbox->xmin)
604                         bbox->xmin = x;
605                     if (x > bbox->xmax)
606                         bbox->xmax = x;
607                     if (y < bbox->ymin)
608                         bbox->ymin = y;
609                     if (y > bbox->ymax)
610                         bbox->ymax = y;
611                 }
612             }
613             ssrc  += span;
614             ssrc2 += span;
615         }
616     }
617 }
618 
rediff(unsigned char * bmp,unsigned char * bmp2,int span,int bpp,BBox * bbox2)619 static void rediff(unsigned char *bmp,
620                    unsigned char *bmp2,
621                    int            span,
622                    int            bpp,
623                    BBox          *bbox2)
624 {
625     int    x, y;
626     int   *isrc, *isrc2;
627     short *ssrc, *ssrc2;
628     BBox   bbox;
629     int    w;
630     int    h;
631 
632     w = bbox2->xmax - bbox2->xmin;
633     h = bbox2->ymax - bbox2->ymin;
634     bbox.xmin = w;
635     bbox.ymin = h;
636     bbox.xmax = -1;
637     bbox.ymax = -1;
638 
639     if (bpp == 32)
640     {
641         isrc  = (int *)bmp;
642         isrc2 = (int *)bmp2;
643         span >>= 2;
644         isrc  += span*(bbox2->ymin)+bbox2->xmin;
645         isrc2 += span*(bbox2->ymin)+bbox2->xmin;
646         span -= w;
647         for (y = 0; y < h; y++)
648         {
649             for (x = 0; x < w; x++)
650             {
651                 if (*isrc++ != *isrc2++)
652                 {
653                     if (x < bbox.xmin)
654                         bbox.xmin = x;
655                     if (x > bbox.xmax)
656                         bbox.xmax = x;
657                     if (y < bbox.ymin)
658                         bbox.ymin = y;
659                     if (y > bbox.ymax)
660                         bbox.ymax = y;
661                 }
662             }
663             isrc  += span;
664             isrc2 += span;
665         }
666     }
667     else
668     {
669         ssrc  = (short *)bmp;
670         ssrc2 = (short *)bmp2;
671         ssrc  += span*(bbox2->ymin)+bbox2->xmin;
672         ssrc2 += span*(bbox2->ymin)+bbox2->xmin;
673         span >>= 1;
674         span -= w;
675         for (y = 0; y < h; y++)
676         {
677             for (x = 0; x < w; x++)
678             {
679                 if (*ssrc++ != *ssrc2++)
680                 {
681                     if (x < bbox.xmin)
682                         bbox.xmin = x;
683                     if (x > bbox.xmax)
684                         bbox.xmax = x;
685                     if (y < bbox.ymin)
686                         bbox.ymin = y;
687                     if (y > bbox.ymax)
688                         bbox.ymax = y;
689                 }
690             }
691             ssrc  += span;
692             ssrc2 += span;
693         }
694     }
695     if ((bbox.xmin > bbox.xmax) || (bbox.ymin > bbox.ymax))
696     {
697         /* No changes. Return with invalid bbox */
698         *bbox2 = bbox;
699         return;
700     }
701     bbox.xmin += bbox2->xmin;
702     bbox.ymin += bbox2->ymin;
703     bbox.xmax += bbox2->xmin;
704     bbox.ymax += bbox2->ymin;
705     bbox.xmax++;
706     bbox.ymax++;
707     if ((bbox.xmax-bbox.xmin > 0) &&
708         (bbox.xmax-bbox.xmin < MINX))
709     {
710         int d = MINX;
711 
712         if (d > w)
713             d = w;
714         d -= (bbox.xmax-bbox.xmin);
715         bbox.xmin -= d>>1;
716         bbox.xmax += d-(d>>1);
717         if (bbox.xmin < bbox2->xmin)
718         {
719             bbox.xmax += bbox2->xmin-bbox.xmin;
720             bbox.xmin  = bbox2->xmin;
721         }
722         if (bbox.xmax > bbox2->xmax)
723         {
724             bbox.xmin -= bbox.xmax-bbox2->xmax;
725             bbox.xmax  = bbox2->xmax;
726         }
727     }
728     if ((bbox.ymax-bbox.ymin > 0) && (bbox.ymax-bbox.ymin < MINY))
729     {
730         int d = MINY;
731 
732         if (d > h)
733             d = h;
734         d -= (bbox.ymax-bbox.ymin);
735         bbox.ymin -= d>>1;
736         bbox.ymax += d-(d>>1);
737         if (bbox.ymin < bbox2->ymin)
738         {
739             bbox.ymax += bbox2->ymin-bbox.ymin;
740             bbox.ymin  = bbox2->ymin;
741         }
742         if (bbox.ymax > bbox2->ymax)
743         {
744             bbox.ymin -= bbox.ymax-bbox2->ymax;
745             bbox.ymax  = bbox2->ymax;
746         }
747     }
748     *bbox2 = bbox;
749 }
750 
BBox_valid(BBox * bbox)751 static int BBox_valid(BBox *bbox)
752 {
753     return ((bbox->xmin < bbox->xmax) && (bbox->ymin < bbox->ymax));
754 }
755 
diff_bmp(unsigned char * bmp,unsigned char * bmp2,int w,int h,int span,int bpp)756 static void diff_bmp(unsigned char *bmp,
757                      unsigned char *bmp2,
758                      int            w,
759                      int            h,
760                      int            span,
761                      int            bpp)
762 {
763     int    x, y;
764     int   *isrc, *isrc2;
765     int    i, i2;
766     short *ssrc, *ssrc2;
767     short  s, s2;
768 
769     if (bpp == 32)
770     {
771         isrc  = (int *)bmp;
772         isrc2 = (int *)bmp2;
773         span >>= 2;
774         span -= w;
775         for (y = 0; y < h; y++)
776         {
777             for (x = 0; x < w; x++)
778             {
779                 i  = *isrc++;
780                 i2 = *isrc2++;
781                 if (i == i2)
782                 {
783                     int a;
784 
785                     a  =  i      & 0xFF;
786                     a += (i>> 8) & 0xFF;
787                     a += (i>>16) & 0xFF;
788                     a += 0xFF*3*2;
789                     a /= 6*2;
790 
791                     isrc[-1] = a | (a<<8) | (a<<16);
792                 }
793                 else
794                 {
795                     int r,g,b,r2,g2,b2;
796 
797                     r  = i  & 0x0000FF;
798                     g  = i  & 0x00FF00;
799                     b  = i  & 0xFF0000;
800                     r2 = i2 & 0x0000FF;
801                     g2 = i2 & 0x00FF00;
802                     b2 = i2 & 0xFF0000;
803                     if ((abs(r-r2) <= 3) && (abs(g-g2) <= 0x300) && (abs(b-b2)<= 0x30000))
804                         isrc[-1] = 0x00FF00;
805                     else
806                         isrc[-1] = 0x00FFFF;
807                 }
808             }
809             isrc  += span;
810             isrc2 += span;
811         }
812     }
813     else
814     {
815         ssrc  = (short *)bmp;
816         ssrc2 = (short *)bmp2;
817         span >>= 1;
818         span -= w;
819         for (y = 0; y < h; y++)
820         {
821             for (x = 0; x < w; x++)
822             {
823                 s  = *ssrc++;
824                 s2 = *ssrc2++;
825                 if (s == s2)
826                 {
827                     int a;
828 
829                     a  =  s      & 0x1F;
830                     a += (s>> 6) & 0x1F;
831                     a += (s>>11) & 0x1F;
832                     a += 3*0x1F*2;
833                     a /= 6*2;
834 
835                     ssrc[-1] = a | (a<<6) | ((a & 0x10)<<5) | (a<<11);
836                 }
837                 else
838                 {
839                     int r,g,b,r2,g2,b2;
840 
841                     r  =  s       & 0x1f;
842                     g  = (s >> 5) & 0x3f;
843                     b  = (s >>11) & 0x1f;
844                     r2 =  s2      & 0x1f;
845                     g2 = (s2>> 5) & 0x3f;
846                     b2 = (s2>>11) & 0x1f;
847                     if ((abs(r-r2) <= 1) && (abs(g-g2) <= 3) && (abs(b-b2)<= 1))
848                         ssrc[-1] = 0x07E0;
849                     else
850                         ssrc[-1] = 0x001F;
851                 }
852             }
853             ssrc  += span;
854             ssrc2 += span;
855         }
856     }
857 }
858 
save_meta(BBox * bbox,char * str,int w,int h,int page)859 static void save_meta(BBox *bbox, char *str, int w, int h, int page)
860 {
861     FILE *file;
862 
863     file = fopen(str, "wb");
864     if (file == NULL)
865         return;
866 
867     fprintf(file, "PW=%d\nPH=%d\nX=%d\nY=%d\nW=%d\nH=%d\nPAGE=%d\n",
868             w, h, bbox->xmin, h-bbox->ymax,
869             bbox->xmax-bbox->xmin, bbox->ymax-bbox->ymin, page);
870     fclose(file);
871 }
872 
save_bmp(unsigned char * data,BBox * bbox,int span,int bpp,char * str)873 static void save_bmp(unsigned char *data,
874                      BBox          *bbox,
875                      int            span,
876                      int            bpp,
877                      char          *str)
878 {
879     FILE *file;
880     char  bmp[14+40];
881     int   word_width;
882     int   src_bypp;
883     int   width, height;
884     int   x, y;
885 
886     file = fopen(str, "wb");
887     if (file == NULL)
888         return;
889 
890     width  = bbox->xmax - bbox->xmin;
891     height = bbox->ymax - bbox->ymin;
892 
893     src_bypp = (bpp == 16 ? 2 : 4);
894     if (bpp == 16)
895         word_width = width*2;
896     else
897         word_width = width*3;
898     word_width += 3;
899     word_width &= ~3;
900 
901     bmp[0] = 'B';
902     bmp[1] = 'M';
903     putdword(bmp+2, 14+40+word_width*height);
904     putdword(bmp+6, 0);
905     putdword(bmp+10, 14+40);
906     /* Now the bitmap header */
907     putdword(bmp+14, 40);
908     putdword(bmp+18, width);
909     putdword(bmp+22, height);
910     putword (bmp+26, 1);			/* Bit planes */
911     putword (bmp+28, (bpp == 16 ? 16 : 24));
912     putdword(bmp+30, 0);			/* Compression type */
913     putdword(bmp+34, 0);			/* Compressed size */
914     putdword(bmp+38, 354);
915     putdword(bmp+42, 354);
916     putdword(bmp+46, 0);
917     putdword(bmp+50, 0);
918 
919     fwrite(bmp, 1, 14+40, file);
920 
921     data += bbox->xmin * src_bypp;
922     data += bbox->ymin * span;
923 
924     if (bpp == 16)
925         fwrite(data, 1, span*height, file);
926     else
927     {
928         int zero = 0;
929 
930         word_width -= width*3;
931         for (y=0; y<height; y++)
932         {
933             for (x=0; x<width; x++)
934             {
935                 fwrite(data, 1, 3, file);
936                 data += 4;
937             }
938             if (word_width)
939                 fwrite(&zero, 1, word_width, file);
940             data += span-(4*width);
941         }
942     }
943     fclose(file);
944 }
945 
main(int argc,char * argv[])946 int main(int argc, char *argv[])
947 {
948     int            w,  h,  s,  bpp;
949     int            w2, h2, s2, bpp2;
950     int            nx, ny, n;
951     int            basenum;
952     int            imagecount;
953     int            maxdiffs;
954     unsigned char *bmp;
955     unsigned char *bmp2;
956     BBox           bbox, bbox2;
957     BBox          *boxlist;
958     char           str1[256];
959     char           str2[256];
960     char           str3[256];
961     char           str4[256];
962     ImageReader    image1, image2;
963 
964     if (argc < 4)
965     {
966         fprintf(stderr, "Syntax: bmpcmp <file1> <file2> <outfile_root> [<basenum>] [<maxdiffs>]\n");
967         fprintf(stderr, "  <file1> and <file2> can be bmp, ppm, pgm or pbm files.\n");
968         fprintf(stderr, "  This will produce a series of <outfile_root>.<number>.bmp files\n");
969         fprintf(stderr, "  and a series of <outfile_root>.<number>.meta files.\n");
970         fprintf(stderr, "  The maxdiffs value determines the maximum number of bitmaps\n");
971         fprintf(stderr, "  produced - 0 (or unsupplied) is taken to mean unlimited.\n");
972         exit(EXIT_FAILURE);
973     }
974 
975     if (argc > 4)
976     {
977         basenum = atoi(argv[4]);
978     }
979     else
980     {
981         basenum = 0;
982     }
983 
984     if (argc > 5)
985     {
986         maxdiffs = atoi(argv[5]);
987     }
988     else
989     {
990         maxdiffs = 0;
991     }
992 
993     image_open(&image1, argv[1]);
994     image_open(&image2, argv[2]);
995 
996     imagecount = 1;
997     while (((bmp2 = NULL,
998              bmp  = image1.read(&image1, &w,  &h,  &s,  &bpp )) != NULL) &&
999            ((bmp2 = image2.read(&image2, &w2, &h2, &s2, &bpp2)) != NULL))
1000     {
1001         /* Check images are compatible */
1002         if ((w != w2) || (h != h2) || (s != s2) || (bpp != bpp2))
1003         {
1004             fprintf(stderr,
1005                     "Can't compare images "
1006                     "(w=%d,%d) (h=%d,%d) (s=%d,%d) (bpp=%d,%d)!\n",
1007                     w, w2, h, h2, s, s2, bpp, bpp2);
1008             exit(EXIT_FAILURE);
1009         }
1010 
1011         find_changed_bbox(bmp, bmp2, w, h, s, bpp, &bbox);
1012         if ((bbox.xmin > bbox.xmax) && (bbox.ymin > bbox.ymax))
1013         {
1014             /* The script will scream for us */
1015             /* fprintf(stderr, "No differences found!\n"); */
1016             /* Unchanged */
1017             exit(EXIT_SUCCESS);
1018         }
1019         /* Make the bbox sensibly exclusive */
1020         bbox.xmax++;
1021         bbox.ymax++;
1022 
1023         /* Make bbox2.xmin/ymin be the centre of the changed area */
1024         bbox2.xmin = (bbox.xmin + bbox.xmax + 1)/2;
1025         bbox2.ymin = (bbox.ymin + bbox.ymax + 1)/2;
1026 
1027         /* Make bbox2.xmax/ymax be the width of the changed area */
1028         nx = 1;
1029         ny = 1;
1030         bbox2.xmax = bbox.xmax - bbox.xmin;
1031         if (bbox2.xmax < MINX)
1032             bbox2.xmax = MINX;
1033         if (bbox2.xmax > MAXX)
1034         {
1035             nx = 1+(bbox2.xmax/MAXX);
1036             bbox2.xmax = MAXX*nx;
1037         }
1038         bbox2.ymax = bbox.ymax - bbox.ymin;
1039         if (bbox2.ymax < MINY)
1040             bbox2.ymax = MINY;
1041         if (bbox2.ymax > MAXY)
1042         {
1043             ny = 1+(bbox2.ymax/MAXY);
1044             bbox2.ymax = MAXY*ny;
1045         }
1046 
1047         /* Now make the real bbox */
1048         bbox2.xmin -= bbox2.xmax>>1;
1049         if (bbox2.xmin < 0)
1050             bbox2.xmin = 0;
1051         bbox2.ymin -= bbox2.ymax>>1;
1052         if (bbox2.ymin < 0)
1053             bbox2.ymin = 0;
1054         bbox2.xmax += bbox2.xmin;
1055         if (bbox2.xmax > w)
1056         {
1057             bbox2.xmin -= bbox2.xmax-w;
1058             if (bbox2.xmin < 0)
1059                 bbox2.xmin = 0;
1060             bbox2.xmax = w;
1061         }
1062         bbox2.ymax += bbox2.ymin;
1063         if (bbox2.ymax > h)
1064         {
1065             bbox2.ymin -= bbox2.ymax-h;
1066             if (bbox2.ymin < 0)
1067                 bbox2.ymin = 0;
1068             bbox2.ymax = h;
1069         }
1070 
1071         /* bbox */
1072         boxlist = Malloc(sizeof(*boxlist) * nx * ny);
1073 
1074         /* Now save the changed bmps */
1075         n = basenum;
1076         boxlist--;
1077         for (w2=0; w2 < nx; w2++)
1078         {
1079             for (h2=0; h2 < ny; h2++)
1080             {
1081                 boxlist++;
1082                 boxlist->xmin = bbox2.xmin + MAXX*w2;
1083                 boxlist->xmax = boxlist->xmin + MAXX;
1084                 if (boxlist->xmax > bbox2.xmax)
1085                     boxlist->xmax = bbox2.xmax;
1086                 if (boxlist->xmin > boxlist->xmax-MINX)
1087                 {
1088                     boxlist->xmin = boxlist->xmax-MINX;
1089                     if (boxlist->xmin < 0)
1090                         boxlist->xmin = 0;
1091                 }
1092                 boxlist->ymin = bbox2.ymin + MAXY*h2;
1093                 boxlist->ymax = boxlist->ymin + MAXY;
1094                 if (boxlist->ymax > bbox2.ymax)
1095                     boxlist->ymax = bbox2.ymax;
1096                 if (boxlist->ymin > boxlist->ymax-MINY)
1097                 {
1098                     boxlist->ymin = boxlist->ymax-MINY;
1099                     if (boxlist->ymin < 0)
1100                         boxlist->ymin = 0;
1101                 }
1102                 rediff(bmp, bmp2, s, bpp, boxlist);
1103                 if (!BBox_valid(boxlist))
1104                     continue;
1105                 sprintf(str1, "%s.%05d.bmp", argv[3], n);
1106                 sprintf(str2, "%s.%05d.bmp", argv[3], n+1);
1107                 save_bmp(bmp,  boxlist, s, bpp, str1);
1108                 save_bmp(bmp2, boxlist, s, bpp, str2);
1109                 sprintf(str4, "%s.%05d.meta", argv[3], n);
1110                 save_meta(boxlist, str4, w, h, imagecount);
1111                 n += 3;
1112             }
1113         }
1114         boxlist -= nx*ny;
1115         diff_bmp(bmp, bmp2, w, h, s, bpp);
1116         n = basenum;
1117         for (w2=0; w2 < nx; w2++)
1118         {
1119             for (h2=0; h2 < ny; h2++)
1120             {
1121                 boxlist++;
1122                 if (!BBox_valid(boxlist))
1123                     continue;
1124                 sprintf(str3, "%s.%05d.bmp", argv[3], n+2);
1125                 save_bmp(bmp, boxlist, s, bpp, str3);
1126                 n += 3;
1127             }
1128         }
1129         basenum = n;
1130 
1131         boxlist -= nx*ny;
1132 	boxlist++;
1133         free(boxlist);
1134         free(bmp);
1135         free(bmp2);
1136 
1137         /* If there is a maximum set */
1138         if (maxdiffs > 0)
1139         {
1140             /* Check to see we haven't exceeded it */
1141             maxdiffs--;
1142             if (maxdiffs == 0)
1143             {
1144                 break;
1145             }
1146         }
1147     }
1148 
1149     /* If one loaded, and the other didn't - that's an error */
1150     if ((bmp2 != NULL) && (bmp == NULL))
1151     {
1152         fprintf(stderr, "Failed to load image %d from '%s'\n",
1153                 imagecount, argv[1]);
1154         exit(EXIT_FAILURE);
1155     }
1156     if ((bmp != NULL) && (bmp2 == NULL))
1157     {
1158         fprintf(stderr, "Failed to load image %d from '%s'\n",
1159                 imagecount, argv[2]);
1160         exit(EXIT_FAILURE);
1161     }
1162 
1163     image_close(&image1);
1164     image_close(&image2);
1165 
1166 
1167     return EXIT_SUCCESS;
1168 }
1169