| 11 |  | #include "standard.h" | 
| 12 |  | #include "cmatrix.h" | 
| 13 |  | #include "platform.h" | 
| 14 | + | #include "resolu.h" | 
| 15 |  |  | 
| 16 | + | const char      *cm_fmt_id[] = { | 
| 17 | + | "unknown", "ascii", COLRFMT, CIEFMT, | 
| 18 | + | "float", "double" | 
| 19 | + | }; | 
| 20 | + |  | 
| 21 | + | const int       cm_elem_size[] = { | 
| 22 | + | 0, 0, 3*sizeof(float), 3*sizeof(double), 4, 4 | 
| 23 | + | }; | 
| 24 | + |  | 
| 25 |  | /* Allocate a color coefficient matrix */ | 
| 26 |  | CMATRIX * | 
| 27 |  | cm_alloc(int nrows, int ncols) | 
| 31 |  | if ((nrows <= 0) | (ncols <= 0)) | 
| 32 |  | error(USER, "attempt to create empty matrix"); | 
| 33 |  | cm = (CMATRIX *)malloc(sizeof(CMATRIX) + | 
| 34 | < | 3*sizeof(COLORV)*(nrows*ncols - 1)); | 
| 34 | > | sizeof(COLOR)*(nrows*ncols - 1)); | 
| 35 |  | if (cm == NULL) | 
| 36 |  | error(SYSTEM, "out of memory in cm_alloc()"); | 
| 37 |  | cm->nrows = nrows; | 
| 39 |  | return(cm); | 
| 40 |  | } | 
| 41 |  |  | 
| 42 | + | static void | 
| 43 | + | adjacent_ra_sizes(size_t bounds[2], size_t target) | 
| 44 | + | { | 
| 45 | + | bounds[0] = 0; bounds[1] = 2048; | 
| 46 | + | while (bounds[1] < target) { | 
| 47 | + | bounds[0] = bounds[1]; | 
| 48 | + | bounds[1] += bounds[1]>>1; | 
| 49 | + | } | 
| 50 | + | } | 
| 51 | + |  | 
| 52 |  | /* Resize color coefficient matrix */ | 
| 53 |  | CMATRIX * | 
| 54 |  | cm_resize(CMATRIX *cm, int nrows) | 
| 55 |  | { | 
| 56 | + | size_t  old_size, new_size, ra_bounds[2]; | 
| 57 | + |  | 
| 58 |  | if (nrows == cm->nrows) | 
| 59 |  | return(cm); | 
| 60 |  | if (nrows <= 0) { | 
| 61 |  | cm_free(cm); | 
| 62 |  | return(NULL); | 
| 63 |  | } | 
| 64 | < | cm = (CMATRIX *)realloc(cm, sizeof(CMATRIX) + | 
| 65 | < | 3*sizeof(COLORV)*(nrows*cm->ncols - 1)); | 
| 66 | < | if (cm == NULL) | 
| 67 | < | error(SYSTEM, "out of memory in cm_resize()"); | 
| 64 | > | old_size = sizeof(CMATRIX) + sizeof(COLOR)*(cm->nrows*cm->ncols - 1); | 
| 65 | > | adjacent_ra_sizes(ra_bounds, old_size); | 
| 66 | > | new_size = sizeof(CMATRIX) + sizeof(COLOR)*(nrows*cm->ncols - 1); | 
| 67 | > | if (nrows < cm->nrows ? new_size <= ra_bounds[0] : | 
| 68 | > | new_size > ra_bounds[1]) { | 
| 69 | > | adjacent_ra_sizes(ra_bounds, new_size); | 
| 70 | > | cm = (CMATRIX *)realloc(cm, ra_bounds[1]); | 
| 71 | > | if (cm == NULL) | 
| 72 | > | error(SYSTEM, "out of memory in cm_resize()"); | 
| 73 | > | } | 
| 74 |  | cm->nrows = nrows; | 
| 75 |  | return(cm); | 
| 76 |  | } | 
| 77 |  |  | 
| 78 | + | typedef struct { | 
| 79 | + | int     dtype;          /* data type */ | 
| 80 | + | int     nrows, ncols;   /* matrix size */ | 
| 81 | + | char    *err;           /* error message */ | 
| 82 | + | } CMINFO;               /* header info record */ | 
| 83 | + |  | 
| 84 |  | static int | 
| 85 | < | getDT(char *s, void *p) | 
| 85 | > | get_cminfo(char *s, void *p) | 
| 86 |  | { | 
| 87 | + | CMINFO  *ip = (CMINFO *)p; | 
| 88 |  | char    fmt[32]; | 
| 89 | < |  | 
| 90 | < | if (formatval(fmt, s)) { | 
| 91 | < | if (!strcmp(fmt, "ascii")) | 
| 92 | < | *((int *)p) = DTascii; | 
| 93 | < | else if (!strcmp(fmt, "float")) | 
| 59 | < | *((int *)p) = DTfloat; | 
| 60 | < | else if (!strcmp(fmt, "double")) | 
| 61 | < | *((int *)p) = DTdouble; | 
| 62 | < | else if (!strcmp(fmt, COLRFMT)) | 
| 63 | < | *((int *)p) = DTrgbe; | 
| 64 | < | else if (!strcmp(fmt, CIEFMT)) | 
| 65 | < | *((int *)p) = DTxyze; | 
| 89 | > | int     i; | 
| 90 | > |  | 
| 91 | > | if (!strncmp(s, "NCOMP=", 6) && atoi(s+6) != 3) { | 
| 92 | > | ip->err = "unexpected # components (must be 3)"; | 
| 93 | > | return(-1); | 
| 94 |  | } | 
| 95 | + | if (!strncmp(s, "NROWS=", 6)) { | 
| 96 | + | ip->nrows = atoi(s+6); | 
| 97 | + | return(0); | 
| 98 | + | } | 
| 99 | + | if (!strncmp(s, "NCOLS=", 6)) { | 
| 100 | + | ip->ncols = atoi(s+6); | 
| 101 | + | return(0); | 
| 102 | + | } | 
| 103 | + | if (!formatval(fmt, s)) | 
| 104 | + | return(0); | 
| 105 | + | for (i = 1; i < DTend; i++) | 
| 106 | + | if (!strcmp(fmt, cm_fmt_id[i])) | 
| 107 | + | ip->dtype = i; | 
| 108 |  | return(0); | 
| 109 |  | } | 
| 110 |  |  | 
| 111 | < | /* Load header to obtain data type */ | 
| 112 | < | int | 
| 113 | < | getDTfromHeader(FILE *fp) | 
| 111 | > | /* Load header to obtain/check data type and number of columns */ | 
| 112 | > | char * | 
| 113 | > | cm_getheader(int *dt, int *nr, int *nc, FILE *fp) | 
| 114 |  | { | 
| 115 | < | int     dt = DTfromHeader; | 
| 116 | < |  | 
| 117 | < | if (getheader(fp, getDT, &dt) < 0) | 
| 118 | < | error(SYSTEM, "header read error"); | 
| 119 | < | if (dt == DTfromHeader) | 
| 120 | < | error(USER, "missing data format in header"); | 
| 121 | < | return(dt); | 
| 115 | > | CMINFO  cmi; | 
| 116 | > | /* read header */ | 
| 117 | > | cmi.dtype = DTfromHeader; | 
| 118 | > | cmi.nrows = cmi.ncols = 0; | 
| 119 | > | cmi.err = "unexpected EOF in header"; | 
| 120 | > | if (getheader(fp, get_cminfo, &cmi) < 0) | 
| 121 | > | return(cmi.err); | 
| 122 | > | if (dt != NULL) {                       /* get/check data type? */ | 
| 123 | > | if (cmi.dtype == DTfromHeader) { | 
| 124 | > | if (*dt == DTfromHeader) | 
| 125 | > | return("missing/unknown data format in header"); | 
| 126 | > | } else if (*dt == DTfromHeader) | 
| 127 | > | *dt = cmi.dtype; | 
| 128 | > | else if (*dt != cmi.dtype) | 
| 129 | > | return("unexpected data format in header"); | 
| 130 | > | } | 
| 131 | > | if (nr != NULL) {                       /* get/check #rows? */ | 
| 132 | > | if (*nr <= 0) | 
| 133 | > | *nr = cmi.nrows; | 
| 134 | > | else if ((cmi.nrows > 0) & (*nr != cmi.nrows)) | 
| 135 | > | return("unexpected row count in header"); | 
| 136 | > | } | 
| 137 | > | if (nc != NULL) {                       /* get/check #columns? */ | 
| 138 | > | if (*nc <= 0) | 
| 139 | > | *nc = cmi.ncols; | 
| 140 | > | else if ((cmi.ncols > 0) & (*nc != cmi.ncols)) | 
| 141 | > | return("unexpected column count in header"); | 
| 142 | > | } | 
| 143 | > | return(NULL); | 
| 144 |  | } | 
| 145 |  |  | 
| 146 |  | /* Allocate and load a matrix from the given file (or stdin if NULL) */ | 
| 150 |  | FILE    *fp = stdin; | 
| 151 |  | CMATRIX *cm; | 
| 152 |  |  | 
| 90 | – | if (ncols <= 0) | 
| 91 | – | error(USER, "Non-positive number of columns"); | 
| 153 |  | if (fname == NULL) | 
| 154 |  | fname = "<stdin>"; | 
| 155 |  | else if ((fp = fopen(fname, "r")) == NULL) { | 
| 161 |  | #endif | 
| 162 |  | if (dtype != DTascii) | 
| 163 |  | SET_FILE_BINARY(fp);            /* doesn't really work */ | 
| 164 | < | if (dtype == DTfromHeader) | 
| 165 | < | dtype = getDTfromHeader(fp); | 
| 164 | > | if (!dtype | !ncols) {                  /* expecting header? */ | 
| 165 | > | char    *err = cm_getheader(&dtype, &nrows, &ncols, fp); | 
| 166 | > | if (err != NULL) | 
| 167 | > | error(USER, err); | 
| 168 | > | if (ncols <= 0) | 
| 169 | > | error(USER, "unspecified number of columns"); | 
| 170 | > | } | 
| 171 |  | switch (dtype) { | 
| 172 |  | case DTascii: | 
| 173 |  | case DTfloat: | 
| 231 |  | break; | 
| 232 |  | } | 
| 233 |  | } else {                                        /* read binary file */ | 
| 234 | < | if (sizeof(COLORV) == (dtype==DTfloat ? sizeof(float) : | 
| 169 | < | sizeof(double))) { | 
| 234 | > | if (sizeof(COLOR) == cm_elem_size[dtype]) { | 
| 235 |  | int     nread = 0; | 
| 236 |  | do {                            /* read all we can */ | 
| 237 |  | nread += fread(cm->cmem + 3*nread, | 
| 238 | < | 3*sizeof(COLORV), | 
| 238 | > | sizeof(COLOR), | 
| 239 |  | cm->nrows*cm->ncols - nread, | 
| 240 |  | fp); | 
| 241 |  | if (nrows <= 0) {       /* unknown length */ | 
| 377 |  | for (dr = 0; dr < cmr->nrows; dr++) | 
| 378 |  | for (dc = 0; dc < cmr->ncols; dc++) { | 
| 379 |  | COLORV  *dp = cm_lval(cmr,dr,dc); | 
| 380 | + | double  res[3]; | 
| 381 |  | dp[0] = dp[1] = dp[2] = 0; | 
| 382 |  | if (rowcheck != NULL && !rowcheck[dr]) | 
| 383 |  | continue; | 
| 384 |  | if (colcheck != NULL && !colcheck[dc]) | 
| 385 |  | continue; | 
| 386 | + | res[0] = res[1] = res[2] = 0; | 
| 387 |  | for (i = 0; i < cm1->ncols; i++) { | 
| 388 |  | const COLORV        *cp1 = cm_lval(cm1,dr,i); | 
| 389 |  | const COLORV        *cp2 = cm_lval(cm2,i,dc); | 
| 390 | < | dp[0] += cp1[0] * cp2[0]; | 
| 391 | < | dp[1] += cp1[1] * cp2[1]; | 
| 392 | < | dp[2] += cp1[2] * cp2[2]; | 
| 390 | > | res[0] += cp1[0] * cp2[0]; | 
| 391 | > | res[1] += cp1[1] * cp2[1]; | 
| 392 | > | res[2] += cp1[2] * cp2[2]; | 
| 393 |  | } | 
| 394 | + | copycolor(dp, res); | 
| 395 |  | } | 
| 396 |  | if (rowcheck != NULL) free(rowcheck); | 
| 397 |  | if (colcheck != NULL) free(colcheck); | 
| 398 |  | return(cmr); | 
| 399 |  | } | 
| 400 |  |  | 
| 401 | < | /* print out matrix as ASCII text -- no header */ | 
| 402 | < | void | 
| 403 | < | cm_print(const CMATRIX *cm, FILE *fp) | 
| 401 | > | /* write out matrix to file (precede by resolution string if picture) */ | 
| 402 | > | int | 
| 403 | > | cm_write(const CMATRIX *cm, int dtype, FILE *fp) | 
| 404 |  | { | 
| 405 | < | int             r, c; | 
| 406 | < | const COLORV    *mp = cm->cmem; | 
| 407 | < |  | 
| 408 | < | for (r = 0; r < cm->nrows; r++) { | 
| 409 | < | for (c = 0; c < cm->ncols; c++, mp += 3) | 
| 410 | < | fprintf(fp, "\t%.6e %.6e %.6e", mp[0], mp[1], mp[2]); | 
| 411 | < | fputc('\n', fp); | 
| 405 | > | static const char       tabEOL[2] = {'\t','\n'}; | 
| 406 | > | const COLORV            *mp = cm->cmem; | 
| 407 | > | int                     r, c; | 
| 408 | > |  | 
| 409 | > | switch (dtype) { | 
| 410 | > | case DTascii: | 
| 411 | > | for (r = 0; r < cm->nrows; r++) | 
| 412 | > | for (c = 0; c < cm->ncols; c++, mp += 3) | 
| 413 | > | fprintf(fp, "%.6e %.6e %.6e%c", | 
| 414 | > | mp[0], mp[1], mp[2], | 
| 415 | > | tabEOL[c >= cm->ncols-1]); | 
| 416 | > | break; | 
| 417 | > | case DTfloat: | 
| 418 | > | case DTdouble: | 
| 419 | > | if (sizeof(COLOR) == cm_elem_size[dtype]) { | 
| 420 | > | r = cm->ncols*cm->nrows; | 
| 421 | > | while (r > 0) { | 
| 422 | > | c = fwrite(mp, sizeof(COLOR), r, fp); | 
| 423 | > | if (c <= 0) | 
| 424 | > | return(0); | 
| 425 | > | mp += 3*c; | 
| 426 | > | r -= c; | 
| 427 | > | } | 
| 428 | > | } else if (dtype == DTdouble) { | 
| 429 | > | double  dc[3]; | 
| 430 | > | r = cm->ncols*cm->nrows; | 
| 431 | > | while (r--) { | 
| 432 | > | copycolor(dc, mp); | 
| 433 | > | if (fwrite(dc, sizeof(double), 3, fp) != 3) | 
| 434 | > | return(0); | 
| 435 | > | mp += 3; | 
| 436 | > | } | 
| 437 | > | } else /* dtype == DTfloat */ { | 
| 438 | > | float   fc[3]; | 
| 439 | > | r = cm->ncols*cm->nrows; | 
| 440 | > | while (r--) { | 
| 441 | > | copycolor(fc, mp); | 
| 442 | > | if (fwrite(fc, sizeof(float), 3, fp) != 3) | 
| 443 | > | return(0); | 
| 444 | > | mp += 3; | 
| 445 | > | } | 
| 446 | > | } | 
| 447 | > | break; | 
| 448 | > | case DTrgbe: | 
| 449 | > | case DTxyze: | 
| 450 | > | fprtresolu(cm->ncols, cm->nrows, fp); | 
| 451 | > | for (r = 0; r < cm->nrows; r++, mp += 3*cm->ncols) | 
| 452 | > | if (fwritescan((COLOR *)mp, cm->ncols, fp) < 0) | 
| 453 | > | return(0); | 
| 454 | > | break; | 
| 455 | > | default: | 
| 456 | > | fputs("Unsupported data type in cm_write()!\n", stderr); | 
| 457 | > | return(0); | 
| 458 |  | } | 
| 459 | + | return(fflush(fp) == 0); | 
| 460 |  | } |