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