ViewVC Help
View File | Revision Log | Show Annotations | Download File | Root Listing
root/Development/ray/src/util/pvsum.c
Revision: 2.7
Committed: Fri Oct 24 16:31:18 2025 UTC (8 weeks ago) by greg
Content type: text/plain
Branch: MAIN
Changes since 2.6: +24 -7 lines
Log Message:
feat(pvsum): Added preservation of VIEW and PIXASPECT from input header

File Contents

# User Rev Content
1 greg 2.1 #ifndef lint
2 greg 2.7 static const char RCSid[] = "$Id: pvsum.c,v 2.6 2025/06/28 17:30:50 greg Exp $";
3 greg 2.1 #endif
4     /*
5     * pvsum.c - add together spectral and/or float pictures
6     * based on vector or matrix, similar to dctimestep
7     */
8    
9     #include <math.h>
10     #include "rtio.h"
11     #include "resolu.h"
12     #include "platform.h"
13 greg 2.2 #include "random.h"
14 greg 2.1 #include "rmatrix.h"
15     #if !defined(_WIN32) && !defined(_WIN64)
16     #include <sys/mman.h>
17 greg 2.5 #include <sys/wait.h>
18 greg 2.1 #endif
19    
20 greg 2.7 #define VIEWSTR "VIEW=" /* borrowed from common/view.h */
21     #define VIEWSTRL 5
22    
23 greg 2.1 int nprocs = 1; /* # of calculation processes (Unix) */
24     int in_type = DTfromHeader; /* input data type */
25     int out_type = DTfromHeader; /* output data type */
26     char *in_spec = NULL; /* input specification */
27     char *out_spec = NULL; /* output file specification */
28    
29     int iswapped = 0; /* input data is byte-swapped? */
30     int ncomp = 3; /* # input components */
31     int xres=0, yres=0; /* input image dimensions */
32 greg 2.7 char viewspec[128] = ""; /* VIEW= line from first header */
33     char pixasp[48] = ""; /* PIXASPECT= line from header */
34 greg 2.1
35     RMATRIX *cmtx = NULL; /* coefficient matrix */
36    
37     /* does the given spec contain integer format? */
38     int
39     hasFormat(const char *s)
40     {
41     restart:
42     if (s) s = strchr(s, '%');
43     if (!s) return(0);
44     if (s[1] == '%') { /* "%%" ? */
45     s += 2;
46     goto restart;
47     }
48     while (*++s) {
49     if (strchr("diouxX", *s))
50     return(1); /* integer format */
51     if (strchr("%fFeEgGaAcsb", *s))
52     break; /* non-integer format */
53     }
54     return(0);
55     }
56    
57     /* get first input header data we'll need */
58     int
59     iheadline(char *s, void *p)
60     {
61     char fmt[MAXFMTLEN];
62     int i;
63    
64     if (!strncmp(s, "NCOLS=", 6)) {
65     xres = atoi(s+6);
66     return(1);
67     }
68     if (!strncmp(s, "NROWS=", 6)) {
69     yres = atoi(s+6);
70     return(1);
71     }
72     if (isncomp(s)) {
73     ncomp = ncompval(s);
74     return(1);
75     }
76     if (formatval(fmt, s)) {
77     for (in_type = DTend; --in_type > DTfromHeader; )
78     if (!strcmp(fmt, cm_fmt_id[in_type]))
79     return(1);
80     return(-1);
81     }
82     i = isbigendian(s);
83     if (i >= 0) {
84     iswapped = (i != nativebigendian());
85     return(1);
86     }
87 greg 2.7 if (!strncmp(s, VIEWSTR, VIEWSTRL)) {
88     strcpy(viewspec, s);
89     return(1);
90     }
91     if (isaspect(s)) {
92     strcpy(pixasp, s);
93     return(1);
94     }
95 greg 2.1 return(0);
96     }
97    
98     /* open initial file and get relevant dimensions and type */
99     int
100     get_iotypes(void)
101     {
102     char fbuf[256];
103     FILE *fp;
104    
105     sprintf(fbuf, in_spec, 0);
106     fp = fopen(fbuf, "rb");
107     if (!fp) {
108     fprintf(stderr, "%s: cannot open for reading\n", fbuf);
109     return(0);
110     }
111     if (getheader(fp, iheadline, NULL) < 0) {
112     fprintf(stderr, "%s: bad header - wrong format?\n", fbuf);
113     fclose(fp);
114     return(0);
115     }
116     if ((xres <= 0) | (yres <= 0) && !fscnresolu(&xres, &yres, fp)) {
117     fprintf(stderr, "%s: missing input resolution\n", fbuf);
118     fclose(fp);
119     return(0);
120     }
121     if (nprocs > 1 && (in_type==DTrgbe) | (in_type==DTxyze)) {
122     long data_start = ftell(fp); /* make sure input flat */
123     off_t dend = lseek(fileno(fp), 0, SEEK_END);
124     if (dend < data_start + 4L*xres*yres) {
125     fputs("Warning - multi-processing requires flat input files\n",
126     stderr);
127     nprocs = 1;
128     }
129     }
130     fclose(fp);
131     if ((cmtx->ncomp == 1) & (ncomp != 1)) {
132     double xfm[MAXCSAMP];
133     RMATRIX *nmtx;
134     int i;
135     for (i = ncomp; i--; )
136     xfm[i] = 1.;
137     nmtx = rmx_transform(cmtx, ncomp, xfm);
138     if (!nmtx)
139     return(0);
140     rmx_free(cmtx);
141     cmtx = nmtx;
142     } else if (cmtx->ncomp != ncomp) {
143     fprintf(stderr, "operation %s needs %d components, has %d\n",
144     cmtx->ncols == 1 ? "vector" : "matrix",
145     ncomp, cmtx->ncomp);
146     return(0);
147     }
148     if ((in_type != DTrgbe) & (in_type != DTxyze) & (in_type != DTspec) &
149     (in_type != DTfloat)) {
150     fprintf(stderr, "%s unsupported input data type: %s\n",
151     fbuf, cm_fmt_id[in_type]);
152     return(0);
153     }
154     if ((out_type == DTrgbe) & (ncomp > 3))
155     out_type = DTspec;
156     else if (out_type == DTfromHeader ||
157     (out_type == DTrgbe) & (in_type != DTfloat))
158     out_type = in_type;
159     return(1);
160     }
161    
162     /* check subsequent headers match initial file */
163     int
164     checkline(char *s, void *p)
165     {
166 greg 2.3 static int exposWarned = 0;
167     int *xyres = (int *)p;
168     char fmt[MAXFMTLEN];
169 greg 2.1
170     if (!strncmp(s, "NCOLS=", 6)) {
171     xyres[0] = atoi(s+6);
172     if (xyres[0] <= 0)
173     return(-1);
174     return(1);
175     }
176     if (!strncmp(s, "NROWS=", 6)) {
177     xyres[1] = atoi(s+6);
178     if (xyres[1] <= 0)
179     return(-1);
180     return(1);
181     }
182     if (isncomp(s)) {
183     if (ncompval(s) != ncomp)
184     return(-1);
185     return(1);
186     }
187     if (isexpos(s)) {
188 greg 2.3 if (!exposWarned && fabs(1. - exposval(s)) > 0.04) {
189     fputs("Warning - ignoring EXPOSURE setting(s)\n",
190     stderr);
191     exposWarned++;
192     }
193 greg 2.1 return(1);
194     }
195     if (formatval(fmt, s)) {
196     if (strcmp(fmt, cm_fmt_id[in_type]))
197     return(-1);
198     return(1);
199     }
200     return(0);
201     }
202    
203     /* open and check input file */
204     FILE *
205     open_input(char *fname)
206     {
207     int xyres[2];
208     FILE *fp = fopen(fname, "rb");
209    
210     if (!fp) {
211     fprintf(stderr, "%s: cannot open for reading\n", fname);
212     return(NULL);
213     }
214     xyres[0] = xyres[1] = 0;
215     if (getheader(fp, checkline, xyres) < 0) {
216     fprintf(stderr, "%s: bad/inconsistent header\n", fname);
217     fclose(fp);
218     return(NULL);
219     }
220     if ((xyres[0] <= 0) | (xyres[1] <= 0) &&
221     !fscnresolu(&xyres[0], &xyres[1], fp)) {
222     fprintf(stderr, "%s: missing resolution\n", fname);
223     fclose(fp);
224     return(NULL);
225     }
226     if ((xyres[0] != xres) | (xyres[1] != yres)) {
227     fprintf(stderr, "%s: mismatched resolution\n", fname);
228     fclose(fp);
229     return(NULL);
230     }
231     return(fp);
232     }
233    
234     /* open output file or command (if !NULL) and write info header */
235     FILE *
236     open_output(char *ospec, int fno)
237     {
238     FILE *fp;
239    
240     if (!ospec) {
241     ospec = "<stdout>";
242     fp = stdout;
243     } else if (ospec[0] == '!') {
244     if (!(fp = popen(ospec+1, "w"))) {
245     fprintf(stderr, "Cannot start: %s\n", ospec);
246     return(NULL);
247     }
248 greg 2.3 } else if (!(fp = fopen(ospec, "w"))) {
249 greg 2.1 fprintf(stderr, "%s: cannot open for writing\n", ospec);
250     return(NULL);
251     }
252 greg 2.3 SET_FILE_BINARY(fp);
253 greg 2.1 newheader("RADIANCE", fp);
254 greg 2.2 if (cmtx->info) /* prepend matrix metadata */
255     fputs(cmtx->info, fp);
256     else
257     fputnow(fp);
258 greg 2.1 if (fno >= 0)
259     fprintf(fp, "FRAME=%d\n", fno);
260 greg 2.7 if (viewspec[0])
261     fputs(viewspec, fp);
262     if (pixasp[0])
263     fputs(pixasp, fp);
264 greg 2.1 switch (out_type) {
265     case DTfloat:
266     case DTdouble:
267     fprintf(fp, "NCOLS=%d\nNROWS=%d\n", xres, yres);
268     fputncomp(ncomp, fp);
269     fputendian(fp);
270     fputformat(cm_fmt_id[out_type], fp);
271     fputc('\n', fp);
272     break;
273     case DTrgbe:
274     fputformat(COLRFMT, fp);
275     fputc('\n', fp);
276     fprtresolu(xres, yres, fp);
277     break;
278     case DTxyze:
279     fputformat(CIEFMT, fp);
280     fputc('\n', fp);
281     fprtresolu(xres, yres, fp);
282     break;
283     case DTspec:
284     fputncomp(ncomp, fp);
285     fputwlsplit(cmtx->wlpart, fp);
286     fputformat(SPECFMT, fp);
287     fputc('\n', fp);
288     fprtresolu(xres, yres, fp);
289     break;
290     default:
291     fputs("Unsupported output type!\n", stderr);
292     return(NULL);
293     }
294     if (fflush(fp) < 0) {
295     fprintf(stderr, "%s: write error\n", ospec);
296     fclose(fp);
297     return(NULL);
298     }
299     return(fp);
300     }
301    
302     /* run calculation from a single process */
303     int
304     solo_process(void)
305     {
306     float *osum = (float *)calloc((size_t)xres*yres, sizeof(float)*ncomp);
307     COLORV *iscan = (COLORV *)malloc(sizeof(COLORV)*ncomp*xres);
308     char fbuf[512];
309     int c;
310    
311     if (!osum | !iscan) {
312     fprintf(stderr, "Cannot allocate %dx%d %d-component accumulator\n",
313     xres, yres, ncomp);
314     return(0);
315     }
316     if (sizeof(float) != sizeof(COLORV)) {
317     fputs("Code Error 1 in solo_process()\n", stderr);
318     return(0);
319     }
320     for (c = 0; c < cmtx->ncols; c++) { /* run through each column/output */
321     FILE *fout;
322     int y;
323     int rc = cmtx->nrows;
324     if (c > 0) /* clear accumulator? */
325     memset(osum, 0, sizeof(float)*ncomp*xres*yres);
326     while (rc-- > 0) { /* run through each input file */
327     const int r = c&1 ? rc : cmtx->nrows-1 - rc;
328     const rmx_dtype *cval = rmx_val(cmtx, r, c);
329     FILE *finp;
330     int i, x;
331     for (i = ncomp; i--; )
332     if (cval[i] != 0) break;
333     if (i < 0) /* this coefficient is zero, skip */
334     continue;
335     sprintf(fbuf, in_spec, r);
336     finp = open_input(fbuf);
337     if (!finp)
338     return(0);
339     for (y = 0; y < yres; y++) {
340     float *dst = osum + y*xres*ncomp;
341     if (in_type == DTfloat ? getbinary(iscan, sizeof(float)*ncomp,
342     xres, finp) != xres
343     : freadsscan(iscan, ncomp, xres, finp) < 0)
344     goto readerr;
345     if ((in_type == DTfloat) & iswapped)
346     swap32((char *)iscan, ncomp*xres);
347     for (x = 0; x < xres; x++, dst += ncomp)
348     for (i = ncomp; i--; )
349     dst[i] += cval[i]*iscan[x*ncomp + i];
350     }
351     fclose(finp);
352     } /* write out accumulated column result... */
353     if (out_spec) { /* ...to file or command */
354     if (cmtx->ncols > 1 && !hasFormat(out_spec)) {
355     fputs("Sequential result must go to stdout\n", stderr);
356     return(0);
357     }
358     sprintf(fbuf, out_spec, c);
359     fout = open_output(fbuf, c-(cmtx->ncols==1));
360     } else { /* ...to stdout */
361     if ((out_type == DTfloat) & (cmtx->ncols > 1)) {
362     fputs("Float outputs must have separate destinations\n",
363     stderr);
364     return(0);
365     }
366     strcpy(fbuf, "<stdout>");
367     fout = open_output(NULL, c-(cmtx->ncols==1));
368     }
369     if (!fout)
370     return(0); /* assume error was reported */
371     if (out_type != DTfloat) {
372     for (y = 0; y < yres; y++)
373     if (fwritesscan(osum + (size_t)y*xres*ncomp,
374     ncomp, xres, fout) < 0)
375     goto writerr;
376     } else if (fwrite(osum, sizeof(float)*ncomp, (size_t)xres*yres, fout) !=
377     (size_t)xres*yres)
378     goto writerr;
379    
380     if (fbuf[0] == '!') {
381     if (pclose(fout) != 0) {
382     fprintf(stderr, "Bad status from: %s\n", fbuf);
383     return(0);
384     }
385     } else if (fout != stdout && fclose(fout) == EOF)
386     goto writerr;
387     }
388     free(osum); /* clean up on success */
389     free(iscan);
390     return(1);
391     readerr:
392     fprintf(stderr, "%s: read error\n", fbuf);
393     return(0);
394     writerr:
395     fprintf(stderr, "%s: write error\n", fbuf);
396     return(0);
397     }
398    
399 greg 2.7 #if defined(_WIN32) || defined(_WIN64)
400     #define multi_process solo_process
401     #else
402    
403 greg 2.2 /* allocate a scrambled index array of the specified length */
404     int *
405     scramble(int n)
406     {
407     int *scarr = (int *)malloc(sizeof(int)*n);
408     int i;
409    
410     if (!scarr) {
411     fprintf(stderr, "Out of memory in scramble(%d)\n", n);
412     exit(1);
413     }
414     for (i = n; i--; )
415     scarr[i] = i;
416     /* perform Fisher-Yates shuffle */
417     for (i = 0; i < n-1; i++) {
418     int ix = irandom(n-i) + i;
419     int ndx = scarr[i];
420     scarr[i] = scarr[ix];
421     scarr[ix] = ndx;
422     }
423     return(scarr);
424     }
425    
426 greg 2.1 /* run calculation on multiple processes, using memory maps and fork() */
427     int
428     multi_process(void)
429     {
430     int coff = nprocs;
431     int odd = 0;
432     char fbuf[512];
433     float *osum;
434 greg 2.2 int *syarr;
435 greg 2.1 int c;
436     /* sanity check */
437     if (sizeof(float) != sizeof(COLORV)) {
438     fputs("Code Error 1 in multi_process()\n", stderr);
439     return(0);
440     }
441     while (--coff > 0) { /* parent births children */
442     int pid = fork();
443     if (pid < 0) {
444     fputs("fork() call failed!\n", stderr);
445     return(0);
446     }
447     if (pid == 0) break; /* child gets to work */
448     }
449     osum = (float *)calloc((size_t)xres*yres, sizeof(float)*ncomp);
450     if (!osum) {
451     fprintf(stderr, "Cannot allocate %dx%d %d-component accumulator\n",
452     xres, yres, ncomp);
453     return(0);
454     }
455 greg 2.2 srandom(113*coff + 5669); /* randomize row access for this process */
456     syarr = scramble(yres);
457 greg 2.1 /* run through our unique set of columns */
458     for (c = coff; c < cmtx->ncols; c += nprocs) {
459     FILE *fout;
460     int y;
461     int rc = cmtx->nrows;
462     if (c > coff) /* clear accumulator? */
463     memset(osum, 0, sizeof(float)*ncomp*xres*yres);
464     while (rc-- > 0) { /* map & sum each input file */
465     const int r = odd ? rc : cmtx->nrows-1 - rc;
466     const rmx_dtype *cval = rmx_val(cmtx, r, c);
467 greg 2.2 long dstart;
468 greg 2.1 size_t maplen;
469     void *imap;
470     FILE *finp;
471     float *dst;
472 greg 2.2 int i, x;
473 greg 2.1 for (i = ncomp; i--; )
474     if (cval[i] != 0) break;
475     if (i < 0) /* this coefficient is zero, skip */
476     continue;
477     sprintf(fbuf, in_spec, r);
478     finp = open_input(fbuf);
479     if (!finp)
480     return(0);
481     dstart = ftell(finp);
482     if (dstart < 0) {
483     fprintf(stderr, "%s: ftell() failed!\n", fbuf);
484     return(0);
485     }
486     if (in_type == DTfloat && dstart%sizeof(float)) {
487     fprintf(stderr, "%s: float header misalignment\n", fbuf);
488     return(0);
489     }
490     i = in_type==DTfloat ? ncomp*(int)sizeof(float) : ncomp+1;
491 greg 2.6 maplen = dstart + (size_t)yres*xres*i;
492 greg 2.1 imap = mmap(NULL, maplen, PROT_READ,
493     MAP_FILE|MAP_SHARED, fileno(finp), 0);
494 greg 2.3 fclose(finp); /* will read from map (randomly) */
495 greg 2.1 if (imap == MAP_FAILED) {
496     fprintf(stderr, "%s: unable to map input file\n", fbuf);
497     return(0);
498     }
499 greg 2.2 if (in_type == DTfloat)
500     for (y = yres; y-- > 0; ) {
501     const float *fvp = (float *)((char *)imap + dstart) +
502     (size_t)ncomp*xres*syarr[y];
503     dst = osum + (size_t)ncomp*xres*syarr[y];
504     for (x = xres; x-- > 0; dst += ncomp, fvp += ncomp)
505     for (i = ncomp; i--; )
506     dst[i] += cval[i]*fvp[i];
507     }
508     else
509     for (y = yres; y-- > 0; ) {
510     const COLRV *cvp = (COLRV *)((char *)imap + dstart) +
511     (ncomp+1L)*xres*syarr[y];
512     dst = osum + (size_t)ncomp*xres*syarr[y];
513     for (x = xres; x-- > 0; dst += ncomp, cvp += ncomp+1) {
514     const rmx_dtype fe = cxponent[cvp[ncomp]];
515     for (i = ncomp; i--; )
516     dst[i] += cval[i]*(cvp[i]+(rmx_dtype).5)*fe;
517     }
518 greg 2.1 }
519     munmap(imap, maplen);
520 greg 2.3 } /* write accumulated column picture/matrix */
521 greg 2.1 sprintf(fbuf, out_spec, c);
522     fout = open_output(fbuf, c);
523     if (!fout)
524     return(0); /* assume error was reported */
525     if (out_type != DTfloat) {
526     for (y = 0; y < yres; y++)
527     if (fwritesscan(osum + (size_t)y*xres*ncomp,
528     ncomp, xres, fout) < 0)
529     goto writerr;
530     } else if (fwrite(osum, sizeof(float)*ncomp, (size_t)xres*yres, fout) !=
531     (size_t)xres*yres)
532     goto writerr;
533    
534     if (fbuf[0] == '!') {
535     if (pclose(fout) != 0) {
536     fprintf(stderr, "Bad status from: %s\n", fbuf);
537     return(0);
538     }
539     } else if (fclose(fout) == EOF)
540     goto writerr;
541     odd = !odd; /* go back & forth to milk page cache */
542     }
543     free(osum);
544 greg 2.2 free(syarr);
545 greg 2.3 if (coff) /* child processes return here... */
546 greg 2.1 return(1);
547     c = 0; /* ...but parent waits for children */
548     while (++coff < nprocs) {
549     int st;
550     if (wait(&st) < 0)
551     break;
552     if (st) c = st;
553     }
554     return(c == 0);
555     writerr:
556     fprintf(stderr, "%s: write error\n", fbuf);
557     return(0);
558     }
559    
560 greg 2.7 #endif /* ! Windows */
561    
562 greg 2.1 int
563     main(int argc, char *argv[])
564     {
565     int a;
566    
567     for (a = 1; a < argc-1 && argv[a][0] == '-'; a++)
568     switch (argv[a][1]) {
569     case 'o': /* output spec/format */
570     switch (argv[a][2]) {
571     case '\0':
572     out_spec = argv[++a];
573     break;
574     case 'f':
575     out_type = DTfloat;
576     break;
577     case 'c':
578     out_type = DTrgbe;
579     break;
580     default:
581     goto badopt;
582     }
583     break;
584     case 'N': /* number of desired processes */
585     case 'n': /* quietly supported alternate */
586     nprocs = atoi(argv[++a]);
587     if (nprocs <= 0)
588     goto userr;
589     break;
590     default:;
591     badopt: fprintf(stderr, "%s: bad option: %s\n", argv[0], argv[a]);
592     goto userr;
593     return(1);
594     }
595     if ((argc-a < 1) | (argc-a > 2) || argv[a][0] == '-')
596     goto userr;
597     in_spec = argv[a];
598 greg 2.4 cmtx = rmx_load(argv[a+1]); /* loads from stdin if a+1==argc */
599 greg 2.1 if (cmtx == NULL)
600     return(1); /* error reported */
601 greg 2.3 if (nprocs > cmtx->ncols)
602     nprocs = cmtx->ncols;
603 greg 2.1 #if defined(_WIN32) || defined(_WIN64)
604     if (nprocs > 1) {
605     fprintf(stderr, "%s: warning - Windows only allows -N 1\n", argv[0]);
606     nprocs = 1;
607     }
608     #else
609     if ((nprocs > 1) & !out_spec) {
610     fprintf(stderr, "%s: multi-processing result cannot go to stdout\n",
611     argv[0]);
612     nprocs = 1;
613     }
614     if ((nprocs > 1) & iswapped && (in_type==DTfloat) | (in_type==DTdouble)) {
615     fprintf(stderr, "%s: multi-processing unsupported on swapped input\n",
616     argv[0]);
617     nprocs = 1;
618     }
619     #endif
620     if (cmtx->nrows > 1 && !hasFormat(in_spec)) {
621     fprintf(stderr, "%s: input specification '%s' needs %%d format\n",
622     argv[0], in_spec);
623     goto userr;
624     }
625     if (!get_iotypes())
626     return(1);
627     if (!(nprocs == 1 ? solo_process() : multi_process()))
628     return(1);
629     return(0);
630     userr:
631     fprintf(stderr, "Usage: %s [-oc | -of][-o ospec][-N nproc] inpspec [mtx]\n",
632     argv[0]);
633     return(1);
634     }