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