ViewVC Help
View File | Revision Log | Show Annotations | Download File | Root Listing
root/radiance/ray/src/util/radcompare.c
Revision: 2.27
Committed: Thu Feb 18 23:16:35 2021 UTC (3 years, 2 months ago) by greg
Content type: text/plain
Branch: MAIN
Changes since 2.26: +4 -3 lines
Log Message:
fix: relative minimum was not being used correctly

File Contents

# User Rev Content
1 greg 2.1 #ifndef lint
2 greg 2.27 static const char RCSid[] = "$Id: radcompare.c,v 2.26 2020/07/27 16:49:56 greg Exp $";
3 greg 2.1 #endif
4     /*
5     * Compare Radiance files for significant differences
6     *
7     * G. Ward
8     */
9    
10     #include <stdlib.h>
11     #include <ctype.h>
12 greg 2.19 #include "rtmath.h"
13 greg 2.1 #include "platform.h"
14     #include "rtio.h"
15     #include "resolu.h"
16     #include "color.h"
17 greg 2.19 #include "depthcodec.h"
18     #include "normcodec.h"
19 greg 2.1 #include "lookup.h"
20     /* Reporting levels */
21     #define REP_QUIET 0 /* no reporting */
22     #define REP_ERROR 1 /* report errors only */
23     #define REP_WARN 2 /* report warnings as well */
24     #define REP_VERBOSE 3 /* verbose reporting */
25    
26     int report = REP_WARN; /* reporting level */
27    
28     int ign_header = 0; /* ignore header differences? */
29    
30     double rel_min = 1e-5; /* positive for relative comparisons */
31    
32     double rms_lim = 0.01; /* RMS difference limit */
33    
34     double max_lim = 0.25; /* difference limit if non-negative */
35    
36     int lin1cnt=0, lin2cnt=0; /* file line position */
37    
38 greg 2.26 int comment_c = '\0'; /* comment delimiter for text files */
39    
40 greg 2.6 const char nsuffix[10][3] = { /* 1st, 2nd, 3rd, etc. */
41     "th","st","nd","rd","th","th","th","th","th","th"
42     };
43     #define num_sfx(n) nsuffix[(n)%10]
44    
45 greg 2.1 /* file types */
46     const char *file_type[] = {
47     "Unrecognized",
48     "TEXT_generic",
49     "ascii",
50     COLRFMT,
51     CIEFMT,
52 greg 2.19 DEPTH16FMT,
53     NORMAL32FMT,
54 greg 2.4 "float",
55     "double",
56 greg 2.1 "BSDF_RBFmesh",
57     "Radiance_octree",
58     "Radiance_tmesh",
59 greg 2.19 "8-bit_indexed_name",
60     "16-bit_indexed_name",
61     "24-bit_indexed_name",
62 greg 2.1 "BINARY_unknown",
63     NULL /* terminator */
64     };
65 greg 2.6 /* keep consistent with above */
66 greg 2.19 enum {TYP_UNKNOWN, TYP_TEXT, TYP_ASCII, TYP_RGBE, TYP_XYZE,
67     TYP_DEPTH, TYP_NORM, TYP_FLOAT, TYP_DOUBLE,
68     TYP_RBFMESH, TYP_OCTREE, TYP_TMESH,
69     TYP_ID8, TYP_ID16, TYP_ID24, TYP_BINARY};
70 greg 2.1
71     #define has_header(t) (!( 1L<<(t) & (1L<<TYP_TEXT | 1L<<TYP_BINARY) ))
72    
73     /* header variables to always ignore */
74     const char *hdr_ignkey[] = {
75     "SOFTWARE",
76     "CAPDATE",
77     "GMT",
78 greg 2.18 "FRAME",
79 greg 2.1 NULL /* terminator */
80     };
81     /* header variable settings */
82     LUTAB hdr1 = LU_SINIT(free,free);
83     LUTAB hdr2 = LU_SINIT(free,free);
84    
85     /* advance appropriate file line count */
86     #define adv_linecnt(htp) (lin1cnt += (htp == &hdr1), \
87     lin2cnt += (htp == &hdr2))
88    
89 greg 2.12 typedef struct { /* dynamic line buffer */
90 greg 2.14 char *str;
91 greg 2.12 int len;
92     int siz;
93     } LINEBUF;
94    
95 greg 2.14 #define init_line(bp) ((bp)->str = NULL, (bp)->siz = 0)
96 greg 2.12 /* 100 MByte limit on line buffer */
97     #define MAXBUF (100L<<20)
98    
99 greg 2.1 /* input files */
100     char *progname = NULL;
101     const char stdin_name[] = "<stdin>";
102     const char *f1name=NULL, *f2name=NULL;
103     FILE *f1in=NULL, *f2in=NULL;
104 greg 2.20 int f1swap=0, f2swap=0;
105 greg 2.3
106 greg 2.1 /* running real differences */
107     double diff2sum = 0;
108 greg 2.8 long nsum = 0;
109 greg 2.1
110     /* Report usage and exit */
111     static void
112     usage()
113     {
114     fputs("Usage: ", stderr);
115     fputs(progname, stderr);
116 greg 2.26 fputs(" [-h][-c#][-s|-w|-v][-rel min_test][-rms epsilon][-max epsilon] reference test\n",
117 greg 2.1 stderr);
118 greg 2.9 exit(2);
119 greg 2.1 }
120    
121 greg 2.12 /* Read a text line, increasing buffer size as necessary */
122     static int
123     read_line(LINEBUF *bp, FILE *fp)
124     {
125 greg 2.15 static int doneWarn = 0;
126    
127 greg 2.12 bp->len = 0;
128 greg 2.14 if (!bp->str) {
129     bp->str = (char *)malloc(bp->siz = 512);
130     if (!bp->str)
131 greg 2.12 goto memerr;
132     }
133 greg 2.14 while (fgets(bp->str + bp->len, bp->siz - bp->len, fp)) {
134     bp->len += strlen(bp->str + bp->len);
135     if (bp->str[bp->len-1] == '\n')
136 greg 2.12 break; /* found EOL */
137     if (bp->len < bp->siz - 4)
138     continue; /* at EOF? */
139 greg 2.15 if (bp->siz >= MAXBUF) {
140     if ((report >= REP_WARN) & !doneWarn) {
141     fprintf(stderr,
142     "%s: warning - input line(s) past %ld MByte limit\n",
143     progname, MAXBUF>>20);
144     doneWarn++;
145     }
146     break; /* return MAXBUF partial line */
147     }
148 greg 2.12 if ((bp->siz += bp->siz/2) > MAXBUF)
149     bp->siz = MAXBUF;
150 greg 2.14 bp->str = (char *)realloc(bp->str, bp->siz);
151     if (!bp->str)
152 greg 2.12 goto memerr;
153     }
154 greg 2.26 if (comment_c) { /* elide comment? */
155     char *cp = sskip2(bp->str,0);
156     if (*cp == comment_c) {
157     *cp++ = '\n';
158     *cp = '\0';
159     bp->len = cp - bp->str;
160     }
161     }
162 greg 2.12 return(bp->len);
163     memerr:
164     fprintf(stderr,
165     "%s: out of memory in read_line() allocating %d byte buffer\n",
166     progname, bp->siz);
167     exit(2);
168     }
169    
170     /* Free line buffer */
171     static void
172     free_line(LINEBUF *bp)
173     {
174 greg 2.14 if (bp->str) free(bp->str);
175 greg 2.13 init_line(bp);
176 greg 2.12 }
177    
178 greg 2.1 /* Get type ID from name (or 0 if not found) */
179     static int
180     xlate_type(const char *nm)
181     {
182     int i;
183    
184     if (!nm || !*nm)
185     return(TYP_UNKNOWN);
186     for (i = 1; file_type[i]; i++)
187     if (!strcmp(nm, file_type[i]))
188     return(i);
189     return(TYP_UNKNOWN);
190     }
191    
192     /* Compare real values and keep track of differences */
193     static int
194     real_check(double r1, double r2)
195     {
196     double diff2 = (r1 - r2)*(r1 - r2);
197    
198     if (rel_min > 0) { /* doing relative differences? */
199     double av2 = .25*(r1*r1 + 2.*fabs(r1*r2) + r2*r2);
200 greg 2.27 if (av2 < rel_min*rel_min)
201     av2 = rel_min*rel_min;
202     diff2 /= av2;
203 greg 2.1 }
204     if (max_lim >= 0 && diff2 > max_lim*max_lim) {
205     if (report != REP_QUIET)
206 greg 2.4 printf(
207 greg 2.5 "%s: %sdifference between %.8g and %.8g exceeds epsilon of %.8g\n",
208 greg 2.1 progname,
209     (rel_min > 0) ? "relative " : "",
210 greg 2.5 r1, r2, max_lim);
211 greg 2.1 return(0);
212     }
213     diff2sum += diff2;
214     nsum++;
215     return(1);
216     }
217    
218 greg 2.8 /* Compare two color values for equivalence */
219     static int
220     color_check(COLOR c1, COLOR c2)
221     {
222     int p;
223    
224 greg 2.17 if (!real_check((colval(c1,RED)+colval(c1,GRN)+colval(c1,BLU))*(1./3.),
225     (colval(c2,RED)+colval(c2,GRN)+colval(c2,BLU))*(1./3.)))
226 greg 2.8 return(0);
227    
228     p = (colval(c1,GRN) > colval(c1,RED)) ? GRN : RED;
229     if (colval(c1,BLU) > colval(c1,p)) p = BLU;
230    
231     return(real_check(colval(c1,p), colval(c2,p)));
232     }
233    
234 greg 2.19 /* Compare two normal directions for equivalence */
235     static int
236     norm_check(FVECT nv1, FVECT nv2)
237     {
238 greg 2.22 double max2 = nv1[2]*nv2[2];
239 greg 2.19 int imax = 2;
240     int i = 2;
241     /* identify largest component */
242     while (i--) {
243 greg 2.22 double tm2 = nv1[i]*nv2[i];
244 greg 2.19 if (tm2 > max2) {
245     imax = i;
246     max2 = tm2;
247     }
248     }
249     i = 3; /* compare smaller components */
250     while (i--) {
251     if (i == imax)
252     continue;
253     if (!real_check(nv1[i], nv2[i]))
254     return(0);
255     }
256     return(1);
257     }
258    
259 greg 2.1 /* Compare two strings for equivalence */
260     static int
261     equiv_string(char *s1, char *s2)
262     {
263     #define CLS_STR 0
264     #define CLS_INT 1
265     #define CLS_FLT 2
266     /* skip whitespace at beginning */
267     while (isspace(*s1)) s1++;
268     while (isspace(*s2)) s2++;
269     while (*s1) { /* check each word */
270 greg 2.2 int inquote;
271     if (!*s2) /* unexpected EOL in s2? */
272     return(0);
273     inquote = *s1;
274 greg 2.1 if ((inquote != '\'') & (inquote != '"'))
275     inquote = 0;
276     if (inquote) { /* quoted text must match exactly */
277     if (*s1++ != *s2++)
278     return(0);
279     while (*s1 != inquote) {
280     if (!*s1)
281     return(0);
282     if (*s1++ != *s2++)
283     return(0);
284     }
285     s1++;
286     if (*s2++ != inquote)
287     return(0);
288     } else { /* else classify word type */
289     char *s1s = s1;
290     char *s2s = s2;
291     int cls = CLS_STR;
292     s1 = sskip(s1);
293     s2 = sskip(s2);
294     if (iskip(s1s) == s1) {
295     if (iskip(s2s) == s2)
296     cls = CLS_INT;
297     else if (fskip(s2s) == s2)
298     cls = CLS_FLT;
299     } else if (fskip(s1s) == s1) {
300     if (fskip(s2s) != s2)
301     return(0);
302     cls = CLS_FLT;
303     }
304     switch (cls) {
305     case CLS_INT: /* strncmp() faster */
306     case CLS_STR:
307     if (s1 - s1s != s2 - s2s)
308     return(0);
309     if (strncmp(s1s, s2s, s1 - s1s))
310     return(0);
311     break;
312     case CLS_FLT:
313     if (!real_check(atof(s1s), atof(s2s)))
314     return(0);
315     break;
316     }
317     }
318     while (isspace(*s1)) s1++;
319     while (isspace(*s2)) s2++;
320     }
321     return(!*s2); /* match if we reached the end of s2, too */
322     #undef CLS_STR
323     #undef CLS_INT
324     #undef CLS_FLT
325     }
326    
327     /* Check if string is var=value pair and set if not in ignore list */
328     static int
329     setheadvar(char *val, void *p)
330     {
331 greg 2.24 char newval[128];
332 greg 2.1 LUTAB *htp = (LUTAB *)p;
333     LUENT *tep;
334     char *key;
335     int kln, vln;
336     int n;
337    
338 greg 2.3 adv_linecnt(htp); /* side-effect is to count lines */
339 greg 2.1 if (!isalpha(*val)) /* key must start line */
340     return(0);
341 greg 2.20 /* check if we need to swap binary data */
342     if ((n = isbigendian(val)) >= 0) {
343     if (nativebigendian() == n)
344     return(0);
345     f1swap += (htp == &hdr1);
346     f2swap += (htp == &hdr2);
347     return(0);
348     }
349 greg 2.1 key = val++;
350     while (*val && !isspace(*val) & (*val != '='))
351     val++;
352     kln = val - key;
353     while (isspace(*val)) /* check for value */
354     *val++ = '\0';
355     if (*val != '=')
356     return(0);
357     *val++ = '\0';
358     while (isspace(*val))
359     val++;
360     if (!*val) /* nothing set? */
361     return(0);
362     /* check if key to ignore */
363     for (n = 0; hdr_ignkey[n]; n++)
364     if (!strcmp(key, hdr_ignkey[n]))
365     return(0);
366 greg 2.6 vln = strlen(val); /* eliminate space and newline at end */
367     while (isspace(val[--vln]))
368     ;
369     val[++vln] = '\0';
370 greg 2.1 if (!(tep = lu_find(htp, key)))
371 greg 2.4 return(-1); /* memory allocation error */
372 greg 2.1 if (!tep->key)
373     tep->key = strcpy(malloc(kln+1), key);
374 greg 2.24 if (tep->data) { /* check for special cases */
375     if (!strcmp(key, "EXPOSURE")) {
376     sprintf(newval, "%f", atof(tep->data)*atof(val));
377     vln = strlen(val = newval);
378     }
379 greg 2.1 free(tep->data);
380 greg 2.24 }
381 greg 2.1 tep->data = strcpy(malloc(vln+1), val);
382     return(1);
383     }
384    
385     /* Lookup correspondent in other header */
386     static int
387     match_val(const LUENT *ep1, void *p2)
388     {
389     const LUENT *ep2 = lu_find((LUTAB *)p2, ep1->key);
390     if (!ep2 || !ep2->data) {
391     if (report != REP_QUIET)
392 greg 2.5 printf("%s: variable '%s' missing in '%s'\n",
393 greg 2.1 progname, ep1->key, f2name);
394     return(-1);
395     }
396     if (!equiv_string((char *)ep1->data, (char *)ep2->data)) {
397 greg 2.4 if (report != REP_QUIET) {
398 greg 2.5 printf("%s: header variable '%s' has different values\n",
399 greg 2.1 progname, ep1->key);
400 greg 2.4 if (report >= REP_VERBOSE) {
401     printf("%s: %s=%s\n", f1name,
402     ep1->key, (char *)ep1->data);
403     printf("%s: %s=%s\n", f2name,
404     ep2->key, (char *)ep2->data);
405     }
406     }
407 greg 2.1 return(-1);
408     }
409     return(1); /* good match */
410     }
411    
412     /* Compare two sets of header variables */
413     static int
414 greg 2.10 headers_match()
415 greg 2.1 {
416 greg 2.10 int ne = lu_doall(&hdr1, match_val, &hdr2);
417 greg 2.1 if (ne < 0)
418     return(0); /* something didn't match! */
419 greg 2.4 /* non-fatal if second header has extra */
420 greg 2.10 if (report >= REP_WARN && (ne = lu_doall(&hdr2, NULL, NULL) - ne))
421 greg 2.5 printf("%s: warning - '%s' has %d extra header setting(s)\n",
422 greg 2.1 progname, f2name, ne);
423     return(1); /* good match */
424     }
425    
426     /* Check generic input to determine if it is binary, -1 on error */
427     static int
428     input_is_binary(FILE *fin)
429     {
430     int n = 0;
431     int c = 0;
432    
433     while ((c = getc(fin)) != EOF) {
434 greg 2.5 ++n;
435 greg 2.1 if (!c | (c > 127))
436     break; /* non-ascii character */
437 greg 2.5 if (n >= 10240)
438 greg 2.1 break; /* enough to be confident */
439     }
440     if (!n)
441     return(-1); /* first read failed */
442     if (fseek(fin, 0L, 0) < 0)
443     return(-1); /* rewind failed */
444     return(!c | (c > 127));
445     }
446    
447     /* Identify and return data type from header (if one) */
448     static int
449     identify_type(const char *name, FILE *fin, LUTAB *htp)
450     {
451     extern const char HDRSTR[];
452     int c;
453     /* check magic header start */
454     if ((c = getc(fin)) != HDRSTR[0]) {
455     if (c == EOF) goto badeof;
456     ungetc(c, fin);
457     c = 0;
458     } else if ((c = getc(fin)) != HDRSTR[1]) {
459     if (c == EOF) goto badeof;
460     ungetc(c, fin); ungetc(HDRSTR[0], fin);
461     c = 0;
462     }
463     if (c) { /* appears to have a header */
464     char sbuf[32];
465     if (!fgets(sbuf, sizeof(sbuf), fin))
466     goto badeof;
467 greg 2.3 adv_linecnt(htp); /* for #?ID string */
468 greg 2.1 if (report >= REP_WARN && strncmp(sbuf, "RADIANCE", 8)) {
469 greg 2.4 fputs(name, stdout);
470     fputs(": warning - unexpected header ID: ", stdout);
471     fputs(sbuf, stdout);
472 greg 2.1 }
473     if (getheader(fin, setheadvar, htp) < 0) {
474     fputs(name, stderr);
475 greg 2.6 fputs(": unknown error reading header\n", stderr);
476 greg 2.1 return(-1);
477     }
478     adv_linecnt(htp); /* for trailing emtpy line */
479     return(xlate_type((const char *)lu_find(htp,"FORMAT")->data));
480     }
481     c = input_is_binary(fin); /* else peek to see if binary */
482     if (c < 0) {
483     fputs(name, stderr);
484     fputs(": read/seek error\n", stderr);
485     return(-1);
486     }
487     if (c)
488     return(TYP_BINARY);
489     return(TYP_TEXT);
490     badeof:
491     if (report != REP_QUIET) {
492 greg 2.4 fputs(name, stdout);
493 greg 2.6 fputs(": unexpected end-of-file\n", stdout);
494 greg 2.1 }
495     return(-1);
496     }
497    
498     /* Check that overall RMS error is below threshold */
499     static int
500     good_RMS()
501     {
502     if (!nsum)
503     return(1);
504     if (diff2sum/(double)nsum > rms_lim*rms_lim) {
505     if (report != REP_QUIET)
506 greg 2.4 printf(
507 greg 2.5 "%s: %sRMS difference between '%s' and '%s' of %.5g exceeds limit of %.5g\n",
508 greg 2.1 progname,
509     (rel_min > 0) ? "relative " : "",
510     f1name, f2name,
511 greg 2.5 sqrt(diff2sum/(double)nsum), rms_lim);
512 greg 2.1 return(0);
513     }
514     if (report >= REP_VERBOSE)
515 greg 2.4 printf("%s: %sRMS difference of reals in '%s' and '%s' is %.5g\n",
516 greg 2.1 progname, (rel_min > 0) ? "relative " : "",
517     f1name, f2name, sqrt(diff2sum/(double)nsum));
518     return(1);
519     }
520    
521     /* Compare two inputs as generic binary files */
522     static int
523     compare_binary()
524     {
525 greg 2.5 int c1=0, c2=0;
526    
527 greg 2.1 if (report >= REP_VERBOSE) {
528 greg 2.4 fputs(progname, stdout);
529     fputs(": comparing inputs as binary\n", stdout);
530 greg 2.1 }
531     for ( ; ; ) { /* exact byte matching */
532 greg 2.5 c1 = getc(f1in);
533     c2 = getc(f2in);
534 greg 2.1 if (c1 == EOF) {
535     if (c2 == EOF)
536     return(1); /* success! */
537     if (report != REP_QUIET) {
538 greg 2.4 fputs(f1name, stdout);
539 greg 2.6 fputs(": unexpected end-of-file\n", stdout);
540 greg 2.1 }
541     return(0);
542     }
543     if (c2 == EOF) {
544     if (report != REP_QUIET) {
545 greg 2.4 fputs(f2name, stdout);
546 greg 2.6 fputs(": unexpected end-of-file\n", stdout);
547 greg 2.1 }
548     return(0);
549     }
550     if (c1 != c2)
551     break; /* quit and report difference */
552     }
553     if (report == REP_QUIET)
554     return(0);
555 greg 2.5 printf("%s: binary files '%s' and '%s' differ at byte offset %ld|%ld\n",
556 greg 2.1 progname, f1name, f2name, ftell(f1in), ftell(f2in));
557 greg 2.5 if (report >= REP_VERBOSE)
558     printf("%s: byte in '%s' is 0x%X, byte in '%s' is 0x%X\n",
559     progname, f1name, c1, f2name, c2);
560 greg 2.1 return(0);
561     }
562    
563     /* Compare two inputs as generic text files */
564     static int
565     compare_text()
566     {
567 greg 2.12 LINEBUF l1buf, l2buf;
568 greg 2.1
569     if (report >= REP_VERBOSE) {
570 greg 2.4 fputs(progname, stdout);
571 greg 2.26 fputs(": comparing inputs as ASCII text", stdout);
572     if (comment_c) {
573     fputs(", ignoring comments starting with '", stdout);
574     fputc(comment_c, stdout);
575     fputc('\'', stdout);
576     }
577     fputc('\n', stdout);
578 greg 2.1 }
579 greg 2.25 SET_FILE_TEXT(f1in); /* originally set to binary */
580     SET_FILE_TEXT(f2in);
581 greg 2.12 init_line(&l1buf); init_line(&l2buf); /* compare a line at a time */
582     while (read_line(&l1buf, f1in)) {
583     lin1cnt++;
584 greg 2.14 if (!*sskip2(l1buf.str,0))
585 greg 2.1 continue; /* ignore empty lines */
586 greg 2.12
587     while (read_line(&l2buf, f2in)) {
588     lin2cnt++;
589 greg 2.14 if (*sskip2(l2buf.str,0))
590 greg 2.1 break; /* found other non-empty line */
591     }
592 greg 2.13 if (!l2buf.len) { /* input 2 EOF? */
593 greg 2.1 if (report != REP_QUIET) {
594 greg 2.4 fputs(f2name, stdout);
595 greg 2.6 fputs(": unexpected end-of-file\n", stdout);
596 greg 2.1 }
597 greg 2.12 free_line(&l1buf); free_line(&l2buf);
598 greg 2.1 return(0);
599     }
600     /* compare non-empty lines */
601 greg 2.14 if (!equiv_string(l1buf.str, l2buf.str)) {
602 greg 2.1 if (report != REP_QUIET) {
603 greg 2.4 printf("%s: inputs '%s' and '%s' differ at line %d|%d\n",
604 greg 2.1 progname, f1name, f2name,
605     lin1cnt, lin2cnt);
606 greg 2.12 if ( report >= REP_VERBOSE &&
607     (l1buf.len < 256) &
608     (l2buf.len < 256) ) {
609 greg 2.4 fputs("------------- Mismatch -------------\n", stdout);
610 greg 2.5 printf("%s@%d:\t%s", f1name,
611 greg 2.14 lin1cnt, l1buf.str);
612 greg 2.5 printf("%s@%d:\t%s", f2name,
613 greg 2.14 lin2cnt, l2buf.str);
614 greg 2.1 }
615     }
616 greg 2.12 free_line(&l1buf); free_line(&l2buf);
617 greg 2.1 return(0);
618     }
619     }
620 greg 2.13 free_line(&l1buf); /* check for EOF on input 2 */
621 greg 2.12 while (read_line(&l2buf, f2in)) {
622 greg 2.14 if (!*sskip2(l2buf.str,0))
623 greg 2.1 continue;
624     if (report != REP_QUIET) {
625 greg 2.4 fputs(f1name, stdout);
626 greg 2.6 fputs(": unexpected end-of-file\n", stdout);
627 greg 2.1 }
628 greg 2.13 free_line(&l2buf);
629 greg 2.1 return(0);
630     }
631 greg 2.13 free_line(&l2buf);
632 greg 2.1 return(good_RMS()); /* final check for reals */
633     }
634    
635 greg 2.19 /* Check image/map resolutions */
636     static int
637     check_resolu(const char *class, RESOLU *r1p, RESOLU *r2p)
638     {
639     if (r1p->rt != r2p->rt) {
640     if (report != REP_QUIET)
641     printf(
642     "%s: %ss '%s' and '%s' have different pixel ordering\n",
643     progname, class, f1name, f2name);
644     return(0);
645     }
646     if ((r1p->xr != r2p->xr) | (r1p->yr != r2p->yr)) {
647     if (report != REP_QUIET)
648     printf(
649     "%s: %ss '%s' and '%s' are different sizes\n",
650     progname, class, f1name, f2name);
651     return(0);
652     }
653     return(1);
654     }
655    
656 greg 2.1 /* Compare two inputs that are known to be RGBE or XYZE images */
657     static int
658     compare_hdr()
659     {
660     RESOLU rs1, rs2;
661     COLOR *scan1, *scan2;
662     int x, y;
663    
664 greg 2.6 if (report >= REP_VERBOSE) {
665     fputs(progname, stdout);
666     fputs(": comparing inputs as HDR images\n", stdout);
667     }
668 greg 2.1 fgetsresolu(&rs1, f1in);
669     fgetsresolu(&rs2, f2in);
670 greg 2.19 if (!check_resolu("HDR image", &rs1, &rs2))
671 greg 2.1 return(0);
672     scan1 = (COLOR *)malloc(scanlen(&rs1)*sizeof(COLOR));
673     scan2 = (COLOR *)malloc(scanlen(&rs2)*sizeof(COLOR));
674     if (!scan1 | !scan2) {
675     fprintf(stderr, "%s: out of memory in compare_hdr()\n", progname);
676     exit(2);
677     }
678     for (y = 0; y < numscans(&rs1); y++) {
679     if ((freadscan(scan1, scanlen(&rs1), f1in) < 0) |
680     (freadscan(scan2, scanlen(&rs2), f2in) < 0)) {
681     if (report != REP_QUIET)
682 greg 2.6 printf("%s: unexpected end-of-file\n",
683 greg 2.1 progname);
684     free(scan1);
685     free(scan2);
686     return(0);
687     }
688 greg 2.5 for (x = 0; x < scanlen(&rs1); x++) {
689 greg 2.8 if (color_check(scan1[x], scan2[x]))
690 greg 2.1 continue;
691     if (report != REP_QUIET) {
692 greg 2.4 printf(
693 greg 2.1 "%s: pixels at scanline %d offset %d differ\n",
694     progname, y, x);
695     if (report >= REP_VERBOSE) {
696 greg 2.4 printf("%s: (R,G,B)=(%g,%g,%g)\n",
697 greg 2.1 f1name, colval(scan1[x],RED),
698     colval(scan1[x],GRN),
699     colval(scan1[x],BLU));
700 greg 2.4 printf("%s: (R,G,B)=(%g,%g,%g)\n",
701 greg 2.1 f2name, colval(scan2[x],RED),
702     colval(scan2[x],GRN),
703     colval(scan2[x],BLU));
704     }
705     }
706     free(scan1);
707     free(scan2);
708     return(0);
709     }
710     }
711     free(scan1);
712     free(scan2);
713     return(good_RMS()); /* final check of RMS */
714     }
715    
716 greg 2.19 /* Set reference depth based on header variable */
717     static int
718     set_refdepth(DEPTHCODEC *dcp, LUTAB *htp)
719     {
720     static char depthvar[] = DEPTHSTR;
721     const char *drval;
722    
723 greg 2.23 depthvar[LDEPTHSTR-1] = '\0';
724 greg 2.19 drval = (const char *)lu_find(htp, depthvar)->data;
725     if (!drval)
726     return(0);
727     dcp->refdepth = atof(drval);
728     if (dcp->refdepth <= 0) {
729     if (report != REP_QUIET) {
730     fputs(dcp->inpname, stderr);
731     fputs(": bad reference depth '", stderr);
732     fputs(drval, stderr);
733     fputs("'\n", stderr);
734     }
735     return(-1);
736     }
737     return(1);
738     }
739    
740     /* Compare two encoded depth maps */
741     static int
742     compare_depth()
743     {
744     long nread = 0;
745     DEPTHCODEC dc1, dc2;
746    
747     if (report >= REP_VERBOSE) {
748     fputs(progname, stdout);
749     fputs(": comparing inputs as depth maps\n", stdout);
750     }
751     set_dc_defaults(&dc1);
752     dc1.hdrflags = HF_RESIN;
753     dc1.finp = f1in;
754     dc1.inpname = f1name;
755     set_dc_defaults(&dc2);
756     dc2.hdrflags = HF_RESIN;
757     dc2.finp = f2in;
758     dc2.inpname = f2name;
759     if (report != REP_QUIET) {
760     dc1.hdrflags |= HF_STDERR;
761     dc2.hdrflags |= HF_STDERR;
762     }
763     if (!process_dc_header(&dc1, 0, NULL))
764     return(0);
765     if (!process_dc_header(&dc2, 0, NULL))
766     return(0);
767     if (!check_resolu("Depth map", &dc1.res, &dc2.res))
768     return(0);
769     if (set_refdepth(&dc1, &hdr1) < 0)
770     return(0);
771     if (set_refdepth(&dc2, &hdr2) < 0)
772     return(0);
773     while (nread < dc1.res.xr*dc1.res.yr) {
774     double d1 = decode_depth_next(&dc1);
775     double d2 = decode_depth_next(&dc2);
776     if ((d1 < 0) | (d2 < 0)) {
777     if (report != REP_QUIET)
778     printf("%s: unexpected end-of-file\n",
779     progname);
780     return(0);
781     }
782     ++nread;
783     if (real_check(d1, d2))
784     continue;
785     if (report != REP_QUIET)
786     printf("%s: %ld%s depth values differ\n",
787     progname, nread, num_sfx(nread));
788     return(0);
789     }
790     return(good_RMS()); /* final check of RMS */
791     }
792    
793     /* Compare two encoded normal maps */
794     static int
795     compare_norm()
796     {
797     long nread = 0;
798     NORMCODEC nc1, nc2;
799    
800     if (report >= REP_VERBOSE) {
801     fputs(progname, stdout);
802     fputs(": comparing inputs as normal maps\n", stdout);
803     }
804     set_nc_defaults(&nc1);
805     nc1.hdrflags = HF_RESIN;
806     nc1.finp = f1in;
807     nc1.inpname = f1name;
808     set_nc_defaults(&nc2);
809     nc2.hdrflags = HF_RESIN;
810     nc2.finp = f2in;
811     nc2.inpname = f2name;
812     if (report != REP_QUIET) {
813     nc1.hdrflags |= HF_STDERR;
814     nc2.hdrflags |= HF_STDERR;
815     }
816     if (!process_nc_header(&nc1, 0, NULL))
817     return(0);
818     if (!process_nc_header(&nc2, 0, NULL))
819     return(0);
820     if (!check_resolu("Normal map", &nc1.res, &nc2.res))
821     return(0);
822     while (nread < nc1.res.xr*nc1.res.yr) {
823     FVECT nv1, nv2;
824     int rv1 = decode_normal_next(nv1, &nc1);
825     int rv2 = decode_normal_next(nv2, &nc2);
826     if ((rv1 < 0) | (rv2 < 0)) {
827     if (report != REP_QUIET)
828     printf("%s: unexpected end-of-file\n",
829     progname);
830     return(0);
831     }
832     ++nread;
833     if (rv1 == rv2 && (!rv1 || norm_check(nv1, nv2)))
834     continue;
835     if (report != REP_QUIET)
836     printf("%s: %ld%s normal vectors differ\n",
837     progname, nread, num_sfx(nread));
838     return(0);
839     }
840     return(good_RMS()); /* final check of RMS */
841     }
842    
843 greg 2.1 /* Compare two inputs that are known to be 32-bit floating-point data */
844     static int
845     compare_float()
846     {
847     long nread = 0;
848     float f1, f2;
849    
850 greg 2.6 if (report >= REP_VERBOSE) {
851     fputs(progname, stdout);
852 greg 2.7 fputs(": comparing inputs as 32-bit IEEE floats\n", stdout);
853 greg 2.6 }
854 greg 2.1 while (getbinary(&f1, sizeof(f1), 1, f1in)) {
855     if (!getbinary(&f2, sizeof(f2), 1, f2in))
856     goto badeof;
857     ++nread;
858 greg 2.20 if (f1swap) swap32((char *)&f1, 1);
859     if (f2swap) swap32((char *)&f2, 1);
860 greg 2.1 if (real_check(f1, f2))
861     continue;
862     if (report != REP_QUIET)
863 greg 2.4 printf("%s: %ld%s float values differ\n",
864 greg 2.6 progname, nread, num_sfx(nread));
865 greg 2.1 return(0);
866     }
867     if (!getbinary(&f2, sizeof(f2), 1, f2in))
868     return(good_RMS()); /* final check of RMS */
869     badeof:
870     if (report != REP_QUIET)
871 greg 2.6 printf("%s: unexpected end-of-file\n", progname);
872 greg 2.1 return(0);
873     }
874    
875     /* Compare two inputs that are known to be 64-bit floating-point data */
876     static int
877     compare_double()
878     {
879     long nread = 0;
880     double f1, f2;
881    
882 greg 2.6 if (report >= REP_VERBOSE) {
883     fputs(progname, stdout);
884 greg 2.7 fputs(": comparing inputs as 64-bit IEEE doubles\n", stdout);
885 greg 2.6 }
886 greg 2.1 while (getbinary(&f1, sizeof(f1), 1, f1in)) {
887     if (!getbinary(&f2, sizeof(f2), 1, f2in))
888     goto badeof;
889     ++nread;
890 greg 2.20 if (f1swap) swap64((char *)&f1, 1);
891     if (f2swap) swap64((char *)&f2, 1);
892 greg 2.1 if (real_check(f1, f2))
893     continue;
894     if (report != REP_QUIET)
895 greg 2.6 printf("%s: %ld%s double values differ\n",
896     progname, nread, num_sfx(nread));
897 greg 2.1 return(0);
898     }
899     if (!getbinary(&f2, sizeof(f2), 1, f2in))
900     return(good_RMS()); /* final check of RMS */
901     badeof:
902     if (report != REP_QUIET)
903 greg 2.6 printf("%s: unexpected end-of-file\n", progname);
904 greg 2.1 return(0);
905     }
906    
907     /* Compare two Radiance files for equivalence */
908     int
909     main(int argc, char *argv[])
910     {
911     int typ1, typ2;
912     int a;
913    
914     progname = argv[0];
915     for (a = 1; a < argc && argv[a][0] == '-'; a++) {
916     switch (argv[a][1]) {
917     case 'h': /* ignore header info. */
918     ign_header = !ign_header;
919     continue;
920 greg 2.26 case 'c': /* ignore comments */
921     comment_c = argv[a][2];
922     continue;
923 greg 2.1 case 's': /* silent operation */
924     report = REP_QUIET;
925     continue;
926     case 'w': /* turn off warnings */
927     if (report > REP_ERROR)
928     report = REP_ERROR;
929     continue;
930     case 'v': /* turn on verbose mode */
931     report = REP_VERBOSE;
932     continue;
933     case 'm': /* maximum epsilon */
934     max_lim = atof(argv[++a]);
935     continue;
936     case 'r':
937     if (argv[a][2] == 'e') /* relative difference */
938     rel_min = atof(argv[++a]);
939     else if (argv[a][2] == 'm') /* RMS limit */
940     rms_lim = atof(argv[++a]);
941     else
942     usage();
943     continue;
944     case '\0': /* first file from stdin */
945     f1in = stdin;
946     f1name = stdin_name;
947     break;
948     default:
949     usage();
950     }
951     break;
952     }
953 greg 2.7 if (a != argc-2) /* make sure of two inputs */
954 greg 2.1 usage();
955 greg 2.7 if (!strcmp(argv[a], argv[a+1])) { /* inputs are same? */
956 greg 2.1 if (report >= REP_WARN)
957 greg 2.4 printf("%s: warning - identical inputs given\n",
958 greg 2.1 progname);
959     return(0);
960     }
961 greg 2.7 if (!f1name) f1name = argv[a];
962     if (!f2name) f2name = argv[a+1];
963 greg 2.1 /* open inputs */
964     SET_FILE_BINARY(stdin); /* in case we're using it */
965     if (!f1in && !(f1in = fopen(f1name, "rb"))) {
966     fprintf(stderr, "%s: cannot open for reading\n", f1name);
967 greg 2.9 return(2);
968 greg 2.1 }
969     if (!strcmp(f2name, "-")) {
970     f2in = stdin;
971     f2name = stdin_name;
972     } else if (!(f2in = fopen(f2name, "rb"))) {
973     fprintf(stderr, "%s: cannot open for reading\n", f2name);
974 greg 2.9 return(2);
975 greg 2.1 }
976     /* load headers */
977     if ((typ1 = identify_type(f1name, f1in, &hdr1)) < 0)
978 greg 2.9 return(2);
979 greg 2.1 if ((typ2 = identify_type(f2name, f2in, &hdr2)) < 0)
980 greg 2.9 return(2);
981 greg 2.1 if (typ1 != typ2) {
982     if (report != REP_QUIET)
983 greg 2.21 printf("%s: '%s' format is %s and '%s' is %s\n",
984 greg 2.1 progname, f1name, file_type[typ1],
985     f2name, file_type[typ2]);
986     return(1);
987     }
988     ign_header |= !has_header(typ1); /* check headers if indicated */
989 greg 2.10 if (!ign_header && !headers_match())
990 greg 2.1 return(1);
991     if (!ign_header & (report >= REP_WARN)) {
992     if (lin1cnt != lin2cnt)
993 greg 2.4 printf("%s: warning - headers are different lengths\n",
994 greg 2.1 progname);
995 greg 2.16 if (typ1 == TYP_UNKNOWN)
996     printf("%s: warning - unrecognized format\n",
997     progname);
998 greg 2.1 }
999 greg 2.21 if (report >= REP_VERBOSE) {
1000     printf("%s: data format is %s\n", progname, file_type[typ1]);
1001     if ((typ1 == TYP_FLOAT) | (typ1 == TYP_DOUBLE)) {
1002     if (f1swap)
1003     printf("%s: input '%s' is byte-swapped\n",
1004     progname, f1name);
1005     if (f2swap)
1006     printf("%s: input '%s' is byte-swapped\n",
1007     progname, f2name);
1008     }
1009     }
1010 greg 2.1 switch (typ1) { /* compare based on type */
1011     case TYP_BINARY:
1012     case TYP_TMESH:
1013     case TYP_OCTREE:
1014     case TYP_RBFMESH:
1015 greg 2.19 case TYP_ID8:
1016     case TYP_ID16:
1017     case TYP_ID24:
1018 greg 2.1 case TYP_UNKNOWN:
1019     return( !compare_binary() );
1020     case TYP_TEXT:
1021     case TYP_ASCII:
1022     return( !compare_text() );
1023     case TYP_RGBE:
1024     case TYP_XYZE:
1025     return( !compare_hdr() );
1026 greg 2.19 case TYP_DEPTH:
1027     return( !compare_depth() );
1028     case TYP_NORM:
1029     return( !compare_norm() );
1030 greg 2.1 case TYP_FLOAT:
1031     return( !compare_float() );
1032     case TYP_DOUBLE:
1033     return( !compare_double() );
1034     }
1035     return(1);
1036     }