ViewVC Help
View File | Revision Log | Show Annotations | Download File | Root Listing
root/radiance/ray/src/util/rmtxop.c
Revision: 2.33
Committed: Thu May 16 18:59:19 2024 UTC (21 hours, 40 minutes ago) by greg
Content type: text/plain
Branch: MAIN
CVS Tags: HEAD
Changes since 2.32: +1 -2 lines
Error occurred while calculating annotation data.
Log Message:
fix: Made use of resolu.h more consistent and reliable

File Contents

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