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, ¶ms);
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(¶ms, 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, ¶ms);
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, ¶ms);
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