1 /*
2  * bmpcmp.c: BMP Comparison - utility for use with htmldiff.pl
3  */
4 
5 /* Compile from inside ghostpdl with:
6  * gcc -Igs/libpng -Igs/zlib -o bmpcmp -DHAVE_LIBPNG gs/toolbin/bmpcmp.c gs/libpng/png.c gs/libpng/pngerror.c gs/libpng/pngget.c gs/libpng/pngmem.c gs/libpng/pngpread.c gs/libpng/pngread.c gs/libpng/pngrio.c gs/libpng/pngrtran.c gs/libpng/pngrutil.c gs/libpng/pngset.c gs/libpng/pngtrans.c gs/libpng/pngwio.c gs/libpng/pngwrite.c gs/libpng/pngwtran.c gs/libpng/pngwutil.c gs/zlib/adler32.c gs/zlib/crc32.c gs/zlib/infback.c gs/zlib/inflate.c gs/zlib/uncompr.c gs/zlib/compress.c gs/zlib/deflate.c gs/zlib/gzio.c gs/zlib/inffast.c gs/zlib/inftrees.c gs/zlib/trees.c gs/zlib/zutil.c -lm
7  */
8 
9 #include <stdio.h>
10 #include <stdlib.h>
11 #include <string.h>
12 #include <stddef.h>
13 #include <ctype.h>
14 
15 #ifdef HAVE_LIBPNG
16 #include <png.h>
17 #endif
18 
19 #define DEBUG_BBOX(A) /* do {(A);} while(0==1) */
20 
21 /* Values in map field:
22  *
23  *  0 means Completely unchanged pixel
24  *  Bit 0 set means: Pixel is not an exact 1:1 match with original.
25  *  Bit 1,2,3 set means: Pixel does not match with another within the window.
26  *
27  * In detail mode:
28  *  0 means Completely unchanged pixel
29  *  Bit 0 set means: Pixel is not an exact 1:1 match with original.
30  *  Bit 1 set means: Pixel does not match exactly with another within the
31  *                   window.
32  *  Bit 2 set means: Pixel is not a thresholded match with original.
33  *  Bit 3 set means: Pixel is not a thresholded match with another within the
34  *                   window.
35  *
36  * Colors:
37  *   0 => Greyscale  Unchanged pixel
38  *   1 => Green      Translated within window
39  *   3 => Cyan       Thresholded match (no translation)
40  *   5 => Yellow     Both Exact translated, and Thresholded match
41  *   7 => Orange     Thresholded match
42  *   15=> Red        No Match
43  */
44 
45 enum {
46     Default_MinX = 300,
47     Default_MinY = 320,
48     Default_MaxX = 600,
49     Default_MaxY = 960
50 };
51 
52 typedef struct
53 {
54     int xmin;
55     int ymin;
56     int xmax;
57     int ymax;
58 } BBox;
59 
60 typedef struct
61 {
62     /* Read from the command line */
63     int        window;
64     int        threshold;
65     int        exhaustive;
66     int        basenum;
67     int        maxdiffs;
68     char      *filename1;
69     char      *filename2;
70     char      *outroot;
71     /* Fuzzy table */
72     int        wTabLen;
73     ptrdiff_t *wTab;
74     /* Image details */
75     int        width;
76     int        height;
77     int        span;
78     int        bpp;
79     /* Output BMP sizes */
80     BBox       output_size;
81 } Params;
82 
83 typedef struct ImageReader
84 {
85     FILE *file;
86     void *(*read)(struct ImageReader *,
87                   int                *w,
88                   int                *h,
89                   int                *s,
90                   int                *bpp,
91                   int                *cmyk);
92 } ImageReader;
93 
94 /*
95  * Generic diff function type. Diff bmp and bmp2, both of geometry (width,
96  * height, span (bytes), bpp). Return the changed bbox area in bbox.
97  * Make a map of changes in map
98  */
99 typedef void (DiffFn)(unsigned char *bmp,
100                       unsigned char *bmp2,
101                       unsigned char *map,
102                       BBox          *bbox,
103                       Params        *params);
104 
105 /* Nasty (as if the rest of this code isn't!) global variables for holding
106  * spot color details. */
107 static unsigned char spots[256*4];
108 static int spotfill = 0;
109 
Malloc(size_t size)110 static void *Malloc(size_t size) {
111     void *block;
112 
113     block = malloc(size);
114     if (block == NULL) {
115         fprintf(stderr, "bmpcmp: Failed to malloc %u bytes\n", (unsigned int)size);
116         exit(EXIT_FAILURE);
117     }
118     return block;
119 }
120 
putword(unsigned char * buf,int word)121 static void putword(unsigned char *buf, int word) {
122   buf[0] = word;
123   buf[1] = (word>>8);
124 }
125 
getdword(unsigned char * bmp)126 static int getdword(unsigned char *bmp) {
127 
128   return bmp[0]+(bmp[1]<<8)+(bmp[2]<<16)+(bmp[3]<<24);
129 }
130 
getword(unsigned char * bmp)131 static int getword(unsigned char *bmp) {
132 
133   return bmp[0]+(bmp[1]<<8);
134 }
135 
putdword(unsigned char * bmp,int i)136 static void putdword(unsigned char *bmp, int i) {
137 
138   bmp[0] = i & 0xFF;
139   bmp[1] = (i>>8) & 0xFF;
140   bmp[2] = (i>>16) & 0xFF;
141   bmp[3] = (i>>24) & 0xFF;
142 }
143 
bmp_load_sub(unsigned char * bmp,int * width_ret,int * height_ret,int * span,int * bpp,int image_offset,int filelen)144 static unsigned char *bmp_load_sub(unsigned char *bmp,
145                                    int           *width_ret,
146                                    int           *height_ret,
147                                    int           *span,
148                                    int           *bpp,
149                                    int            image_offset,
150                                    int            filelen)
151 {
152   int size, src_bpp, dst_bpp, comp, xdpi, ydpi, i, x, y, cols;
153   int masks, redmask, greenmask, bluemask, col, col2;
154   int width, byte_width, word_width, height;
155   unsigned char *palette;
156   unsigned char *src, *dst;
157   int           *dst2;
158   short         *dst3;
159   int            pal[256];
160 
161   size = getdword(bmp);
162   if (size == 12) {
163     /* Windows v2 - thats fine */
164   } else if (size == 40) {
165     /* Windows v3 - also fine */
166   } else if (size == 108) {
167     /* Windows v4 - also fine */
168   } else {
169     /* Windows 8004 - god knows. Give up */
170     return NULL;
171   }
172   if (size == 12) {
173     width  = getword(bmp+4);
174     height = getword(bmp+6);
175     i      = getword(bmp+8);
176     if (i != 1) {
177       /* Single plane images only! */
178       return NULL;
179     }
180     src_bpp = getword(bmp+10);
181     comp    = 0;
182     xdpi    = 90;
183     ydpi    = 90;
184     cols    = 1<<src_bpp;
185     masks   = 0;
186     palette = bmp+14;
187   } else {
188     width  = getdword(bmp+4);
189     height = getdword(bmp+8);
190     i      = getword(bmp+12);
191     if (i != 1) {
192       /* Single plane images only! */
193       return NULL;
194     }
195     src_bpp = getword(bmp+14);
196     comp    = getdword(bmp+16);
197     cols    = getdword(bmp+32);
198     if (comp == 3) {
199       /* Windows NT */
200       redmask = getdword(bmp+40);
201       greenmask = getdword(bmp+44);
202       bluemask = getdword(bmp+48);
203       masks = 1;
204       palette = bmp+52;
205     } else {
206       if (size == 108) {
207         /* Windows 95 */
208         redmask   = getdword(bmp+40);
209         greenmask = getdword(bmp+44);
210         bluemask  = getdword(bmp+48);
211         /* ColorSpace rubbish ignored for now */
212         masks = 1;
213         palette = bmp+108;
214       } else {
215         masks = 0;
216         palette = bmp+40;
217       }
218     }
219   }
220 
221   if (src_bpp == 24)
222       src_bpp = 32;
223   dst_bpp = src_bpp;
224   if (dst_bpp <= 8)
225       dst_bpp = 32;
226 
227   /* Read the palette */
228   if (src_bpp <= 8) {
229     for (i = 0; i < (1<<dst_bpp); i++) {
230       col = getdword(palette+i*4);
231       col2  = (col & 0x0000FF)<<24;
232       col2 |= (col & 0x00FF00)<<8;
233       col2 |= (col & 0xFF0000)>>8;
234       pal[i] = col2;
235     }
236   }
237   if (image_offset <= 0) {
238     src = palette + (1<<src_bpp)*4;
239   } else {
240     src = bmp + image_offset;
241   }
242 
243   byte_width  = (width+7)>>3;
244   word_width  = width * ((dst_bpp+7)>>3);
245   word_width += 3;
246   word_width &= ~3;
247 
248   dst = Malloc(word_width * height);
249 
250   /* Now we do the actual conversion */
251   if (comp == 0) {
252     switch (src_bpp) {
253       case 1:
254         for (y = height-1; y >= 0; y--) {
255           dst2 = (int *)dst;
256           for (x = 0; x < byte_width; x++) {
257             i  = *src++;
258             *dst2++ = pal[(i   ) & 1];
259             *dst2++ = pal[(i>>1) & 1];
260             *dst2++ = pal[(i>>2) & 1];
261             *dst2++ = pal[(i>>3) & 1];
262             *dst2++ = pal[(i>>4) & 1];
263             *dst2++ = pal[(i>>5) & 1];
264             *dst2++ = pal[(i>>6) & 1];
265             *dst2++ = pal[(i>>7) & 1];
266           }
267           while (x & 3) {
268             src++;
269             x += 1;
270           }
271           dst += word_width;
272         }
273         break;
274       case 4:
275         for (y = height-1; y >= 0; y--) {
276           dst2 = (int *)dst;
277           for (x = 0; x < byte_width; x++) {
278             i  = *src++;
279             *dst2++ = pal[(i   ) & 0xF];
280             *dst2++ = pal[(i>>4) & 0xF];
281           }
282           while (x & 3) {
283             src++;
284             x += 1;
285           }
286           dst += word_width;
287         }
288         break;
289       case 8:
290         for (y = height-1; y >= 0; y--) {
291           dst2 = (int *)dst;
292           for (x = 0; x < byte_width; x++) {
293             *dst2++ = pal[*src++];
294           }
295           while (x & 3) {
296             src++;
297             x += 1;
298           }
299           dst += word_width;
300         }
301         break;
302       case 16:
303         for (y = height-1; y >= 0; y--) {
304           dst3 = (short *)dst;
305           for (x = 0; x < byte_width; x+=2) {
306             i  = (*src++);
307             i |= (*src++)<<8;
308             *dst3++ = i;
309             *dst3++ = i>>8;
310           }
311           while (x & 3) {
312             src++;
313             x += 1;
314           }
315           dst += word_width;
316         }
317         break;
318       case 32:
319         if (masks) {
320           printf("bmpcmp: Send this file to Robin, now! (32bpp with colour masks)\n");
321           free(dst);
322           return NULL;
323         } else {
324           for (y = 0; y < height; y++) {
325             dst2 = (int *)dst;
326             for (x = 0; x < width; x++) {
327               i  = (*src++);
328               i |= (*src++)<<8;
329               i |= (*src++)<<16;
330               *dst2++=i;
331             }
332             x=x*3;
333             while (x & 3) {
334               src++;
335               x += 1;
336             }
337             dst += word_width;
338           }
339         }
340         break;
341     }
342   } else {
343     printf("bmpcmp: Send this file to Robin, now! (compressed)\n");
344     free(dst);
345     return NULL;
346   }
347 
348   *span       = word_width;
349   *width_ret  = width;
350   *height_ret = height;
351   *bpp        = dst_bpp;
352 
353   return dst - word_width*height;
354 }
355 
bmp_read(ImageReader * im,int * width,int * height,int * span,int * bpp,int * cmyk)356 static void *bmp_read(ImageReader *im,
357                       int         *width,
358                       int         *height,
359                       int         *span,
360                       int         *bpp,
361                       int         *cmyk)
362 {
363     int            offset;
364     long           filelen, filepos;
365     unsigned char *data;
366     unsigned char *bmp;
367 
368     /* No CMYK bmp support */
369     *cmyk = 0;
370 
371     filepos = ftell(im->file);
372     fseek(im->file, 0, SEEK_END);
373     filelen = ftell(im->file);
374     fseek(im->file, 0, SEEK_SET);
375 
376     /* If we were at the end to start, then we'd read our one and only
377      * image. */
378     if (filepos == filelen)
379         return NULL;
380 
381     /* Load the whole lot into memory */
382     bmp = Malloc(filelen);
383     fread(bmp, 1, filelen, im->file);
384 
385     offset = getdword(bmp+10);
386     data = bmp_load_sub(bmp+14, width, height, span, bpp, offset-14, filelen);
387     free(bmp);
388     return data;
389 }
390 
skip_bytes(FILE * file,int count)391 static int skip_bytes(FILE *file, int count)
392 {
393     int c;
394     while (count--)
395     {
396         c = fgetc(file);
397         if (c == EOF)
398             return c;
399     }
400     return 0;
401 }
402 
get_int(FILE * file,int rev)403 static int get_int(FILE *file, int rev)
404 {
405     int c;
406 
407     if (rev) {
408         c  = fgetc(file)<<24;
409         c |= fgetc(file)<<16;
410         c |= fgetc(file)<<8;
411         c |= fgetc(file);
412     } else {
413         c  = fgetc(file);
414         c |= fgetc(file)<<8;
415         c |= fgetc(file)<<16;
416         c |= fgetc(file)<<24;
417     }
418     return c;
419 }
420 
get_short(FILE * file,int rev)421 static int get_short(FILE *file, int rev)
422 {
423     int c;
424 
425     if (rev) {
426         c  = fgetc(file)<<8;
427         c |= fgetc(file);
428     } else {
429         c  = fgetc(file);
430         c |= fgetc(file)<<8;
431     }
432     return c;
433 }
434 
cups_read(ImageReader * im,int * width,int * height,int * span,int * bpp,int * cmyk,int rev)435 static void *cups_read(ImageReader *im,
436                        int         *width,
437                        int         *height,
438                        int         *span,
439                        int         *bpp,
440                        int         *cmyk,
441                        int          rev)
442 {
443     unsigned char *data, *d;
444     int            c, x, y, b, bpc, bpl;
445     int            colspace;
446 
447     if (skip_bytes(im->file, 372) == EOF)
448         return NULL;
449     *width  = get_int(im->file, rev);
450     *height = get_int(im->file, rev);
451     if (skip_bytes(im->file, 4) == EOF)
452         return NULL;
453     bpc  = get_int(im->file, rev);
454     if (get_int(im->file, rev) != 1) {
455         fprintf(stderr, "bmpcmp: Only 1bpp cups files for now!\n");
456         return NULL;
457     }
458     bpl = get_int(im->file, rev);
459     if (get_int(im->file, rev) != 0) {
460         fprintf(stderr, "bmpcmp: Only chunky cups files for now!\n");
461         return NULL;
462     }
463     colspace = get_int(im->file, rev);
464     if ((colspace != 0) && (colspace != 3)) {
465         fprintf(stderr, "bmpcmp: Only gray/black cups files for now!\n");
466         return NULL;
467     }
468     if (get_int(im->file, rev) != 0) {
469         fprintf(stderr, "bmpcmp: Only uncompressed cups files for now!\n");
470         return NULL;
471     }
472     if (skip_bytes(im->file, 12) == EOF)
473         return NULL;
474     if (get_int(im->file, rev) != 1) {
475         fprintf(stderr, "bmpcmp: Only num_colors=1 cups files for now!\n");
476         return NULL;
477     }
478     if (skip_bytes(im->file, 1796-424) == EOF)
479         return NULL;
480 
481     d = data = Malloc(*width * *height * 4);
482     *span = *width * 4;
483     *bpp = 32;
484     for (y = *height; y > 0; y--) {
485         b = 0;
486         c = 0;
487         for (x = *width; x > 0; x--) {
488             b >>= 1;
489             if (b == 0) {
490                 c = fgetc(im->file);
491                 if (colspace == 0)
492                     c ^= 0xff;
493                 b = 0x80;
494             }
495             if (c & b) {
496                 *d++ = 0;
497                 *d++ = 0;
498                 *d++ = 0;
499                 *d++ = 0;
500             } else {
501                 *d++ = 255;
502                 *d++ = 255;
503                 *d++ = 255;
504                 *d++ = 0;
505             }
506         }
507         skip_bytes(im->file, bpl-((*width+7)>>3));
508     }
509 
510     /* No CMYK cups support */
511     *cmyk = 0;
512 
513     return data;
514 }
515 
cups_read_le(ImageReader * im,int * width,int * height,int * span,int * bpp,int * cmyk)516 static void *cups_read_le(ImageReader *im,
517                           int         *width,
518                           int         *height,
519                           int         *span,
520                           int         *bpp,
521                           int         *cmyk)
522 {
523     return cups_read(im, width, height, span, bpp, cmyk, 0);
524 }
525 
cups_read_be(ImageReader * im,int * width,int * height,int * span,int * bpp,int * cmyk)526 static void *cups_read_be(ImageReader *im,
527                           int         *width,
528                           int         *height,
529                           int         *span,
530                           int         *bpp,
531                           int         *cmyk)
532 {
533     return cups_read(im, width, height, span, bpp, cmyk, 1);
534 }
535 
skip_to_eol(FILE * file)536 static void skip_to_eol(FILE *file)
537 {
538     int c;
539 
540     do
541     {
542         c = fgetc(file);
543     }
544     while ((c != EOF) && (c != '\n') && (c != '\r'));
545 }
546 
get_uncommented_char(FILE * file)547 static int get_uncommented_char(FILE *file)
548 {
549     int c;
550 
551     do
552     {
553         c = fgetc(file);
554         if (c != '#')
555             return c;
556         skip_to_eol(file);
557     }
558     while (c != EOF);
559 
560     return EOF;
561 }
562 
get_pnm_num(FILE * file)563 static int get_pnm_num(FILE *file)
564 {
565     int c;
566     int val = 0;
567 
568     /* Skip over any whitespace */
569     do {
570         c = get_uncommented_char(file);
571     } while (isspace(c));
572 
573     /* Read the number */
574     while (isdigit(c))
575     {
576         val = val*10 + c - '0';
577         c = get_uncommented_char(file);
578     }
579 
580     /* assume the last c is whitespace */
581     return val;
582 }
583 
pbm_read(FILE * file,int width,int height,int maxval,unsigned char * bmp)584 static void pbm_read(FILE          *file,
585                      int            width,
586                      int            height,
587                      int            maxval,
588                      unsigned char *bmp)
589 {
590     int w;
591     int byte, mask, g;
592 
593     bmp += width*(height-1)<<2;
594 
595     for (; height>0; height--) {
596         mask = 0;
597         for (w=width; w>0; w--) {
598             if (mask == 0)
599             {
600                 byte = fgetc(file);
601                 mask = 0x80;
602             }
603             g = byte & mask;
604             if (g != 0)
605                 g = 255;
606             g=255-g;
607             mask >>= 1;
608             *bmp++ = g;
609             *bmp++ = g;
610             *bmp++ = g;
611             *bmp++ = 0;
612         }
613         bmp -= width<<3;
614     }
615 }
616 
pgm_read(FILE * file,int width,int height,int maxval,unsigned char * bmp)617 static void pgm_read(FILE          *file,
618                      int            width,
619                      int            height,
620                      int            maxval,
621                      unsigned char *bmp)
622 {
623     int w;
624     unsigned char *out;
625 
626     bmp += width*(height-1)<<2;
627 
628     if (maxval == 255)
629     {
630         for (; height>0; height--) {
631             fread(bmp, 1, width, file);
632             out  = bmp + width*4;
633             bmp += width;
634             for (w=width; w>0; w--) {
635                 int g = *--bmp;
636                 *--out = 0;
637                 *--out = g;
638                 *--out = g;
639                 *--out = g;
640             }
641             bmp -= width<<2;
642         }
643     } else if (maxval < 255) {
644         for (; height>0; height--) {
645             fread(bmp, 1, width, file);
646             out  = bmp + width*4;
647             bmp += width;
648             for (w=width; w>0; w--) {
649                 int g = (*--bmp)*255/maxval;
650                 *--out = 0;
651                 *--out = g;
652                 *--out = g;
653                 *--out = g;
654             }
655             bmp -= width<<2;
656         }
657     } else {
658         for (; height>0; height--) {
659             for (w=width; w>0; w--) {
660                 int g = ((fgetc(file)<<8) + (fgetc(file)))*255/maxval;
661                 *bmp++ = g;
662                 *bmp++ = g;
663                 *bmp++ = g;
664                 *bmp++ = 0;
665             }
666             bmp -= width<<3;
667         }
668     }
669 }
670 
ppm_read(FILE * file,int width,int height,int maxval,unsigned char * bmp)671 static void ppm_read(FILE          *file,
672                      int            width,
673                      int            height,
674                      int            maxval,
675                      unsigned char *bmp)
676 {
677     int w;
678     unsigned char *out;
679 
680     bmp += width*(height-1)<<2;
681 
682     if (maxval == 255)
683     {
684         for (; height>0; height--) {
685             fread(bmp, 1, 3*width, file);
686             out  = bmp + 4*width;
687             bmp += 3*width;
688             for (w=width; w>0; w--) {
689                 unsigned char b = *--bmp;
690                 unsigned char g = *--bmp;
691                 unsigned char r = *--bmp;
692                 *--out = 0;
693                 *--out = r;
694                 *--out = g;
695                 *--out = b;
696             }
697             bmp -= width<<2;
698         }
699     } else if (maxval < 255) {
700         for (; height>0; height--) {
701             fread(bmp, 1, 3*width, file);
702             out  = bmp + 4*width;
703             bmp += 3*width;
704             for (w=width; w>0; w--) {
705                 unsigned char b = *--bmp;
706                 unsigned char g = *--bmp;
707                 unsigned char r = *--bmp;
708                 *--out = 0;
709                 *--out = r * 255/maxval;
710                 *--out = g * 255/maxval;
711                 *--out = b * 255/maxval;
712             }
713             bmp -= width<<2;
714         }
715     } else {
716         for (; height>0; height--) {
717             for (w=width; w>0; w--) {
718                 int r = ((fgetc(file)<<8) + (fgetc(file)))*255/maxval;
719                 int g = ((fgetc(file)<<8) + (fgetc(file)))*255/maxval;
720                 int b = ((fgetc(file)<<8) + (fgetc(file)))*255/maxval;
721                 *bmp++ = b;
722                 *bmp++ = g;
723                 *bmp++ = r;
724                 *bmp++ = 0;
725             }
726             bmp -= width<<3;
727         }
728     }
729 }
730 
pam_read(FILE * file,int width,int height,int maxval,unsigned char * bmp)731 static void pam_read(FILE          *file,
732                      int            width,
733                      int            height,
734                      int            maxval,
735                      unsigned char *bmp)
736 {
737     int c,m,y,k;
738     int w;
739 
740     bmp += width*(height-1)<<2;
741 
742     if (maxval == 255)
743     {
744         for (; height>0; height--) {
745             fread(bmp, 1, 4*width, file);
746             bmp -= width<<2;
747         }
748     } else if (maxval < 255) {
749         for (; height>0; height--) {
750             fread(bmp, 1, 4*width, file);
751             for (w=width; w>0; w--) {
752                 *bmp++ = (*bmp++)*255/maxval;
753                 *bmp++ = (*bmp++)*255/maxval;
754                 *bmp++ = (*bmp++)*255/maxval;
755                 *bmp++ = (*bmp++)*255/maxval;
756             }
757             bmp -= width<<3;
758         }
759     } else {
760         for (; height>0; height--) {
761             for (w=width; w>0; w--) {
762                 c = ((fgetc(file)<<8) + (fgetc(file)))*255/maxval;
763                 m = ((fgetc(file)<<8) + (fgetc(file)))*255/maxval;
764                 y = ((fgetc(file)<<8) + (fgetc(file)))*255/maxval;
765                 k = ((fgetc(file)<<8) + (fgetc(file)))*255/maxval;
766                 *bmp++ = c;
767                 *bmp++ = m;
768                 *bmp++ = y;
769                 *bmp++ = k;
770             }
771             bmp -= width<<3;
772         }
773     }
774 }
775 
pbm_read_plain(FILE * file,int width,int height,int maxval,unsigned char * bmp)776 static void pbm_read_plain(FILE          *file,
777                            int            width,
778                            int            height,
779                            int            maxval,
780                            unsigned char *bmp)
781 {
782     int w;
783     int g;
784 
785     bmp += width*(height-1)<<2;
786 
787     for (; height>0; height--) {
788         for (w=width; w>0; w--) {
789             g = get_pnm_num(file);
790             if (g != 0)
791                 g = 255;
792             *bmp++ = g;
793             *bmp++ = g;
794             *bmp++ = g;
795             *bmp++ = 0;
796         }
797         bmp -= width<<3;
798     }
799 }
800 
pgm_read_plain(FILE * file,int width,int height,int maxval,unsigned char * bmp)801 static void pgm_read_plain(FILE          *file,
802                            int            width,
803                            int            height,
804                            int            maxval,
805                            unsigned char *bmp)
806 {
807     int w;
808 
809     bmp += width*(height-1)<<2;
810 
811     if (maxval == 255)
812     {
813         for (; height>0; height--) {
814             for (w=width; w>0; w--) {
815                 int g = get_pnm_num(file);
816                 *bmp++ = g;
817                 *bmp++ = g;
818                 *bmp++ = g;
819                 *bmp++ = 0;
820             }
821             bmp -= width<<3;
822         }
823     } else {
824         for (; height>0; height--) {
825             for (w=width; w>0; w--) {
826                 int g = get_pnm_num(file)*255/maxval;
827                 *bmp++ = g;
828                 *bmp++ = g;
829                 *bmp++ = g;
830                 *bmp++ = 0;
831             }
832             bmp -= width<<3;
833         }
834     }
835 }
836 
ppm_read_plain(FILE * file,int width,int height,int maxval,unsigned char * bmp)837 static void ppm_read_plain(FILE          *file,
838                            int            width,
839                            int            height,
840                            int            maxval,
841                            unsigned char *bmp)
842 {
843     int r,g,b;
844     int w;
845 
846     bmp += width*(height-1)<<2;
847 
848     if (maxval == 255)
849     {
850         for (; height>0; height--) {
851             for (w=width; w>0; w--) {
852                 r = get_pnm_num(file);
853                 g = get_pnm_num(file);
854                 b = get_pnm_num(file);
855                 *bmp++ = b;
856                 *bmp++ = g;
857                 *bmp++ = r;
858                 *bmp++ = 0;
859             }
860             bmp -= width<<3;
861         }
862     }
863     else
864     {
865         for (; height>0; height--) {
866             for (w=width; w>0; w--) {
867                 r = get_pnm_num(file)*255/maxval;
868                 g = get_pnm_num(file)*255/maxval;
869                 b = get_pnm_num(file)*255/maxval;
870                 *bmp++ = b;
871                 *bmp++ = g;
872                 *bmp++ = r;
873                 *bmp++ = 0;
874             }
875             bmp -= width<<3;
876         }
877     }
878 }
879 
880 /* Crap function, but does the job - assumes that if the first char matches
881  * for the rest not to match is an error */
skip_string(FILE * file,const char * string)882 static int skip_string(FILE *file, const char *string)
883 {
884     int c;
885 
886     /* Skip over any whitespace */
887     do {
888         c = get_uncommented_char(file);
889     } while (isspace(c));
890 
891     /* Read the string */
892     if (c != *string++) {
893         ungetc(c, file);
894         return 0;
895     }
896 
897     /* Skip the string */
898     while (*string) {
899         c = fgetc(file);
900         if (c != *string++) {
901             ungetc(c, file);
902             return 0;
903         }
904     }
905     return 1;
906 }
907 
pam_header_read(FILE * file,int * width,int * height,int * maxval)908 static void pam_header_read(FILE *file,
909                             int  *width,
910                             int  *height,
911                             int  *maxval)
912 {
913     while (1) {
914         if        (skip_string(file, "WIDTH")) {
915             *width = get_pnm_num(file);
916         } else if (skip_string(file, "HEIGHT")) {
917             *height = get_pnm_num(file);
918         } else if (skip_string(file, "DEPTH")) {
919             if (get_pnm_num(file) != 4) {
920                 fprintf(stderr, "bmpcmp: Only CMYK PAMs!\n");
921                 exit(1);
922             }
923         } else if (skip_string(file, "MAXVAL")) {
924             *maxval = get_pnm_num(file);
925         } else if (skip_string(file, "TUPLTYPE")) {
926             if (!skip_string(file, "CMYK")) {
927                 fprintf(stderr, "bmpcmp: Only CMYK PAMs!\n");
928                 exit(1);
929             }
930         } else if (skip_string(file, "ENDHDR")) {
931           skip_to_eol(file);
932           return;
933         } else {
934             /* Unknown header string. Just skip to the end of the line */
935             skip_to_eol(file);
936         }
937     }
938 }
939 
pnm_read(ImageReader * im,int * width,int * height,int * span,int * bpp,int * cmyk)940 static void *pnm_read(ImageReader *im,
941                       int         *width,
942                       int         *height,
943                       int         *span,
944                       int         *bpp,
945                       int         *cmyk)
946 {
947     unsigned char *bmp;
948     int            c, maxval;
949     void          (*read)(FILE *, int, int, int, unsigned char *);
950 
951     c = fgetc(im->file);
952     /* Skip over any white space before the P */
953     while ((c != 'P') && (c != EOF)) {
954         c = fgetc(im->file);
955     }
956     if (c == EOF)
957         return NULL;
958     *cmyk = 0;
959     switch (get_pnm_num(im->file))
960     {
961         case 1:
962             read = pbm_read_plain;
963             break;
964         case 2:
965             read = pgm_read_plain;
966             break;
967         case 3:
968             read = ppm_read_plain;
969             break;
970         case 4:
971             read = pbm_read;
972             break;
973         case 5:
974             read = pgm_read;
975             break;
976         case 6:
977             read = ppm_read;
978             break;
979         case 7:
980             read = pam_read;
981             *cmyk = 1;
982             break;
983         default:
984             /* Eh? */
985             fprintf(stderr, "bmpcmp: Unknown PxM format!\n");
986             return NULL;
987     }
988     if (read == pam_read) {
989         pam_header_read(im->file, width, height, &maxval);
990     } else {
991         *width  = get_pnm_num(im->file);
992         *height = get_pnm_num(im->file);
993         if (read != pbm_read)
994             maxval = get_pnm_num(im->file);
995         else
996             maxval = 1;
997     }
998 
999     *span   = *width * 4;
1000     *bpp = 32; /* We always convert to 32bpp */
1001 
1002     bmp = Malloc(*width * *height * 4);
1003 
1004     read(im->file, *width, *height, maxval, bmp);
1005     return bmp;
1006 }
1007 
1008 #ifdef HAVE_LIBPNG
png_read(ImageReader * im,int * width,int * height,int * span,int * bpp,int * cmyk)1009 static void *png_read(ImageReader *im,
1010                       int         *width,
1011                       int         *height,
1012                       int         *span,
1013                       int         *bpp,
1014                       int         *cmyk)
1015 {
1016     png_structp png;
1017     png_infop info;
1018     int stride, w, h, y, x;
1019     unsigned char *data;
1020     int expand = 0;
1021 
1022     /* There is only one image in each file */
1023     if (ftell(im->file) != 0)
1024         return NULL;
1025 
1026     png = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
1027     info = png_create_info_struct(png);
1028     if (setjmp(png_jmpbuf(png))) {
1029         fprintf(stderr, "bmpcmp: libpng failure\n");
1030         exit(EXIT_FAILURE);
1031     }
1032 
1033     png_init_io(png, im->file);
1034     png_set_sig_bytes(png, 0); /* we didn't read the signature */
1035     png_read_info(png, info);
1036 
1037     /* We only handle 8bpp GRAY and 32bpp RGBA */
1038     png_set_expand(png);
1039     png_set_packing(png);
1040     png_set_strip_16(png);
1041     if (png_get_color_type(png, info) == PNG_COLOR_TYPE_GRAY_ALPHA) {
1042         png_set_strip_alpha(png);
1043         expand = 1;
1044     } else if (png_get_color_type(png, info) == PNG_COLOR_TYPE_GRAY) {
1045         expand = 1;
1046     } else if (png_get_color_type(png, info) == PNG_COLOR_TYPE_RGB)
1047         png_set_add_alpha(png, 0xff, PNG_FILLER_AFTER);
1048 
1049     png_read_update_info(png, info);
1050 
1051     w = png_get_image_width(png, info);
1052     h = png_get_image_height(png, info);
1053     stride = png_get_rowbytes(png, info);
1054     if (expand)
1055         stride *= sizeof(int);
1056 
1057     data = malloc(h * stride);
1058     for (y = 0; y < h; y++) {
1059         unsigned char *row = data + (h - y - 1) * stride;
1060         png_read_row(png, row, NULL);
1061         if (expand) {
1062             unsigned char *dst = row + w*sizeof(int);
1063             unsigned char *src = row + w;
1064             for (x = w; x > 0; x--) {
1065                 unsigned char c = *--src;
1066                 *--dst = 0;
1067                 *--dst = c;
1068                 *--dst = c;
1069                 *--dst = c;
1070             }
1071         }
1072     }
1073 
1074     png_read_end(png, NULL);
1075     png_destroy_read_struct(&png, &info, NULL);
1076 
1077     *width = w;
1078     *height = h;
1079     *span = stride;
1080     *bpp = (stride * 8) / w;
1081     *cmyk = 0;
1082     return data;
1083 }
1084 #endif
1085 
psd_read(ImageReader * im,int * width,int * height,int * span,int * bpp,int * cmyk)1086 static void *psd_read(ImageReader *im,
1087                       int         *width,
1088                       int         *height,
1089                       int         *span,
1090                       int         *bpp,
1091                       int         *cmyk)
1092 {
1093     int c, ir_start, ir_len, w, h, n, x, y, z, i, N;
1094     unsigned char *bmp, *line, *ptr;
1095 
1096     if (feof(im->file))
1097         return NULL;
1098 
1099     /* Skip version */
1100     c = get_short(im->file, 1);
1101     if (c != 1) {
1102         fprintf(stderr, "bmpcmp: We only support v1 psd files!\n");
1103         exit(1);
1104     }
1105 
1106     /* Skip zeros */
1107     c = get_short(im->file, 1);
1108     c = get_int(im->file, 1);
1109 
1110     n = get_short(im->file, 1);
1111     *bpp = n * 8;
1112 
1113     h = *height = get_int(im->file, 1);
1114     w = *width = get_int(im->file, 1);
1115     c = get_short(im->file, 1);
1116     if (c != 8) {
1117         fprintf(stderr, "bmpcmp: We only support 8bpp psd files!\n");
1118         exit(1);
1119     }
1120     c = get_short(im->file, 1);
1121     if (c == 4) {
1122         *cmyk = 1;
1123         if (n < 4) {
1124             fprintf(stderr, "bmpcmp: Unexpected number of components (%d) in a CMYK (+spots) PSD file!\n", n);
1125             exit(1);
1126         }
1127     } else if (c == 3) {
1128         *cmyk = 0; /* RGB */
1129         if (n != 3) {
1130             fprintf(stderr, "bmpcmp: Unexpected number of components (%d) in a RGB PSD file!\n", n);
1131             exit(1);
1132         }
1133     } else if (c == 1) {
1134         *cmyk = 0; /* Greyscale */
1135         if (n != 1) {
1136             fprintf(stderr, "bmpcmp: Unexpected number of components (%d) in a Greyscale PSD file!\n", n);
1137             exit(1);
1138         }
1139     } else {
1140         fprintf(stderr, "bmpcmp: We only support Grey/RGB/CMYK psd files!\n");
1141         exit(1);
1142     }
1143 
1144     /* Skip color data section */
1145     c = get_int(im->file, 1);
1146     if (c != 0) {
1147         fprintf(stderr, "bmpcmp: Unexpected color data found!\n");
1148         exit(1);
1149     }
1150 
1151     /* Image Resources section */
1152     spotfill = 0;
1153     ir_len = get_int(im->file, 1);
1154     while (ir_len > 0)
1155     {
1156         int data_len, pad;
1157         c  = fgetc(im->file);     if (--ir_len == 0) break;
1158         c |= fgetc(im->file)<<8;  if (--ir_len == 0) break;
1159         c |= fgetc(im->file)<<16; if (--ir_len == 0) break;
1160         c |= fgetc(im->file)<<24; if (--ir_len == 0) break;
1161         /* c == 8BIM */
1162         c  = fgetc(im->file);     if (--ir_len == 0) break;
1163         c |= fgetc(im->file)<<8;  if (--ir_len == 0) break;
1164         /* Skip the padded id (which will always be 00 00) */
1165         pad  = fgetc(im->file);     if (--ir_len == 0) break;
1166         pad |= fgetc(im->file)<<8;  if (--ir_len == 0) break;
1167         /* Get the data len */
1168         data_len  = fgetc(im->file)<<24; if (--ir_len == 0) break;
1169         data_len |= fgetc(im->file)<<16; if (--ir_len == 0) break;
1170         data_len |= fgetc(im->file)<<8;  if (--ir_len == 0) break;
1171         data_len |= fgetc(im->file);     if (--ir_len == 0) break;
1172         if (c == 0x3ef) {
1173           while (data_len > 0) {
1174                 /* Read the colorspace */
1175                 c  = fgetc(im->file)<<8;  if (--ir_len == 0) break;
1176                 c |= fgetc(im->file);     if (--ir_len == 0) break;
1177                 /* We only support CMYK spots! */
1178                 if (c != 2) {
1179                     fprintf(stderr, "bmpcmp: Spot color equivalent not CMYK!\n");
1180                     exit(EXIT_FAILURE);
1181                 }
1182                 /* c == 2 = COLORSPACE = CMYK */
1183                 /* 16 bits C, 16 bits M, 16 bits Y, 16 bits K */
1184                 spots[spotfill++] = fgetc(im->file);  if (--ir_len == 0) break;
1185                 c = fgetc(im->file);  if (--ir_len == 0) break;
1186                 spots[spotfill++] = fgetc(im->file);  if (--ir_len == 0) break;
1187                 c = fgetc(im->file);  if (--ir_len == 0) break;
1188                 spots[spotfill++] = fgetc(im->file);  if (--ir_len == 0) break;
1189                 c = fgetc(im->file);  if (--ir_len == 0) break;
1190                 spots[spotfill++] = fgetc(im->file);  if (--ir_len == 0) break;
1191                 c = fgetc(im->file);  if (--ir_len == 0) break;
1192                 /* 2 bytes opacity (always seems to be 0) */
1193                 c = fgetc(im->file);  if (--ir_len == 0) break;
1194                 c = fgetc(im->file);  if (--ir_len == 0) break;
1195                 /* 1 byte 'kind' (0 = selected, 1 = protected) */
1196                 c = fgetc(im->file);  if (--ir_len == 0) break;
1197                 /* 1 byte padding */
1198                 c = fgetc(im->file);  if (--ir_len == 0) break;
1199                 data_len -= 14;
1200             }
1201         }
1202         while (data_len > 0)
1203         {
1204             c = fgetc(im->file); if (--ir_len == 0) break;
1205         }
1206     }
1207 
1208     /* Skip Layer and Mask section */
1209     c = get_int(im->file, 1);
1210     if (c != 0) {
1211         fprintf(stderr, "bmpcmp: Unexpected layer and mask section found!\n");
1212         exit(1);
1213     }
1214 
1215     /* Image data section */
1216     c = get_short(im->file, 1);
1217     if (c != 0) {
1218         fprintf(stderr, "bmpcmp: Unexpected compression method found!\n");
1219         exit(1);
1220     }
1221 
1222     N = n;
1223     if (N < 4)
1224         N = 4;
1225     *span = (w * N + 3) & ~3;
1226     bmp = Malloc(*span * h);
1227     line = Malloc(w);
1228     ptr = bmp + *span * (h-1);
1229     if (n == 1) {
1230         /* Greyscale */
1231         for (y = 0; y < h; y++)
1232         {
1233             fread(line, 1, w, im->file);
1234             for (x = 0; x < w; x++)
1235             {
1236                 unsigned char val = 255 - *line++;
1237                 *ptr++ = val;
1238                 *ptr++ = val;
1239                 *ptr++ = val;
1240                 *ptr++ = 0;
1241             }
1242             ptr -= w*N + *span;
1243             line -= w;
1244         }
1245         ptr += *span * h + 1;
1246     } else if (n == 3) {
1247         /* RGB */
1248         for (z = 0; z < n; z++)
1249         {
1250             for (y = 0; y < h; y++)
1251             {
1252                 fread(line, 1, w, im->file);
1253                 for (x = 0; x < w; x++)
1254                 {
1255                     *ptr = *line++;
1256                     ptr += N;
1257                 }
1258                 ptr -= w*N + *span;
1259                 line -= w;
1260             }
1261             ptr += *span * h + 1;
1262         }
1263         for (y = 0; y < h; y++)
1264         {
1265             for (x = 0; x < w; x++)
1266             {
1267                 *ptr = 0;
1268                 ptr += N;
1269             }
1270             ptr -= w*N + *span;
1271         }
1272         ptr += *span * h + 1;
1273     } else {
1274         /* CMYK + (maybe) spots */
1275         for (z = 0; z < n; z++)
1276         {
1277             for (y = 0; y < h; y++)
1278             {
1279                 fread(line, 1, w, im->file);
1280                 for (x = 0; x < w; x++)
1281                 {
1282                     *ptr = 255 - *line++;
1283                     ptr += n;
1284                 }
1285                 ptr -= w*n + *span;
1286                 line -= w;
1287             }
1288             ptr += *span * h + 1;
1289         }
1290     }
1291     free(line);
1292 
1293     /* Skip over any following header */
1294     if (!feof(im->file))
1295         c = fgetc(im->file);
1296     if (!feof(im->file))
1297         c = fgetc(im->file);
1298     if (!feof(im->file))
1299         c = fgetc(im->file);
1300     if (!feof(im->file))
1301         c = fgetc(im->file);
1302 
1303     return bmp;
1304 }
1305 
image_open(ImageReader * im,char * filename)1306 static void image_open(ImageReader *im,
1307                        char        *filename)
1308 {
1309     int type;
1310 
1311     im->file = fopen(filename, "rb");
1312     if (im->file == NULL) {
1313         fprintf(stderr, "bmpcmp: %s failed to open\n", filename);
1314         exit(EXIT_FAILURE);
1315     }
1316 
1317     /* Identify the filetype */
1318     type  = fgetc(im->file);
1319 
1320     if (type == 0x50) {
1321         /* Starts with a P! Must be a P?M file. */
1322         im->read = pnm_read;
1323         ungetc(0x50, im->file);
1324 #ifdef HAVE_LIBPNG
1325     } else if (type == 137) {
1326         im->read = png_read;
1327         ungetc(137, im->file);
1328 #endif
1329     } else if ((type == '3') || (type == 'R')) {
1330         type |= (fgetc(im->file)<<8);
1331         type |= (fgetc(im->file)<<16);
1332         type |= (fgetc(im->file)<<24);
1333         if (type == 0x52615333)
1334             im->read = cups_read_le;
1335         else if (type == 0x33536152)
1336             im->read = cups_read_be;
1337         else
1338             goto fail;
1339     } else {
1340         type |= (fgetc(im->file)<<8);
1341         if (type == 0x4d42) { /* BM */
1342             /* BMP format; Win v2 or above */
1343             im->read = bmp_read;
1344         } else {
1345             type |= (fgetc(im->file)<<16);
1346             type |= (fgetc(im->file)<<24);
1347             if (type == 0x53504238) { /* 8BPS */
1348                 /* PSD format */
1349                 im->read = psd_read;
1350             } else {
1351               fail:
1352                 fprintf(stderr, "bmpcmp: %s: Unrecognised image type\n", filename);
1353                 exit(EXIT_FAILURE);
1354             }
1355         }
1356     }
1357 }
1358 
image_close(ImageReader * im)1359 static void image_close(ImageReader *im)
1360 {
1361     fclose(im->file);
1362     im->file = NULL;
1363 }
1364 
simple_diff_int(unsigned char * bmp,unsigned char * bmp2,unsigned char * map,BBox * bbox2,Params * params)1365 static void simple_diff_int(unsigned char *bmp,
1366                             unsigned char *bmp2,
1367                             unsigned char *map,
1368                             BBox          *bbox2,
1369                             Params        *params)
1370 {
1371     int    x, y;
1372     int   *isrc, *isrc2;
1373     BBox   bbox;
1374     int    w    = params->width;
1375     int    h    = params->height;
1376     int    span = params->span;
1377 
1378     bbox.xmin = w;
1379     bbox.ymin = h;
1380     bbox.xmax = -1;
1381     bbox.ymax = -1;
1382 
1383     isrc  = (int *)bmp;
1384     isrc2 = (int *)bmp2;
1385     span >>= 2;
1386     span -= w;
1387     for (y = 0; y < h; y++)
1388     {
1389         for (x = 0; x < w; x++)
1390         {
1391             if (*isrc++ != *isrc2++)
1392             {
1393                 if (x < bbox.xmin)
1394                     bbox.xmin = x;
1395                 if (x > bbox.xmax)
1396                     bbox.xmax = x;
1397                 if (y < bbox.ymin)
1398                     bbox.ymin = y;
1399                 if (y > bbox.ymax)
1400                     bbox.ymax = y;
1401                 *map++ = 1;
1402             }
1403             else
1404             {
1405                 *map++ = 0;
1406             }
1407         }
1408         isrc  += span;
1409         isrc2 += span;
1410     }
1411     *bbox2 = bbox;
1412 }
1413 
simple_diff_n(unsigned char * bmp,unsigned char * bmp2,unsigned char * map,BBox * bbox2,Params * params)1414 static void simple_diff_n(unsigned char *bmp,
1415                           unsigned char *bmp2,
1416                           unsigned char *map,
1417                           BBox          *bbox2,
1418                           Params        *params)
1419 {
1420     int            x, y, z;
1421     unsigned char *src, *src2;
1422     BBox           bbox;
1423     int            w    = params->width;
1424     int            h    = params->height;
1425     int            n    = params->bpp>>3;
1426     int            span = params->span;
1427 
1428     bbox.xmin = w;
1429     bbox.ymin = h;
1430     bbox.xmax = -1;
1431     bbox.ymax = -1;
1432 
1433     src  = bmp;
1434     src2 = bmp2;
1435     span -= w*n;
1436     for (y = 0; y < h; y++)
1437     {
1438         for (x = 0; x < w; x++)
1439         {
1440             *map = 0;
1441             for (z = 0; z < n; z++)
1442             {
1443                 if (*src++ != *src2++)
1444                 {
1445                     if (x < bbox.xmin)
1446                         bbox.xmin = x;
1447                     if (x > bbox.xmax)
1448                         bbox.xmax = x;
1449                     if (y < bbox.ymin)
1450                         bbox.ymin = y;
1451                     if (y > bbox.ymax)
1452                         bbox.ymax = y;
1453                     *map = 1;
1454                 }
1455             }
1456             map++;
1457         }
1458         src  += span;
1459         src2 += span;
1460     }
1461     *bbox2 = bbox;
1462 }
1463 
simple_diff(unsigned char * bmp,unsigned char * bmp2,unsigned char * map,BBox * bbox2,Params * params)1464 static void simple_diff(unsigned char *bmp,
1465                         unsigned char *bmp2,
1466                         unsigned char *map,
1467                         BBox          *bbox2,
1468                         Params        *params)
1469 {
1470     if (params->bpp <= 32)
1471         simple_diff_int(bmp, bmp2, map, bbox2, params);
1472     else
1473         simple_diff_n(bmp, bmp2, map, bbox2, params);
1474 }
1475 
1476 typedef struct FuzzyParams FuzzyParams;
1477 
1478 struct FuzzyParams {
1479     int            window;
1480     int            threshold;
1481     int            wTabLen;
1482     ptrdiff_t     *wTab;
1483     int            width;
1484     int            height;
1485     int            span;
1486     int            num_chans;
1487     int          (*slowFn)(FuzzyParams   *self,
1488                            unsigned char *src,
1489                            unsigned char *src2,
1490                            unsigned char *map,
1491                            int            x,
1492                            int            y);
1493     int          (*fastFn)(FuzzyParams   *self,
1494                            unsigned char *src,
1495                            unsigned char *src2,
1496                            unsigned char *map);
1497 };
1498 
fuzzy_slow(FuzzyParams * fuzzy_params,unsigned char * isrc,unsigned char * isrc2,unsigned char * map,int x,int y)1499 static int fuzzy_slow(FuzzyParams   *fuzzy_params,
1500                       unsigned char *isrc,
1501                       unsigned char *isrc2,
1502                       unsigned char *map,
1503                       int            x,
1504                       int            y)
1505 {
1506     int xmin, ymin, xmax, ymax;
1507     int span, t;
1508 
1509     /* left of window = max(0, x - window) - x */
1510     xmin = - fuzzy_params->window;
1511     if (xmin < -x)
1512         xmin = -x;
1513     /* right of window = min(width, x + window) - x */
1514     xmax = fuzzy_params->window;
1515     if (xmax > fuzzy_params->width-x)
1516         xmax = fuzzy_params->width-x;
1517     /* top of window = max(0, y - window) - y */
1518     ymin = - fuzzy_params->window;
1519     if (ymin < -y)
1520         ymin = -y;
1521     /* bottom of window = min(height, y + window) - y */
1522     ymax = fuzzy_params->window;
1523     if (ymax > fuzzy_params->height-y)
1524         ymax = fuzzy_params->height-y;
1525     span = fuzzy_params->span;
1526     t    = fuzzy_params->threshold;
1527 
1528     for (y = ymin; y < ymax; y++)
1529     {
1530         for (x = xmin; x < xmax; x++)
1531         {
1532             int o = x*4+y*span;
1533             int v;
1534 
1535             v = isrc[0]-isrc2[o];
1536             if (v < 0)
1537                 v = -v;
1538             if (v > t)
1539                 continue;
1540             v = isrc[1]-isrc2[o+1];
1541             if (v < 0)
1542                 v = -v;
1543             if (v > t)
1544                 continue;
1545             v = isrc[2]-isrc2[o+2];
1546             if (v < 0)
1547                 v = -v;
1548             if (v > t)
1549                 continue;
1550             v = isrc[3]-isrc2[o+3];
1551             if (v < 0)
1552                 v = -v;
1553             if (v <= t)
1554                 return 0;
1555         }
1556     }
1557     *map |= 15;
1558     return 1;
1559 }
1560 
fuzzy_slow_exhaustive(FuzzyParams * fuzzy_params,unsigned char * isrc,unsigned char * isrc2,unsigned char * map,int x,int y)1561 static int fuzzy_slow_exhaustive(FuzzyParams   *fuzzy_params,
1562                                  unsigned char *isrc,
1563                                  unsigned char *isrc2,
1564                                  unsigned char *map,
1565                                  int            x,
1566                                  int            y)
1567 {
1568     int          xmin, ymin, xmax, ymax;
1569     int          span, t;
1570     unsigned int flags = 15;
1571     int          ret   = 1;
1572 
1573     /* left of window = max(0, x - window) - x */
1574     xmin = - fuzzy_params->window;
1575     if (xmin < -x)
1576         xmin = -x;
1577     /* right of window = min(width, x + window) - x */
1578     xmax = fuzzy_params->window;
1579     if (xmax > fuzzy_params->width-x)
1580         xmax = fuzzy_params->width-x;
1581     /* top of window = max(0, y - window) - y */
1582     ymin = - fuzzy_params->window;
1583     if (ymin < -y)
1584         ymin = -y;
1585     /* bottom of window = min(height, y + window) - y */
1586     ymax = fuzzy_params->window;
1587     if (ymax > fuzzy_params->height-y)
1588         ymax = fuzzy_params->height-y;
1589     span = fuzzy_params->span;
1590     t    = fuzzy_params->threshold;
1591 
1592     for (y = ymin; y < ymax; y++)
1593     {
1594         for (x = xmin; x < xmax; x++)
1595         {
1596             int o = x*4+y*span;
1597             int v;
1598             int exact = 1;
1599 
1600             v = isrc[0]-isrc2[o];
1601             if (v < 0)
1602                 v = -v;
1603             if (v != 0)
1604                 exact = 0;
1605             if (v > t)
1606                 continue;
1607             v = isrc[1]-isrc2[o+1];
1608             if (v < 0)
1609                 v = -v;
1610             if (v != 0)
1611                 exact = 0;
1612             if (v > t)
1613                 continue;
1614             v = isrc[2]-isrc2[o+2];
1615             if (v < 0)
1616                 v = -v;
1617             if (v != 0)
1618                 exact = 0;
1619             if (v > t)
1620                 continue;
1621             v = isrc[3]-isrc2[o+3];
1622             if (v < 0)
1623                 v = -v;
1624             if (v != 0)
1625                 exact = 0;
1626             if (v <= t) {
1627                 /* We match within the tolerance */
1628                 flags &= ~(1<<3);
1629                 if ((x | y) == 0)
1630                     flags &= ~(1<<2);
1631                 if (exact) {
1632                     *map |= 1;
1633                     return 0;
1634                 }
1635                 ret = 0;
1636             }
1637         }
1638     }
1639     *map |= flags;
1640     return ret;
1641 }
1642 
fuzzy_fast(FuzzyParams * fuzzy_params,unsigned char * isrc,unsigned char * isrc2,unsigned char * map)1643 static int fuzzy_fast(FuzzyParams   *fuzzy_params,
1644                       unsigned char *isrc,
1645                       unsigned char *isrc2,
1646                       unsigned char *map)
1647 {
1648     int        i;
1649     ptrdiff_t *wTab = fuzzy_params->wTab;
1650     int        t    = fuzzy_params->threshold;
1651 
1652     for (i = fuzzy_params->wTabLen; i > 0; i--)
1653     {
1654         int o = *wTab++;
1655         int v;
1656 
1657         v = isrc[0]-isrc2[o];
1658         if (v < 0)
1659             v = -v;
1660         if (v > t)
1661             continue;
1662         v = isrc[1]-isrc2[o+1];
1663         if (v < 0)
1664             v = -v;
1665         if (v > t)
1666             continue;
1667         v = isrc[2]-isrc2[o+2];
1668         if (v < 0)
1669             v = -v;
1670         if (v > t)
1671             continue;
1672         v = isrc[3]-isrc2[o+3];
1673         if (v < 0)
1674             v = -v;
1675         if (v <= t)
1676             return 0;
1677     }
1678     *map |= 15;
1679     return 1;
1680 }
1681 
fuzzy_fast_exhaustive(FuzzyParams * fuzzy_params,unsigned char * isrc,unsigned char * isrc2,unsigned char * map)1682 static int fuzzy_fast_exhaustive(FuzzyParams   *fuzzy_params,
1683                                  unsigned char *isrc,
1684                                  unsigned char *isrc2,
1685                                  unsigned char *map)
1686 {
1687     int            i;
1688     ptrdiff_t     *wTab  = fuzzy_params->wTab;
1689     int            t     = fuzzy_params->threshold;
1690     unsigned char  flags = 15;
1691     int            ret   = 1;
1692 
1693     for (i = fuzzy_params->wTabLen; i > 0; i--)
1694     {
1695         int o = *wTab++;
1696         int v;
1697         int exact = 1;
1698 
1699         v = isrc[0]-isrc2[o];
1700         if (v < 0)
1701             v = -v;
1702         if (v > t)
1703             continue;
1704         if (v != 0)
1705             exact = 0;
1706         v = isrc[1]-isrc2[o+1];
1707         if (v < 0)
1708             v = -v;
1709         if (v > t)
1710             continue;
1711         if (v != 0)
1712             exact = 0;
1713         v = isrc[2]-isrc2[o+2];
1714         if (v < 0)
1715             v = -v;
1716         if (v > t)
1717             continue;
1718         if (v != 0)
1719             exact = 0;
1720         v = isrc[3]-isrc2[o+3];
1721         if (v < 0)
1722             v = -v;
1723         if (v <= t) {
1724             /* We match within the tolerance */
1725             flags &= ~(1<<3);
1726             if (o == 0)
1727                 flags &= ~(1<<2);
1728             if (exact) {
1729                 *map |= 1;
1730                 return 0;
1731             }
1732             ret = 0;
1733         }
1734     }
1735     *map |= flags;
1736     return ret;
1737 }
1738 
fuzzy_diff_int(unsigned char * bmp,unsigned char * bmp2,unsigned char * map,BBox * bbox2,Params * params)1739 static void fuzzy_diff_int(unsigned char *bmp,
1740                            unsigned char *bmp2,
1741                            unsigned char *map,
1742                            BBox          *bbox2,
1743                            Params        *params)
1744 {
1745     int          x, y;
1746     int         *isrc, *isrc2;
1747     BBox         bbox;
1748     int          w, h, span;
1749     int          border;
1750     int          lb, rb, tb, bb;
1751     FuzzyParams  fuzzy_params;
1752 
1753     w = params->width;
1754     h = params->height;
1755     bbox.xmin = w;
1756     bbox.ymin = h;
1757     bbox.xmax = -1;
1758     bbox.ymax = -1;
1759     fuzzy_params.window    = params->window>>1;
1760     fuzzy_params.threshold = params->threshold;
1761     fuzzy_params.wTab      = params->wTab;
1762     fuzzy_params.wTabLen   = params->wTabLen;
1763     fuzzy_params.threshold = params->threshold;
1764     fuzzy_params.width     = params->width;
1765     fuzzy_params.height    = params->height;
1766     fuzzy_params.span      = params->span;
1767     if (params->exhaustive)
1768     {
1769         fuzzy_params.slowFn    = fuzzy_slow_exhaustive;
1770         fuzzy_params.fastFn    = fuzzy_fast_exhaustive;
1771     } else {
1772         fuzzy_params.slowFn    = fuzzy_slow;
1773         fuzzy_params.fastFn    = fuzzy_fast;
1774     }
1775     span                   = params->span;
1776 
1777     /* Figure out borders */
1778     border = params->window>>1;
1779     lb     = border;
1780     if (lb > params->width)
1781         lb = params->width;
1782     tb     = border;
1783     if (tb > params->height)
1784         tb = params->height;
1785     rb     = border;
1786     if (rb > params->width-lb)
1787         rb = params->width-lb;
1788     bb     = border;
1789     if (bb > params->height-tb)
1790         bb = params->height;
1791 
1792     isrc  = (int *)bmp;
1793     isrc2 = (int *)bmp2;
1794     span >>= 2;
1795     span -= w;
1796     for (y = 0; y < tb; y++)
1797     {
1798         for (x = 0; x < w; x++)
1799         {
1800             if (*isrc++ != *isrc2++)
1801             {
1802                 *map++ |= 1;
1803                 if (fuzzy_params.slowFn(&fuzzy_params,
1804                                         (unsigned char *)(isrc-1),
1805                                         (unsigned char *)(isrc2-1),
1806                                         map-1,
1807                                         x, y))
1808                 {
1809                     if (x < bbox.xmin)
1810                         bbox.xmin = x;
1811                     if (x > bbox.xmax)
1812                         bbox.xmax = x;
1813                     if (y < bbox.ymin)
1814                         bbox.ymin = y;
1815                     if (y > bbox.ymax)
1816                         bbox.ymax = y;
1817                 }
1818             }
1819             else
1820             {
1821                 *map++ = 0;
1822             }
1823         }
1824         isrc  += span;
1825         isrc2 += span;
1826     }
1827     for (; y < h-bb; y++)
1828     {
1829         for (x = 0; x < lb; x++)
1830         {
1831             if (*isrc++ != *isrc2++)
1832             {
1833                 *map++ |= 1;
1834                 if (fuzzy_params.slowFn(&fuzzy_params,
1835                                         (unsigned char *)(isrc-1),
1836                                         (unsigned char *)(isrc2-1),
1837                                         map-1,
1838                                         x, y))
1839                 {
1840                     if (x < bbox.xmin)
1841                         bbox.xmin = x;
1842                     if (x > bbox.xmax)
1843                         bbox.xmax = x;
1844                     if (y < bbox.ymin)
1845                         bbox.ymin = y;
1846                     if (y > bbox.ymax)
1847                         bbox.ymax = y;
1848                 }
1849             }
1850             else
1851             {
1852                 *map++ = 0;
1853             }
1854         }
1855         for (; x < w-rb; x++)
1856         {
1857             if (*isrc++ != *isrc2++)
1858             {
1859                 *map++ |= 1;
1860                 if (fuzzy_params.fastFn(&fuzzy_params,
1861                                         (unsigned char *)(isrc-1),
1862                                         (unsigned char *)(isrc2-1),
1863                                         map-1))
1864                 {
1865                     if (x < bbox.xmin)
1866                         bbox.xmin = x;
1867                     if (x > bbox.xmax)
1868                         bbox.xmax = x;
1869                     if (y < bbox.ymin)
1870                         bbox.ymin = y;
1871                     if (y > bbox.ymax)
1872                         bbox.ymax = y;
1873                 }
1874             }
1875             else
1876             {
1877                 *map++ = 0;
1878             }
1879         }
1880         for (; x < w; x++)
1881         {
1882             if (*isrc++ != *isrc2++)
1883             {
1884                 *map++ |= 1;
1885                 if (fuzzy_params.slowFn(&fuzzy_params,
1886                                         (unsigned char *)(isrc-1),
1887                                         (unsigned char *)(isrc2-1),
1888                                         map-1,
1889                                         x, y))
1890                 {
1891                     if (x < bbox.xmin)
1892                         bbox.xmin = x;
1893                     if (x > bbox.xmax)
1894                         bbox.xmax = x;
1895                     if (y < bbox.ymin)
1896                         bbox.ymin = y;
1897                     if (y > bbox.ymax)
1898                         bbox.ymax = y;
1899                 }
1900             }
1901             else
1902             {
1903                 *map++ = 0;
1904             }
1905         }
1906         isrc  += span;
1907         isrc2 += span;
1908     }
1909     for (; y < bb; y++)
1910     {
1911         for (x = 0; x < w; x++)
1912         {
1913             if (*isrc++ != *isrc2++)
1914             {
1915                 *map++ |= 1;
1916                 if (fuzzy_params.slowFn(&fuzzy_params,
1917                                         (unsigned char *)(isrc-1),
1918                                         (unsigned char *)(isrc2-1),
1919                                         map-1,
1920                                         x, y))
1921                 {
1922                     if (x < bbox.xmin)
1923                         bbox.xmin = x;
1924                     if (x > bbox.xmax)
1925                         bbox.xmax = x;
1926                     if (y < bbox.ymin)
1927                         bbox.ymin = y;
1928                     if (y > bbox.ymax)
1929                         bbox.ymax = y;
1930                 }
1931             }
1932         }
1933         isrc  += span;
1934         isrc2 += span;
1935     }
1936     *bbox2 = bbox;
1937 }
1938 
fuzzy_slow_n(FuzzyParams * fuzzy_params,unsigned char * src,unsigned char * src2,unsigned char * map,int x,int y)1939 static int fuzzy_slow_n(FuzzyParams   *fuzzy_params,
1940                         unsigned char *src,
1941                         unsigned char *src2,
1942                         unsigned char *map,
1943                         int            x,
1944                         int            y)
1945 {
1946     int xmin, ymin, xmax, ymax;
1947     int span, t, n, z;
1948 
1949     /* left of window = max(0, x - window) - x */
1950     xmin = - fuzzy_params->window;
1951     if (xmin < -x)
1952         xmin = -x;
1953     /* right of window = min(width, x + window) - x */
1954     xmax = fuzzy_params->window;
1955     if (xmax > fuzzy_params->width-x)
1956         xmax = fuzzy_params->width-x;
1957     /* top of window = max(0, y - window) - y */
1958     ymin = - fuzzy_params->window;
1959     if (ymin < -y)
1960         ymin = -y;
1961     /* bottom of window = min(height, y + window) - y */
1962     ymax = fuzzy_params->window;
1963     if (ymax > fuzzy_params->height-y)
1964         ymax = fuzzy_params->height-y;
1965     span = fuzzy_params->span;
1966     t    = fuzzy_params->threshold;
1967     n    = fuzzy_params->num_chans;
1968 
1969     for (y = ymin; y < ymax; y++)
1970     {
1971         for (x = xmin; x < xmax; x++)
1972         {
1973             int o = x*n+y*span;
1974             for (z = 0; z < n; z++)
1975             {
1976                 int v;
1977 
1978                 v = src[z]-src2[o++];
1979                 if (v < 0)
1980                     v = -v;
1981                 if (v > t)
1982                     goto too_big;
1983             }
1984             return 0;
1985           too_big:
1986             {}
1987         }
1988     }
1989     *map |= 15;
1990     return 1;
1991 }
1992 
fuzzy_slow_exhaustive_n(FuzzyParams * fuzzy_params,unsigned char * isrc,unsigned char * isrc2,unsigned char * map,int x,int y)1993 static int fuzzy_slow_exhaustive_n(FuzzyParams   *fuzzy_params,
1994                                    unsigned char *isrc,
1995                                    unsigned char *isrc2,
1996                                    unsigned char *map,
1997                                    int            x,
1998                                    int            y)
1999 {
2000     int          xmin, ymin, xmax, ymax;
2001     int          span, t, n, z;
2002     unsigned int flags = 15;
2003     int          ret   = 1;
2004 
2005     /* left of window = max(0, x - window) - x */
2006     xmin = - fuzzy_params->window;
2007     if (xmin < -x)
2008         xmin = -x;
2009     /* right of window = min(width, x + window) - x */
2010     xmax = fuzzy_params->window;
2011     if (xmax > fuzzy_params->width-x)
2012         xmax = fuzzy_params->width-x;
2013     /* top of window = max(0, y - window) - y */
2014     ymin = - fuzzy_params->window;
2015     if (ymin < -y)
2016         ymin = -y;
2017     /* bottom of window = min(height, y + window) - y */
2018     ymax = fuzzy_params->window;
2019     if (ymax > fuzzy_params->height-y)
2020         ymax = fuzzy_params->height-y;
2021     span = fuzzy_params->span;
2022     t    = fuzzy_params->threshold;
2023     n    = fuzzy_params->num_chans;
2024 
2025     for (y = ymin; y < ymax; y++)
2026     {
2027         for (x = xmin; x < xmax; x++)
2028         {
2029             int o = x*n+y*span;
2030             int exact = 1;
2031             for (z = 0; z < n; z++)
2032             {
2033                 int v;
2034 
2035                 v = isrc[z]-isrc2[o++];
2036                 if (v < 0)
2037                     v = -v;
2038                 if (v != 0)
2039                     exact = 0;
2040                 if (v > t)
2041                     goto too_big;
2042             }
2043             /* We match within the tolerance */
2044             flags &= ~(1<<3);
2045             if ((x | y) == 0)
2046                 flags &= ~(1<<2);
2047             if (exact) {
2048                 *map |= 1;
2049                 return 0;
2050             }
2051             ret = 0;
2052           too_big:
2053             {}
2054         }
2055     }
2056     *map |= flags;
2057     return ret;
2058 }
2059 
fuzzy_fast_n(FuzzyParams * fuzzy_params,unsigned char * isrc,unsigned char * isrc2,unsigned char * map)2060 static int fuzzy_fast_n(FuzzyParams   *fuzzy_params,
2061                         unsigned char *isrc,
2062                         unsigned char *isrc2,
2063                         unsigned char *map)
2064 {
2065     int        i, z;
2066     ptrdiff_t *wTab = fuzzy_params->wTab;
2067     int        t    = fuzzy_params->threshold;
2068     int        n    = fuzzy_params->num_chans;
2069 
2070     for (i = fuzzy_params->wTabLen; i > 0; i--)
2071     {
2072         int o = *wTab++;
2073         for (z = 0; z < n; z++)
2074         {
2075             int v;
2076 
2077             v = isrc[z]-isrc2[o++];
2078             if (v < 0)
2079                 v = -v;
2080             if (v > t)
2081                 goto too_big;
2082         }
2083         return 0;
2084       too_big:
2085         {}
2086     }
2087     *map |= 15;
2088     return 1;
2089 }
2090 
fuzzy_fast_exhaustive_n(FuzzyParams * fuzzy_params,unsigned char * isrc,unsigned char * isrc2,unsigned char * map)2091 static int fuzzy_fast_exhaustive_n(FuzzyParams   *fuzzy_params,
2092                                    unsigned char *isrc,
2093                                    unsigned char *isrc2,
2094                                    unsigned char *map)
2095 {
2096     int            i, z;
2097     ptrdiff_t     *wTab  = fuzzy_params->wTab;
2098     int            t     = fuzzy_params->threshold;
2099     unsigned char  flags = 15;
2100     int            ret   = 1;
2101     int            n     = fuzzy_params->num_chans;
2102 
2103     for (i = fuzzy_params->wTabLen; i > 0; i--)
2104     {
2105         int o = *wTab++;
2106         int exact = 1;
2107         for (z = 0; z < n; z++)
2108         {
2109             int v;
2110 
2111             v = isrc[z]-isrc2[o++];
2112             if (v < 0)
2113                 v = -v;
2114             if (v > t)
2115                 goto too_big;
2116             if (v != 0)
2117                 exact = 0;
2118         }
2119         /* We match within the tolerance */
2120         flags &= ~(1<<3);
2121         if (o == 0)
2122             flags &= ~(1<<2);
2123         if (exact) {
2124             *map |= 1;
2125             return 0;
2126         }
2127         ret = 0;
2128       too_big:
2129         {}
2130     }
2131     *map |= flags;
2132     return ret;
2133 }
2134 
fuzzy_diff_n(unsigned char * bmp,unsigned char * bmp2,unsigned char * map,BBox * bbox2,Params * params)2135 static void fuzzy_diff_n(unsigned char *bmp,
2136                          unsigned char *bmp2,
2137                          unsigned char *map,
2138                          BBox          *bbox2,
2139                          Params        *params)
2140 {
2141     int            x, y, z;
2142     unsigned char *src, *src2;
2143     BBox           bbox;
2144     int            w, h, span, n;
2145     int            border;
2146     int            lb, rb, tb, bb;
2147     FuzzyParams    fuzzy_params;
2148 
2149     w = params->width;
2150     h = params->height;
2151     n = params->bpp>>3;
2152     bbox.xmin = w;
2153     bbox.ymin = h;
2154     bbox.xmax = -1;
2155     bbox.ymax = -1;
2156     fuzzy_params.window    = params->window>>1;
2157     fuzzy_params.threshold = params->threshold;
2158     fuzzy_params.wTab      = params->wTab;
2159     fuzzy_params.wTabLen   = params->wTabLen;
2160     fuzzy_params.threshold = params->threshold;
2161     fuzzy_params.width     = params->width;
2162     fuzzy_params.height    = params->height;
2163     fuzzy_params.span      = params->span;
2164     fuzzy_params.num_chans = params->bpp>>3;
2165     if (params->exhaustive)
2166     {
2167         fuzzy_params.slowFn    = fuzzy_slow_exhaustive_n;
2168         fuzzy_params.fastFn    = fuzzy_fast_exhaustive_n;
2169     } else {
2170         fuzzy_params.slowFn    = fuzzy_slow_n;
2171         fuzzy_params.fastFn    = fuzzy_fast_n;
2172     }
2173     span                   = params->span;
2174 
2175     /* Figure out borders */
2176     border = params->window>>1;
2177     lb     = border;
2178     if (lb > params->width)
2179         lb = params->width;
2180     tb     = border;
2181     if (tb > params->height)
2182         tb = params->height;
2183     rb     = border;
2184     if (rb > params->width-lb)
2185         rb = params->width-lb;
2186     bb     = border;
2187     if (bb > params->height-tb)
2188         bb = params->height;
2189 
2190     src  = bmp;
2191     src2 = bmp2;
2192     span -= w * n;
2193     for (y = 0; y < tb; y++)
2194     {
2195         for (x = 0; x < w; x++)
2196         {
2197             int diff = 0;
2198             for (z = 0; z < n; z++)
2199             {
2200                 if (*src++ != *src2++)
2201                     diff = 1;
2202             }
2203             if (diff)
2204             {
2205                 *map++ |= 1;
2206                 if (fuzzy_params.slowFn(&fuzzy_params,
2207                                         src-n,
2208                                         src2-n,
2209                                         map-1,
2210                                         x, y))
2211                 {
2212                     if (x < bbox.xmin)
2213                         bbox.xmin = x;
2214                     if (x > bbox.xmax)
2215                         bbox.xmax = x;
2216                     if (y < bbox.ymin)
2217                         bbox.ymin = y;
2218                     if (y > bbox.ymax)
2219                         bbox.ymax = y;
2220                 }
2221             }
2222             else
2223             {
2224                 *map++ = 0;
2225             }
2226         }
2227         src  += span;
2228         src2 += span;
2229     }
2230     for (; y < h-bb; y++)
2231     {
2232         for (x = 0; x < lb; x++)
2233         {
2234             int diff = 0;
2235             for (z = 0; z < n; z++)
2236             {
2237                 if (*src++ != *src2++)
2238                     diff = 1;
2239             }
2240             if (diff)
2241             {
2242                 *map++ |= 1;
2243                 if (fuzzy_params.slowFn(&fuzzy_params,
2244                                         src-n,
2245                                         src2-n,
2246                                         map-1,
2247                                         x, y))
2248                 {
2249                     if (x < bbox.xmin)
2250                         bbox.xmin = x;
2251                     if (x > bbox.xmax)
2252                         bbox.xmax = x;
2253                     if (y < bbox.ymin)
2254                         bbox.ymin = y;
2255                     if (y > bbox.ymax)
2256                         bbox.ymax = y;
2257                 }
2258             }
2259             else
2260             {
2261                 *map++ = 0;
2262             }
2263         }
2264         for (; x < w-rb; x++)
2265         {
2266             int diff = 0;
2267             for (z = 0; z < n; z++)
2268             {
2269                 if (*src++ != *src2++)
2270                     diff = 1;
2271             }
2272             if (diff)
2273             {
2274                 *map++ |= 1;
2275                 if (fuzzy_params.fastFn(&fuzzy_params,
2276                                         src-n,
2277                                         src2-n,
2278                                         map-1))
2279                 {
2280                     if (x < bbox.xmin)
2281                         bbox.xmin = x;
2282                     if (x > bbox.xmax)
2283                         bbox.xmax = x;
2284                     if (y < bbox.ymin)
2285                         bbox.ymin = y;
2286                     if (y > bbox.ymax)
2287                         bbox.ymax = y;
2288                 }
2289             }
2290             else
2291             {
2292                 *map++ = 0;
2293             }
2294         }
2295         for (; x < w; x++)
2296         {
2297             int diff = 0;
2298             for (z = 0; z < n; z++)
2299             {
2300                 if (*src++ != *src2++)
2301                     diff = 1;
2302             }
2303             if (diff)
2304             {
2305                 *map++ |= 1;
2306                 if (fuzzy_params.slowFn(&fuzzy_params,
2307                                         src-n,
2308                                         src2-n,
2309                                         map-1,
2310                                         x, y))
2311                 {
2312                     if (x < bbox.xmin)
2313                         bbox.xmin = x;
2314                     if (x > bbox.xmax)
2315                         bbox.xmax = x;
2316                     if (y < bbox.ymin)
2317                         bbox.ymin = y;
2318                     if (y > bbox.ymax)
2319                         bbox.ymax = y;
2320                 }
2321             }
2322             else
2323             {
2324                 *map++ = 0;
2325             }
2326         }
2327         src  += span;
2328         src2 += span;
2329     }
2330     for (; y < bb; y++)
2331     {
2332         for (x = 0; x < w; x++)
2333         {
2334             int diff = 0;
2335             for (z = 0; z < n; z++)
2336             {
2337                 if (*src++ != *src2++)
2338                     diff = 1;
2339             }
2340             if (diff)
2341             {
2342                 *map++ |= 1;
2343                 if (fuzzy_params.slowFn(&fuzzy_params,
2344                                         src-n,
2345                                         src2-n,
2346                                         map-1,
2347                                         x, y))
2348                 {
2349                     if (x < bbox.xmin)
2350                         bbox.xmin = x;
2351                     if (x > bbox.xmax)
2352                         bbox.xmax = x;
2353                     if (y < bbox.ymin)
2354                         bbox.ymin = y;
2355                     if (y > bbox.ymax)
2356                         bbox.ymax = y;
2357                 }
2358             }
2359         }
2360         src  += span;
2361         src2 += span;
2362     }
2363     *bbox2 = bbox;
2364 }
2365 
BBox_valid(BBox * bbox)2366 static int BBox_valid(BBox *bbox)
2367 {
2368     return ((bbox->xmin < bbox->xmax) && (bbox->ymin < bbox->ymax));
2369 }
2370 
fuzzy_diff(unsigned char * bmp,unsigned char * bmp2,unsigned char * map,BBox * bbox2,Params * params)2371 static void fuzzy_diff(unsigned char *bmp,
2372                        unsigned char *bmp2,
2373                        unsigned char *map,
2374                        BBox          *bbox2,
2375                        Params        *params)
2376 {
2377     if (params->bpp <= 32)
2378         fuzzy_diff_int(bmp, bmp2, map, bbox2, params);
2379     else
2380         fuzzy_diff_n(bmp, bmp2, map, bbox2, params);
2381 }
2382 
uncmyk_bmp(unsigned char * bmp,BBox * bbox,int span)2383 static void uncmyk_bmp(unsigned char *bmp,
2384                        BBox          *bbox,
2385                        int            span)
2386 {
2387     int w, h;
2388     int x, y;
2389 
2390     bmp  += span    *(bbox->ymin)+(bbox->xmin*4);
2391     w     = bbox->xmax - bbox->xmin;
2392     h     = bbox->ymax - bbox->ymin;
2393     span -= 4*w;
2394     for (y = 0; y < h; y++)
2395     {
2396         for (x = 0; x < w; x++)
2397         {
2398             int c, m, y, k, r, g, b;
2399 
2400             c = *bmp++;
2401             m = *bmp++;
2402             y = *bmp++;
2403             k = *bmp++;
2404 
2405             r = (255-c-k);
2406             if (r < 0)
2407                 r = 0;
2408             g = (255-m-k);
2409             if (g < 0)
2410                 g = 0;
2411             b = (255-y-k);
2412             if (b < 0)
2413                 b = 0;
2414             bmp[-1] = 0;
2415             bmp[-2] = r;
2416             bmp[-3] = g;
2417             bmp[-4] = b;
2418         }
2419         bmp += span;
2420     }
2421 }
2422 
diff_bmp(unsigned char * bmp,unsigned char * map,BBox * bbox,int span,int map_span)2423 static void diff_bmp(unsigned char *bmp,
2424                      unsigned char *map,
2425                      BBox          *bbox,
2426                      int            span,
2427                      int            map_span)
2428 {
2429     int  w, h;
2430     int  x, y;
2431     int *isrc;
2432 
2433     isrc      = (int *)bmp;
2434     span    >>= 2;
2435     isrc     += span    *(bbox->ymin)+bbox->xmin;
2436     map      += map_span*(bbox->ymin)+bbox->xmin;
2437     w         = bbox->xmax - bbox->xmin;
2438     h         = bbox->ymax - bbox->ymin;
2439     span     -= w;
2440     map_span -= w;
2441     for (y = 0; y < h; y++)
2442     {
2443         for (x = 0; x < w; x++)
2444         {
2445             int m = *map++;
2446             int i = *isrc;
2447 
2448             switch (m) {
2449                 case 0:
2450                 {
2451                     /* Matching pixel - greyscale it */
2452                     int a;
2453 
2454                     a  =  i      & 0xFF;
2455                     a += (i>> 8) & 0xFF;
2456                     a += (i>>16) & 0xFF;
2457                     a /= 6*2;
2458 
2459                     *isrc++ = a | (a<<8) | (a<<16);
2460                     break;
2461                 }
2462                 case 1:
2463                     *isrc++ = 0x00FF00; /* Green */
2464                     break;
2465                 case 3:
2466                     *isrc++ = 0x00FFFF; /* Cyan */
2467                     break;
2468                 case 5:
2469                     *isrc++ = 0xFFFF00; /* Yellow */
2470                     break;
2471                 case 7:
2472                     *isrc++ = 0xFF8000; /* Orange */
2473                     break;
2474                 case 15:
2475                     *isrc++ = 0xFF0000; /* Red */
2476                     break;
2477                 default:
2478                     fprintf(stderr,
2479                             "bmpcmp: Internal error: unexpected map type %d\n", m);
2480                     isrc++;
2481                     break;
2482             }
2483         }
2484         isrc += span;
2485         map  += map_span;
2486     }
2487 }
2488 
save_meta(BBox * bbox,char * str,int w,int h,int page,int threshold,int window)2489 static void save_meta(BBox *bbox, char *str, int w, int h, int page, int threshold, int window)
2490 {
2491     FILE *file;
2492 
2493     file = fopen(str, "wb");
2494     if (file == NULL)
2495         return;
2496 
2497     fprintf(file, "PW=%d\nPH=%d\nX=%d\nY=%d\nW=%d\nH=%d\nPAGE=%d\nTHRESHOLD=%d\nWINDOW=%d\n",
2498             w, h, bbox->xmin, h-bbox->ymax,
2499             bbox->xmax-bbox->xmin, bbox->ymax-bbox->ymin, page,
2500             threshold, window);
2501     fclose(file);
2502 }
2503 
save_bmp(unsigned char * data,BBox * bbox,int span,int bpp,char * str)2504 static void save_bmp(unsigned char *data,
2505                      BBox          *bbox,
2506                      int            span,
2507                      int            bpp,
2508                      char          *str)
2509 {
2510     FILE          *file;
2511     unsigned char  bmp[14+40];
2512     int            word_width;
2513     int            src_bypp;
2514     int            width, height;
2515     int            x, y;
2516     int            n = bpp>>3;
2517 
2518     file = fopen(str, "wb");
2519     if (file == NULL)
2520         return;
2521 
2522     width  = bbox->xmax - bbox->xmin;
2523     height = bbox->ymax - bbox->ymin;
2524 
2525     src_bypp = (bpp == 16 ? 2 : 4);
2526     if (bpp == 16)
2527         word_width = width*2;
2528     else
2529         word_width = width*3;
2530     word_width += 3;
2531     word_width &= ~3;
2532 
2533     bmp[0] = 'B';
2534     bmp[1] = 'M';
2535     putdword(bmp+2, 14+40+word_width*height);
2536     putdword(bmp+6, 0);
2537     putdword(bmp+10, 14+40);
2538     /* Now the bitmap header */
2539     putdword(bmp+14, 40);
2540     putdword(bmp+18, width);
2541     putdword(bmp+22, height);
2542     putword (bmp+26, 1);                        /* Bit planes */
2543     putword (bmp+28, (bpp == 16 ? 16 : 24));
2544     putdword(bmp+30, 0);                        /* Compression type */
2545     putdword(bmp+34, 0);                        /* Compressed size */
2546     putdword(bmp+38, 354);
2547     putdword(bmp+42, 354);
2548     putdword(bmp+46, 0);
2549     putdword(bmp+50, 0);
2550 
2551     fwrite(bmp, 1, 14+40, file);
2552 
2553     data += bbox->xmin * 4;
2554     data += bbox->ymin * span;
2555 
2556     if (bpp == 16)
2557         fwrite(data, 1, span*height, file);
2558     else
2559     {
2560         int zero = 0;
2561 
2562         word_width -= width*3;
2563         for (y=0; y<height; y++)
2564         {
2565             for (x=0; x<width; x++)
2566             {
2567                 fwrite(data, 1, 3, file);
2568                 data += 4;
2569             }
2570             if (word_width)
2571                 fwrite(&zero, 1, word_width, file);
2572             data += span-(4*width);
2573         }
2574     }
2575     fclose(file);
2576 }
2577 
2578 #ifdef HAVE_LIBPNG
save_png(unsigned char * data,BBox * bbox,int span,int bpp,char * str)2579 static void save_png(unsigned char *data,
2580                      BBox          *bbox,
2581                      int            span,
2582                      int            bpp,
2583                      char          *str)
2584 {
2585     FILE *file;
2586     png_structp png;
2587     png_infop   info;
2588     png_bytep   *rows;
2589     int   word_width;
2590     int   src_bypp;
2591     int   bpc;
2592     int   width, height;
2593     int   y;
2594 
2595     file = fopen(str, "wb");
2596     if (file == NULL)
2597         return;
2598 
2599     png = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
2600     if (png == NULL) {
2601         fclose(file);
2602         return;
2603     }
2604     info = png_create_info_struct(png);
2605     if (info == NULL)
2606         /* info being set to NULL above makes this safe */
2607         goto png_cleanup;
2608 
2609     /* libpng using longjmp() for error 'callback' */
2610     if (setjmp(png_jmpbuf(png)))
2611         goto png_cleanup;
2612 
2613     /* hook the png writer up to our FILE pointer */
2614     png_init_io(png, file);
2615 
2616     /* fill out the image header */
2617     width  = bbox->xmax - bbox->xmin;
2618     height = bbox->ymax - bbox->ymin;
2619     bpc = 8; /* FIXME */
2620     png_set_IHDR(png, info, width, height, bpc, PNG_COLOR_TYPE_RGB,
2621                  PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT,
2622                  PNG_FILTER_TYPE_DEFAULT);
2623 
2624     /* fill out pointers to each row */
2625     /* we use bmp coordinates where the zero-th row is at the bottom */
2626     if (bpp == 16)
2627         src_bypp = 2;
2628     else
2629         src_bypp = 4;
2630     if (bpp == 16)
2631         word_width = width*2;
2632     else
2633         word_width = width*3;
2634     word_width += 3;
2635     word_width &= ~3;
2636     rows = malloc(sizeof(*rows)*height);
2637     if (rows == NULL)
2638         goto png_cleanup;
2639     for (y = 0; y < height; y++)
2640       rows[height - y - 1] = &data[(y + bbox->ymin)*span + bbox->xmin * src_bypp - 1];
2641     png_set_rows(png, info, rows);
2642 
2643     /* write out the image */
2644     png_write_png(png, info,
2645         PNG_TRANSFORM_STRIP_FILLER_BEFORE|
2646         PNG_TRANSFORM_BGR, NULL);
2647 
2648     free(rows);
2649 
2650 png_cleanup:
2651     png_destroy_write_struct(&png, &info);
2652     fclose(file);
2653     return;
2654 }
2655 #endif /* HAVE_LIBPNG */
2656 
syntax(void)2657 static void syntax(void)
2658 {
2659     fprintf(stderr, "Syntax: bmpcmp [options] <file1> <file2> <outfile_root> [<basenum>] [<maxdiffs>]\n");
2660     fprintf(stderr, "  -w <window> or -w<window>         window size (default=1)\n");
2661     fprintf(stderr, "                                    (good values = 1, 3, 5, 7, etc)\n");
2662     fprintf(stderr, "  -t <threshold> or -t<threshold>   threshold   (default=0)\n");
2663     fprintf(stderr, "  -e                                exhaustive search\n");
2664     fprintf(stderr, "  -o <minx> <maxx> <miny> <maxy>    Output bitmap size hints (0 for default)\n");
2665     fprintf(stderr, "  -h or --help or -?                Output this message and exit\n");
2666     fprintf(stderr, "\n");
2667     fprintf(stderr, "  <file1> and <file2> can be "
2668 #ifdef HAVE_LIBPNG
2669                     "png, "
2670 #endif /* HAVE_LIBPNG */
2671                     "bmp, ppm, pgm, pbm or pam files.\n");
2672     fprintf(stderr, "  This will produce a series of <outfile_root>.<number>.bmp files\n");
2673     fprintf(stderr, "  and a series of <outfile_root>.<number>.meta files.\n");
2674     fprintf(stderr, "  The maxdiffs value determines the maximum number of bitmaps\n");
2675     fprintf(stderr, "  produced - 0 (or unsupplied) is taken to mean unlimited.\n");
2676     fprintf(stderr, "\n");
2677     fprintf(stderr, "Examples:\n");
2678     fprintf(stderr, "  To ignore 1 pixel moves:\n");
2679     fprintf(stderr, "    bmpcmp in.pam out.pam out\\diff -w 3\n");
2680     fprintf(stderr, "  To ignore small color changes:\n");
2681     fprintf(stderr, "    bmpcmp in.pam out.pam out\\diff -t 7\n");
2682     fprintf(stderr, "  To see the types of pixel changes in a picture:\n");
2683     fprintf(stderr, "    bmpcmp in.pam out.pam out\\diff -w 3 -t 7 -e\n");
2684     exit(EXIT_FAILURE);
2685 }
2686 
parseArgs(int argc,char * argv[],Params * params)2687 static void parseArgs(int argc, char *argv[], Params *params)
2688 {
2689     int arg;
2690     int i;
2691 
2692     /* Set defaults */
2693     memset(params, 0, sizeof(*params));
2694     params->window = 1;
2695 
2696     arg = 0;
2697     i = 1;
2698     while (i < argc)
2699     {
2700         if (argv[i][0] == '-')
2701         {
2702             switch (argv[i][1]) {
2703                 case 'w':
2704                     if (argv[i][2]) {
2705                         params->window = atoi(&argv[i][2]);
2706                     } else if (++i < argc) {
2707                         params->window = atoi(argv[i]);
2708                     }
2709                     break;
2710                 case 't':
2711                     if (argv[i][2]) {
2712                         params->threshold = atoi(&argv[i][2]);
2713                     } else if (++i < argc) {
2714                         params->threshold = atoi(argv[i]);
2715                     }
2716                     break;
2717                 case 'o':
2718                     if (argc <= i+3)
2719                         syntax();
2720                     if (argv[i][2]) {
2721                         params->output_size.xmin = atoi(&argv[i][2]);
2722                         params->output_size.xmax = atoi(argv[i+1]);
2723                         params->output_size.ymin = atoi(argv[i+2]);
2724                         params->output_size.ymax = atoi(argv[i+3]);
2725                         i += 3;
2726                     } else if (argc <= i+4) {
2727                         syntax();
2728                     } else {
2729                         params->output_size.xmin = atoi(argv[++i]);
2730                         params->output_size.xmax = atoi(argv[++i]);
2731                         params->output_size.ymin = atoi(argv[++i]);
2732                         params->output_size.ymax = atoi(argv[++i]);
2733                     }
2734                     break;
2735                 case 'e':
2736                     params->exhaustive = 1;
2737                     break;
2738                 case 'h':
2739                 case '?':
2740                 case '-': /* Hack :) */
2741                     syntax();
2742                     break;
2743                 default:
2744                     syntax();
2745             }
2746         } else {
2747             switch (arg) {
2748                 case 0:
2749                     params->filename1 = argv[i];
2750                     break;
2751                 case 1:
2752                     params->filename2 = argv[i];
2753                     break;
2754                 case 2:
2755                     params->outroot = argv[i];
2756                     break;
2757                 case 3:
2758                     params->basenum = atoi(argv[i]);
2759                     break;
2760                 case 4:
2761                     params->maxdiffs = atoi(argv[i]);
2762                     break;
2763                 default:
2764                     syntax();
2765             }
2766             arg++;
2767         }
2768         i++;
2769     }
2770 
2771     if (arg < 3)
2772     {
2773         syntax();
2774     }
2775 
2776     /* Sanity check */
2777     if (params->output_size.xmin == 0)
2778         params->output_size.xmin = Default_MinX;
2779     if (params->output_size.xmax == 0)
2780         params->output_size.xmax = Default_MaxX;
2781     if (params->output_size.ymin == 0)
2782         params->output_size.ymin = Default_MinY;
2783     if (params->output_size.ymax == 0)
2784         params->output_size.ymax = Default_MaxY;
2785     if (params->output_size.xmax < params->output_size.xmin)
2786         params->output_size.xmax = params->output_size.xmin;
2787     if (params->output_size.ymax < params->output_size.ymin)
2788         params->output_size.ymax = params->output_size.ymin;
2789 }
2790 
makeWindowTable(Params * params,int span,int bpp)2791 static void makeWindowTable(Params *params, int span, int bpp)
2792 {
2793     int        x, y, i, w;
2794     ptrdiff_t *wnd;
2795 
2796     params->window |= 1;
2797     w = (params->window+1)>>1;
2798     if (params->wTab == NULL) {
2799         params->wTabLen = params->window*params->window;
2800         params->wTab    = Malloc(params->wTabLen*sizeof(ptrdiff_t));
2801     }
2802     wnd = params->wTab;
2803     *wnd++ = 0;
2804 
2805 #define OFFSET(x,y) (x*(bpp>>3)+y*span)
2806 
2807     for(i=1;i<params->window;i++) {
2808         x = i;
2809         y = 0;
2810         while (x != 0)
2811         {
2812             if ((x < w) && (y < w)) {
2813                 *wnd++ = OFFSET( x, y);
2814                 *wnd++ = OFFSET(-x,-y);
2815                 *wnd++ = OFFSET(-y, x);
2816                 *wnd++ = OFFSET( y,-x);
2817             }
2818             x--;
2819             y++;
2820         }
2821     }
2822 
2823 #undef OFFSET
2824 
2825 }
2826 
rediff(unsigned char * map,BBox * global,Params * params)2827 static void rediff(unsigned char *map,
2828                    BBox          *global,
2829                    Params        *params)
2830 {
2831     BBox  local;
2832     int   x, y;
2833     int   w, h;
2834     int   span = params->width;
2835 
2836     w = global->xmax - global->xmin;
2837     h = global->ymax - global->ymin;
2838     local.xmin = global->xmax;
2839     local.ymin = global->ymax;
2840     local.xmax = -1;
2841     local.ymax = -1;
2842 
2843     map += span*(global->ymin)+global->xmin;
2844     span -= w;
2845     for (y = 0; y < h; y++)
2846     {
2847         for (x = 0; x < w; x++)
2848         {
2849             if (*map++ != 0)
2850             {
2851                 if (x < local.xmin)
2852                     local.xmin = x;
2853                 if (x > local.xmax)
2854                     local.xmax = x;
2855                 if (y < local.ymin)
2856                     local.ymin = y;
2857                 if (y > local.ymax)
2858                     local.ymax = y;
2859             }
2860         }
2861         map += span;
2862     }
2863 
2864     local.xmin += global->xmin;
2865     local.ymin += global->ymin;
2866     local.xmax += global->xmin;
2867     local.ymax += global->ymin;
2868     local.xmax++;
2869     local.ymax++;
2870     if ((local.xmax-local.xmin > 0) &&
2871         (local.xmax-local.xmin < params->output_size.xmin))
2872     {
2873         int d = params->output_size.xmin;
2874 
2875         if (d > w)
2876             d = w;
2877         d -= (local.xmax-local.xmin);
2878         local.xmin -= d>>1;
2879         local.xmax += d-(d>>1);
2880         if (local.xmin < global->xmin)
2881         {
2882             local.xmax += global->xmin-local.xmin;
2883             local.xmin  = global->xmin;
2884         }
2885         if (local.xmax > global->xmax)
2886         {
2887             local.xmin -= local.xmax-global->xmax;
2888             local.xmax  = global->xmax;
2889         }
2890     }
2891     if ((local.ymax-local.ymin > 0) &&
2892         (local.ymax-local.ymin < params->output_size.ymin))
2893     {
2894         int d = params->output_size.ymin;
2895 
2896         if (d > h)
2897             d = h;
2898         d -= (local.ymax-local.ymin);
2899         local.ymin -= d>>1;
2900         local.ymax += d-(d>>1);
2901         if (local.ymin < global->ymin)
2902         {
2903             local.ymax += global->ymin-local.ymin;
2904             local.ymin  = global->ymin;
2905         }
2906         if (local.ymax > global->ymax)
2907         {
2908             local.ymin -= local.ymax-global->ymax;
2909             local.ymax  = global->ymax;
2910         }
2911     }
2912     *global = local;
2913 }
2914 
unspot(unsigned char * bmp,int w,int h,int span,int bpp)2915 static void unspot(unsigned char *bmp, int w, int h, int span, int bpp)
2916 {
2917     int x, y, z, n = bpp>>3;
2918     unsigned char *p = bmp;
2919 
2920     span -= w*4;
2921     n -= 4;
2922     for (y = h; y > 0; y--)
2923     {
2924         unsigned char *q = p;
2925         for (x = w; x > 0; x--)
2926         {
2927             int C = *q++;
2928             int M = *q++;
2929             int Y = *q++;
2930             int K = *q++;
2931             for (z = 0; z < n; z++)
2932             {
2933                 int v = *q++;
2934                 C += spots[4*z  ]*v/0xff;
2935                 M += spots[4*z+1]*v/0xff;
2936                 Y += spots[4*z+2]*v/0xff;
2937                 K += spots[4*z+3]*v/0xff;
2938             }
2939             if (C > 255) C = 255;
2940             if (M > 255) M = 255;
2941             if (Y > 255) Y = 255;
2942             if (K > 255) K = 255;
2943             *p++ = C;
2944             *p++ = M;
2945             *p++ = Y;
2946             *p++ = K;
2947         }
2948         p += span;
2949     }
2950 }
2951 
main(int argc,char * argv[])2952 int main(int argc, char *argv[])
2953 {
2954     int            w,  h,  s,  bpp,  cmyk;
2955     int            w2, h2, s2, bpp2, cmyk2;
2956     int            nx, ny, n;
2957     int            xstep, ystep;
2958     int            imagecount;
2959     unsigned char *bmp;
2960     unsigned char *bmp2;
2961     unsigned char *map;
2962     BBox           bbox, bbox2;
2963     BBox          *boxlist;
2964     char           str1[256];
2965     char           str2[256];
2966     char           str3[256];
2967     char           str4[256];
2968     ImageReader    image1, image2;
2969     DiffFn        *diffFn;
2970     Params         params;
2971     int            noDifferences = 1;
2972 
2973     parseArgs(argc, argv, &params);
2974     if (params.window <= 1 && params.threshold == 0) {
2975         diffFn = simple_diff;
2976     } else {
2977         diffFn = fuzzy_diff;
2978     }
2979 
2980     image_open(&image1, params.filename1);
2981     image_open(&image2, params.filename2);
2982 
2983     imagecount = 0;
2984     while (((bmp2 = NULL,
2985              bmp  = image1.read(&image1,&w, &h, &s, &bpp, &cmyk )) != NULL) &&
2986            ((bmp2 = image2.read(&image2,&w2,&h2,&s2,&bpp2,&cmyk2)) != NULL))
2987     {
2988         imagecount++;
2989         /* Check images are compatible */
2990         if ((w != w2) || (h != h2) || (s != s2) || (bpp != bpp2) ||
2991             (cmyk != cmyk2))
2992         {
2993             fprintf(stderr,
2994                     "bmpcmp: Page %d: Can't compare images "
2995                     "(w=%d,%d) (h=%d,%d) (s=%d,%d) (bpp=%d,%d) (cmyk=%d,%d)!\n",
2996                     imagecount, w, w2, h, h2, s, s2, bpp, bpp2, cmyk, cmyk2);
2997             continue;
2998         }
2999 
3000         if (params.window != 0)
3001         {
3002             makeWindowTable(&params, s, bpp);
3003         }
3004         map = Malloc(s*h*sizeof(unsigned char));
3005         memset(map, 0, s*h*sizeof(unsigned char));
3006         params.width  = w;
3007         params.height = h;
3008         params.span   = s;
3009         params.bpp    = bpp;
3010         (*diffFn)(bmp, bmp2, map, &bbox, &params);
3011         if ((bbox.xmin <= bbox.xmax) && (bbox.ymin <= bbox.ymax))
3012         {
3013             /* Make the bbox sensibly exclusive */
3014             bbox.xmax++;
3015             bbox.ymax++;
3016 
3017             DEBUG_BBOX(fprintf(stderr, "bmpcmp: Raw bbox=%d %d %d %d\n",
3018                                bbox.xmin, bbox.ymin, bbox.xmax, bbox.ymax));
3019             /* Make bbox2.xmin/ymin be the centre of the changed area */
3020             bbox2.xmin = (bbox.xmin + bbox.xmax + 1)/2;
3021             bbox2.ymin = (bbox.ymin + bbox.ymax + 1)/2;
3022 
3023             /* Calculate subdivisions of image to fit as best as possible
3024              * into our max/min sizes. */
3025             nx = 1;
3026             ny = 1;
3027             xstep = bbox.xmax - bbox.xmin;
3028             if (xstep > params.output_size.xmax)
3029             {
3030                 nx = 1+(xstep/params.output_size.xmax);
3031                 xstep = params.output_size.xmax;
3032             }
3033             if (xstep < params.output_size.xmin)
3034                 xstep = params.output_size.xmin;
3035             if (xstep*nx > w)
3036                 xstep = (w+nx-1)/nx;
3037             bbox2.xmax = xstep*nx;
3038             ystep = bbox.ymax - bbox.ymin;
3039             bbox2.ymax = ystep;
3040             if (ystep > params.output_size.ymax)
3041             {
3042                 ny = 1+(ystep/params.output_size.ymax);
3043                 ystep = params.output_size.ymax;
3044             }
3045             if (ystep < params.output_size.ymin)
3046                 ystep = params.output_size.ymin;
3047             if (ystep*ny > h)
3048                 ystep = (h+ny-1)/ny;
3049             bbox2.ymax = ystep*ny;
3050 
3051             /* Now make the real bbox */
3052             bbox2.xmin -= bbox2.xmax>>1;
3053             if (bbox2.xmin < 0)
3054                 bbox2.xmin = 0;
3055             bbox2.ymin -= bbox2.ymax>>1;
3056             if (bbox2.ymin < 0)
3057                 bbox2.ymin = 0;
3058             bbox2.xmax += bbox2.xmin;
3059             if (bbox2.xmax > w)
3060             {
3061                 bbox2.xmin -= bbox2.xmax-w;
3062                 if (bbox2.xmin < 0)
3063                     bbox2.xmin = 0;
3064                 bbox2.xmax = w;
3065             }
3066             bbox2.ymax += bbox2.ymin;
3067             if (bbox2.ymax > h)
3068             {
3069                 bbox2.ymin -= bbox2.ymax-h;
3070                 if (bbox2.ymin < 0)
3071                     bbox2.ymin = 0;
3072                 bbox2.ymax = h;
3073             }
3074 
3075             DEBUG_BBOX(fprintf(stderr, "bmpcmp: Expanded bbox=%d %d %d %d\n",
3076                                bbox2.xmin, bbox2.ymin, bbox2.xmax, bbox2.ymax));
3077 
3078             /* bbox */
3079             boxlist = Malloc(sizeof(*boxlist) * nx * ny);
3080 
3081             if (bpp >= 32)
3082             {
3083                 unspot(bmp, w, h, s, bpp);
3084                 unspot(bmp2, w, h, s, bpp);
3085             }
3086 
3087             /* Now save the changed bmps */
3088             n = params.basenum;
3089             boxlist--;
3090             for (w2=0; w2 < nx; w2++)
3091             {
3092                 for (h2=0; h2 < ny; h2++)
3093                 {
3094                     boxlist++;
3095                     boxlist->xmin = bbox2.xmin + xstep*w2;
3096                     boxlist->xmax = boxlist->xmin + xstep;
3097                     if (boxlist->xmax > bbox2.xmax)
3098                         boxlist->xmax = bbox2.xmax;
3099                     boxlist->ymin = bbox2.ymin + ystep*h2;
3100                     boxlist->ymax = boxlist->ymin + ystep;
3101                     if (boxlist->ymax > bbox2.ymax)
3102                         boxlist->ymax = bbox2.ymax;
3103                     DEBUG_BBOX(fprintf(stderr, "bmpcmp: Retesting bbox=%d %d %d %d\n",
3104                                        boxlist->xmin, boxlist->ymin,
3105                                        boxlist->xmax, boxlist->ymax));
3106 
3107                     rediff(map, boxlist, &params);
3108                     if (!BBox_valid(boxlist))
3109                         continue;
3110                     DEBUG_BBOX(fprintf(stderr, "bmpcmp: Reduced bbox=%d %d %d %d\n",
3111                                        boxlist->xmin, boxlist->ymin,
3112                                        boxlist->xmax, boxlist->ymax));
3113                     if (cmyk)
3114                     {
3115                         uncmyk_bmp(bmp,  boxlist, s);
3116                         uncmyk_bmp(bmp2, boxlist, s);
3117                     }
3118 #ifdef HAVE_LIBPNG
3119                     sprintf(str1, "%s.%05d.png", params.outroot, n);
3120                     sprintf(str2, "%s.%05d.png", params.outroot, n+1);
3121                     sprintf(str3, "%s.%05d.png", params.outroot, n+2);
3122                     save_png(bmp,  boxlist, s, bpp, str1);
3123                     save_png(bmp2, boxlist, s, bpp, str2);
3124 #else
3125                     sprintf(str1, "%s.%05d.bmp", params.outroot, n);
3126                     sprintf(str2, "%s.%05d.bmp", params.outroot, n+1);
3127                     sprintf(str3, "%s.%05d.bmp", params.outroot, n+2);
3128                     save_bmp(bmp,  boxlist, s, bpp, str1);
3129                     save_bmp(bmp2, boxlist, s, bpp, str2);
3130 #endif
3131                     diff_bmp(bmp, map, boxlist, s, w);
3132 #ifdef HAVE_LIBPNG
3133                     save_png(bmp, boxlist, s, bpp, str3);
3134 #else
3135                     save_bmp(bmp, boxlist, s, bpp, str3);
3136 #endif
3137                     sprintf(str4, "%s.%05d.meta", params.outroot, n);
3138                     save_meta(boxlist, str4, w, h, imagecount, params.threshold, params.window);
3139                     n += 3;
3140                     noDifferences = 0;
3141                     /* If there is a maximum set */
3142                     if (params.maxdiffs > 0)
3143                     {
3144                         /* Check to see we haven't exceeded it */
3145                         params.maxdiffs--;
3146                         if (params.maxdiffs == 0)
3147                         {
3148                             goto done;
3149                         }
3150                     }
3151                 }
3152             }
3153             params.basenum = n;
3154 
3155             boxlist -= nx*ny;
3156             boxlist++;
3157             free(boxlist);
3158         }
3159         free(bmp);
3160         free(bmp2);
3161         free(map);
3162     }
3163 
3164 done:
3165     /* If one loaded, and the other didn't - that's an error */
3166     if ((bmp2 != NULL) && (bmp == NULL))
3167     {
3168         fprintf(stderr, "bmpcmp: Failed to load (candidate) image %d from '%s'\n",
3169                 imagecount+1, params.filename1);
3170         exit(EXIT_FAILURE);
3171     }
3172     if ((bmp != NULL) && (bmp2 == NULL))
3173     {
3174         fprintf(stderr, "bmpcmp: Failed to load (reference) image %d from '%s'\n",
3175                 imagecount+1, params.filename2);
3176         exit(EXIT_FAILURE);
3177     }
3178 
3179     image_close(&image1);
3180     image_close(&image2);
3181 
3182     if (noDifferences == 1)
3183       fprintf(stderr, "bmpcmp: no differences detected\n");
3184 
3185     return EXIT_SUCCESS;
3186 }
3187