| 8 |  | */ | 
| 9 |  |  | 
| 10 |  | #include <ctype.h> | 
| 11 | + | #include "platform.h" | 
| 12 |  | #include "standard.h" | 
| 13 |  | #include "cmatrix.h" | 
| 14 |  | #include "platform.h" | 
| 15 | + | #include "paths.h" | 
| 16 |  | #include "resolu.h" | 
| 17 |  |  | 
| 18 | + | const char      stdin_name[] = "<stdin>"; | 
| 19 | + |  | 
| 20 |  | const char      *cm_fmt_id[] = { | 
| 21 | < | "unknown", "ascii", "float", "double", | 
| 22 | < | COLRFMT, CIEFMT | 
| 21 | > | "unknown", COLRFMT, CIEFMT, | 
| 22 | > | "float", "ascii", "double" | 
| 23 |  | }; | 
| 24 |  |  | 
| 25 |  | const int       cm_elem_size[] = { | 
| 26 | < | 0, 0, 3*sizeof(float), 3*sizeof(double), 4, 4 | 
| 26 | > | 0, 4, 4, 3*sizeof(float), 0, 3*sizeof(double) | 
| 27 |  | }; | 
| 28 |  |  | 
| 29 |  | /* Allocate a color coefficient matrix */ | 
| 35 |  | if ((nrows <= 0) | (ncols <= 0)) | 
| 36 |  | error(USER, "attempt to create empty matrix"); | 
| 37 |  | cm = (CMATRIX *)malloc(sizeof(CMATRIX) + | 
| 38 | < | 3*sizeof(COLORV)*(nrows*ncols - 1)); | 
| 39 | < | if (cm == NULL) | 
| 38 | > | sizeof(COLOR)*((size_t)nrows*ncols - 1)); | 
| 39 | > | if (!cm) | 
| 40 |  | error(SYSTEM, "out of memory in cm_alloc()"); | 
| 41 |  | cm->nrows = nrows; | 
| 42 |  | cm->ncols = ncols; | 
| 43 |  | return(cm); | 
| 44 |  | } | 
| 45 |  |  | 
| 46 | + | static void | 
| 47 | + | adjacent_ra_sizes(size_t bounds[2], size_t target) | 
| 48 | + | { | 
| 49 | + | bounds[0] = 0; bounds[1] = 2048; | 
| 50 | + | while (bounds[1] < target) { | 
| 51 | + | bounds[0] = bounds[1]; | 
| 52 | + | bounds[1] += bounds[1]>>1; | 
| 53 | + | } | 
| 54 | + | } | 
| 55 | + |  | 
| 56 |  | /* Resize color coefficient matrix */ | 
| 57 |  | CMATRIX * | 
| 58 |  | cm_resize(CMATRIX *cm, int nrows) | 
| 59 |  | { | 
| 60 | + | size_t  old_size, new_size, ra_bounds[2]; | 
| 61 | + |  | 
| 62 | + | if (!cm) | 
| 63 | + | return(NULL); | 
| 64 |  | if (nrows == cm->nrows) | 
| 65 |  | return(cm); | 
| 66 |  | if (nrows <= 0) { | 
| 67 |  | cm_free(cm); | 
| 68 |  | return(NULL); | 
| 69 |  | } | 
| 70 | < | cm = (CMATRIX *)realloc(cm, sizeof(CMATRIX) + | 
| 71 | < | 3*sizeof(COLORV)*(nrows*cm->ncols - 1)); | 
| 72 | < | if (cm == NULL) | 
| 73 | < | error(SYSTEM, "out of memory in cm_resize()"); | 
| 70 | > | old_size = sizeof(CMATRIX) + sizeof(COLOR)*((size_t)cm->nrows*cm->ncols - 1); | 
| 71 | > | adjacent_ra_sizes(ra_bounds, old_size); | 
| 72 | > | new_size = sizeof(CMATRIX) + sizeof(COLOR)*((size_t)nrows*cm->ncols - 1); | 
| 73 | > | if (nrows < cm->nrows ? new_size <= ra_bounds[0] : | 
| 74 | > | new_size > ra_bounds[1]) { | 
| 75 | > | adjacent_ra_sizes(ra_bounds, new_size); | 
| 76 | > | cm = (CMATRIX *)realloc(cm, ra_bounds[1]); | 
| 77 | > | if (!cm) | 
| 78 | > | error(SYSTEM, "out of memory in cm_resize()"); | 
| 79 | > | } | 
| 80 |  | cm->nrows = nrows; | 
| 81 |  | return(cm); | 
| 82 |  | } | 
| 83 |  |  | 
| 84 | + | typedef struct { | 
| 85 | + | int     dtype;          /* data type */ | 
| 86 | + | int     need2swap;      /* need byte swap? */ | 
| 87 | + | int     nrows, ncols;   /* matrix size */ | 
| 88 | + | COLOR   expos;          /* exposure value */ | 
| 89 | + | char    *err;           /* error message */ | 
| 90 | + | } CMINFO;               /* header info record */ | 
| 91 | + |  | 
| 92 |  | static int | 
| 93 | < | getDT(char *s, void *p) | 
| 93 | > | get_cminfo(char *s, void *p) | 
| 94 |  | { | 
| 95 | < | char    fmt[32]; | 
| 95 | > | CMINFO  *ip = (CMINFO *)p; | 
| 96 | > | char    fmt[MAXFMTLEN]; | 
| 97 |  | int     i; | 
| 98 | < |  | 
| 98 | > |  | 
| 99 | > | if (!strncmp(s, "NCOMP=", 6) && atoi(s+6) != 3) { | 
| 100 | > | ip->err = "unexpected # components (must be 3)"; | 
| 101 | > | return(-1); | 
| 102 | > | } | 
| 103 | > | if (!strncmp(s, "NROWS=", 6)) { | 
| 104 | > | ip->nrows = atoi(s+6); | 
| 105 | > | return(0); | 
| 106 | > | } | 
| 107 | > | if (!strncmp(s, "NCOLS=", 6)) { | 
| 108 | > | ip->ncols = atoi(s+6); | 
| 109 | > | return(0); | 
| 110 | > | } | 
| 111 | > | if ((i = isbigendian(s)) >= 0) { | 
| 112 | > | ip->need2swap = (nativebigendian() != i); | 
| 113 | > | return(0); | 
| 114 | > | } | 
| 115 | > | if (isexpos(s)) { | 
| 116 | > | double  d = exposval(s); | 
| 117 | > | scalecolor(ip->expos, d); | 
| 118 | > | return(0); | 
| 119 | > | } | 
| 120 | > | if (iscolcor(s)) { | 
| 121 | > | COLOR   ctmp; | 
| 122 | > | colcorval(ctmp, s); | 
| 123 | > | multcolor(ip->expos, ctmp); | 
| 124 | > | return(0); | 
| 125 | > | } | 
| 126 |  | if (!formatval(fmt, s)) | 
| 127 |  | return(0); | 
| 128 |  | for (i = 1; i < DTend; i++) | 
| 129 |  | if (!strcmp(fmt, cm_fmt_id[i])) | 
| 130 | < | *((int *)p) = i; | 
| 130 | > | ip->dtype = i; | 
| 131 |  | return(0); | 
| 132 |  | } | 
| 133 |  |  | 
| 134 | < | /* Load header to obtain data type */ | 
| 135 | < | int | 
| 136 | < | getDTfromHeader(FILE *fp) | 
| 134 | > | /* Load header to obtain/check data type and number of columns */ | 
| 135 | > | char * | 
| 136 | > | cm_getheader(int *dt, int *nr, int *nc, int *swp, COLOR scale, FILE *fp) | 
| 137 |  | { | 
| 138 | < | int     dt = DTfromHeader; | 
| 139 | < |  | 
| 140 | < | if (getheader(fp, getDT, &dt) < 0) | 
| 141 | < | error(SYSTEM, "header read error"); | 
| 142 | < | if (dt == DTfromHeader) | 
| 143 | < | error(USER, "missing data format in header"); | 
| 144 | < | return(dt); | 
| 138 | > | CMINFO  cmi; | 
| 139 | > | /* read header */ | 
| 140 | > | cmi.dtype = DTfromHeader; | 
| 141 | > | cmi.need2swap = 0; | 
| 142 | > | cmi.expos[0] = cmi.expos[1] = cmi.expos[2] = 1.f; | 
| 143 | > | cmi.nrows = cmi.ncols = 0; | 
| 144 | > | cmi.err = "unexpected EOF in header"; | 
| 145 | > | if (getheader(fp, get_cminfo, &cmi) < 0) | 
| 146 | > | return(cmi.err); | 
| 147 | > | if (dt) {                               /* get/check data type? */ | 
| 148 | > | if (cmi.dtype == DTfromHeader) { | 
| 149 | > | if (*dt == DTfromHeader) | 
| 150 | > | return("missing/unknown data format in header"); | 
| 151 | > | } else if (*dt == DTfromHeader) | 
| 152 | > | *dt = cmi.dtype; | 
| 153 | > | else if (*dt != cmi.dtype) | 
| 154 | > | return("unexpected data format in header"); | 
| 155 | > | } | 
| 156 | > | if (nr) {                               /* get/check #rows? */ | 
| 157 | > | if (*nr <= 0) | 
| 158 | > | *nr = cmi.nrows; | 
| 159 | > | else if ((cmi.nrows > 0) & (*nr != cmi.nrows)) | 
| 160 | > | return("unexpected row count in header"); | 
| 161 | > | } | 
| 162 | > | if (nc) {                               /* get/check #columns? */ | 
| 163 | > | if (*nc <= 0) | 
| 164 | > | *nc = cmi.ncols; | 
| 165 | > | else if ((cmi.ncols > 0) & (*nc != cmi.ncols)) | 
| 166 | > | return("unexpected column count in header"); | 
| 167 | > | } | 
| 168 | > | if (swp)                                /* get/check swap? */ | 
| 169 | > | *swp = cmi.need2swap; | 
| 170 | > | if (scale) {                            /* transfer exposure comp. */ | 
| 171 | > | scale[0] = 1.f/cmi.expos[0]; | 
| 172 | > | scale[1] = 1.f/cmi.expos[1]; | 
| 173 | > | scale[2] = 1.f/cmi.expos[2]; | 
| 174 | > | } | 
| 175 | > | return(NULL); | 
| 176 |  | } | 
| 177 |  |  | 
| 178 | < | /* Allocate and load a matrix from the given file (or stdin if NULL) */ | 
| 179 | < | CMATRIX * | 
| 180 | < | cm_load(const char *fname, int nrows, int ncols, int dtype) | 
| 178 | > | /* Allocate and load image data into matrix */ | 
| 179 | > | static CMATRIX * | 
| 180 | > | cm_load_rgbe(FILE *fp, int nrows, int ncols, COLOR scale) | 
| 181 |  | { | 
| 182 | < | FILE    *fp = stdin; | 
| 182 | > | int     doscale; | 
| 183 |  | CMATRIX *cm; | 
| 184 | + | COLORV  *mp; | 
| 185 | + | /* header already loaded */ | 
| 186 | + | cm = cm_alloc(nrows, ncols); | 
| 187 | + | if (!cm) | 
| 188 | + | return(NULL); | 
| 189 | + | doscale = (scale[0] < .99) | (scale[0] > 1.01) | | 
| 190 | + | (scale[1] < .99) | (scale[1] > 1.01) | | 
| 191 | + | (scale[2] < .99) | (scale[2] > 1.01) ; | 
| 192 | + | mp = cm->cmem; | 
| 193 | + | while (nrows--) { | 
| 194 | + | if (freadscan((COLOR *)mp, ncols, fp) < 0) { | 
| 195 | + | error(USER, "error reading color picture as matrix"); | 
| 196 | + | cm_free(cm); | 
| 197 | + | return(NULL); | 
| 198 | + | } | 
| 199 | + | if (doscale) { | 
| 200 | + | int     i = ncols; | 
| 201 | + | while (i--) { | 
| 202 | + | *mp++ *= scale[0]; | 
| 203 | + | *mp++ *= scale[1]; | 
| 204 | + | *mp++ *= scale[2]; | 
| 205 | + | } | 
| 206 | + | } else | 
| 207 | + | mp += 3*ncols; | 
| 208 | + | }                                       /* caller closes stream */ | 
| 209 | + | return(cm); | 
| 210 | + | } | 
| 211 |  |  | 
| 212 | < | if (ncols <= 0) | 
| 213 | < | error(USER, "Non-positive number of columns"); | 
| 214 | < | if (fname == NULL) | 
| 215 | < | fname = "<stdin>"; | 
| 216 | < | else if ((fp = fopen(fname, "r")) == NULL) { | 
| 217 | < | sprintf(errmsg, "cannot open file '%s'", fname); | 
| 212 | > | /* Allocate and load a matrix from the given input (or stdin if NULL) */ | 
| 213 | > | CMATRIX * | 
| 214 | > | cm_load(const char *inspec, int nrows, int ncols, int dtype) | 
| 215 | > | { | 
| 216 | > | const int       ROWINC = 2048; | 
| 217 | > | int             swap = 0; | 
| 218 | > | FILE            *fp; | 
| 219 | > | COLOR           scale; | 
| 220 | > | CMATRIX         *cm; | 
| 221 | > |  | 
| 222 | > | if (!inspec) | 
| 223 | > | inspec = stdin_name; | 
| 224 | > | else if (!*inspec) | 
| 225 | > | return(NULL); | 
| 226 | > | if (inspec == stdin_name) {             /* reading from stdin? */ | 
| 227 | > | fp = stdin; | 
| 228 | > | } else if (inspec[0] == '!') { | 
| 229 | > | fp = popen(inspec+1, "r"); | 
| 230 | > | if (!fp) { | 
| 231 | > | sprintf(errmsg, "cannot start command '%s'", inspec); | 
| 232 | > | error(SYSTEM, errmsg); | 
| 233 | > | } | 
| 234 | > | } else if (!(fp = fopen(inspec, "r"))) { | 
| 235 | > | sprintf(errmsg, "cannot open file '%s'", inspec); | 
| 236 |  | error(SYSTEM, errmsg); | 
| 237 |  | } | 
| 238 |  | #ifdef getc_unlocked | 
| 240 |  | #endif | 
| 241 |  | if (dtype != DTascii) | 
| 242 |  | SET_FILE_BINARY(fp);            /* doesn't really work */ | 
| 243 | < | if (dtype == DTfromHeader) | 
| 244 | < | dtype = getDTfromHeader(fp); | 
| 243 | > | if (!dtype | !ncols) {                  /* expecting header? */ | 
| 244 | > | char    *err = cm_getheader(&dtype, &nrows, &ncols, &swap, scale, fp); | 
| 245 | > | if (err) | 
| 246 | > | error(USER, err); | 
| 247 | > | } | 
| 248 | > | if (ncols <= 0 && !fscnresolu(&ncols, &nrows, fp)) | 
| 249 | > | error(USER, "unspecified number of columns"); | 
| 250 |  | switch (dtype) { | 
| 251 |  | case DTascii: | 
| 252 |  | case DTfloat: | 
| 253 |  | case DTdouble: | 
| 254 |  | break; | 
| 255 | + | case DTrgbe: | 
| 256 | + | case DTxyze: | 
| 257 | + | cm = cm_load_rgbe(fp, nrows, ncols, scale); | 
| 258 | + | goto cleanup; | 
| 259 |  | default: | 
| 260 |  | error(USER, "unexpected data type in cm_load()"); | 
| 261 |  | } | 
| 262 |  | if (nrows <= 0) {                       /* don't know length? */ | 
| 263 |  | int     guessrows = 147;        /* usually big enough */ | 
| 264 | < | if ((dtype != DTascii) & (fp != stdin)) { | 
| 264 | > | if (cm_elem_size[dtype] && (fp != stdin) & (inspec[0] != '!')) { | 
| 265 |  | long    startpos = ftell(fp); | 
| 266 |  | if (fseek(fp, 0L, SEEK_END) == 0) { | 
| 267 | + | long    rowsiz = (long)ncols*cm_elem_size[dtype]; | 
| 268 |  | long    endpos = ftell(fp); | 
| 123 | – | long    elemsiz = 3*(dtype==DTfloat ? | 
| 124 | – | sizeof(float) : sizeof(double)); | 
| 269 |  |  | 
| 270 | < | if ((endpos - startpos) % (ncols*elemsiz)) { | 
| 270 | > | if ((endpos - startpos) % rowsiz) { | 
| 271 |  | sprintf(errmsg, | 
| 272 |  | "improper length for binary file '%s'", | 
| 273 | < | fname); | 
| 273 | > | inspec); | 
| 274 |  | error(USER, errmsg); | 
| 275 |  | } | 
| 276 | < | guessrows = (endpos - startpos)/(ncols*elemsiz); | 
| 276 | > | guessrows = (endpos - startpos)/rowsiz; | 
| 277 |  | if (fseek(fp, startpos, SEEK_SET) < 0) { | 
| 278 |  | sprintf(errmsg, | 
| 279 |  | "fseek() error on file '%s'", | 
| 280 | < | fname); | 
| 280 | > | inspec); | 
| 281 |  | error(SYSTEM, errmsg); | 
| 282 |  | } | 
| 283 |  | nrows = guessrows;      /* we're confident */ | 
| 286 |  | cm = cm_alloc(guessrows, ncols); | 
| 287 |  | } else | 
| 288 |  | cm = cm_alloc(nrows, ncols); | 
| 289 | < | if (cm == NULL)                                 /* XXX never happens */ | 
| 289 | > | if (!cm)                                        /* XXX never happens */ | 
| 290 |  | return(NULL); | 
| 291 |  | if (dtype == DTascii) {                         /* read text file */ | 
| 292 |  | int     maxrow = (nrows > 0 ? nrows : 32000); | 
| 293 |  | int     r, c; | 
| 294 |  | for (r = 0; r < maxrow; r++) { | 
| 295 |  | if (r >= cm->nrows)                 /* need more space? */ | 
| 296 | < | cm = cm_resize(cm, 2*cm->nrows); | 
| 296 | > | cm = cm_resize(cm, cm->nrows+ROWINC); | 
| 297 |  | for (c = 0; c < ncols; c++) { | 
| 298 |  | COLORV  *cv = cm_lval(cm,r,c); | 
| 299 | < | if (fscanf(fp, COLSPEC, cv, cv+1, cv+2) != 3) | 
| 299 | > | if (fscanf(fp, COLSPEC, cv, cv+1, cv+2) != 3) { | 
| 300 |  | if ((nrows <= 0) & (r > 0) & !c) { | 
| 301 |  | cm = cm_resize(cm, maxrow=r); | 
| 302 |  | break; | 
| 303 |  | } else | 
| 304 |  | goto EOFerror; | 
| 305 | + | } | 
| 306 |  | } | 
| 307 |  | } | 
| 308 |  | while ((c = getc(fp)) != EOF) | 
| 309 |  | if (!isspace(c)) { | 
| 310 |  | sprintf(errmsg, | 
| 311 | < | "unexpected data at end of ascii file %s", | 
| 312 | < | fname); | 
| 311 | > | "unexpected data at end of ascii input '%s'", | 
| 312 | > | inspec); | 
| 313 |  | error(WARNING, errmsg); | 
| 314 |  | break; | 
| 315 |  | } | 
| 316 |  | } else {                                        /* read binary file */ | 
| 317 |  | if (sizeof(COLOR) == cm_elem_size[dtype]) { | 
| 318 | < | int     nread = 0; | 
| 318 | > | size_t  nread = 0; | 
| 319 |  | do {                            /* read all we can */ | 
| 320 | < | nread += fread(cm->cmem + 3*nread, | 
| 321 | < | 3*sizeof(COLORV), | 
| 322 | < | cm->nrows*cm->ncols - nread, | 
| 320 | > | nread += getbinary(cm->cmem + 3*nread, | 
| 321 | > | sizeof(COLOR), | 
| 322 | > | (size_t)cm->nrows*cm->ncols - nread, | 
| 323 |  | fp); | 
| 324 |  | if (nrows <= 0) {       /* unknown length */ | 
| 325 | < | if (nread == cm->nrows*cm->ncols) | 
| 325 | > | if (nread == (size_t)cm->nrows*cm->ncols) | 
| 326 |  | /* need more space? */ | 
| 327 | < | cm = cm_resize(cm, 2*cm->nrows); | 
| 327 | > | cm = cm_resize(cm, cm->nrows+ROWINC); | 
| 328 |  | else if (nread && !(nread % cm->ncols)) | 
| 329 |  | /* seem to be  done */ | 
| 330 |  | cm = cm_resize(cm, nread/cm->ncols); | 
| 331 |  | else            /* ended mid-row */ | 
| 332 |  | goto EOFerror; | 
| 333 | < | } else if (nread < cm->nrows*cm->ncols) | 
| 333 | > | } else if (nread < (size_t)cm->nrows*cm->ncols) | 
| 334 |  | goto EOFerror; | 
| 335 | < | } while (nread < cm->nrows*cm->ncols); | 
| 335 | > | } while (nread < (size_t)cm->nrows*cm->ncols); | 
| 336 |  |  | 
| 337 | + | if (swap) { | 
| 338 | + | if (sizeof(COLORV) == 4) | 
| 339 | + | swap32((char *)cm->cmem, | 
| 340 | + | 3*(size_t)cm->nrows*cm->ncols); | 
| 341 | + | else /* sizeof(COLORV) == 8 */ | 
| 342 | + | swap64((char *)cm->cmem, | 
| 343 | + | 3*(size_t)cm->nrows*cm->ncols); | 
| 344 | + | } | 
| 345 |  | } else if (dtype == DTdouble) { | 
| 346 |  | double  dc[3];                  /* load from double */ | 
| 347 |  | COLORV  *cvp = cm->cmem; | 
| 348 | < | int     n = nrows*ncols; | 
| 348 | > | size_t  n = (size_t)nrows*ncols; | 
| 349 |  |  | 
| 350 |  | if (n <= 0) | 
| 351 |  | goto not_handled; | 
| 352 |  | while (n--) { | 
| 353 | < | if (fread(dc, sizeof(double), 3, fp) != 3) | 
| 353 | > | if (getbinary(dc, sizeof(double), 3, fp) != 3) | 
| 354 |  | goto EOFerror; | 
| 355 | + | if (swap) swap64((char *)dc, 3); | 
| 356 |  | copycolor(cvp, dc); | 
| 357 |  | cvp += 3; | 
| 358 |  | } | 
| 359 |  | } else /* dtype == DTfloat */ { | 
| 360 |  | float   fc[3];                  /* load from float */ | 
| 361 |  | COLORV  *cvp = cm->cmem; | 
| 362 | < | int     n = nrows*ncols; | 
| 362 | > | size_t  n = (size_t)nrows*ncols; | 
| 363 |  |  | 
| 364 |  | if (n <= 0) | 
| 365 |  | goto not_handled; | 
| 366 |  | while (n--) { | 
| 367 | < | if (fread(fc, sizeof(float), 3, fp) != 3) | 
| 367 | > | if (getbinary(fc, sizeof(float), 3, fp) != 3) | 
| 368 |  | goto EOFerror; | 
| 369 | + | if (swap) swap32((char *)fc, 3); | 
| 370 |  | copycolor(cvp, fc); | 
| 371 |  | cvp += 3; | 
| 372 |  | } | 
| 373 |  | } | 
| 374 |  | if (fgetc(fp) != EOF) { | 
| 375 |  | sprintf(errmsg, | 
| 376 | < | "unexpected data at end of binary file %s", | 
| 377 | < | fname); | 
| 376 | > | "unexpected data at end of binary input '%s'", | 
| 377 | > | inspec); | 
| 378 |  | error(WARNING, errmsg); | 
| 379 |  | } | 
| 380 |  | } | 
| 381 | < | if (fp != stdin) | 
| 382 | < | fclose(fp); | 
| 381 | > | cleanup: | 
| 382 | > | if (fp != stdin) { | 
| 383 | > | if (inspec[0] != '!') | 
| 384 | > | fclose(fp); | 
| 385 | > | else if (pclose(fp)) { | 
| 386 | > | sprintf(errmsg, "error running command '%s'", inspec); | 
| 387 | > | error(WARNING, errmsg); | 
| 388 | > | } | 
| 389 | > | } | 
| 390 |  | #ifdef getc_unlocked | 
| 391 |  | else | 
| 392 |  | funlockfile(fp); | 
| 393 |  | #endif | 
| 394 |  | return(cm); | 
| 395 |  | EOFerror: | 
| 396 | < | sprintf(errmsg, "unexpected EOF reading %s", fname); | 
| 396 | > | sprintf(errmsg, "unexpected EOF reading %s", inspec); | 
| 397 |  | error(USER, errmsg); | 
| 398 | + | return(NULL); | 
| 399 |  | not_handled: | 
| 400 |  | error(INTERNAL, "unhandled data size or length in cm_load()"); | 
| 401 |  | return(NULL);   /* gratis return */ | 
| 408 |  | CMATRIX *cvr; | 
| 409 |  | int     dr; | 
| 410 |  |  | 
| 411 | + | if (!cm) | 
| 412 | + | return(NULL); | 
| 413 |  | if ((c < 0) | (c >= cm->ncols)) | 
| 414 |  | error(INTERNAL, "column requested outside matrix"); | 
| 415 |  | cvr = cm_alloc(cm->nrows, 1); | 
| 416 | < | if (cvr == NULL) | 
| 416 | > | if (!cvr) | 
| 417 |  | return(NULL); | 
| 418 |  | for (dr = 0; dr < cm->nrows; dr++) { | 
| 419 |  | const COLORV    *sp = cm_lval(cm,dr,c); | 
| 425 |  | return(cvr); | 
| 426 |  | } | 
| 427 |  |  | 
| 263 | – | /* Scale a matrix by a single value */ | 
| 264 | – | CMATRIX * | 
| 265 | – | cm_scale(const CMATRIX *cm1, const COLOR sca) | 
| 266 | – | { | 
| 267 | – | CMATRIX *cmr; | 
| 268 | – | int     dr, dc; | 
| 269 | – |  | 
| 270 | – | cmr = cm_alloc(cm1->nrows, cm1->ncols); | 
| 271 | – | if (cmr == NULL) | 
| 272 | – | return(NULL); | 
| 273 | – | for (dr = 0; dr < cmr->nrows; dr++) | 
| 274 | – | for (dc = 0; dc < cmr->ncols; dc++) { | 
| 275 | – | const COLORV    *sp = cm_lval(cm1,dr,dc); | 
| 276 | – | COLORV          *dp = cm_lval(cmr,dr,dc); | 
| 277 | – | dp[0] = sp[0] * sca[0]; | 
| 278 | – | dp[1] = sp[1] * sca[1]; | 
| 279 | – | dp[2] = sp[2] * sca[2]; | 
| 280 | – | } | 
| 281 | – | return(cmr); | 
| 282 | – | } | 
| 283 | – |  | 
| 428 |  | /* Multiply two matrices (or a matrix and a vector) and allocate the result */ | 
| 429 |  | CMATRIX * | 
| 430 |  | cm_multiply(const CMATRIX *cm1, const CMATRIX *cm2) | 
| 433 |  | CMATRIX *cmr; | 
| 434 |  | int     dr, dc, i; | 
| 435 |  |  | 
| 436 | + | if (!cm1 | !cm2) | 
| 437 | + | return(NULL); | 
| 438 |  | if ((cm1->ncols <= 0) | (cm1->ncols != cm2->nrows)) | 
| 439 |  | error(INTERNAL, "matrix dimension mismatch in cm_multiply()"); | 
| 440 |  | cmr = cm_alloc(cm1->nrows, cm2->ncols); | 
| 441 | < | if (cmr == NULL) | 
| 441 | > | if (!cmr) | 
| 442 |  | return(NULL); | 
| 443 |  | /* optimization: check for zero rows & cols */ | 
| 444 |  | if (((cm1->nrows > 5) | (cm2->ncols > 5)) & (cm1->ncols > 5)) { | 
| 461 |  | for (dr = 0; dr < cmr->nrows; dr++) | 
| 462 |  | for (dc = 0; dc < cmr->ncols; dc++) { | 
| 463 |  | COLORV  *dp = cm_lval(cmr,dr,dc); | 
| 464 | + | double  res[3]; | 
| 465 |  | dp[0] = dp[1] = dp[2] = 0; | 
| 466 | < | if (rowcheck != NULL && !rowcheck[dr]) | 
| 466 | > | if (rowcheck && !rowcheck[dr]) | 
| 467 |  | continue; | 
| 468 | < | if (colcheck != NULL && !colcheck[dc]) | 
| 468 | > | if (colcheck && !colcheck[dc]) | 
| 469 |  | continue; | 
| 470 | + | res[0] = res[1] = res[2] = 0; | 
| 471 |  | for (i = 0; i < cm1->ncols; i++) { | 
| 472 |  | const COLORV        *cp1 = cm_lval(cm1,dr,i); | 
| 473 |  | const COLORV        *cp2 = cm_lval(cm2,i,dc); | 
| 474 | < | dp[0] += cp1[0] * cp2[0]; | 
| 475 | < | dp[1] += cp1[1] * cp2[1]; | 
| 476 | < | dp[2] += cp1[2] * cp2[2]; | 
| 474 | > | res[0] += (double)cp1[0] * cp2[0]; | 
| 475 | > | res[1] += (double)cp1[1] * cp2[1]; | 
| 476 | > | res[2] += (double)cp1[2] * cp2[2]; | 
| 477 |  | } | 
| 478 | + | copycolor(dp, res); | 
| 479 |  | } | 
| 480 | < | if (rowcheck != NULL) free(rowcheck); | 
| 481 | < | if (colcheck != NULL) free(colcheck); | 
| 480 | > | if (rowcheck) free(rowcheck); | 
| 481 | > | if (colcheck) free(colcheck); | 
| 482 |  | return(cmr); | 
| 483 |  | } | 
| 484 |  |  | 
| 487 |  | cm_write(const CMATRIX *cm, int dtype, FILE *fp) | 
| 488 |  | { | 
| 489 |  | static const char       tabEOL[2] = {'\t','\n'}; | 
| 490 | < | const COLORV            *mp = cm->cmem; | 
| 490 | > | const COLORV            *mp; | 
| 491 |  | int                     r, c; | 
| 492 | + | size_t                  n, rv; | 
| 493 |  |  | 
| 494 | + | if (!cm) | 
| 495 | + | return(0); | 
| 496 | + | mp = cm->cmem; | 
| 497 |  | switch (dtype) { | 
| 498 |  | case DTascii: | 
| 499 |  | for (r = 0; r < cm->nrows; r++) | 
| 505 |  | case DTfloat: | 
| 506 |  | case DTdouble: | 
| 507 |  | if (sizeof(COLOR) == cm_elem_size[dtype]) { | 
| 508 | < | r = cm->ncols*cm->nrows; | 
| 509 | < | while (r > 0) { | 
| 510 | < | c = fwrite(mp, sizeof(COLOR), r, fp); | 
| 511 | < | if (c <= 0) | 
| 508 | > | n = (size_t)cm->ncols*cm->nrows; | 
| 509 | > | while (n > 0) { | 
| 510 | > | rv = fwrite(mp, sizeof(COLOR), n, fp); | 
| 511 | > | if (rv <= 0) | 
| 512 |  | return(0); | 
| 513 | < | mp += 3*c; | 
| 514 | < | r -= c; | 
| 513 | > | mp += 3*rv; | 
| 514 | > | n -= rv; | 
| 515 |  | } | 
| 516 |  | } else if (dtype == DTdouble) { | 
| 517 |  | double  dc[3]; | 
| 518 | < | r = cm->ncols*cm->nrows; | 
| 519 | < | while (r--) { | 
| 518 | > | n = (size_t)cm->ncols*cm->nrows; | 
| 519 | > | while (n--) { | 
| 520 |  | copycolor(dc, mp); | 
| 521 | < | if (fwrite(dc, sizeof(double), 3, fp) != 3) | 
| 521 | > | if (putbinary(dc, sizeof(double), 3, fp) != 3) | 
| 522 |  | return(0); | 
| 523 |  | mp += 3; | 
| 524 |  | } | 
| 525 |  | } else /* dtype == DTfloat */ { | 
| 526 |  | float   fc[3]; | 
| 527 | < | r = cm->ncols*cm->nrows; | 
| 528 | < | while (r--) { | 
| 527 | > | n = (size_t)cm->ncols*cm->nrows; | 
| 528 | > | while (n--) { | 
| 529 |  | copycolor(fc, mp); | 
| 530 | < | if (fwrite(fc, sizeof(float), 3, fp) != 3) | 
| 530 | > | if (putbinary(fc, sizeof(float), 3, fp) != 3) | 
| 531 |  | return(0); | 
| 532 |  | mp += 3; | 
| 533 |  | } |