ViewVC Help
View File | Revision Log | Show Annotations | Download File | Root Listing
root/radiance/ray/src/util/rmatrix.c
Revision: 2.62
Committed: Tue Nov 28 00:39:56 2023 UTC (4 months, 4 weeks ago) by greg
Content type: text/plain
Branch: MAIN
Changes since 2.61: +112 -83 lines
Log Message:
refactor: Separated header and data loading from matrices (except XMLs)

File Contents

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