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