ViewVC Help
View File | Revision Log | Show Annotations | Download File | Root Listing
root/radiance/ray/src/util/rmatrix.c
Revision: 2.66
Committed: Tue Nov 28 21:28:11 2023 UTC (4 months, 4 weeks ago) by greg
Content type: text/plain
Branch: MAIN
Changes since 2.65: +3 -8 lines
Log Message:
perf(rmtxop): Eliminated unnecessary loop for writing double matrix data

File Contents

# Content
1 #ifndef lint
2 static const char RCSid[] = "$Id: rmatrix.c,v 2.65 2023/11/28 21:07:20 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 return(putbinary(rm->mtx, sizeof(rm->mtx[0])*rm->ncomp,
490 rm->nrows*rm->ncols, fp) == rm->nrows*rm->ncols);
491 }
492
493 static int
494 rmx_write_rgbe(const RMATRIX *rm, FILE *fp)
495 {
496 COLR *scan = (COLR *)malloc(sizeof(COLR)*rm->ncols);
497 int i, j;
498
499 if (!scan)
500 return(0);
501 for (i = 0; i < rm->nrows; i++) {
502 for (j = rm->ncols; j--; ) {
503 const double *dp = rmx_val(rm,i,j);
504 if (rm->ncomp == 1)
505 setcolr(scan[j], dp[0], dp[0], dp[0]);
506 else
507 setcolr(scan[j], dp[0], dp[1], dp[2]);
508 }
509 if (fwritecolrs(scan, rm->ncols, fp) < 0) {
510 free(scan);
511 return(0);
512 }
513 }
514 free(scan);
515 return(1);
516 }
517
518 static int
519 rmx_write_spec(const RMATRIX *rm, FILE *fp)
520 {
521 uby8 *scan = (uby8 *)malloc((rm->ncomp+1)*rm->ncols);
522 int ok = 1;
523 SCOLOR scol;
524 int i, j, k;
525
526 if (!scan)
527 return(0);
528 for (i = 0; i < rm->nrows; i++) {
529 for (j = rm->ncols; j--; ) {
530 const double *dp = rmx_val(rm,i,j);
531 for (k = rm->ncomp; k--; )
532 scol[k] = dp[k];
533 scolor2scolr(scan+j*(rm->ncomp+1), scol, rm->ncomp);
534 }
535 if (fwritescolrs(scan, rm->ncomp, rm->ncols, fp) < 0) {
536 ok = 0;
537 break;
538 }
539 }
540 free(scan);
541 return(ok);
542 }
543
544 /* Check if CIE XYZ primaries were specified */
545 static int
546 findCIEprims(const char *info)
547 {
548 RGBPRIMS prims;
549
550 if (!info)
551 return(0);
552 info = strstr(info, PRIMARYSTR);
553 if (!info || !primsval(prims, info))
554 return(0);
555
556 return((prims[RED][CIEX] > .99) & (prims[RED][CIEY] < .01) &&
557 (prims[GRN][CIEX] < .01) & (prims[GRN][CIEY] > .99) &&
558 (prims[BLU][CIEX] < .01) & (prims[BLU][CIEY] < .01));
559 }
560
561 /* Write matrix to file type indicated by dtype */
562 int
563 rmx_write(const RMATRIX *rm, int dtype, FILE *fp)
564 {
565 int ok = 1;
566
567 if (!rm | !fp || !rm->mtx)
568 return(0);
569 #ifdef getc_unlocked
570 flockfile(fp);
571 #endif
572 if (rm->info) /* complete header */
573 fputs(rm->info, fp);
574 if (dtype == DTfromHeader)
575 dtype = rm->dtype;
576 else if (dtype == DTrgbe && (rm->dtype == DTxyze ||
577 findCIEprims(rm->info)))
578 dtype = DTxyze;
579 else if ((dtype == DTxyze) & (rm->dtype == DTrgbe))
580 dtype = DTrgbe;
581 /* write exposure? */
582 if (rm->ncomp == 3 && (rm->cexp[RED] != rm->cexp[GRN]) |
583 (rm->cexp[GRN] != rm->cexp[BLU]))
584 fputcolcor(rm->cexp, fp);
585 else if (rm->cexp[GRN] != 1.f)
586 fputexpos(rm->cexp[GRN], fp);
587 if ((dtype != DTrgbe) & (dtype != DTxyze)) {
588 if (dtype != DTspec) {
589 fprintf(fp, "NROWS=%d\n", rm->nrows);
590 fprintf(fp, "NCOLS=%d\n", rm->ncols);
591 } else if (rm->ncomp < 3)
592 return(0); /* bad # components */
593 fputncomp(rm->ncomp, fp);
594 if (dtype == DTspec || (rm->ncomp > 3 &&
595 memcmp(rm->wlpart, WLPART, sizeof(WLPART))))
596 fputwlsplit(rm->wlpart, fp);
597 } else if ((rm->ncomp != 3) & (rm->ncomp != 1))
598 return(0); /* wrong # components */
599 if ((dtype == DTfloat) | (dtype == DTdouble))
600 fputendian(fp); /* important to record */
601 fputformat(cm_fmt_id[dtype], fp);
602 fputc('\n', fp); /* end of header */
603 switch (dtype) { /* write data */
604 case DTascii:
605 ok = rmx_write_ascii(rm, fp);
606 break;
607 case DTfloat:
608 ok = rmx_write_float(rm, fp);
609 break;
610 case DTdouble:
611 ok = rmx_write_double(rm, fp);
612 break;
613 case DTrgbe:
614 case DTxyze:
615 fprtresolu(rm->ncols, rm->nrows, fp);
616 ok = rmx_write_rgbe(rm, fp);
617 break;
618 case DTspec:
619 fprtresolu(rm->ncols, rm->nrows, fp);
620 ok = rmx_write_spec(rm, fp);
621 break;
622 default:
623 return(0);
624 }
625 ok &= (fflush(fp) == 0);
626 #ifdef getc_unlocked
627 funlockfile(fp);
628 #endif
629 if (!ok) fputs("Error writing matrix\n", stderr);
630 return(ok);
631 }
632
633 /* Allocate and assign square identity matrix with n components */
634 RMATRIX *
635 rmx_identity(const int dim, const int n)
636 {
637 RMATRIX *rid = rmx_alloc(dim, dim, n);
638 int i, k;
639
640 if (!rid)
641 return(NULL);
642 memset(rid->mtx, 0, array_size(rid));
643 for (i = dim; i--; ) {
644 double *dp = rmx_lval(rid,i,i);
645 for (k = n; k--; )
646 dp[k] = 1.;
647 }
648 return(rid);
649 }
650
651 /* Duplicate the given matrix */
652 RMATRIX *
653 rmx_copy(const RMATRIX *rm)
654 {
655 RMATRIX *dnew;
656
657 if (!rm)
658 return(NULL);
659 dnew = rmx_alloc(rm->nrows, rm->ncols, rm->ncomp);
660 if (!dnew)
661 return(NULL);
662 rmx_addinfo(dnew, rm->info);
663 dnew->dtype = rm->dtype;
664 copycolor(dnew->cexp, rm->cexp);
665 memcpy(dnew->wlpart, rm->wlpart, sizeof(dnew->wlpart));
666 memcpy(dnew->mtx, rm->mtx, array_size(dnew));
667 return(dnew);
668 }
669
670 /* Allocate and assign transposed matrix */
671 RMATRIX *
672 rmx_transpose(const RMATRIX *rm)
673 {
674 RMATRIX *dnew;
675 int i, j;
676
677 if (!rm)
678 return(0);
679 if ((rm->nrows == 1) | (rm->ncols == 1)) {
680 dnew = rmx_copy(rm);
681 if (!dnew)
682 return(NULL);
683 dnew->nrows = rm->ncols;
684 dnew->ncols = rm->nrows;
685 return(dnew);
686 }
687 dnew = rmx_alloc(rm->ncols, rm->nrows, rm->ncomp);
688 if (!dnew)
689 return(NULL);
690 if (rm->info) {
691 rmx_addinfo(dnew, rm->info);
692 rmx_addinfo(dnew, "Transposed rows and columns\n");
693 }
694 dnew->dtype = rm->dtype;
695 copycolor(dnew->cexp, rm->cexp);
696 memcpy(dnew->wlpart, rm->wlpart, sizeof(dnew->wlpart));
697 for (j = dnew->ncols; j--; )
698 for (i = dnew->nrows; i--; )
699 memcpy(rmx_lval(dnew,i,j), rmx_val(rm,j,i),
700 sizeof(double)*dnew->ncomp);
701 return(dnew);
702 }
703
704 /* Multiply (concatenate) two matrices and allocate the result */
705 RMATRIX *
706 rmx_multiply(const RMATRIX *m1, const RMATRIX *m2)
707 {
708 RMATRIX *mres;
709 int i, j, k, h;
710
711 if (!m1 | !m2 || (m1->ncomp != m2->ncomp) | (m1->ncols != m2->nrows))
712 return(NULL);
713 mres = rmx_alloc(m1->nrows, m2->ncols, m1->ncomp);
714 if (!mres)
715 return(NULL);
716 i = rmx_newtype(m1->dtype, m2->dtype);
717 if (i)
718 mres->dtype = i;
719 else
720 rmx_addinfo(mres, rmx_mismatch_warn);
721 for (i = mres->nrows; i--; )
722 for (j = mres->ncols; j--; )
723 for (k = mres->ncomp; k--; ) {
724 double d = 0;
725 for (h = m1->ncols; h--; )
726 d += rmx_val(m1,i,h)[k] * rmx_val(m2,h,j)[k];
727 rmx_lval(mres,i,j)[k] = d;
728 }
729 return(mres);
730 }
731
732 /* Element-wise multiplication (or division) of m2 into m1 */
733 int
734 rmx_elemult(RMATRIX *m1, const RMATRIX *m2, int divide)
735 {
736 int zeroDivides = 0;
737 int i, j, k;
738
739 if (!m1 | !m2 || (m1->ncols != m2->ncols) | (m1->nrows != m2->nrows))
740 return(0);
741 if ((m2->ncomp > 1) & (m2->ncomp != m1->ncomp))
742 return(0);
743 i = rmx_newtype(m1->dtype, m2->dtype);
744 if (i)
745 m1->dtype = i;
746 else
747 rmx_addinfo(m1, rmx_mismatch_warn);
748 for (i = m1->nrows; i--; )
749 for (j = m1->ncols; j--; )
750 if (divide) {
751 double d;
752 if (m2->ncomp == 1) {
753 d = rmx_val(m2,i,j)[0];
754 if (d == 0) {
755 ++zeroDivides;
756 for (k = m1->ncomp; k--; )
757 rmx_lval(m1,i,j)[k] = 0;
758 } else {
759 d = 1./d;
760 for (k = m1->ncomp; k--; )
761 rmx_lval(m1,i,j)[k] *= d;
762 }
763 } else
764 for (k = m1->ncomp; k--; ) {
765 d = rmx_val(m2,i,j)[k];
766 if (d == 0) {
767 ++zeroDivides;
768 rmx_lval(m1,i,j)[k] = 0;
769 } else
770 rmx_lval(m1,i,j)[k] /= d;
771 }
772 } else {
773 if (m2->ncomp == 1) {
774 const double d = rmx_val(m2,i,j)[0];
775 for (k = m1->ncomp; k--; )
776 rmx_lval(m1,i,j)[k] *= d;
777 } else
778 for (k = m1->ncomp; k--; )
779 rmx_lval(m1,i,j)[k] *= rmx_val(m2,i,j)[k];
780 }
781 if (zeroDivides) {
782 rmx_addinfo(m1, "WARNING: zero divide(s) corrupted results\n");
783 errno = ERANGE;
784 }
785 return(1);
786 }
787
788 /* Sum second matrix into first, applying scale factor beforehand */
789 int
790 rmx_sum(RMATRIX *msum, const RMATRIX *madd, const double sf[])
791 {
792 double *mysf = NULL;
793 int i, j, k;
794
795 if (!msum | !madd ||
796 (msum->nrows != madd->nrows) |
797 (msum->ncols != madd->ncols) |
798 (msum->ncomp != madd->ncomp))
799 return(0);
800 if (!sf) {
801 mysf = (double *)malloc(sizeof(double)*msum->ncomp);
802 if (!mysf)
803 return(0);
804 for (k = msum->ncomp; k--; )
805 mysf[k] = 1;
806 sf = mysf;
807 }
808 i = rmx_newtype(msum->dtype, madd->dtype);
809 if (i)
810 msum->dtype = i;
811 else
812 rmx_addinfo(msum, rmx_mismatch_warn);
813 for (i = msum->nrows; i--; )
814 for (j = msum->ncols; j--; ) {
815 const double *da = rmx_val(madd,i,j);
816 double *ds = rmx_lval(msum,i,j);
817 for (k = msum->ncomp; k--; )
818 ds[k] += sf[k] * da[k];
819 }
820 if (mysf)
821 free(mysf);
822 return(1);
823 }
824
825 /* Scale the given matrix by the indicated scalar component vector */
826 int
827 rmx_scale(RMATRIX *rm, const double sf[])
828 {
829 int i, j, k;
830
831 if (!rm | !sf)
832 return(0);
833 for (i = rm->nrows; i--; )
834 for (j = rm->ncols; j--; ) {
835 double *dp = rmx_lval(rm,i,j);
836 for (k = rm->ncomp; k--; )
837 dp[k] *= sf[k];
838 }
839 if (rm->info)
840 rmx_addinfo(rm, "Applied scalar\n");
841 /* XXX: should record as exposure for COLR and SCOLR types? */
842 return(1);
843 }
844
845 /* Allocate new matrix and apply component transformation */
846 RMATRIX *
847 rmx_transform(const RMATRIX *msrc, int n, const double cmat[])
848 {
849 int i, j, ks, kd;
850 RMATRIX *dnew;
851
852 if (!msrc | (n <= 0) | !cmat)
853 return(NULL);
854 dnew = rmx_alloc(msrc->nrows, msrc->ncols, n);
855 if (!dnew)
856 return(NULL);
857 if (msrc->info) {
858 char buf[128];
859 sprintf(buf, "Applied %dx%d component transform\n",
860 dnew->ncomp, msrc->ncomp);
861 rmx_addinfo(dnew, msrc->info);
862 rmx_addinfo(dnew, buf);
863 }
864 dnew->dtype = msrc->dtype;
865 for (i = dnew->nrows; i--; )
866 for (j = dnew->ncols; j--; ) {
867 const double *ds = rmx_val(msrc,i,j);
868 for (kd = dnew->ncomp; kd--; ) {
869 double d = 0;
870 for (ks = msrc->ncomp; ks--; )
871 d += cmat[kd*msrc->ncomp + ks] * ds[ks];
872 rmx_lval(dnew,i,j)[kd] = d;
873 }
874 }
875 return(dnew);
876 }
877
878 /* Convert a color matrix to newly allocated RMATRIX buffer */
879 RMATRIX *
880 rmx_from_cmatrix(const CMATRIX *cm)
881 {
882 int i, j;
883 RMATRIX *dnew;
884
885 if (!cm)
886 return(NULL);
887 dnew = rmx_alloc(cm->nrows, cm->ncols, 3);
888 if (!dnew)
889 return(NULL);
890 dnew->dtype = DTfloat;
891 for (i = dnew->nrows; i--; )
892 for (j = dnew->ncols; j--; ) {
893 const COLORV *cv = cm_lval(cm,i,j);
894 double *dp = rmx_lval(dnew,i,j);
895 dp[0] = cv[0];
896 dp[1] = cv[1];
897 dp[2] = cv[2];
898 }
899 return(dnew);
900 }
901
902 /* Convert general matrix to newly allocated CMATRIX buffer */
903 CMATRIX *
904 cm_from_rmatrix(const RMATRIX *rm)
905 {
906 int i, j;
907 CMATRIX *cnew;
908
909 if (!rm || !rm->mtx | (rm->ncomp == 2))
910 return(NULL);
911 cnew = cm_alloc(rm->nrows, rm->ncols);
912 if (!cnew)
913 return(NULL);
914 for (i = cnew->nrows; i--; )
915 for (j = cnew->ncols; j--; ) {
916 const double *dp = rmx_val(rm,i,j);
917 COLORV *cv = cm_lval(cnew,i,j);
918 switch (rm->ncomp) {
919 case 3:
920 setcolor(cv, dp[0], dp[1], dp[2]);
921 break;
922 case 1:
923 setcolor(cv, dp[0], dp[0], dp[0]);
924 break;
925 default: {
926 SCOLOR scol;
927 int k;
928 for (k = rm->ncomp; k--; )
929 scol[k] = dp[k];
930 scolor2color(cv, scol, rm->ncomp, rm->wlpart);
931 } break;
932 }
933 }
934 return(cnew);
935 }