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