ViewVC Help
View File | Revision Log | Show Annotations | Download File | Root Listing
root/radiance/ray/src/util/rmatrix.c
Revision: 2.65
Committed: Tue Nov 28 21:07:20 2023 UTC (4 months, 4 weeks ago) by greg
Content type: text/plain
Branch: MAIN
Changes since 2.64: +80 -93 lines
Log Message:
refactor: Added ability to read matrix data a row at a time

File Contents

# Content
1 #ifndef lint
2 static const char RCSid[] = "$Id: rmatrix.c,v 2.64 2023/11/28 16:19:31 greg Exp $";
3 #endif
4 /*
5 * General matrix operations.
6 */
7
8 #include <stdlib.h>
9 #include <errno.h>
10 #include "rtio.h"
11 #include "platform.h"
12 #include "resolu.h"
13 #include "paths.h"
14 #include "rmatrix.h"
15 #if !defined(_WIN32) && !defined(_WIN64)
16 #include <sys/mman.h>
17 #endif
18
19 static const char rmx_mismatch_warn[] = "WARNING: data type mismatch\n";
20
21 #define array_size(rm) (sizeof(double)*(rm)->nrows*(rm)->ncols*(rm)->ncomp)
22 #define mapped_size(rm) ((char *)(rm)->mtx + array_size(rm) - (char *)(rm)->mapped)
23
24 /* Initialize a RMATRIX struct but don't allocate array space */
25 RMATRIX *
26 rmx_new(int nr, int nc, int n)
27 {
28 RMATRIX *dnew;
29
30 if (n <= 0)
31 return(NULL);
32
33 dnew = (RMATRIX *)calloc(1, sizeof(RMATRIX));
34 if (dnew) {
35 dnew->dtype = DTdouble;
36 dnew->nrows = nr;
37 dnew->ncols = nc;
38 dnew->ncomp = n;
39 setcolor(dnew->cexp, 1.f, 1.f, 1.f);
40 memcpy(dnew->wlpart, WLPART, sizeof(dnew->wlpart));
41 }
42 return(dnew);
43 }
44
45 /* Prepare a RMATRIX for writing (allocate array if needed) */
46 int
47 rmx_prepare(RMATRIX *rm)
48 {
49 if (!rm) return(0);
50 if (rm->mtx)
51 return(1);
52 if ((rm->nrows <= 0) | (rm->ncols <= 0) | (rm->ncomp <= 0))
53 return(0);
54 rm->mtx = (double *)malloc(array_size(rm));
55 return(rm->mtx != NULL);
56 }
57
58 /* Call rmx_new() and rmx_prepare() */
59 RMATRIX *
60 rmx_alloc(int nr, int nc, int n)
61 {
62 RMATRIX *dnew = rmx_new(nr, nc, n);
63
64 if (dnew && !rmx_prepare(dnew)) {
65 rmx_free(dnew);
66 dnew = NULL;
67 }
68 return(dnew);
69 }
70
71 /* Free a RMATRIX array */
72 void
73 rmx_free(RMATRIX *rm)
74 {
75 if (!rm) return;
76 if (rm->info)
77 free(rm->info);
78 #ifdef MAP_FILE
79 if (rm->mapped)
80 munmap(rm->mapped, mapped_size(rm));
81 else
82 #endif
83 free(rm->mtx);
84 free(rm);
85 }
86
87 /* Resolve data type based on two input types (returns 0 for mismatch) */
88 int
89 rmx_newtype(int dtyp1, int dtyp2)
90 {
91 if ((dtyp1==DTxyze) | (dtyp1==DTrgbe) | (dtyp1==DTspec) |
92 (dtyp2==DTxyze) | (dtyp2==DTrgbe) | (dtyp2==DTspec)
93 && dtyp1 != dtyp2)
94 return(0);
95 if (dtyp1 < dtyp2)
96 return(dtyp1);
97 return(dtyp2);
98 }
99
100 /* Append header information associated with matrix data */
101 int
102 rmx_addinfo(RMATRIX *rm, const char *info)
103 {
104 int oldlen = 0;
105
106 if (!rm || !info || !*info)
107 return(0);
108 if (!rm->info) {
109 rm->info = (char *)malloc(strlen(info)+1);
110 if (rm->info) rm->info[0] = '\0';
111 } else {
112 oldlen = strlen(rm->info);
113 rm->info = (char *)realloc(rm->info,
114 oldlen+strlen(info)+1);
115 }
116 if (!rm->info)
117 return(0);
118 strcpy(rm->info+oldlen, info);
119 return(1);
120 }
121
122 static int
123 get_dminfo(char *s, void *p)
124 {
125 RMATRIX *ip = (RMATRIX *)p;
126 char fmt[MAXFMTLEN];
127 int i;
128
129 if (headidval(NULL, s))
130 return(0);
131 if (isncomp(s)) {
132 ip->ncomp = ncompval(s);
133 return(0);
134 }
135 if (!strncmp(s, "NROWS=", 6)) {
136 ip->nrows = atoi(s+6);
137 return(0);
138 }
139 if (!strncmp(s, "NCOLS=", 6)) {
140 ip->ncols = atoi(s+6);
141 return(0);
142 }
143 if ((i = isbigendian(s)) >= 0) {
144 ip->swapin = (nativebigendian() != i);
145 return(0);
146 }
147 if (isexpos(s)) {
148 float f = exposval(s);
149 scalecolor(ip->cexp, f);
150 return(0);
151 }
152 if (iscolcor(s)) {
153 COLOR ctmp;
154 colcorval(ctmp, s);
155 multcolor(ip->cexp, ctmp);
156 return(0);
157 }
158 if (iswlsplit(s)) {
159 wlsplitval(ip->wlpart, s);
160 return(0);
161 }
162 if (!formatval(fmt, s)) {
163 rmx_addinfo(ip, s);
164 return(0);
165 } /* else check format */
166 for (i = 1; i < DTend; i++)
167 if (!strcmp(fmt, cm_fmt_id[i])) {
168 ip->dtype = i;
169 return(0);
170 }
171 return(-1);
172 }
173
174 static int
175 rmx_load_ascii(double *drp, const RMATRIX *rm, FILE *fp)
176 {
177 int j, k;
178
179 for (j = 0; j < rm->ncols; j++)
180 for (k = rm->ncomp; k-- > 0; )
181 if (fscanf(fp, "%lf", drp++) != 1)
182 return(0);
183 return(1);
184 }
185
186 static int
187 rmx_load_float(double *drp, const RMATRIX *rm, FILE *fp)
188 {
189 int j, k;
190 float val[100];
191
192 if (rm->ncomp > 100) {
193 fputs("Unsupported # components in rmx_load_float()\n", stderr);
194 exit(1);
195 }
196 for (j = 0; j < rm->ncols; j++) {
197 if (getbinary(val, sizeof(val[0]), rm->ncomp, fp) != rm->ncomp)
198 return(0);
199 if (rm->swapin)
200 swap32((char *)val, rm->ncomp);
201 for (k = 0; k < rm->ncomp; k++)
202 *drp++ = val[k];
203 }
204 return(1);
205 }
206
207 static int
208 rmx_load_double(double *drp, const RMATRIX *rm, FILE *fp)
209 {
210 if (getbinary(drp, sizeof(*drp)*rm->ncomp, rm->ncols, fp) != rm->ncols)
211 return(0);
212 if (rm->swapin)
213 swap64((char *)drp, rm->ncols*rm->ncomp);
214 return(1);
215 }
216
217 static int
218 rmx_load_rgbe(double *drp, const RMATRIX *rm, FILE *fp)
219 {
220 COLR *scan;
221 COLOR col;
222 int j;
223
224 if (rm->ncomp != 3)
225 return(0);
226 scan = (COLR *)tempbuffer(sizeof(COLR)*rm->ncols);
227 if (!scan)
228 return(0);
229 if (freadcolrs(scan, rm->ncols, fp) < 0)
230 return(0);
231 for (j = 0; j < rm->ncols; j++) {
232 colr_color(col, scan[j]);
233 *drp++ = colval(col,RED);
234 *drp++ = colval(col,GRN);
235 *drp++ = colval(col,BLU);
236 }
237 return(1);
238 }
239
240 static int
241 rmx_load_spec(double *drp, const RMATRIX *rm, FILE *fp)
242 {
243 uby8 *scan;
244 SCOLOR scol;
245 int j, k;
246
247 if ((rm->ncomp < 3) | (rm->ncomp > MAXCSAMP))
248 return(0);
249 scan = (uby8 *)tempbuffer((rm->ncomp+1)*rm->ncols);
250 if (!scan)
251 return(0);
252 if (freadscolrs(scan, rm->ncomp, rm->ncols, fp) < 0)
253 return(0);
254 for (j = 0; j < rm->ncols; j++) {
255 scolr2scolor(scol, scan+j*(rm->ncomp+1), rm->ncomp);
256 for (k = 0; k < rm->ncomp; k++)
257 *drp++ = scol[k];
258 }
259 return(1);
260 }
261
262 /* Read matrix header from input stream (cannot be XML) */
263 int
264 rmx_load_header(RMATRIX *rm, FILE *fp)
265 {
266 if (!rm | !fp)
267 return(0);
268 if (rm->info) { /* clear state */
269 free(rm->info);
270 rm->info = NULL;
271 }
272 if (rm->mtx) { /* ...and data */
273 #ifdef MAP_FILE
274 if (rm->mapped) {
275 munmap(rm->mapped, mapped_size(rm));
276 rm->mapped = NULL;
277 } else
278 #endif
279 free(rm->mtx);
280 rm->mtx = NULL;
281 }
282 if (rm->nrows | rm->ncols | !rm->dtype) {
283 rm->nrows = rm->ncols = 0;
284 rm->ncomp = 3;
285 setcolor(rm->cexp, 1.f, 1.f, 1.f);
286 memcpy(rm->wlpart, WLPART, sizeof(rm->wlpart));
287 rm->swapin = 0;
288 }
289 SET_FILE_BINARY(fp);
290 rm->dtype = DTascii; /* assumed w/o FORMAT */
291 if (getheader(fp, get_dminfo, rm) < 0) {
292 fputs("Unrecognized matrix format\n", stderr);
293 return(0);
294 }
295 /* resolution string? */
296 if ((rm->nrows <= 0) | (rm->ncols <= 0)) {
297 if (!fscnresolu(&rm->ncols, &rm->nrows, fp))
298 return(0);
299 if ((rm->dtype == DTrgbe) | (rm->dtype == DTxyze) &&
300 rm->ncomp != 3)
301 return(0);
302 }
303 return(1);
304 }
305
306 /* Load next row as double (cannot be XML) */
307 int
308 rmx_load_row(double *drp, const RMATRIX *rm, FILE *fp)
309 {
310 switch (rm->dtype) {
311 case DTascii:
312 return(rmx_load_ascii(drp, rm, fp));
313 case DTfloat:
314 return(rmx_load_float(drp, rm, fp));
315 case DTdouble:
316 return(rmx_load_double(drp, rm, fp));
317 case DTrgbe:
318 case DTxyze:
319 return(rmx_load_rgbe(drp, rm, fp));
320 case DTspec:
321 return(rmx_load_spec(drp, rm, fp));
322 default:
323 fputs("Unsupported data type in rmx_load_row()\n", stderr);
324 }
325 return(0);
326 }
327
328 /* Allocate & load post-header data from stream given type set in rm->dtype */
329 int
330 rmx_load_data(RMATRIX *rm, FILE *fp)
331 {
332 int i;
333 #ifdef MAP_FILE
334 long pos; /* map memory for file > 1MB if possible */
335 if ((rm->dtype == DTdouble) & !rm->swapin && array_size(rm) >= 1L<<20 &&
336 (pos = ftell(fp)) >= 0 && !(pos % sizeof(double))) {
337 rm->mapped = mmap(NULL, array_size(rm)+pos, PROT_READ|PROT_WRITE,
338 MAP_PRIVATE, fileno(fp), 0);
339 if (rm->mapped != MAP_FAILED) {
340 rm->mtx = (double *)rm->mapped + pos/sizeof(double);
341 return(1);
342 } /* else fall back on reading into memory */
343 rm->mapped = NULL;
344 }
345 #endif
346 if (!rmx_prepare(rm)) { /* need in-core matrix array */
347 fprintf(stderr, "Cannot allocate %g MByte matrix array\n",
348 (1./(1L<<20))*(double)array_size(rm));
349 return(0);
350 }
351 if (rm->dtype == DTascii)
352 SET_FILE_TEXT(fp);
353 for (i = 0; i < rm->nrows; i++)
354 if (!rmx_load_row(rmx_lval(rm,i,0), rm, fp))
355 return(0);
356 return(1);
357 }
358
359 /* Load matrix from supported file type */
360 RMATRIX *
361 rmx_load(const char *inspec, RMPref rmp)
362 {
363 FILE *fp;
364 RMATRIX *dnew;
365 int ok;
366
367 if (!inspec)
368 inspec = stdin_name;
369 else if (!*inspec)
370 return(NULL);
371 if (inspec == stdin_name) /* reading from stdin? */
372 fp = stdin;
373 else if (inspec[0] == '!')
374 fp = popen(inspec+1, "r");
375 else {
376 const char *sp = inspec; /* check suffix */
377 while (*sp)
378 ++sp;
379 while (sp > inspec && sp[-1] != '.')
380 --sp;
381 if (!strcasecmp(sp, "XML")) { /* assume it's a BSDF */
382 CMATRIX *cm = rmp==RMPtrans ? cm_loadBTDF(inspec) :
383 cm_loadBRDF(inspec, rmp==RMPreflB) ;
384 if (!cm)
385 return(NULL);
386 dnew = rmx_from_cmatrix(cm);
387 cm_free(cm);
388 dnew->dtype = DTascii;
389 return(dnew); /* return here */
390 } /* else open it ourselves */
391 fp = fopen(inspec, "r");
392 }
393 if (!fp)
394 return(NULL);
395 #ifdef getc_unlocked
396 flockfile(fp);
397 #endif
398 /* load header info */
399 if (!rmx_load_header(dnew = rmx_new(0,0,3), fp)) {
400 fprintf(stderr, "Bad header in: %s\n", inspec);
401 if (inspec[0] == '!') pclose(fp);
402 else fclose(fp);
403 rmx_free(dnew);
404 return(NULL);
405 }
406 ok = rmx_load_data(dnew, fp); /* allocate & load data */
407
408 if (fp != stdin) { /* close input stream */
409 if (inspec[0] == '!')
410 pclose(fp);
411 else
412 fclose(fp);
413 }
414 #ifdef getc_unlocked
415 else
416 funlockfile(fp);
417 #endif
418 if (!ok) { /* load failure? */
419 fprintf(stderr, "Error loading data from: %s\n", inspec);
420 rmx_free(dnew);
421 return(NULL);
422 }
423 /* undo exposure? */
424 if ((dnew->cexp[0] != 1.f) |
425 (dnew->cexp[1] != 1.f) | (dnew->cexp[2] != 1.f)) {
426 double cmlt[MAXCSAMP];
427 int i;
428 cmlt[0] = 1./dnew->cexp[0];
429 cmlt[1] = 1./dnew->cexp[1];
430 cmlt[2] = 1./dnew->cexp[2];
431 if (dnew->ncomp > MAXCSAMP) {
432 fprintf(stderr, "Excess spectral components in: %s\n",
433 inspec);
434 rmx_free(dnew);
435 return(NULL);
436 }
437 for (i = dnew->ncomp; i-- > 3; )
438 cmlt[i] = cmlt[1];
439 rmx_scale(dnew, cmlt);
440 setcolor(dnew->cexp, 1.f, 1.f, 1.f);
441 }
442 return(dnew);
443 }
444
445 static int
446 rmx_write_ascii(const RMATRIX *rm, FILE *fp)
447 {
448 const char *fmt = (rm->dtype == DTfloat) ? " %.7e" :
449 (rm->dtype == DTrgbe) | (rm->dtype == DTxyze) |
450 (rm->dtype == DTspec) ? " %.3e" : " %.15e" ;
451 int i, j, k;
452
453 for (i = 0; i < rm->nrows; i++) {
454 for (j = 0; j < rm->ncols; j++) {
455 const double *dp = rmx_val(rm,i,j);
456 for (k = 0; k < rm->ncomp; k++)
457 fprintf(fp, fmt, dp[k]);
458 fputc('\t', fp);
459 }
460 fputc('\n', fp);
461 }
462 return(1);
463 }
464
465 static int
466 rmx_write_float(const RMATRIX *rm, FILE *fp)
467 {
468 int i, j, k;
469 float val[100];
470
471 if (rm->ncomp > 100) {
472 fputs("Unsupported # components in rmx_write_float()\n", stderr);
473 exit(1);
474 }
475 for (i = 0; i < rm->nrows; i++)
476 for (j = 0; j < rm->ncols; j++) {
477 const double *dp = rmx_val(rm,i,j);
478 for (k = rm->ncomp; k--; )
479 val[k] = (float)dp[k];
480 if (putbinary(val, sizeof(float), rm->ncomp, fp) != rm->ncomp)
481 return(0);
482 }
483 return(1);
484 }
485
486 static int
487 rmx_write_double(const RMATRIX *rm, FILE *fp)
488 {
489 int i;
490
491 for (i = 0; i < rm->nrows; i++)
492 if (putbinary(rmx_val(rm,i,0), sizeof(double)*rm->ncomp,
493 rm->ncols, fp) != rm->ncols)
494 return(0);
495 return(1);
496 }
497
498 static int
499 rmx_write_rgbe(const RMATRIX *rm, FILE *fp)
500 {
501 COLR *scan = (COLR *)malloc(sizeof(COLR)*rm->ncols);
502 int i, j;
503
504 if (!scan)
505 return(0);
506 for (i = 0; i < rm->nrows; i++) {
507 for (j = rm->ncols; j--; ) {
508 const double *dp = rmx_val(rm,i,j);
509 if (rm->ncomp == 1)
510 setcolr(scan[j], dp[0], dp[0], dp[0]);
511 else
512 setcolr(scan[j], dp[0], dp[1], dp[2]);
513 }
514 if (fwritecolrs(scan, rm->ncols, fp) < 0) {
515 free(scan);
516 return(0);
517 }
518 }
519 free(scan);
520 return(1);
521 }
522
523 static int
524 rmx_write_spec(const RMATRIX *rm, FILE *fp)
525 {
526 uby8 *scan = (uby8 *)malloc((rm->ncomp+1)*rm->ncols);
527 int ok = 1;
528 SCOLOR scol;
529 int i, j, k;
530
531 if (!scan)
532 return(0);
533 for (i = 0; i < rm->nrows; i++) {
534 for (j = rm->ncols; j--; ) {
535 const double *dp = rmx_val(rm,i,j);
536 for (k = rm->ncomp; k--; )
537 scol[k] = dp[k];
538 scolor2scolr(scan+j*(rm->ncomp+1), scol, rm->ncomp);
539 }
540 if (fwritescolrs(scan, rm->ncomp, rm->ncols, fp) < 0) {
541 ok = 0;
542 break;
543 }
544 }
545 free(scan);
546 return(ok);
547 }
548
549 /* Check if CIE XYZ primaries were specified */
550 static int
551 findCIEprims(const char *info)
552 {
553 RGBPRIMS prims;
554
555 if (!info)
556 return(0);
557 info = strstr(info, PRIMARYSTR);
558 if (!info || !primsval(prims, info))
559 return(0);
560
561 return((prims[RED][CIEX] > .99) & (prims[RED][CIEY] < .01) &&
562 (prims[GRN][CIEX] < .01) & (prims[GRN][CIEY] > .99) &&
563 (prims[BLU][CIEX] < .01) & (prims[BLU][CIEY] < .01));
564 }
565
566 /* Write matrix to file type indicated by dtype */
567 int
568 rmx_write(const RMATRIX *rm, int dtype, FILE *fp)
569 {
570 int ok = 1;
571
572 if (!rm | !fp || !rm->mtx)
573 return(0);
574 #ifdef getc_unlocked
575 flockfile(fp);
576 #endif
577 if (rm->info) /* complete header */
578 fputs(rm->info, fp);
579 if (dtype == DTfromHeader)
580 dtype = rm->dtype;
581 else if (dtype == DTrgbe && (rm->dtype == DTxyze ||
582 findCIEprims(rm->info)))
583 dtype = DTxyze;
584 else if ((dtype == DTxyze) & (rm->dtype == DTrgbe))
585 dtype = DTrgbe;
586 /* write exposure? */
587 if (rm->ncomp == 3 && (rm->cexp[RED] != rm->cexp[GRN]) |
588 (rm->cexp[GRN] != rm->cexp[BLU]))
589 fputcolcor(rm->cexp, fp);
590 else if (rm->cexp[GRN] != 1.f)
591 fputexpos(rm->cexp[GRN], fp);
592 if ((dtype != DTrgbe) & (dtype != DTxyze)) {
593 if (dtype != DTspec) {
594 fprintf(fp, "NROWS=%d\n", rm->nrows);
595 fprintf(fp, "NCOLS=%d\n", rm->ncols);
596 } else if (rm->ncomp < 3)
597 return(0); /* bad # components */
598 fputncomp(rm->ncomp, fp);
599 if (dtype == DTspec || (rm->ncomp > 3 &&
600 memcmp(rm->wlpart, WLPART, sizeof(WLPART))))
601 fputwlsplit(rm->wlpart, fp);
602 } else if ((rm->ncomp != 3) & (rm->ncomp != 1))
603 return(0); /* wrong # components */
604 if ((dtype == DTfloat) | (dtype == DTdouble))
605 fputendian(fp); /* important to record */
606 fputformat(cm_fmt_id[dtype], fp);
607 fputc('\n', fp); /* end of header */
608 switch (dtype) { /* write data */
609 case DTascii:
610 ok = rmx_write_ascii(rm, fp);
611 break;
612 case DTfloat:
613 ok = rmx_write_float(rm, fp);
614 break;
615 case DTdouble:
616 ok = rmx_write_double(rm, fp);
617 break;
618 case DTrgbe:
619 case DTxyze:
620 fprtresolu(rm->ncols, rm->nrows, fp);
621 ok = rmx_write_rgbe(rm, fp);
622 break;
623 case DTspec:
624 fprtresolu(rm->ncols, rm->nrows, fp);
625 ok = rmx_write_spec(rm, fp);
626 break;
627 default:
628 return(0);
629 }
630 ok &= (fflush(fp) == 0);
631 #ifdef getc_unlocked
632 funlockfile(fp);
633 #endif
634 if (!ok) fputs("Error writing matrix\n", stderr);
635 return(ok);
636 }
637
638 /* Allocate and assign square identity matrix with n components */
639 RMATRIX *
640 rmx_identity(const int dim, const int n)
641 {
642 RMATRIX *rid = rmx_alloc(dim, dim, n);
643 int i, k;
644
645 if (!rid)
646 return(NULL);
647 memset(rid->mtx, 0, array_size(rid));
648 for (i = dim; i--; ) {
649 double *dp = rmx_lval(rid,i,i);
650 for (k = n; k--; )
651 dp[k] = 1.;
652 }
653 return(rid);
654 }
655
656 /* Duplicate the given matrix */
657 RMATRIX *
658 rmx_copy(const RMATRIX *rm)
659 {
660 RMATRIX *dnew;
661
662 if (!rm)
663 return(NULL);
664 dnew = rmx_alloc(rm->nrows, rm->ncols, rm->ncomp);
665 if (!dnew)
666 return(NULL);
667 rmx_addinfo(dnew, rm->info);
668 dnew->dtype = rm->dtype;
669 copycolor(dnew->cexp, rm->cexp);
670 memcpy(dnew->wlpart, rm->wlpart, sizeof(dnew->wlpart));
671 memcpy(dnew->mtx, rm->mtx, array_size(dnew));
672 return(dnew);
673 }
674
675 /* Allocate and assign transposed matrix */
676 RMATRIX *
677 rmx_transpose(const RMATRIX *rm)
678 {
679 RMATRIX *dnew;
680 int i, j;
681
682 if (!rm)
683 return(0);
684 if ((rm->nrows == 1) | (rm->ncols == 1)) {
685 dnew = rmx_copy(rm);
686 if (!dnew)
687 return(NULL);
688 dnew->nrows = rm->ncols;
689 dnew->ncols = rm->nrows;
690 return(dnew);
691 }
692 dnew = rmx_alloc(rm->ncols, rm->nrows, rm->ncomp);
693 if (!dnew)
694 return(NULL);
695 if (rm->info) {
696 rmx_addinfo(dnew, rm->info);
697 rmx_addinfo(dnew, "Transposed rows and columns\n");
698 }
699 dnew->dtype = rm->dtype;
700 copycolor(dnew->cexp, rm->cexp);
701 memcpy(dnew->wlpart, rm->wlpart, sizeof(dnew->wlpart));
702 for (j = dnew->ncols; j--; )
703 for (i = dnew->nrows; i--; )
704 memcpy(rmx_lval(dnew,i,j), rmx_val(rm,j,i),
705 sizeof(double)*dnew->ncomp);
706 return(dnew);
707 }
708
709 /* Multiply (concatenate) two matrices and allocate the result */
710 RMATRIX *
711 rmx_multiply(const RMATRIX *m1, const RMATRIX *m2)
712 {
713 RMATRIX *mres;
714 int i, j, k, h;
715
716 if (!m1 | !m2 || (m1->ncomp != m2->ncomp) | (m1->ncols != m2->nrows))
717 return(NULL);
718 mres = rmx_alloc(m1->nrows, m2->ncols, m1->ncomp);
719 if (!mres)
720 return(NULL);
721 i = rmx_newtype(m1->dtype, m2->dtype);
722 if (i)
723 mres->dtype = i;
724 else
725 rmx_addinfo(mres, rmx_mismatch_warn);
726 for (i = mres->nrows; i--; )
727 for (j = mres->ncols; j--; )
728 for (k = mres->ncomp; k--; ) {
729 double d = 0;
730 for (h = m1->ncols; h--; )
731 d += rmx_val(m1,i,h)[k] * rmx_val(m2,h,j)[k];
732 rmx_lval(mres,i,j)[k] = d;
733 }
734 return(mres);
735 }
736
737 /* Element-wise multiplication (or division) of m2 into m1 */
738 int
739 rmx_elemult(RMATRIX *m1, const RMATRIX *m2, int divide)
740 {
741 int zeroDivides = 0;
742 int i, j, k;
743
744 if (!m1 | !m2 || (m1->ncols != m2->ncols) | (m1->nrows != m2->nrows))
745 return(0);
746 if ((m2->ncomp > 1) & (m2->ncomp != m1->ncomp))
747 return(0);
748 i = rmx_newtype(m1->dtype, m2->dtype);
749 if (i)
750 m1->dtype = i;
751 else
752 rmx_addinfo(m1, rmx_mismatch_warn);
753 for (i = m1->nrows; i--; )
754 for (j = m1->ncols; j--; )
755 if (divide) {
756 double d;
757 if (m2->ncomp == 1) {
758 d = rmx_val(m2,i,j)[0];
759 if (d == 0) {
760 ++zeroDivides;
761 for (k = m1->ncomp; k--; )
762 rmx_lval(m1,i,j)[k] = 0;
763 } else {
764 d = 1./d;
765 for (k = m1->ncomp; k--; )
766 rmx_lval(m1,i,j)[k] *= d;
767 }
768 } else
769 for (k = m1->ncomp; k--; ) {
770 d = rmx_val(m2,i,j)[k];
771 if (d == 0) {
772 ++zeroDivides;
773 rmx_lval(m1,i,j)[k] = 0;
774 } else
775 rmx_lval(m1,i,j)[k] /= d;
776 }
777 } else {
778 if (m2->ncomp == 1) {
779 const double d = rmx_val(m2,i,j)[0];
780 for (k = m1->ncomp; k--; )
781 rmx_lval(m1,i,j)[k] *= d;
782 } else
783 for (k = m1->ncomp; k--; )
784 rmx_lval(m1,i,j)[k] *= rmx_val(m2,i,j)[k];
785 }
786 if (zeroDivides) {
787 rmx_addinfo(m1, "WARNING: zero divide(s) corrupted results\n");
788 errno = ERANGE;
789 }
790 return(1);
791 }
792
793 /* Sum second matrix into first, applying scale factor beforehand */
794 int
795 rmx_sum(RMATRIX *msum, const RMATRIX *madd, const double sf[])
796 {
797 double *mysf = NULL;
798 int i, j, k;
799
800 if (!msum | !madd ||
801 (msum->nrows != madd->nrows) |
802 (msum->ncols != madd->ncols) |
803 (msum->ncomp != madd->ncomp))
804 return(0);
805 if (!sf) {
806 mysf = (double *)malloc(sizeof(double)*msum->ncomp);
807 if (!mysf)
808 return(0);
809 for (k = msum->ncomp; k--; )
810 mysf[k] = 1;
811 sf = mysf;
812 }
813 i = rmx_newtype(msum->dtype, madd->dtype);
814 if (i)
815 msum->dtype = i;
816 else
817 rmx_addinfo(msum, rmx_mismatch_warn);
818 for (i = msum->nrows; i--; )
819 for (j = msum->ncols; j--; ) {
820 const double *da = rmx_val(madd,i,j);
821 double *ds = rmx_lval(msum,i,j);
822 for (k = msum->ncomp; k--; )
823 ds[k] += sf[k] * da[k];
824 }
825 if (mysf)
826 free(mysf);
827 return(1);
828 }
829
830 /* Scale the given matrix by the indicated scalar component vector */
831 int
832 rmx_scale(RMATRIX *rm, const double sf[])
833 {
834 int i, j, k;
835
836 if (!rm | !sf)
837 return(0);
838 for (i = rm->nrows; i--; )
839 for (j = rm->ncols; j--; ) {
840 double *dp = rmx_lval(rm,i,j);
841 for (k = rm->ncomp; k--; )
842 dp[k] *= sf[k];
843 }
844 if (rm->info)
845 rmx_addinfo(rm, "Applied scalar\n");
846 /* XXX: should record as exposure for COLR and SCOLR types? */
847 return(1);
848 }
849
850 /* Allocate new matrix and apply component transformation */
851 RMATRIX *
852 rmx_transform(const RMATRIX *msrc, int n, const double cmat[])
853 {
854 int i, j, ks, kd;
855 RMATRIX *dnew;
856
857 if (!msrc | (n <= 0) | !cmat)
858 return(NULL);
859 dnew = rmx_alloc(msrc->nrows, msrc->ncols, n);
860 if (!dnew)
861 return(NULL);
862 if (msrc->info) {
863 char buf[128];
864 sprintf(buf, "Applied %dx%d component transform\n",
865 dnew->ncomp, msrc->ncomp);
866 rmx_addinfo(dnew, msrc->info);
867 rmx_addinfo(dnew, buf);
868 }
869 dnew->dtype = msrc->dtype;
870 for (i = dnew->nrows; i--; )
871 for (j = dnew->ncols; j--; ) {
872 const double *ds = rmx_val(msrc,i,j);
873 for (kd = dnew->ncomp; kd--; ) {
874 double d = 0;
875 for (ks = msrc->ncomp; ks--; )
876 d += cmat[kd*msrc->ncomp + ks] * ds[ks];
877 rmx_lval(dnew,i,j)[kd] = d;
878 }
879 }
880 return(dnew);
881 }
882
883 /* Convert a color matrix to newly allocated RMATRIX buffer */
884 RMATRIX *
885 rmx_from_cmatrix(const CMATRIX *cm)
886 {
887 int i, j;
888 RMATRIX *dnew;
889
890 if (!cm)
891 return(NULL);
892 dnew = rmx_alloc(cm->nrows, cm->ncols, 3);
893 if (!dnew)
894 return(NULL);
895 dnew->dtype = DTfloat;
896 for (i = dnew->nrows; i--; )
897 for (j = dnew->ncols; j--; ) {
898 const COLORV *cv = cm_lval(cm,i,j);
899 double *dp = rmx_lval(dnew,i,j);
900 dp[0] = cv[0];
901 dp[1] = cv[1];
902 dp[2] = cv[2];
903 }
904 return(dnew);
905 }
906
907 /* Convert general matrix to newly allocated CMATRIX buffer */
908 CMATRIX *
909 cm_from_rmatrix(const RMATRIX *rm)
910 {
911 int i, j;
912 CMATRIX *cnew;
913
914 if (!rm || !rm->mtx | (rm->ncomp == 2))
915 return(NULL);
916 cnew = cm_alloc(rm->nrows, rm->ncols);
917 if (!cnew)
918 return(NULL);
919 for (i = cnew->nrows; i--; )
920 for (j = cnew->ncols; j--; ) {
921 const double *dp = rmx_val(rm,i,j);
922 COLORV *cv = cm_lval(cnew,i,j);
923 switch (rm->ncomp) {
924 case 3:
925 setcolor(cv, dp[0], dp[1], dp[2]);
926 break;
927 case 1:
928 setcolor(cv, dp[0], dp[0], dp[0]);
929 break;
930 default: {
931 SCOLOR scol;
932 int k;
933 for (k = rm->ncomp; k--; )
934 scol[k] = dp[k];
935 scolor2color(cv, scol, rm->ncomp, rm->wlpart);
936 } break;
937 }
938 }
939 return(cnew);
940 }