ViewVC Help
View File | Revision Log | Show Annotations | Download File | Root Listing
root/radiance/ray/src/util/rmtxop.c
Revision: 2.42
Committed: Sat Apr 19 03:58:00 2025 UTC (13 days ago) by greg
Content type: text/plain
Branch: MAIN
CVS Tags: HEAD
Changes since 2.41: +3 -1 lines
Log Message:
fix(rmtxop): Missed error test in last change

File Contents

# Content
1 #ifndef lint
2 static const char RCSid[] = "$Id: rmtxop.c,v 2.41 2025/04/18 23:59:03 greg Exp $";
3 #endif
4 /*
5 * General component matrix operations.
6 */
7
8 #include <errno.h>
9 #include "rtio.h"
10 #include "rmatrix.h"
11 #include "platform.h"
12
13 /* Preferred BSDF component:
14 none, transmission, reflection front (normal side), reflection back */
15 typedef enum {RMPnone=-1, RMPtrans=0, RMPreflF, RMPreflB} RMPref;
16
17 /* Unary matrix operation(s) */
18 typedef struct {
19 double cmat[MAXCOMP*MAXCOMP]; /* component transformation */
20 double sca[MAXCOMP]; /* scalar coefficients */
21 const char *csym; /* symbolic coefs or file */
22 short clen; /* number of coefficients */
23 short nsf; /* number of scalars */
24 short transpose; /* do transpose? */
25 } RUNARYOP;
26
27 /* Matrix input source and requested operation(s) */
28 typedef struct {
29 const char *inspec; /* input specification */
30 RMPref rmp; /* matrix preference */
31 RUNARYOP preop; /* unary operation(s) */
32 RMATRIX *mtx; /* original matrix if loaded */
33 int binop; /* binary op with next (or 0) */
34 } ROPMAT;
35
36 int verbose = 0; /* verbose reporting? */
37
38 /* Load matrix */
39 int
40 loadmatrix(ROPMAT *rop)
41 {
42 if (rop->mtx) /* already loaded? */
43 return(0);
44 /* check for BSDF input */
45 if ((rop->inspec[0] != '!') & (rop->rmp != RMPnone)) {
46 const char *sp = strrchr(rop->inspec, '.');
47 if (sp > rop->inspec && !strcasecmp(sp+1, "XML")) {
48 CMATRIX *cm = rop->rmp==RMPtrans ? cm_loadBTDF(rop->inspec) :
49 cm_loadBRDF(rop->inspec, rop->rmp==RMPreflB) ;
50 if (!cm)
51 return(-1);
52 rop->mtx = rmx_from_cmatrix(cm);
53 cm_free(cm);
54 if (!rop->mtx)
55 return(-1);
56 rop->mtx->dtype = DTascii;
57 return(1); /* loaded BSDF XML file */
58 }
59 } /* else load regular matrix */
60 rop->mtx = rmx_load(rop->inspec);
61
62 return(rop->mtx ? 1 : -1);
63 }
64
65 extern int checksymbolic(ROPMAT *rop);
66
67 /* Check/set transform based on a reference input file */
68 int
69 checkreffile(ROPMAT *rop)
70 {
71 static const char *curRF = NULL;
72 static RMATRIX refm;
73 const int nc = rop->mtx->ncomp;
74 int i;
75
76 if (!curRF || strcmp(rop->preop.csym, curRF)) {
77 FILE *fp = fopen(rop->preop.csym, "rb");
78 if (!rmx_load_header(&refm, fp)) {
79 fprintf(stderr, "%s: cannot read info header\n",
80 rop->preop.csym);
81 curRF = NULL;
82 if (fp) fclose(fp);
83 return(-1);
84 }
85 fclose(fp);
86 curRF = rop->preop.csym;
87 }
88 if (refm.ncomp == 3) {
89 rop->preop.csym = (refm.dtype == DTxyze) ? "XYZ" : "RGB";
90 return(checksymbolic(rop));
91 }
92 if (refm.ncomp == 2) {
93 fprintf(stderr, "%s: cannot convert to 2 components\n",
94 curRF);
95 return(-1);
96 }
97 if (refm.ncomp == 1) {
98 rop->preop.csym = "Y"; /* XXX big assumption */
99 return(checksymbolic(rop));
100 }
101 if (refm.ncomp == nc &&
102 !memcmp(refm.wlpart, rop->mtx->wlpart, sizeof(refm.wlpart)))
103 return(0); /* nothing to do */
104
105 if ((nc <= 3) | (nc > MAXCSAMP) | (refm.ncomp > MAXCSAMP)) {
106 fprintf(stderr, "%s: cannot resample from %d to %d components\n",
107 curRF, nc, refm.ncomp);
108 return(-1);
109 }
110 rop->preop.clen = refm.ncomp * nc; /* compute spec to ref */
111
112 for (i = 0; i < nc; i++) {
113 SCOLOR scstim, scresp;
114 int j;
115 memset(scstim, 0, sizeof(COLORV)*nc);
116 scstim[i] = 1.f;
117 convertscolor(scresp, refm.ncomp, refm.wlpart[0], refm.wlpart[3],
118 scstim, nc, rop->mtx->wlpart[0], rop->mtx->wlpart[3]);
119 for (j = refm.ncomp; j-- > 0; )
120 rop->preop.cmat[j*nc + i] = scresp[j];
121 }
122 memcpy(rop->mtx->wlpart, refm.wlpart, sizeof(rop->mtx->wlpart));
123 return(0);
124 }
125
126 /* Compute conversion row from spectrum to one channel of RGB */
127 void
128 rgbrow(ROPMAT *rop, int r, int p)
129 {
130 const int nc = rop->mtx->ncomp;
131 const float * wlp = rop->mtx->wlpart;
132 int i;
133
134 for (i = nc; i--; ) {
135 int nmEnd = wlp[0] + (wlp[3] - wlp[0])*i/nc;
136 int nmStart = wlp[0] + (wlp[3] - wlp[0])*(i+1)/nc;
137 COLOR crgb;
138 spec_rgb(crgb, nmStart, nmEnd);
139 rop->preop.cmat[r*nc+i] = crgb[p];
140 }
141 }
142
143 /* Compute conversion row from spectrum to one channel of XYZ */
144 void
145 xyzrow(ROPMAT *rop, int r, int p)
146 {
147 const int nc = rop->mtx->ncomp;
148 const float * wlp = rop->mtx->wlpart;
149 int i;
150
151 for (i = nc; i--; ) {
152 int nmEnd = wlp[0] + (wlp[3] - wlp[0])*i/nc;
153 int nmStart = wlp[0] + (wlp[3] - wlp[0])*(i+1)/nc;
154 COLOR cxyz;
155 spec_cie(cxyz, nmStart, nmEnd);
156 rop->preop.cmat[r*nc+i] = cxyz[p];
157 }
158 }
159
160 /* Use the spectral sensitivity function to compute matrix coefficients */
161 void
162 sensrow(ROPMAT *rop, int r, double (*sf)(const SCOLOR sc, int ncs, const float wlpt[4]))
163 {
164 const int nc = rop->mtx->ncomp;
165 int i;
166
167 for (i = nc; i--; ) {
168 SCOLOR sclr;
169 memset(sclr, 0, sizeof(COLORV)*nc);
170 sclr[i] = 1.f;
171 rop->preop.cmat[r*nc+i] = (*sf)(sclr, nc, rop->mtx->wlpart);
172 }
173 }
174
175 /* Check/set symbolic transform */
176 int
177 checksymbolic(ROPMAT *rop)
178 {
179 const int nc = rop->mtx->ncomp;
180 const int dt = rop->mtx->dtype;
181 double cf = 1;
182 int i, j;
183 /* check suffix => reference file */
184 if (strchr(rop->preop.csym, '.') > rop->preop.csym)
185 return(checkreffile(rop));
186
187 if (nc < 3) {
188 fprintf(stderr, "%s: -c '%s' requires at least 3 components\n",
189 rop->inspec, rop->preop.csym);
190 return(-1);
191 }
192 rop->preop.clen = strlen(rop->preop.csym) * nc;
193 if (rop->preop.clen > MAXCOMP*MAXCOMP) {
194 fprintf(stderr, "%s: -c '%s' results in too many components\n",
195 rop->inspec, rop->preop.csym);
196 return(-1);
197 }
198 for (j = 0; rop->preop.csym[j]; j++) {
199 int comp = 0;
200 switch (rop->preop.csym[j]) {
201 case 'B':
202 case 'b':
203 ++comp;
204 /* fall through */
205 case 'G':
206 case 'g':
207 ++comp;
208 /* fall through */
209 case 'R':
210 case 'r':
211 if (rop->preop.csym[j] <= 'Z')
212 cf = 1./WHTEFFICACY;
213 if (dt == DTxyze) {
214 for (i = 3; i--; )
215 rop->preop.cmat[j*nc+i] = cf*xyz2rgbmat[comp][i];
216 } else if (nc == 3)
217 rop->preop.cmat[j*nc+comp] = 1.;
218 else
219 rgbrow(rop, j, comp);
220 break;
221 case 'Z':
222 case 'z':
223 ++comp;
224 /* fall through */
225 case 'Y':
226 case 'y':
227 ++comp;
228 /* fall through */
229 case 'X':
230 case 'x':
231 if ((rop->preop.csym[j] <= 'Z') & (dt != DTxyze))
232 cf = WHTEFFICACY;
233 if (dt == DTxyze) {
234 rop->preop.cmat[j*nc+comp] = 1.;
235 } else if (nc == 3) {
236 for (i = 3; i--; )
237 rop->preop.cmat[j*nc+i] =
238 rgb2xyzmat[comp][i];
239 } else if (comp == CIEY)
240 sensrow(rop, j, scolor2photopic);
241 else
242 xyzrow(rop, j, comp);
243
244 for (i = nc*(cf != 1); i--; )
245 rop->preop.cmat[j*nc+i] *= cf;
246 break;
247 case 'S': /* scotopic (il)luminance */
248 cf = WHTSCOTOPIC;
249 /* fall through */
250 case 's':
251 sensrow(rop, j, scolor2scotopic);
252 for (i = nc*(cf != 1); i--; )
253 rop->preop.cmat[j*nc+i] *= cf;
254 break;
255 case 'M': /* melanopic (il)luminance */
256 cf = WHTMELANOPIC;
257 /* fall through */
258 case 'm':
259 sensrow(rop, j, scolor2melanopic);
260 for (i = nc*(cf != 1); i--; )
261 rop->preop.cmat[j*nc+i] *= cf;
262 break;
263 case 'A': /* average component */
264 case 'a':
265 for (i = nc; i--; )
266 rop->preop.cmat[j*nc+i] = 1./(double)nc;
267 break;
268 default:
269 fprintf(stderr, "%s: -c '%c' unsupported\n",
270 rop->inspec, rop->preop.csym[j]);
271 return(-1);
272 }
273 }
274 /* return recommended output type */
275 if (!strcasecmp(rop->preop.csym, "XYZ")) {
276 if (dt <= DTspec)
277 return(DTxyze);
278 } else if (!strcasecmp(rop->preop.csym, "RGB")) {
279 if (dt <= DTspec)
280 return(DTrgbe);
281 } else if (dt == DTspec)
282 return(DTfloat); /* probably not actual spectrum */
283 return(0);
284 }
285
286 /* Get matrix and perform unary operations */
287 RMATRIX *
288 loadop(ROPMAT *rop)
289 {
290 int outtype = 0;
291 RMATRIX *mres;
292 int i, j;
293
294 if (loadmatrix(rop) < 0) /* make sure we're loaded */
295 return(NULL);
296
297 if (rop->preop.csym && /* symbolic transform? */
298 (outtype = checksymbolic(rop)) < 0)
299 goto failure;
300 if (rop->preop.clen > 0) { /* apply component transform? */
301 if (rop->preop.clen % rop->mtx->ncomp) {
302 fprintf(stderr, "%s: -c must have N x %d coefficients\n",
303 rop->inspec, rop->mtx->ncomp);
304 goto failure;
305 }
306 if (rop->preop.nsf > 0) { /* scale transform, first */
307 if (rop->preop.nsf == 1) {
308 for (i = rop->preop.clen; i--; )
309 rop->preop.cmat[i] *= rop->preop.sca[0];
310 } else if (rop->preop.nsf*rop->mtx->ncomp != rop->preop.clen) {
311 fprintf(stderr, "%s: -s must have one or %d factors\n",
312 rop->inspec,
313 rop->preop.clen/rop->mtx->ncomp);
314 goto failure;
315 } else {
316 for (i = rop->preop.nsf; i--; )
317 for (j = rop->mtx->ncomp; j--; )
318 rop->preop.cmat[i*rop->mtx->ncomp+j]
319 *= rop->preop.sca[i];
320 }
321 }
322 mres = rmx_transform(rop->mtx, rop->preop.clen/rop->mtx->ncomp,
323 rop->preop.cmat);
324 if (mres == NULL) {
325 fprintf(stderr, "%s: matrix transform failed\n",
326 rop->inspec);
327 goto failure;
328 }
329 if (verbose)
330 fprintf(stderr, "%s: applied %d x %d transform%s\n",
331 rop->inspec, mres->ncomp,
332 rop->mtx->ncomp,
333 rop->preop.nsf ? " (* scalar)" : "");
334 rop->preop.nsf = 0; /* now folded in */
335 if ((mres->ncomp > 3) & (mres->dtype <= DTspec))
336 outtype = DTfloat; /* probably not actual spectrum */
337 rmx_free(rop->mtx);
338 rop->mtx = mres;
339 }
340 if (rop->preop.nsf > 0) { /* apply scalar(s)? */
341 if (rop->preop.nsf == 1) {
342 for (i = rop->mtx->ncomp; --i; )
343 rop->preop.sca[i] = rop->preop.sca[0];
344 } else if (rop->preop.nsf != rop->mtx->ncomp) {
345 fprintf(stderr, "%s: -s must have one or %d factors\n",
346 rop->inspec, rop->mtx->ncomp);
347 goto failure;
348 }
349 if (!rmx_scale(rop->mtx, rop->preop.sca)) {
350 fputs(rop->inspec, stderr);
351 fputs(": scalar operation failed\n", stderr);
352 goto failure;
353 }
354 if (verbose) {
355 fputs(rop->inspec, stderr);
356 fputs(": applied scalar (", stderr);
357 for (i = 0; i < rop->preop.nsf; i++)
358 fprintf(stderr, " %f", rop->preop.sca[i]);
359 fputs(" )\n", stderr);
360 }
361 }
362 if (rop->preop.transpose) { /* transpose matrix? */
363 if (!rmx_transpose(rop->mtx)) {
364 fputs(rop->inspec, stderr);
365 fputs(": transpose failed\n", stderr);
366 goto failure;
367 }
368 if (verbose) {
369 fputs(rop->inspec, stderr);
370 fputs(": transposed rows and columns\n", stderr);
371 }
372 }
373 mres = rop->mtx;
374 rop->mtx = NULL;
375 if (outtype)
376 mres->dtype = outtype;
377 return(mres);
378 failure:
379 rmx_free(rop->mtx);
380 return(rop->mtx = NULL);
381 }
382
383 /* Execute binary operation, free matrix arguments and return new result */
384 RMATRIX *
385 binaryop(const char *inspec, RMATRIX *mleft, int op, RMATRIX *mright)
386 {
387 RMATRIX *mres = NULL;
388 int i;
389
390 if ((mleft == NULL) | (mright == NULL))
391 return(NULL);
392 switch (op) {
393 case '.': /* concatenate */
394 if (mleft->ncomp != mright->ncomp) {
395 fputs(inspec, stderr);
396 fputs(": # components do not match\n", stderr);
397 } else if (mleft->ncols != mright->nrows) {
398 fputs(inspec, stderr);
399 fputs(": mismatched dimensions\n",
400 stderr);
401 } else
402 mres = rmx_multiply(mleft, mright);
403 rmx_free(mleft);
404 rmx_free(mright);
405 if (mres == NULL) {
406 fputs(inspec, stderr);
407 fputs(": concatenation failed\n", stderr);
408 return(NULL);
409 }
410 if (verbose) {
411 fputs(inspec, stderr);
412 fputs(": concatenated matrix\n", stderr);
413 }
414 break;
415 case '+':
416 if (!rmx_sum(mleft, mright, NULL)) {
417 fputs(inspec, stderr);
418 fputs(": matrix sum failed\n", stderr);
419 rmx_free(mleft);
420 rmx_free(mright);
421 return(NULL);
422 }
423 if (verbose) {
424 fputs(inspec, stderr);
425 fputs(": added in matrix\n", stderr);
426 }
427 rmx_free(mright);
428 mres = mleft;
429 break;
430 case '*':
431 case '/': {
432 const char * tnam = (op == '/') ?
433 "division" : "multiplication";
434 errno = 0;
435 if (!rmx_elemult(mleft, mright, (op == '/'))) {
436 fprintf(stderr, "%s: element-wise %s failed\n",
437 inspec, tnam);
438 rmx_free(mleft);
439 rmx_free(mright);
440 return(NULL);
441 }
442 if (errno)
443 fprintf(stderr,
444 "%s: warning - error during element-wise %s\n",
445 inspec, tnam);
446 else if (verbose)
447 fprintf(stderr, "%s: element-wise %s\n", inspec, tnam);
448 rmx_free(mright);
449 mres = mleft;
450 } break;
451 default:
452 fprintf(stderr, "%s: unknown operation '%c'\n", inspec, op);
453 rmx_free(mleft);
454 rmx_free(mright);
455 return(NULL);
456 }
457 return(mres);
458 }
459
460 /* Perform matrix operations from left to right */
461 RMATRIX *
462 op_left2right(ROPMAT *mop)
463 {
464 RMATRIX *mleft = loadop(mop);
465
466 while (mop->binop) {
467 if (mleft == NULL)
468 break;
469 mleft = binaryop(mop[1].inspec,
470 mleft, mop->binop, loadop(mop+1));
471 mop++;
472 }
473 return(mleft);
474 }
475
476 /* Perform matrix operations from right to left */
477 RMATRIX *
478 op_right2left(ROPMAT *mop)
479 {
480 RMATRIX *mright;
481 int rpos = 0;
482 /* find end of list */
483 while (mop[rpos].binop)
484 if (mop[rpos++].binop != '.') {
485 fputs(
486 "Right-to-left evaluation only for matrix multiplication!\n",
487 stderr);
488 return(NULL);
489 }
490 mright = loadop(mop+rpos);
491 while (rpos-- > 0) {
492 if (mright == NULL)
493 break;
494 mright = binaryop(mop[rpos+1].inspec,
495 loadop(mop+rpos), mop[rpos].binop, mright);
496 }
497 return(mright);
498 }
499
500 #define t_nrows(mop) ((mop)->preop.transpose ? (mop)->mtx->ncols \
501 : (mop)->mtx->nrows)
502 #define t_ncols(mop) ((mop)->preop.transpose ? (mop)->mtx->nrows \
503 : (mop)->mtx->ncols)
504
505 /* Should we prefer concatenating from rightmost matrix towards left? */
506 int
507 prefer_right2left(ROPMAT *mop)
508 {
509 int mri = 0;
510
511 while (mop[mri].binop) /* find rightmost matrix */
512 if (mop[mri++].binop != '.')
513 return(0); /* pre-empt reversal for other ops */
514
515 if (mri <= 1)
516 return(0); /* won't matter */
517
518 if (loadmatrix(mop+mri) < 0) /* load rightmost cat */
519 return(1); /* fail will bail in a moment */
520
521 if (t_ncols(mop+mri) == 1)
522 return(1); /* definitely better R->L */
523
524 if (t_ncols(mop+mri) >= t_nrows(mop+mri))
525 return(0); /* ...probably worse */
526
527 if (loadmatrix(mop) < 0) /* load leftmost */
528 return(0); /* fail will bail in a moment */
529
530 return(t_ncols(mop+mri) < t_nrows(mop));
531 }
532
533 int
534 get_factors(double da[], int n, char *av[])
535 {
536 int ac;
537
538 for (ac = 0; ac < n && isflt(av[ac]); ac++)
539 da[ac] = atof(av[ac]);
540 return(ac);
541 }
542
543 ROPMAT *
544 resize_moparr(ROPMAT *mop, int n2alloc)
545 {
546 int nmats = 0;
547 int i;
548
549 while (mop[nmats++].binop)
550 ;
551 for (i = nmats; i >= n2alloc; i--)
552 rmx_free(mop[i].mtx);
553 mop = (ROPMAT *)realloc(mop, n2alloc*sizeof(ROPMAT));
554 if (mop == NULL) {
555 fputs("Out of memory in resize_moparr()\n", stderr);
556 exit(1);
557 }
558 if (n2alloc > nmats)
559 memset(mop+nmats, 0, (n2alloc-nmats)*sizeof(ROPMAT));
560 return(mop);
561 }
562
563 /* Load one or more matrices and operate on them, sending results to stdout */
564 int
565 main(int argc, char *argv[])
566 {
567 int outfmt = DTfromHeader;
568 const char *defCsym = NULL;
569 int nall = 2;
570 ROPMAT *mop = (ROPMAT *)calloc(nall, sizeof(ROPMAT));
571 int nmats = 0;
572 RMATRIX *mres = NULL;
573 int stdin_used = 0;
574 int i;
575 /* get options and arguments */
576 for (i = 1; i < argc; i++) {
577 if (argv[i][0] && !argv[i][1] &&
578 strchr(".+*/", argv[i][0]) != NULL) {
579 if (!nmats || mop[nmats-1].binop) {
580 fprintf(stderr,
581 "%s: missing matrix argument before '%c' operation\n",
582 argv[0], argv[i][0]);
583 return(1);
584 }
585 mop[nmats-1].binop = argv[i][0];
586 } else if (argv[i][0] != '-' || !argv[i][1]) {
587 if (argv[i][0] == '-') {
588 if (stdin_used++) {
589 fprintf(stderr,
590 "%s: standard input used for more than one matrix\n",
591 argv[0]);
592 return(1);
593 }
594 mop[nmats].inspec = stdin_name;
595 } else
596 mop[nmats].inspec = argv[i];
597 if (!mop[nmats].preop.csym)
598 mop[nmats].preop.csym = defCsym;
599 if (nmats > 0 && !mop[nmats-1].binop)
600 mop[nmats-1].binop = '.';
601 nmats++;
602 } else {
603 int n = argc-1 - i;
604 switch (argv[i][1]) { /* get option */
605 case 'v':
606 verbose++;
607 break;
608 case 'f':
609 switch (argv[i][2]) {
610 case 'd':
611 outfmt = DTdouble;
612 break;
613 case 'f':
614 outfmt = DTfloat;
615 break;
616 case 'a':
617 outfmt = DTascii;
618 break;
619 case 'c':
620 outfmt = DTrgbe;
621 break;
622 default:
623 goto userr;
624 }
625 break;
626 case 't':
627 mop[nmats].preop.transpose = 1;
628 break;
629 case 's':
630 if (n > MAXCOMP) n = MAXCOMP;
631 i += mop[nmats].preop.nsf =
632 get_factors(mop[nmats].preop.sca,
633 n, argv+i+1);
634 if (mop[nmats].preop.nsf <= 0) {
635 fprintf(stderr, "%s: -s missing arguments\n",
636 argv[0]);
637 goto userr;
638 }
639 break;
640 case 'C':
641 if (!n || isflt(argv[i+1]))
642 goto userr;
643 defCsym = mop[nmats].preop.csym = argv[++i];
644 mop[nmats].preop.clen = 0;
645 break;
646 case 'c':
647 if (n && !isflt(argv[i+1])) {
648 mop[nmats].preop.csym = argv[++i];
649 mop[nmats].preop.clen = 0;
650 break;
651 }
652 if (n > MAXCOMP*MAXCOMP) n = MAXCOMP*MAXCOMP;
653 i += mop[nmats].preop.clen =
654 get_factors(mop[nmats].preop.cmat,
655 n, argv+i+1);
656 if (mop[nmats].preop.clen <= 0) {
657 fprintf(stderr, "%s: -c missing arguments\n",
658 argv[0]);
659 goto userr;
660 }
661 mop[nmats].preop.csym = NULL;
662 break;
663 case 'r':
664 if (argv[i][2] == 'f')
665 mop[nmats].rmp = RMPreflF;
666 else if (argv[i][2] == 'b')
667 mop[nmats].rmp = RMPreflB;
668 else
669 goto userr;
670 break;
671 default:
672 fprintf(stderr, "%s: unknown operation '%s'\n",
673 argv[0], argv[i]);
674 goto userr;
675 }
676 }
677 if (nmats >= nall)
678 mop = resize_moparr(mop, nall += 2);
679 }
680 if (mop[0].inspec == NULL) /* nothing to do? */
681 goto userr;
682 if (mop[nmats-1].binop) {
683 fprintf(stderr,
684 "%s: missing matrix argument after '%c' operation\n",
685 argv[0], mop[nmats-1].binop);
686 return(1);
687 }
688 /* favor quicker concatenation */
689 mop[nmats].mtx = prefer_right2left(mop) ? op_right2left(mop)
690 : op_left2right(mop);
691 if (mop[nmats].mtx == NULL)
692 return(1);
693 /* apply trailing unary operations */
694 mop[nmats].inspec = "trailing_ops";
695 mres = loadop(mop+nmats);
696 if (mres == NULL)
697 return(1);
698 if ((outfmt == DTfromHeader) & (mres->dtype < DTspec))
699 outfmt = mres->dtype;
700 if (outfmt == DTrgbe) { /* check data type */
701 if (mres->ncomp > 3)
702 outfmt = DTspec;
703 else if (mres->dtype == DTxyze)
704 outfmt = DTxyze;
705 }
706 #if DTrmx_native==DTfloat
707 if (outfmt == DTdouble)
708 fprintf(stderr,
709 "%s: warning - writing float result as double\n",
710 argv[0]);
711 #endif
712 newheader("RADIANCE", stdout); /* write result to stdout */
713 printargs(argc, argv, stdout);
714 return(rmx_write(mres, outfmt, stdout) ? 0 : 1);
715 userr:
716 fprintf(stderr,
717 "Usage: %s [-v][-f{adfc}][-t][-s sf .. | -c ce ..][-rf|-rb] m1 [.+*/] .. > mres\n",
718 argv[0]);
719 return(1);
720 }