| 1 | greg | 3.1 | #ifndef lint | 
| 2 | greg | 3.9 | static const char       RCSid[] = "$Id: tmaptiff.c,v 3.8 2009/02/09 20:48:08 greg Exp $"; | 
| 3 | greg | 3.1 | #endif | 
| 4 |  |  | /* | 
| 5 |  |  | * Perform tone mapping on TIFF input. | 
| 6 |  |  | * | 
| 7 |  |  | * Externals declared in tmaptiff.h | 
| 8 |  |  | */ | 
| 9 |  |  |  | 
| 10 | greg | 3.2 | #include "copyright.h" | 
| 11 | greg | 3.1 |  | 
| 12 |  |  | #include <stdio.h> | 
| 13 | greg | 3.8 | #include <stdlib.h> | 
| 14 | greg | 3.9 | #include "tiffio.h" | 
| 15 | greg | 3.7 | #include "tmprivat.h" | 
| 16 | greg | 3.1 | #include "tmaptiff.h" | 
| 17 |  |  |  | 
| 18 | greg | 3.3 | /* input cases we handle */ | 
| 19 |  |  | #define TC_LOGLUV32     1 | 
| 20 |  |  | #define TC_LOGLUV24     2 | 
| 21 |  |  | #define TC_LOGL16       3 | 
| 22 |  |  | #define TC_GRYFLOAT     4 | 
| 23 |  |  | #define TC_RGBFLOAT     5 | 
| 24 |  |  | #define TC_GRYSHORT     6 | 
| 25 |  |  | #define TC_RGBSHORT     7 | 
| 26 |  |  |  | 
| 27 |  |  | /* figure out what kind of TIFF we have and if we can tone-map it */ | 
| 28 |  |  | static int | 
| 29 |  |  | getTIFFtype(TIFF *tif) | 
| 30 |  |  | { | 
| 31 |  |  | uint16  comp, phot, pconf; | 
| 32 |  |  | uint16  samp_fmt, bits_samp; | 
| 33 |  |  |  | 
| 34 |  |  | TIFFGetFieldDefaulted(tif, TIFFTAG_PLANARCONFIG, &pconf); | 
| 35 |  |  | if (pconf != PLANARCONFIG_CONTIG) | 
| 36 |  |  | return(0); | 
| 37 |  |  | TIFFGetFieldDefaulted(tif, TIFFTAG_PHOTOMETRIC, &phot); | 
| 38 |  |  | TIFFGetFieldDefaulted(tif, TIFFTAG_COMPRESSION, &comp); | 
| 39 |  |  | TIFFGetFieldDefaulted(tif, TIFFTAG_SAMPLEFORMAT, &samp_fmt); | 
| 40 |  |  | TIFFGetFieldDefaulted(tif, TIFFTAG_BITSPERSAMPLE, &bits_samp); | 
| 41 |  |  | switch (phot) { | 
| 42 |  |  | case PHOTOMETRIC_LOGLUV: | 
| 43 |  |  | if (comp == COMPRESSION_SGILOG) | 
| 44 |  |  | return(TC_LOGLUV32); | 
| 45 |  |  | if (comp == COMPRESSION_SGILOG24) | 
| 46 |  |  | return(TC_LOGLUV24); | 
| 47 |  |  | return(0); | 
| 48 |  |  | case PHOTOMETRIC_LOGL: | 
| 49 |  |  | if (comp == COMPRESSION_SGILOG) | 
| 50 |  |  | return(TC_LOGL16); | 
| 51 |  |  | return(0); | 
| 52 |  |  | case PHOTOMETRIC_MINISBLACK: | 
| 53 |  |  | if (samp_fmt == SAMPLEFORMAT_UINT) { | 
| 54 |  |  | if (bits_samp == 16) | 
| 55 |  |  | return(TC_GRYSHORT); | 
| 56 |  |  | return(0); | 
| 57 |  |  | } | 
| 58 |  |  | if (samp_fmt == SAMPLEFORMAT_IEEEFP) { | 
| 59 |  |  | if (bits_samp == 8*sizeof(float)) | 
| 60 |  |  | return(TC_GRYFLOAT); | 
| 61 |  |  | return(0); | 
| 62 |  |  | } | 
| 63 |  |  | return(0); | 
| 64 |  |  | case PHOTOMETRIC_RGB: | 
| 65 |  |  | if (samp_fmt == SAMPLEFORMAT_UINT) { | 
| 66 |  |  | if (bits_samp == 16) | 
| 67 |  |  | return(TC_RGBSHORT); | 
| 68 |  |  | return(0); | 
| 69 |  |  | } | 
| 70 |  |  | if (samp_fmt == SAMPLEFORMAT_IEEEFP) { | 
| 71 |  |  | if (bits_samp == 8*sizeof(float)) | 
| 72 |  |  | return(TC_RGBFLOAT); | 
| 73 |  |  | return(0); | 
| 74 |  |  | } | 
| 75 |  |  | return(0); | 
| 76 |  |  | } | 
| 77 |  |  | return(0); | 
| 78 |  |  | } | 
| 79 | greg | 3.1 |  | 
| 80 | greg | 3.3 | /* load and convert TIFF */ | 
| 81 | greg | 3.1 | int | 
| 82 | greg | 3.5 | tmLoadTIFF(TMstruct *tms, TMbright **lpp, BYTE **cpp, | 
| 83 |  |  | int *xp, int *yp, char *fname, TIFF *tp) | 
| 84 | greg | 3.1 | { | 
| 85 |  |  | char    *funcName = fname==NULL ? "tmLoadTIFF" : fname; | 
| 86 | greg | 3.5 | RGBPRIMP        inppri = tms->monpri; | 
| 87 | greg | 3.3 | RGBPRIMS        myprims; | 
| 88 |  |  | float   *fa; | 
| 89 | greg | 3.1 | TIFF    *tif; | 
| 90 |  |  | int     err; | 
| 91 | greg | 3.3 | union {uint16 *w; uint32 *l; float *f; MEM_PTR p;} sl; | 
| 92 | greg | 3.1 | uint32  width, height; | 
| 93 | greg | 3.3 | int     tcase; | 
| 94 | greg | 3.1 | double  stonits; | 
| 95 |  |  | int     y; | 
| 96 |  |  | /* check arguments */ | 
| 97 | greg | 3.5 | if (tms == NULL) | 
| 98 | greg | 3.1 | returnErr(TM_E_TMINVAL); | 
| 99 | schorsch | 3.4 | if ((lpp == NULL) | (xp == NULL) | (yp == NULL) | | 
| 100 |  |  | ((fname == NULL) & (tp == NULL))) | 
| 101 | greg | 3.1 | returnErr(TM_E_ILLEGAL); | 
| 102 |  |  | /* check/get TIFF tags */ | 
| 103 |  |  | sl.p = NULL; *lpp = NULL; | 
| 104 |  |  | if (cpp != TM_NOCHROMP) *cpp = TM_NOCHROM; | 
| 105 |  |  | err = TM_E_BADFILE; | 
| 106 |  |  | if ((tif = tp) == NULL && (tif = TIFFOpen(fname, "r")) == NULL) | 
| 107 |  |  | returnErr(TM_E_BADFILE); | 
| 108 | greg | 3.3 | tcase = getTIFFtype(tif); | 
| 109 |  |  | if (!tcase) | 
| 110 |  |  | goto done; | 
| 111 | greg | 3.1 | if (!TIFFGetField(tif, TIFFTAG_IMAGEWIDTH, &width) || | 
| 112 |  |  | !TIFFGetField(tif, TIFFTAG_IMAGELENGTH, &height)) | 
| 113 |  |  | goto done; | 
| 114 |  |  | *xp = width; *yp = height; | 
| 115 | greg | 3.3 | if (TIFFGetField(tif, TIFFTAG_PRIMARYCHROMATICITIES, &fa)) { | 
| 116 |  |  | myprims[RED][CIEX] = fa[0]; | 
| 117 |  |  | myprims[RED][CIEY] = fa[1]; | 
| 118 |  |  | myprims[GRN][CIEX] = fa[2]; | 
| 119 |  |  | myprims[GRN][CIEY] = fa[3]; | 
| 120 |  |  | myprims[BLU][CIEX] = fa[4]; | 
| 121 |  |  | myprims[BLU][CIEY] = fa[5]; | 
| 122 |  |  | myprims[WHT][CIEX] = 1./3.; | 
| 123 |  |  | myprims[WHT][CIEY] = 1./3.; | 
| 124 |  |  | if (TIFFGetField(tif, TIFFTAG_WHITEPOINT, &fa)) { | 
| 125 |  |  | myprims[WHT][CIEX] = fa[0]; | 
| 126 |  |  | myprims[WHT][CIEY] = fa[1]; | 
| 127 |  |  | } | 
| 128 |  |  | inppri = myprims; | 
| 129 |  |  | } | 
| 130 | greg | 3.1 | if (!TIFFGetField(tif, TIFFTAG_STONITS, &stonits)) | 
| 131 |  |  | stonits = 1.; | 
| 132 | greg | 3.3 | switch (tcase) {                /* set up conversion */ | 
| 133 |  |  | case TC_LOGLUV32: | 
| 134 |  |  | case TC_LOGLUV24: | 
| 135 | greg | 3.1 | TIFFSetField(tif, TIFFTAG_SGILOGDATAFMT, SGILOGDATAFMT_RAW); | 
| 136 |  |  | sl.l = (uint32 *)malloc(width*sizeof(uint32)); | 
| 137 | greg | 3.6 | tmSetSpace(tms, TM_XYZPRIM, stonits, NULL); | 
| 138 | greg | 3.3 | break; | 
| 139 |  |  | case TC_LOGL16: | 
| 140 | greg | 3.1 | TIFFSetField(tif, TIFFTAG_SGILOGDATAFMT, SGILOGDATAFMT_16BIT); | 
| 141 |  |  | sl.w = (uint16 *)malloc(width*sizeof(uint16)); | 
| 142 | greg | 3.6 | tmSetSpace(tms, tms->monpri, stonits, NULL); | 
| 143 | greg | 3.3 | break; | 
| 144 |  |  | case TC_RGBFLOAT: | 
| 145 |  |  | sl.f = (float *)malloc(width*3*sizeof(float)); | 
| 146 | greg | 3.6 | tmSetSpace(tms, inppri, stonits, NULL); | 
| 147 | greg | 3.3 | break; | 
| 148 |  |  | case TC_GRYFLOAT: | 
| 149 |  |  | sl.f = (float *)malloc(width*sizeof(float)); | 
| 150 | greg | 3.6 | tmSetSpace(tms, tms->monpri, stonits, NULL); | 
| 151 | greg | 3.3 | break; | 
| 152 |  |  | case TC_RGBSHORT: | 
| 153 |  |  | sl.w = (uint16 *)malloc(width*3*sizeof(uint16)); | 
| 154 | greg | 3.6 | tmSetSpace(tms, inppri, stonits, NULL); | 
| 155 | greg | 3.3 | break; | 
| 156 |  |  | case TC_GRYSHORT: | 
| 157 |  |  | sl.w = (uint16 *)malloc(width*sizeof(uint16)); | 
| 158 | greg | 3.6 | tmSetSpace(tms, tms->monpri, stonits, NULL); | 
| 159 | greg | 3.3 | break; | 
| 160 |  |  | default: | 
| 161 |  |  | err = TM_E_CODERR1; | 
| 162 | greg | 3.1 | goto done; | 
| 163 | greg | 3.3 | } | 
| 164 | greg | 3.1 | *lpp = (TMbright *)malloc(width*height*sizeof(TMbright)); | 
| 165 | schorsch | 3.4 | if ((sl.p == NULL) | (*lpp == NULL)) { | 
| 166 | greg | 3.1 | err = TM_E_NOMEM; | 
| 167 |  |  | goto done; | 
| 168 |  |  | } | 
| 169 | greg | 3.3 | switch (tcase) {                /* allocate color if needed */ | 
| 170 |  |  | case TC_LOGLUV32: | 
| 171 |  |  | case TC_LOGLUV24: | 
| 172 |  |  | case TC_RGBFLOAT: | 
| 173 |  |  | case TC_RGBSHORT: | 
| 174 |  |  | if (cpp == TM_NOCHROMP) | 
| 175 |  |  | break; | 
| 176 |  |  | *cpp = (BYTE *)malloc(width*height*3*sizeof(BYTE)); | 
| 177 |  |  | if (*cpp == NULL) { | 
| 178 |  |  | err = TM_E_NOMEM; | 
| 179 |  |  | goto done; | 
| 180 |  |  | } | 
| 181 |  |  | break; | 
| 182 |  |  | } | 
| 183 | greg | 3.1 | /* read and convert each scanline */ | 
| 184 |  |  | for (y = 0; y < height; y++) { | 
| 185 |  |  | if (TIFFReadScanline(tif, sl.p, y, 0) < 0) { | 
| 186 |  |  | err = TM_E_BADFILE; | 
| 187 |  |  | break; | 
| 188 |  |  | } | 
| 189 | greg | 3.3 | switch (tcase) { | 
| 190 |  |  | case TC_LOGLUV32: | 
| 191 | greg | 3.5 | err = tmCvLuv32(tms, *lpp + y*width, | 
| 192 | greg | 3.3 | cpp==TM_NOCHROMP ? TM_NOCHROM : *cpp+y*3*width, | 
| 193 |  |  | sl.l, width); | 
| 194 |  |  | break; | 
| 195 |  |  | case TC_LOGLUV24: | 
| 196 | greg | 3.5 | err = tmCvLuv24(tms, *lpp + y*width, | 
| 197 | greg | 3.1 | cpp==TM_NOCHROMP ? TM_NOCHROM : *cpp+y*3*width, | 
| 198 |  |  | sl.l, width); | 
| 199 | greg | 3.3 | break; | 
| 200 |  |  | case TC_LOGL16: | 
| 201 | greg | 3.5 | err = tmCvL16(tms, *lpp + y*width, sl.w, width); | 
| 202 | greg | 3.3 | break; | 
| 203 |  |  | case TC_RGBFLOAT: | 
| 204 | greg | 3.5 | err = tmCvColors(tms, *lpp + y*width, | 
| 205 | greg | 3.3 | cpp==TM_NOCHROMP ? TM_NOCHROM : *cpp+y*3*width, | 
| 206 |  |  | (COLOR *)sl.f, width); | 
| 207 |  |  | break; | 
| 208 |  |  | case TC_GRYFLOAT: | 
| 209 | greg | 3.5 | err = tmCvGrays(tms, *lpp + y*width, sl.f, width); | 
| 210 | greg | 3.3 | break; | 
| 211 |  |  | case TC_RGBSHORT: | 
| 212 | greg | 3.5 | err = tmCvRGB48(tms, *lpp + y*width, | 
| 213 | greg | 3.1 | cpp==TM_NOCHROMP ? TM_NOCHROM : *cpp+y*3*width, | 
| 214 | greg | 3.3 | (uint16 (*)[3])sl.w, width, DEFGAM); | 
| 215 |  |  | break; | 
| 216 |  |  | case TC_GRYSHORT: | 
| 217 | greg | 3.5 | err = tmCvGray16(tms, *lpp + y*width, | 
| 218 |  |  | sl.w, width, DEFGAM); | 
| 219 | greg | 3.3 | break; | 
| 220 |  |  | default: | 
| 221 |  |  | err = TM_E_CODERR1; | 
| 222 |  |  | break; | 
| 223 |  |  | } | 
| 224 | greg | 3.1 | if (err != TM_E_OK) | 
| 225 |  |  | break; | 
| 226 |  |  | } | 
| 227 |  |  | done:                                   /* clean up */ | 
| 228 |  |  | if (tp == NULL) | 
| 229 |  |  | TIFFClose(tif); | 
| 230 |  |  | if (sl.p != NULL) | 
| 231 |  |  | free(sl.p); | 
| 232 |  |  | if (err != TM_E_OK) {           /* free buffers on error */ | 
| 233 |  |  | if (*lpp != NULL) | 
| 234 |  |  | free((MEM_PTR)*lpp); | 
| 235 |  |  | *lpp = NULL; | 
| 236 |  |  | if (cpp != TM_NOCHROMP) { | 
| 237 |  |  | if (*cpp != TM_NOCHROM) | 
| 238 |  |  | free((MEM_PTR)*cpp); | 
| 239 |  |  | *cpp = NULL; | 
| 240 |  |  | } | 
| 241 |  |  | *xp = *yp = 0; | 
| 242 |  |  | returnErr(err); | 
| 243 |  |  | } | 
| 244 |  |  | returnOK; | 
| 245 |  |  | } | 
| 246 |  |  |  | 
| 247 |  |  |  | 
| 248 |  |  | /* | 
| 249 |  |  | * Load and tone-map a SGILOG TIFF. | 
| 250 |  |  | * Beware of greyscale input -- you must check the PHOTOMETRIC tag to | 
| 251 |  |  | * determine that the returned array contains only grey values, not RGB. | 
| 252 |  |  | * As in tmMapPicture(), grey values are also returned if flags&TM_F_BW. | 
| 253 |  |  | */ | 
| 254 |  |  | int | 
| 255 | greg | 3.3 | tmMapTIFF(BYTE **psp, int *xp, int *yp, int flags, RGBPRIMP monpri, | 
| 256 |  |  | double gamval, double Lddyn, double Ldmax, char *fname, TIFF *tp) | 
| 257 | greg | 3.1 | { | 
| 258 |  |  | char    *funcName = fname==NULL ? "tmMapTIFF" : fname; | 
| 259 | greg | 3.5 | TMstruct        *tms; | 
| 260 | greg | 3.1 | TMbright        *lp; | 
| 261 |  |  | BYTE    *cp; | 
| 262 |  |  | int     err; | 
| 263 |  |  | /* check arguments */ | 
| 264 | schorsch | 3.4 | if ((psp == NULL) | (xp == NULL) | (yp == NULL) | (monpri == NULL) | | 
| 265 |  |  | ((fname == NULL) & (tp == NULL))) | 
| 266 | greg | 3.1 | returnErr(TM_E_ILLEGAL); | 
| 267 |  |  | if (gamval < MINGAM) gamval = DEFGAM; | 
| 268 |  |  | if (Lddyn < MINLDDYN) Lddyn = DEFLDDYN; | 
| 269 |  |  | if (Ldmax < MINLDMAX) Ldmax = DEFLDMAX; | 
| 270 |  |  | if (flags & TM_F_BW) monpri = stdprims; | 
| 271 |  |  | /* initialize tone mapping */ | 
| 272 | greg | 3.5 | if ((tms = tmInit(flags, monpri, gamval)) == NULL) | 
| 273 | greg | 3.1 | returnErr(TM_E_NOMEM); | 
| 274 |  |  | /* load and convert TIFF */ | 
| 275 |  |  | cp = TM_NOCHROM; | 
| 276 | greg | 3.5 | err = tmLoadTIFF(tms, &lp, flags&TM_F_BW ? TM_NOCHROMP : &cp, | 
| 277 | greg | 3.1 | xp, yp, fname, tp); | 
| 278 |  |  | if (err != TM_E_OK) { | 
| 279 | greg | 3.5 | tmDone(tms); | 
| 280 | greg | 3.1 | return(err); | 
| 281 |  |  | } | 
| 282 |  |  | if (cp == TM_NOCHROM) { | 
| 283 |  |  | *psp = (BYTE *)malloc(*xp * *yp * sizeof(BYTE)); | 
| 284 |  |  | if (*psp == NULL) { | 
| 285 |  |  | free((MEM_PTR)lp); | 
| 286 | greg | 3.5 | tmDone(tms); | 
| 287 | greg | 3.1 | returnErr(TM_E_NOMEM); | 
| 288 |  |  | } | 
| 289 |  |  | } else | 
| 290 |  |  | *psp = cp; | 
| 291 |  |  | /* compute color mapping */ | 
| 292 | greg | 3.5 | err = tmAddHisto(tms, lp, *xp * *yp, 1); | 
| 293 | greg | 3.1 | if (err != TM_E_OK) | 
| 294 |  |  | goto done; | 
| 295 | greg | 3.5 | err = tmComputeMapping(tms, gamval, Lddyn, Ldmax); | 
| 296 | greg | 3.1 | if (err != TM_E_OK) | 
| 297 |  |  | goto done; | 
| 298 |  |  | /* map pixels */ | 
| 299 | greg | 3.5 | err = tmMapPixels(tms, *psp, lp, cp, *xp * *yp); | 
| 300 | greg | 3.1 |  | 
| 301 |  |  | done:                                   /* clean up */ | 
| 302 |  |  | free((MEM_PTR)lp); | 
| 303 | greg | 3.5 | tmDone(tms); | 
| 304 | greg | 3.1 | if (err != TM_E_OK) {           /* free memory on error */ | 
| 305 |  |  | free((MEM_PTR)*psp); | 
| 306 |  |  | *psp = NULL; | 
| 307 |  |  | *xp = *yp = 0; | 
| 308 |  |  | returnErr(err); | 
| 309 |  |  | } | 
| 310 |  |  | returnOK; | 
| 311 |  |  | } |