ViewVC Help
View File | Revision Log | Show Annotations | Download File | Root Listing
root/radiance/ray/src/util/rmtxcomb.c
Revision: 2.1
Committed: Tue Dec 5 01:06:10 2023 UTC (5 months, 1 week ago) by greg
Content type: text/plain
Branch: MAIN
Log Message:
feat(rmtxcomb): Initial check-in of new matrix combining tool

File Contents

# Content
1 #ifndef lint
2 static const char RCSid[] = "$Id: rmtxop.c,v 2.25 2023/11/29 18:57:10 greg Exp $";
3 #endif
4 /*
5 * General component matrix combiner, operating on a row at a time.
6 */
7
8 #include <errno.h>
9 #include <math.h>
10 #include "platform.h"
11 #include "rtio.h"
12 #include "resolu.h"
13 #include "rmatrix.h"
14 #include "calcomp.h"
15 #include "paths.h"
16
17 #ifndef M_PI
18 #define M_PI 3.14159265358979323846
19 #endif
20
21 #define MAXCOMP MAXCSAMP /* #components we support */
22
23 /* Unary matrix operation(s) */
24 typedef struct {
25 double cmat[MAXCOMP*MAXCOMP]; /* component transformation */
26 double sca[MAXCOMP]; /* scalar coefficients */
27 const char *csym; /* symbolic coefficients */
28 short clen; /* number of coefficients */
29 short nsf; /* number of scalars */
30 } RUNARYOP;
31
32 /* Input matrix */
33 typedef struct {
34 const char *inspec; /* input specification */
35 RUNARYOP preop; /* transform operation */
36 RMATRIX imx; /* input matrix header info */
37 RMATRIX *rmp; /* active single-row matrix */
38 FILE *infp; /* open input stream */
39 } ROPMAT;
40
41 ROPMAT *mop = NULL; /* allocated input array */
42 int nall = 0; /* number allocated */
43 int nmats = 0; /* number of actual inputs */
44
45 RMATRIX *mcat = NULL; /* final concatenation */
46 int mcat_last = 0; /* goes after trailing ops? */
47
48 int in_nrows; /* input row count */
49 #define in_ncols (mop[0].rmp->ncols) /* input column count */
50 #define in_ncomp (mop[0].rmp->ncomp) /* input #components */
51
52 extern int nowarn; /* turn off warnings? */
53
54 int cur_row; /* current input/output row */
55 int cur_col; /* current input/output column */
56 int cur_chan; /* if we're looping channels */
57
58 static int checksymbolic(ROPMAT *rop);
59
60 static int
61 split_input(ROPMAT *rop)
62 {
63 if (rop->rmp == &rop->imx && !(rop->rmp = rmx_copy(&rop->imx))) {
64 fputs("Out of memory in split_input()\n", stderr);
65 return(0);
66 }
67 rmx_reset(rop->rmp);
68 return(1);
69 }
70
71 /* Check/set transform based on a reference input file */
72 static int
73 checkreffile(ROPMAT *rop)
74 {
75 static const char *curRF = NULL;
76 static RMATRIX refm;
77 const int nc = rop->imx.ncomp;
78 int i;
79
80 if (!curRF || strcmp(rop->preop.csym, curRF)) {
81 FILE *fp = fopen(rop->preop.csym, "rb");
82 if (!rmx_load_header(&refm, fp)) {
83 fprintf(stderr, "%s: cannot read info header\n",
84 rop->preop.csym);
85 curRF = NULL;
86 if (fp) fclose(fp);
87 return(0);
88 }
89 fclose(fp);
90 curRF = rop->preop.csym;
91 }
92 if ((refm.ncomp == 3) & (refm.dtype != DTspec)) {
93 rop->preop.csym = (refm.dtype == DTxyze) ? "XYZ" : "RGB";
94 return(checksymbolic(rop));
95 }
96 if (refm.ncomp == 2) {
97 fprintf(stderr, "%s: cannot convert to 2 components\n",
98 curRF);
99 return(0);
100 }
101 if (refm.ncomp == 1) {
102 rop->preop.csym = "Y"; /* XXX big assumption */
103 return(checksymbolic(rop));
104 }
105 if (refm.ncomp == nc &&
106 !memcmp(refm.wlpart, rop->imx.wlpart, sizeof(refm.wlpart)))
107 return(1); /* nothing to do */
108
109 if ((nc <= 3) | (nc > MAXCSAMP) | (refm.ncomp > MAXCSAMP)) {
110 fprintf(stderr, "%s: cannot resample from %d to %d components\n",
111 curRF, nc, refm.ncomp);
112 return(0);
113 }
114 if (!split_input(rop)) /* get our own struct */
115 return(0);
116 rop->preop.clen = refm.ncomp * nc; /* compute spec to ref */
117
118 for (i = 0; i < nc; i++) {
119 SCOLOR scstim, scresp;
120 int j;
121 memset(scstim, 0, sizeof(COLORV)*nc);
122 scstim[i] = 1.f;
123 convertscolor(scresp, refm.ncomp, refm.wlpart[0], refm.wlpart[3],
124 scstim, nc, rop->imx.wlpart[0], rop->imx.wlpart[3]);
125 for (j = refm.ncomp; j-- > 0; )
126 rop->preop.cmat[j*nc + i] = scresp[j];
127 }
128 /* remember new spectral params */
129 memcpy(rop->rmp->wlpart, refm.wlpart, sizeof(rop->rmp->wlpart));
130 rop->rmp->ncomp = refm.ncomp;
131 return(1);
132 }
133
134 /* Compute conversion row from spectrum to one channel of RGB */
135 static void
136 rgbrow(ROPMAT *rop, int r, int p)
137 {
138 const int nc = rop->imx.ncomp;
139 const float * wlp = rop->imx.wlpart;
140 int i;
141
142 for (i = nc; i--; ) {
143 int nmEnd = wlp[0] + (wlp[3] - wlp[0])*i/nc;
144 int nmStart = wlp[0] + (wlp[3] - wlp[0])*(i+1)/nc;
145 COLOR crgb;
146 spec_rgb(crgb, nmStart, nmEnd);
147 rop->preop.cmat[r*nc+i] = crgb[p];
148 }
149 }
150
151 /* Compute conversion row from spectrum to one channel of XYZ */
152 static void
153 xyzrow(ROPMAT *rop, int r, int p)
154 {
155 const int nc = rop->imx.ncomp;
156 const float * wlp = rop->imx.wlpart;
157 int i;
158
159 for (i = nc; i--; ) {
160 int nmEnd = wlp[0] + (wlp[3] - wlp[0])*i/nc;
161 int nmStart = wlp[0] + (wlp[3] - wlp[0])*(i+1)/nc;
162 COLOR cxyz;
163 spec_cie(cxyz, nmStart, nmEnd);
164 rop->preop.cmat[r*nc+i] = cxyz[p];
165 }
166 }
167
168 /* Use the spectral sensitivity function to compute matrix coefficients */
169 static void
170 sensrow(ROPMAT *rop, int r, double (*sf)(SCOLOR sc, int ncs, const float wlpt[4]))
171 {
172 const int nc = rop->imx.ncomp;
173 int i;
174
175 for (i = nc; i--; ) {
176 SCOLOR sclr;
177 memset(sclr, 0, sizeof(COLORV)*nc);
178 sclr[i] = 1.f;
179 rop->preop.cmat[r*nc+i] = (*sf)(sclr, nc, rop->imx.wlpart);
180 }
181 }
182
183 /* Check/set symbolic transform */
184 static int
185 checksymbolic(ROPMAT *rop)
186 {
187 const int nc = rop->imx.ncomp;
188 const int dt = rop->imx.dtype;
189 int i, j;
190 /* check suffix => reference file */
191 if (strchr(rop->preop.csym, '.') > rop->preop.csym)
192 return(checkreffile(rop));
193
194 if (nc < 3) {
195 fprintf(stderr, "%s: -c '%s' requires at least 3 components\n",
196 rop->inspec, rop->preop.csym);
197 return(0);
198 }
199 rop->preop.clen = strlen(rop->preop.csym) * nc;
200 if (rop->preop.clen > MAXCOMP*MAXCOMP) {
201 fprintf(stderr, "%s: -c '%s' results in too many components\n",
202 rop->inspec, rop->preop.csym);
203 return(0);
204 }
205 for (j = 0; rop->preop.csym[j]; j++) {
206 int comp = 0;
207 switch (rop->preop.csym[j]) {
208 case 'B':
209 ++comp;
210 /* fall through */
211 case 'G':
212 ++comp;
213 /* fall through */
214 case 'R':
215 if (dt == DTxyze) {
216 for (i = 3; i--; )
217 rop->preop.cmat[j*nc+i] = 1./WHTEFFICACY *
218 xyz2rgbmat[comp][i];
219 } else if (nc == 3)
220 rop->preop.cmat[j*nc+comp] = 1.;
221 else
222 rgbrow(rop, j, comp);
223 break;
224 case 'Z':
225 ++comp;
226 /* fall through */
227 case 'Y':
228 ++comp;
229 /* fall through */
230 case 'X':
231 if (dt == DTxyze) {
232 rop->preop.cmat[j*nc+comp] = 1.;
233 } else if (nc == 3) {
234 for (i = 3; i--; )
235 rop->preop.cmat[j*nc+i] =
236 rgb2xyzmat[comp][i];
237 } else if (comp == CIEY)
238 sensrow(rop, j, scolor2photopic);
239 else
240 xyzrow(rop, j, comp);
241
242 for (i = nc*(dt != DTxyze); i--; )
243 rop->preop.cmat[j*nc+i] *= WHTEFFICACY;
244 break;
245 case 'S': /* scotopic (il)luminance */
246 sensrow(rop, j, scolor2scotopic);
247 for (i = nc; i--; )
248 rop->preop.cmat[j*nc+i] *= WHTSCOTOPIC;
249 break;
250 case 'M': /* melanopic (il)luminance */
251 sensrow(rop, j, scolor2melanopic);
252 for (i = nc; i--; )
253 rop->preop.cmat[j*nc+i] *= WHTMELANOPIC;
254 break;
255 case 'A': /* average component */
256 for (i = nc; i--; )
257 rop->preop.cmat[j*nc+i] = 1./(double)nc;
258 break;
259 default:
260 fprintf(stderr, "%s: -c '%c' unsupported\n",
261 rop->inspec, rop->preop.csym[j]);
262 return(0);
263 }
264 }
265 if (!split_input(rop)) /* get our own struct */
266 return(0);
267 memcpy(rop->rmp->wlpart, WLPART, sizeof(rop->rmp->wlpart));
268 rop->rmp->ncomp = rop->preop.clen / nc;
269 /* decide on output type */
270 if (!strcmp(rop->preop.csym, "XYZ")) {
271 if (dt <= DTspec)
272 rop->rmp->dtype = DTxyze;
273 } else if (!strcmp(rop->preop.csym, "RGB")) {
274 if (dt <= DTspec)
275 rop->rmp->dtype = DTrgbe;
276 }
277 if (rop->rmp->dtype == DTspec)
278 rop->rmp->dtype = DTfloat;
279 return(1);
280 }
281
282 static int
283 get_component_xfm(ROPMAT *rop)
284 {
285 int i, j;
286
287 if (rop->rmp != &rop->imx) { /* reset destination matrix */
288 rmx_free(rop->rmp);
289 rop->rmp = &rop->imx;
290 }
291 if (rop->preop.csym && /* symbolic transform? */
292 !checksymbolic(rop))
293 return(0);
294 /* undo exposure? */
295 if (fabs(1. - bright(rop->rmp->cexp)) > .025) {
296 if (rop->rmp->ncomp == 1)
297 rop->rmp->cexp[RED] = rop->rmp->cexp[GRN] =
298 rop->rmp->cexp[BLU] = bright(rop->rmp->cexp);
299 if (rop->preop.nsf <= 0) {
300 rop->preop.nsf = i = rop->rmp->ncomp;
301 while (i--)
302 rop->preop.sca[i] = 1.;
303 }
304 if (rop->preop.nsf == 1)
305 rop->preop.sca[0] /= rop->rmp->cexp[GRN];
306 else if (rop->preop.nsf == 3)
307 opcolor(rop->preop.sca, /=, rop->rmp->cexp);
308 else if (rop->preop.nsf > 3) { /* punt */
309 double mult = 1./bright(rop->rmp->cexp);
310 for (i = rop->preop.nsf; i--; )
311 rop->preop.sca[i] *= mult;
312 }
313 setcolor(rop->rmp->cexp, 1., 1., 1.);
314 }
315 if (rop->preop.clen > 0) { /* use component transform? */
316 if (rop->preop.clen % rop->imx.ncomp) {
317 fprintf(stderr, "%s: -c must have N x %d coefficients\n",
318 rop->inspec, rop->imx.ncomp);
319 return(0);
320 }
321 if (rop->preop.nsf > 0) { /* scale transform, instead */
322 if (rop->preop.nsf == 1) {
323 for (i = rop->preop.clen; i--; )
324 rop->preop.cmat[i] *= rop->preop.sca[0];
325 } else if (rop->preop.nsf*rop->imx.ncomp != rop->preop.clen) {
326 fprintf(stderr, "%s: -s must have one or %d factors\n",
327 rop->inspec,
328 rop->preop.clen/rop->imx.ncomp);
329 return(0);
330 } else {
331 for (i = rop->preop.nsf; i--; )
332 for (j = rop->imx.ncomp; j--; )
333 rop->preop.cmat[i*rop->imx.ncomp+j]
334 *= rop->preop.sca[i];
335 }
336 }
337 rop->preop.nsf = 0; /* now folded in */
338 if (!split_input(rop)) /* get our own struct */
339 return(0);
340 rop->rmp->ncomp = rop->preop.clen / rop->imx.ncomp;
341 if (rop->rmp->dtype <= DTspec)
342 rop->rmp->dtype = DTfloat; /* probably not actual spectrum */
343 } else if (rop->preop.nsf > 0) { /* else use scalar(s)? */
344 if (rop->preop.nsf == 1) {
345 for (i = rop->rmp->ncomp; --i; )
346 rop->preop.sca[i] = rop->preop.sca[0];
347 rop->preop.nsf = rop->rmp->ncomp;
348 } else if (rop->preop.nsf != rop->rmp->ncomp) {
349 fprintf(stderr, "%s: -s must have one or %d factors\n",
350 rop->inspec, rop->rmp->ncomp);
351 return(0);
352 }
353 }
354 return(1);
355 }
356
357 static int
358 apply_op(RMATRIX *dst, const RMATRIX *src, const RUNARYOP *ro)
359 {
360 /*
361 if (!dst | !src | !ro || (dst->nrows != src->nrows) |
362 (dst->ncols != src->ncols))
363 return(0);
364 */
365 if (ro->clen > 0) {
366 RMATRIX *res = rmx_transform(src, dst->ncomp, ro->cmat);
367 if (!res) {
368 fputs("Error in call to rmx_transform()\n", stderr);
369 return(0);
370 }
371 if (dst->mtx) free(dst->mtx);
372 dst->mtx = res->mtx; res->mtx = NULL;
373 rmx_free(res);
374 } else if (dst != src)
375 memcpy(dst->mtx, src->mtx,
376 sizeof(double)*dst->ncomp*dst->ncols*dst->nrows);
377 if (ro->nsf == dst->ncomp)
378 rmx_scale(dst, ro->sca);
379 return(1);
380 }
381
382 static int
383 open_input(ROPMAT *rop)
384 {
385 int outtype;
386
387 if (!rop || !rop->inspec || !rop->inspec[0])
388 return(0);
389 if (rop->inspec == stdin_name)
390 rop->infp = stdin;
391 else if (rop->inspec[0] == '!')
392 rop->infp = popen(rop->inspec+1, "r");
393 else
394 rop->infp = fopen(rop->inspec, "rb");
395
396 if (!rmx_load_header(&rop->imx, rop->infp)) {
397 fprintf(stderr, "Bad header from: %s\n", rop->inspec);
398 return(0);
399 }
400 return(get_component_xfm(rop));
401 }
402
403 /* Return nominal wavelength associated with input component (return nm) */
404 static double
405 l_wavelength(char *nam)
406 {
407 double comp = argument(1);
408
409 if ((comp < -.5) | (comp >= in_ncomp+.5)) {
410 errno = EDOM;
411 return(.0);
412 }
413 if (comp < .5) /* asking for #components? */
414 return(in_ncomp);
415
416 if (in_ncomp == 3) { /* special case for RGB */
417 const int w0 = (int)(comp - .5);
418 return(mop[0].rmp->wlpart[w0] +
419 (comp-.5)*(mop[0].rmp->wlpart[w0+1] -
420 mop[0].rmp->wlpart[w0]));
421 }
422 return(mop[0].rmp->wlpart[0] + /* general case, even div. */
423 (comp-.5)/(double)in_ncomp *
424 (mop[0].rmp->wlpart[3] - mop[0].rmp->wlpart[0]));
425 }
426
427 /* Return ith input with optional channel selector */
428 static double
429 l_chanin(char *nam)
430 {
431 double inp = argument(1);
432 int mi, chan;
433
434 if ((mi = (int)(inp-.5)) < 0 || mi >= nmats) {
435 errno = EDOM;
436 return(.0);
437 }
438 if (!mi) /* asking for #inputs? */
439 return(nmats);
440
441 if (nargum() >= 2) {
442 double cval = argument(2);
443 if (cval < .5 || (chan = (int)(cval-.5)) >= in_ncomp) {
444 errno = EDOM;
445 return(.0);
446 }
447 } else
448 chan = cur_chan;
449
450 return(mop[mi].rmp->mtx[cur_col*in_ncomp + chan]);
451 }
452
453 static int
454 initialize(RMATRIX *imp)
455 {
456 int i;
457 /* XXX struct is zeroed coming in */
458 setcolor(imp->cexp, 1.f, 1.f, 1.f);
459 for (i = 0; i < nmats; i++) { /* open each input */
460 int restype;
461 if (!open_input(&mop[i]))
462 return(0);
463 restype = mop[i].rmp->dtype;
464 if (!imp->dtype || (restype = rmx_newtype(restype, imp->dtype)) > 0)
465 imp->dtype = restype;
466 else
467 fprintf(stderr, "%s: warning - data type mismatch\n",
468 mop[i].inspec);
469 if (!i) {
470 imp->nrows = in_nrows = mop[0].rmp->nrows;
471 imp->ncols = mop[0].rmp->ncols;
472 imp->ncomp = mop[0].rmp->ncomp;
473 memcpy(imp->wlpart, mop[0].rmp->wlpart, sizeof(imp->wlpart));
474 } else if ((mop[i].rmp->nrows != imp->nrows) |
475 (mop[i].rmp->ncols != imp->ncols) |
476 (mop[i].rmp->ncomp != imp->ncomp)) {
477 fprintf(stderr, "%s: mismatch in size or #components\n",
478 mop[i].inspec);
479 return(0);
480 } /* XXX should check wlpart? */
481 } /* set up .cal environment */
482 esupport |= E_VARIABLE|E_FUNCTION|E_RCONST;
483 esupport &= ~(E_OUTCHAN|E_INCHAN);
484 varset("PI", ':', M_PI);
485 varset("nfiles", ':', nmats);
486 varset("nrows", ':', in_nrows);
487 varset("ncols", ':', in_ncols);
488 varset("ncomp", ':', in_ncomp);
489 varset("R", ':', 1.);
490 varset("G", ':', 2.);
491 varset("B", ':', 3.);
492 funset("wl", 1, ':', l_wavelength);
493 funset("ci", 1, '=', l_chanin);
494 scompile("ri(i)=ci(i,R);gi(i)=ci(i,G);bi(i)=ci(i,B)", NULL, 0);
495 return(1);
496 }
497
498 static void
499 output_headinfo(FILE *fp)
500 {
501 int i;
502
503 for (i = 0; i < nmats; i++) {
504 const char *cp = mop[i].imx.info;
505 fputs(mop[i].inspec, fp);
506 fputs(":\n", fp);
507 if (!cp) continue;
508 while (*cp) {
509 if (*cp == '\n') {
510 cp++; /* avoid inadvertant terminus */
511 continue;
512 }
513 fputc('\t', fp); /* indent this input's info */
514 do
515 putc(*cp, fp);
516 while (*cp++ != '\n');
517 }
518 }
519 }
520
521 static int
522 combine_input(ROPMAT *res, FILE *fout)
523 {
524 int user_set_r, user_set_c;
525 RMATRIX *tmp = NULL;
526 int co_set;
527 int i;
528 /* allocate input row buffers */
529 for (i = 0; i < nmats; i++) {
530 mop[i].imx.nrows = 1; /* we'll be doing a row at a time */
531 if (!rmx_prepare(&mop[i].imx))
532 goto memerror;
533 if (mop[i].rmp != &mop[i].imx) {
534 mop[i].rmp->nrows = 1;
535 if (!rmx_prepare(mop[i].rmp))
536 goto memerror;
537 }
538 }
539 /* prep output row buffers */
540 if (mcat || res->preop.clen > 0) {
541 if (!split_input(res)) /* need separate buffer */
542 return(0);
543 if (res->preop.clen > 0)
544 res->rmp->ncomp = res->preop.clen / res->imx.ncomp;
545 res->rmp->nrows = 1;
546 if (!mcat | !mcat_last && !rmx_prepare(res->rmp))
547 goto memerror;
548 }
549 if (mcat && mcat_last &&
550 !(tmp = rmx_new(1, res->imx.ncols, res->rmp->ncomp)))
551 goto memerror;
552 res->imx.nrows = 1;
553 if (!rmx_prepare(&res->imx))
554 goto memerror;
555 /* figure out what the user set */
556 co_set = fundefined("co");
557 if (!co_set)
558 co_set = -vardefined("co");
559 if (!co_set & (in_ncomp == 3) && vardefined("ro") &&
560 vardefined("go") && vardefined("bo")) {
561 scompile("co(p)=select(p,ro,go,bo)", NULL, 0);
562 co_set = 1;
563 }
564 user_set_r = vardefined("r");
565 user_set_c = vardefined("c");
566 /* read/process row-by-row */
567 for (cur_row = 0; cur_row < in_nrows; cur_row++) {
568 RMATRIX *mres = NULL;
569 for (i = 0; i < nmats; i++) {
570 if (!rmx_load_row(mop[i].imx.mtx, &mop[i].imx, mop[i].infp))
571 return(0);
572 if (!apply_op(mop[i].rmp, &mop[i].imx, &mop[i].preop))
573 return(0);
574 }
575 if (!user_set_r) varset("r", '=', cur_row);
576 for (cur_col = 0; cur_col < in_ncols; cur_col++) {
577 if (!user_set_c) varset("c", '=', cur_col);
578 for (cur_chan = 0; cur_chan < in_ncomp; cur_chan++) {
579 const int ndx = cur_col*in_ncomp + cur_chan;
580 eclock++;
581 if (!co_set) { /* just summing elements? */
582 res->imx.mtx[ndx] = 0;
583 for (i = nmats; i--; )
584 res->imx.mtx[ndx] += mop[i].rmp->mtx[ndx];
585 } else if (co_set > 0) {
586 double dchan = cur_chan;
587 res->imx.mtx[ndx] = funvalue("co", 1, &dchan);
588 } else
589 res->imx.mtx[ndx] = varvalue("co");
590 }
591 } /* final conversions */
592 if (!mcat) {
593 if (!apply_op(res->rmp, &res->imx, &res->preop))
594 return(0);
595 } else if (mcat_last) {
596 if (!apply_op(tmp, &res->imx, &res->preop))
597 return(0);
598 mres = rmx_multiply(tmp, mcat);
599 if (!mres)
600 goto multerror;
601 if (res->rmp->mtx) free(res->rmp->mtx);
602 res->rmp->mtx = mres->mtx; mres->mtx = NULL;
603 } else /* mcat && !mcat_last */ {
604 mres = rmx_multiply(&res->imx, mcat);
605 if (!mres)
606 goto multerror;
607 if (!apply_op(res->rmp, mres, &res->preop))
608 return(0);
609 }
610 rmx_free(mres); mres = NULL;
611 if (!rmx_write_data(res->rmp->mtx, res->rmp->ncomp,
612 res->rmp->ncols, res->rmp->dtype, fout))
613 return(0);
614 }
615 rmx_free(tmp); /* clean up */
616 if (res->rmp != &res->imx) {
617 rmx_free(res->rmp);
618 res->rmp = NULL;
619 }
620 rmx_reset(&res->imx);
621 return(1);
622 memerror:
623 fputs("Out of buffer space in combine_input()\n", stderr);
624 return(0);
625 multerror:
626 fputs("Unexpected matrix multiply error in combine_input()\n", stderr);
627 return(0);
628 }
629
630 static int
631 get_factors(double da[], int n, char *av[])
632 {
633 int ac;
634
635 for (ac = 0; ac < n && isflt(av[ac]); ac++)
636 da[ac] = atof(av[ac]);
637 return(ac);
638 }
639
640 static void
641 resize_inparr(int n2alloc)
642 {
643 int i;
644
645 for (i = nmats; i > n2alloc; i--) {
646 rmx_reset(&mop[i].imx);
647 if (mop[i].rmp != &mop[i].imx)
648 rmx_free(mop[i].rmp);
649 }
650 mop = (ROPMAT *)realloc(mop, n2alloc*sizeof(ROPMAT));
651 if (mop == NULL) {
652 fputs("Out of memory in resize_inparr()\n", stderr);
653 exit(1);
654 }
655 if (n2alloc > nmats)
656 memset(mop+nmats, 0, (n2alloc-nmats)*sizeof(ROPMAT));
657 nall = n2alloc;
658 }
659
660 /* Load one or more matrices and operate on them, sending results to stdout */
661 int
662 main(int argc, char *argv[])
663 {
664
665 int outfmt = DTfromHeader;
666 const char *defCsym = NULL;
667 int echoheader = 1;
668 int stdin_used = 0;
669 const char *mcat_spec = NULL;
670 int n2comp = 0;
671 uby8 comp_ndx[128];
672 int i;
673 /* get starting input array */
674 mop = (ROPMAT *)calloc(nall=2, sizeof(ROPMAT));
675 /* get options and arguments */
676 for (i = 1; i < argc; i++)
677 if (argv[i][0] != '-' || !argv[i][1]) {
678 if (argv[i][0] == '-') {
679 if (stdin_used++) goto stdin_error;
680 mop[nmats].inspec = stdin_name;
681 } else
682 mop[nmats].inspec = argv[i];
683 if (!mop[nmats].preop.csym)
684 mop[nmats].preop.csym = defCsym;
685 if (++nmats >= nall)
686 resize_inparr(nmats + (nmats>>2) + 2);
687 } else {
688 int n = argc-1 - i;
689 switch (argv[i][1]) { /* get option */
690 case 'w':
691 nowarn = !nowarn;
692 break;
693 case 'h':
694 echoheader = !echoheader;
695 break;
696 case 'e':
697 if (!n) goto userr;
698 comp_ndx[n2comp++] = i++;
699 break;
700 case 'f':
701 switch (argv[i][2]) {
702 case '\0':
703 if (!n) goto userr;
704 comp_ndx[n2comp++] = i++;
705 break;
706 case 'd':
707 outfmt = DTdouble;
708 break;
709 case 'f':
710 outfmt = DTfloat;
711 break;
712 case 'a':
713 outfmt = DTascii;
714 break;
715 case 'c':
716 outfmt = DTrgbe;
717 break;
718 default:
719 goto userr;
720 }
721 break;
722 case 's':
723 if (n > MAXCOMP) n = MAXCOMP;
724 i += mop[nmats].preop.nsf =
725 get_factors(mop[nmats].preop.sca,
726 n, argv+i+1);
727 if (mop[nmats].preop.nsf <= 0) {
728 fprintf(stderr, "%s: -s missing arguments\n",
729 argv[0]);
730 goto userr;
731 }
732 break;
733 case 'C':
734 if (!n || isflt(argv[i+1]))
735 goto userr;
736 defCsym = mop[nmats].preop.csym = argv[++i];
737 mop[nmats].preop.clen = 0;
738 mcat_last = 0;
739 break;
740 case 'c':
741 if (n && !isflt(argv[i+1])) {
742 mop[nmats].preop.csym = argv[++i];
743 mop[nmats].preop.clen = 0;
744 break;
745 }
746 if (n > MAXCOMP*MAXCOMP) n = MAXCOMP*MAXCOMP;
747 i += mop[nmats].preop.clen =
748 get_factors(mop[nmats].preop.cmat,
749 n, argv+i+1);
750 if (mop[nmats].preop.clen <= 0) {
751 fprintf(stderr, "%s: -c missing arguments\n",
752 argv[0]);
753 goto userr;
754 }
755 mop[nmats].preop.csym = NULL;
756 mcat_last = 0;
757 break;
758 case 'm':
759 if (!n) goto userr;
760 if (argv[++i][0] == '-' && !argv[i][1]) {
761 if (stdin_used++) goto stdin_error;
762 mcat_spec = stdin_name;
763 } else
764 mcat_spec = argv[i];
765 mcat_last = 1;
766 break;
767 default:
768 fprintf(stderr, "%s: unknown option '%s'\n",
769 argv[0], argv[i]);
770 goto userr;
771 }
772 }
773 if (!nmats) {
774 fprintf(stderr, "%s: need at least one input matrix\n", argv[0]);
775 goto userr;
776 }
777 resize_inparr(nmats+1); /* extra matrix at end for result */
778 mop[nmats].inspec = "trailing_ops";
779 /* load final concatenation matrix */
780 if (mcat_spec && !(mcat = rmx_load(mcat_spec, RMPnone))) {
781 fprintf(stderr, "%s: error loading concatenation matrix: %s",
782 argv[0], mcat_spec);
783 return(1);
784 }
785 /* get/check inputs, set constants */
786 if (!initialize(&mop[nmats].imx))
787 return(1);
788
789 for (i = 0; i < n2comp; i++) /* user .cal files and expressions */
790 if (argv[comp_ndx[i]][1] == 'f') {
791 char *fpath = getpath(argv[comp_ndx[i]+1],
792 getrlibpath(), 0);
793 if (fpath == NULL) {
794 fprintf(stderr, "%s: cannot find file '%s'\n",
795 argv[0], argv[comp_ndx[i]+1]);
796 return(1);
797 }
798 fcompile(fpath);
799 } else /* (argv[comp_ndx[i]][1] == 'e') */
800 scompile(argv[comp_ndx[i]+1], NULL, 0);
801
802 /* get trailing color transform */
803 if (!get_component_xfm(&mop[nmats]))
804 return(1);
805 /* adjust output dimensions and #components */
806 if (mcat) {
807 if (mop[nmats].imx.ncols != mcat->nrows) {
808 fprintf(stderr,
809 "%s: number of input columns does not match number of rows in '%s'\n",
810 argv[0], mcat_spec);
811 return(1);
812 }
813 if (mcat->ncomp != (mcat_last ? mop[nmats].rmp->ncomp : mop[nmats].imx.ncomp)) {
814 fprintf(stderr,
815 "%s: number of components does not match those in '%s'\n",
816 argv[0], mcat_spec);
817 return(1);
818 }
819 mop[nmats].rmp->ncols = mcat->ncols;
820 }
821 if (outfmt == DTfromHeader) /* set final output format */
822 outfmt = mop[nmats].rmp->dtype;
823 if (outfmt == DTrgbe) {
824 if (mop[nmats].rmp->ncomp > 3)
825 outfmt = DTspec;
826 else if (mop[nmats].rmp->dtype == DTxyze)
827 outfmt = DTxyze;
828 }
829 mop[nmats].rmp->dtype = outfmt;
830 if (outfmt != DTascii)
831 SET_FILE_BINARY(stdout);
832 /* write output header */
833 newheader("RADIANCE", stdout);
834 if (echoheader)
835 output_headinfo(stdout);
836 printargs(argc, argv, stdout);
837 fputnow(stdout);
838 mop[nmats].rmp->dtype = rmx_write_header(mop[nmats].rmp, mop[nmats].rmp->dtype, stdout);
839 if (mop[nmats].rmp->dtype <= 0) {
840 fprintf(stderr, "%s: unexpected error writing header!\n", argv[0]);
841 return(1);
842 }
843 /* process & write rows */
844 return(combine_input(&mop[nmats], stdout) ? 0 : 1);
845 stdin_error:
846 fprintf(stderr, "%s: %s used for more than one input\n",
847 argv[0], stdin_name);
848 return(1);
849 userr:
850 fprintf(stderr,
851 "Usage: %s [-h][-f{adfc}][-e expr][-f file][-s sf .. | -c ce ..] m1 .. -m mcat > mres\n",
852 argv[0]);
853 return(1);
854 }