1 /*
2 This file is part of darktable,
3 Copyright (C) 2010-2020 darktable developers.
4
5 darktable is free software: you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation, either version 3 of the License, or
8 (at your option) any later version.
9
10 darktable is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with darktable. If not, see <http://www.gnu.org/licenses/>.
17 */
18 #include "imageio_tiff.h"
19 #include "common/colorspaces.h"
20 #include "common/darktable.h"
21 #include "common/exif.h"
22 #include "control/conf.h"
23 #include "develop/develop.h"
24 #include "imageio.h"
25
26 #include <inttypes.h>
27 #include <memory.h>
28 #include <stdio.h>
29 #include <strings.h>
30 #include <tiffio.h>
31
32 #define LAB_CONVERSION_PROFILE DT_COLORSPACE_LIN_REC2020
33
34 typedef struct tiff_t
35 {
36 TIFF *tiff;
37 uint32_t width;
38 uint32_t height;
39 uint16_t bpp;
40 uint16_t spp;
41 uint16_t sampleformat;
42 uint32_t scanlinesize;
43 dt_image_t *image;
44 float *mipbuf;
45 tdata_t buf;
46 } tiff_t;
47
48 typedef union fp32_t
49 {
50 uint32_t u;
51 float f;
52 } fp32_t;
53
_half_to_float(uint16_t h)54 static inline float _half_to_float(uint16_t h)
55 {
56 /* see https://en.wikipedia.org/wiki/Half-precision_floating-point_format#Exponent_encoding
57 and https://en.wikipedia.org/wiki/Single-precision_floating-point_format#Exponent_encoding */
58
59 /* TODO: use intrinsics when possible */
60
61 /* from https://gist.github.com/rygorous/2156668 */
62 static const fp32_t magic = { 113 << 23 };
63 static const uint32_t shifted_exp = 0x7c00 << 13; // exponent mask after shift
64 fp32_t o;
65
66 o.u = (h & 0x7fff) << 13; // exponent/mantissa bits
67 uint32_t exp = shifted_exp & o.u; // just the exponent
68 o.u += (127 - 15) << 23; // exponent adjust
69
70 // handle exponent special cases
71 if (exp == shifted_exp) // Inf/NaN?
72 o.u += (128 - 16) << 23; // extra exp adjust
73 else if (exp == 0) // Zero/Denormal?
74 {
75 o.u += 1 << 23; // extra exp adjust
76 o.f -= magic.f; // renormalize
77 }
78
79 o.u |= (h & 0x8000) << 16; // sign bit
80 return o.f;
81 }
82
_read_chunky_8(tiff_t * t)83 static inline int _read_chunky_8(tiff_t *t)
84 {
85 for(uint32_t row = 0; row < t->height; row++)
86 {
87 uint8_t *in = ((uint8_t *)t->buf);
88 float *out = ((float *)t->mipbuf) + (size_t)4 * row * t->width;
89
90 /* read scanline */
91 if(TIFFReadScanline(t->tiff, in, row, 0) == -1) return -1;
92
93 for(uint32_t i = 0; i < t->width; i++, in += t->spp, out += 4)
94 {
95 /* set rgb to first sample from scanline */
96 out[0] = ((float)in[0]) * (1.0f / 255.0f);
97
98 if(t->spp == 1)
99 {
100 out[1] = out[2] = out[0];
101 }
102 else
103 {
104 out[1] = ((float)in[1]) * (1.0f / 255.0f);
105 out[2] = ((float)in[2]) * (1.0f / 255.0f);
106 }
107
108 out[3] = 0;
109 }
110 }
111
112 return 1;
113 }
114
_read_chunky_16(tiff_t * t)115 static inline int _read_chunky_16(tiff_t *t)
116 {
117 for(uint32_t row = 0; row < t->height; row++)
118 {
119 uint16_t *in = ((uint16_t *)t->buf);
120 float *out = ((float *)t->mipbuf) + (size_t)4 * row * t->width;
121
122 /* read scanline */
123 if(TIFFReadScanline(t->tiff, in, row, 0) == -1) return -1;
124
125 for(uint32_t i = 0; i < t->width; i++, in += t->spp, out += 4)
126 {
127 out[0] = ((float)in[0]) * (1.0f / 65535.0f);
128
129 if(t->spp == 1)
130 {
131 out[1] = out[2] = out[0];
132 }
133 else
134 {
135 out[1] = ((float)in[1]) * (1.0f / 65535.0f);
136 out[2] = ((float)in[2]) * (1.0f / 65535.0f);
137 }
138
139 out[3] = 0;
140 }
141 }
142
143 return 1;
144 }
145
_read_chunky_h(tiff_t * t)146 static inline int _read_chunky_h(tiff_t *t)
147 {
148 for(uint32_t row = 0; row < t->height; row++)
149 {
150 uint16_t *in = ((uint16_t *)t->buf);
151 float *out = ((float *)t->mipbuf) + (size_t)4 * row * t->width;
152
153 /* read scanline */
154 if(TIFFReadScanline(t->tiff, in, row, 0) == -1) return -1;
155
156 for(uint32_t i = 0; i < t->width; i++, in += t->spp, out += 4)
157 {
158 out[0] = _half_to_float(in[0]);
159
160 if(t->spp == 1)
161 {
162 out[1] = out[2] = out[0];
163 }
164 else
165 {
166 out[1] = _half_to_float(in[1]);
167 out[2] = _half_to_float(in[2]);
168 }
169
170 out[3] = 0;
171 }
172 }
173
174 return 1;
175 }
176
_read_chunky_f(tiff_t * t)177 static inline int _read_chunky_f(tiff_t *t)
178 {
179 for(uint32_t row = 0; row < t->height; row++)
180 {
181 float *in = ((float *)t->buf);
182 float *out = ((float *)t->mipbuf) + (size_t)4 * row * t->width;
183
184 /* read scanline */
185 if(TIFFReadScanline(t->tiff, in, row, 0) == -1) return -1;
186
187 for(uint32_t i = 0; i < t->width; i++, in += t->spp, out += 4)
188 {
189 out[0] = in[0];
190
191 if(t->spp == 1)
192 {
193 out[1] = out[2] = out[0];
194 }
195 else
196 {
197 out[1] = in[1];
198 out[2] = in[2];
199 }
200
201 out[3] = 0;
202 }
203 }
204
205 return 1;
206 }
207
_read_chunky_8_Lab(tiff_t * t,uint16_t photometric)208 static inline int _read_chunky_8_Lab(tiff_t *t, uint16_t photometric)
209 {
210 const cmsHPROFILE Lab = dt_colorspaces_get_profile(DT_COLORSPACE_LAB, "", DT_PROFILE_DIRECTION_ANY)->profile;
211 const cmsHPROFILE output_profile = dt_colorspaces_get_profile(LAB_CONVERSION_PROFILE, "", DT_PROFILE_DIRECTION_OUT | DT_PROFILE_DIRECTION_DISPLAY)->profile;
212 const cmsHTRANSFORM xform = cmsCreateTransform(Lab, TYPE_LabA_FLT, output_profile, TYPE_RGBA_FLT, INTENT_PERCEPTUAL, 0);
213
214 for(uint32_t row = 0; row < t->height; row++)
215 {
216 uint8_t *in = ((uint8_t *)t->buf);
217 float *output = ((float *)t->mipbuf) + (size_t)4 * row * t->width;
218 float *out = output;
219
220 /* read scanline */
221 if(TIFFReadScanline(t->tiff, in, row, 0) == -1) goto failed;
222
223 for(uint32_t i = 0; i < t->width; i++, in += t->spp, out += 4)
224 {
225 out[0] = ((float)in[0]) * (100.0f/255.0f);
226
227 if(t->spp == 1)
228 {
229 out[1] = out[2] = 0;
230 }
231 else
232 {
233 if(photometric == PHOTOMETRIC_CIELAB)
234 {
235 out[1] = ((float)((int8_t)in[1]));
236 out[2] = ((float)((int8_t)in[2]));
237 }
238 else // photometric == PHOTOMETRIC_ICCLAB
239 {
240 out[1] = ((float)(in[1])) - 128.0f;
241 out[2] = ((float)(in[2])) - 128.0f;
242 }
243 }
244
245 out[3] = 0;
246 }
247
248 cmsDoTransform(xform, output, output, t->width);
249 }
250
251 cmsDeleteTransform(xform);
252
253 return 1;
254
255 failed:
256 cmsDeleteTransform(xform);
257 return -1;
258 }
259
260
_read_chunky_16_Lab(tiff_t * t,uint16_t photometric)261 static inline int _read_chunky_16_Lab(tiff_t *t, uint16_t photometric)
262 {
263 const cmsHPROFILE Lab = dt_colorspaces_get_profile(DT_COLORSPACE_LAB, "", DT_PROFILE_DIRECTION_ANY)->profile;
264 const cmsHPROFILE output_profile = dt_colorspaces_get_profile(LAB_CONVERSION_PROFILE, "", DT_PROFILE_DIRECTION_OUT | DT_PROFILE_DIRECTION_DISPLAY)->profile;
265 const cmsHTRANSFORM xform = cmsCreateTransform(Lab, TYPE_LabA_FLT, output_profile, TYPE_RGBA_FLT, INTENT_PERCEPTUAL, 0);
266 const float range = (photometric == PHOTOMETRIC_CIELAB) ? 65535.0f : 65280.0f;
267
268 for(uint32_t row = 0; row < t->height; row++)
269 {
270 uint16_t *in = ((uint16_t *)t->buf);
271 float *output = ((float *)t->mipbuf) + (size_t)4 * row * t->width;
272 float *out = output;
273
274 /* read scanline */
275 if(TIFFReadScanline(t->tiff, in, row, 0) == -1) goto failed;
276
277 for(uint32_t i = 0; i < t->width; i++, in += t->spp, out += 4)
278 {
279 out[0] = ((float)in[0]) * (100.0f/range);
280
281 if(t->spp == 1)
282 {
283 out[1] = out[2] = 0;
284 }
285 else
286 {
287 if(photometric == PHOTOMETRIC_CIELAB)
288 {
289 out[1] = ((float)((int16_t)in[1])) / 256.0f;
290 out[2] = ((float)((int16_t)in[2])) / 256.0f;
291 }
292 else // photometric == PHOTOMETRIC_ICCLAB
293 {
294 out[1] = (((float)(in[1])) - 32768.0f) / 256.0f;
295 out[2] = (((float)(in[2])) - 32768.0f) / 256.0f;
296 }
297 }
298
299 out[3] = 0;
300 }
301
302 cmsDoTransform(xform, output, output, t->width);
303 }
304
305 cmsDeleteTransform(xform);
306
307 return 1;
308
309 failed:
310 cmsDeleteTransform(xform);
311 return -1;
312 }
313
314
_warning_error_handler(const char * type,const char * module,const char * fmt,va_list ap)315 static void _warning_error_handler(const char *type, const char* module, const char* fmt, va_list ap)
316 {
317 fprintf(stderr, "[tiff_open] %s: %s: ", type, module);
318 vfprintf(stderr, fmt, ap);
319 fprintf(stderr, "\n");
320 }
321
_warning_handler(const char * module,const char * fmt,va_list ap)322 static void _warning_handler(const char* module, const char* fmt, va_list ap)
323 {
324 if(darktable.unmuted & DT_DEBUG_IMAGEIO)
325 {
326 _warning_error_handler("warning", module, fmt, ap);
327 }
328 }
329
_error_handler(const char * module,const char * fmt,va_list ap)330 static void _error_handler(const char* module, const char* fmt, va_list ap)
331 {
332 _warning_error_handler("error", module, fmt, ap);
333 }
334
dt_imageio_open_tiff(dt_image_t * img,const char * filename,dt_mipmap_buffer_t * mbuf)335 dt_imageio_retval_t dt_imageio_open_tiff(dt_image_t *img, const char *filename, dt_mipmap_buffer_t *mbuf)
336 {
337 // doing this once would be enough, but our imageio reading code is
338 // compiled into dt's core and doesn't have an init routine.
339 TIFFSetWarningHandler(_warning_handler);
340 TIFFSetErrorHandler(_error_handler);
341
342 const char *ext = filename + strlen(filename);
343 while(*ext != '.' && ext > filename) ext--;
344 if(strncmp(ext, ".tif", 4) && strncmp(ext, ".TIF", 4) && strncmp(ext, ".tiff", 5)
345 && strncmp(ext, ".TIFF", 5))
346 return DT_IMAGEIO_FILE_CORRUPTED;
347 if(!img->exif_inited) (void)dt_exif_read(img, filename);
348
349 tiff_t t;
350 uint16_t config;
351 uint16_t photometric;
352 uint16_t inkset;
353
354 t.image = img;
355
356 #ifdef _WIN32
357 wchar_t *wfilename = g_utf8_to_utf16(filename, -1, NULL, NULL, NULL);
358 t.tiff = TIFFOpenW(wfilename, "rb");
359 g_free(wfilename);
360 #else
361 t.tiff = TIFFOpen(filename, "rb");
362 #endif
363
364 if(t.tiff == NULL) return DT_IMAGEIO_FILE_CORRUPTED;
365
366 TIFFGetField(t.tiff, TIFFTAG_IMAGEWIDTH, &t.width);
367 TIFFGetField(t.tiff, TIFFTAG_IMAGELENGTH, &t.height);
368 TIFFGetField(t.tiff, TIFFTAG_BITSPERSAMPLE, &t.bpp);
369 TIFFGetField(t.tiff, TIFFTAG_SAMPLESPERPIXEL, &t.spp);
370 TIFFGetFieldDefaulted(t.tiff, TIFFTAG_SAMPLEFORMAT, &t.sampleformat);
371 TIFFGetField(t.tiff, TIFFTAG_PLANARCONFIG, &config);
372 TIFFGetField(t.tiff, TIFFTAG_PHOTOMETRIC, &photometric);
373 TIFFGetField(t.tiff, TIFFTAG_INKSET, &inkset);
374
375 if(inkset == INKSET_CMYK || inkset == INKSET_MULTIINK)
376 {
377 fprintf(stderr, "[tiff_open] error: CMYK (or multiink) TIFFs are not supported.\n");
378 TIFFClose(t.tiff);
379 return DT_IMAGEIO_FILE_CORRUPTED;
380 }
381
382 if(TIFFRasterScanlineSize(t.tiff) != TIFFScanlineSize(t.tiff)) return DT_IMAGEIO_FILE_CORRUPTED;
383
384 t.scanlinesize = TIFFScanlineSize(t.tiff);
385
386 dt_print(DT_DEBUG_IMAGEIO, "[tiff_open] %dx%d %dbpp, %d samples per pixel.\n", t.width, t.height, t.bpp, t.spp);
387
388 // we only support 8/16 and 32 bits per pixel formats.
389 if(t.bpp != 8 && t.bpp != 16 && t.bpp != 32)
390 {
391 TIFFClose(t.tiff);
392 return DT_IMAGEIO_FILE_CORRUPTED;
393 }
394
395 /* we only support 1,3 or 4 samples per pixel */
396 if(t.spp != 1 && t.spp != 3 && t.spp != 4)
397 {
398 TIFFClose(t.tiff);
399 return DT_IMAGEIO_FILE_CORRUPTED;
400 }
401
402 /* don't depend on planar config if spp == 1 */
403 if(t.spp > 1 && config != PLANARCONFIG_CONTIG)
404 {
405 fprintf(stderr, "[tiff_open] error: PlanarConfiguration other than chunky is not supported.\n");
406 TIFFClose(t.tiff);
407 return DT_IMAGEIO_FILE_CORRUPTED;
408 }
409
410 /* initialize cached image buffer */
411 t.image->width = t.width;
412 t.image->height = t.height;
413
414 t.image->buf_dsc.channels = 4;
415 t.image->buf_dsc.datatype = TYPE_FLOAT;
416 t.image->buf_dsc.cst = iop_cs_rgb;
417
418 t.mipbuf = (float *)dt_mipmap_cache_alloc(mbuf, t.image);
419 if(!t.mipbuf)
420 {
421 fprintf(stderr, "[tiff_open] error: could not alloc full buffer for image `%s'\n", t.image->filename);
422 TIFFClose(t.tiff);
423 return DT_IMAGEIO_CACHE_FULL;
424 }
425
426 if((t.buf = _TIFFmalloc(t.scanlinesize)) == NULL)
427 {
428 TIFFClose(t.tiff);
429 return DT_IMAGEIO_CACHE_FULL;
430 }
431
432 // flag the image buffer properly depending on sample format
433 if(t.sampleformat == SAMPLEFORMAT_IEEEFP)
434 {
435 // HDR TIFF
436 t.image->flags &= ~DT_IMAGE_LDR;
437 t.image->flags |= DT_IMAGE_HDR;
438 }
439 else
440 {
441 // LDR TIFF
442 t.image->flags |= DT_IMAGE_LDR;
443 t.image->flags &= ~DT_IMAGE_HDR;
444 }
445
446 int ok = 1;
447
448 if((photometric == PHOTOMETRIC_CIELAB || photometric == PHOTOMETRIC_ICCLAB) && t.bpp == 8 && t.sampleformat == SAMPLEFORMAT_UINT)
449 {
450 ok = _read_chunky_8_Lab(&t, photometric);
451 t.image->buf_dsc.cst = iop_cs_Lab;
452 }
453 else if((photometric == PHOTOMETRIC_CIELAB || photometric == PHOTOMETRIC_ICCLAB) && t.bpp == 16 && t.sampleformat == SAMPLEFORMAT_UINT)
454 {
455 ok = _read_chunky_16_Lab(&t, photometric);
456 t.image->buf_dsc.cst = iop_cs_Lab;
457 }
458 else if(t.bpp == 8 && t.sampleformat == SAMPLEFORMAT_UINT)
459 ok = _read_chunky_8(&t);
460 else if(t.bpp == 16 && t.sampleformat == SAMPLEFORMAT_UINT)
461 ok = _read_chunky_16(&t);
462 else if(t.bpp == 16 && t.sampleformat == SAMPLEFORMAT_IEEEFP)
463 ok = _read_chunky_h(&t);
464 else if(t.bpp == 32 && t.sampleformat == SAMPLEFORMAT_IEEEFP)
465 ok = _read_chunky_f(&t);
466 else
467 {
468 fprintf(stderr, "[tiff_open] error: not a supported tiff image format.\n");
469 ok = 0;
470 }
471
472 _TIFFfree(t.buf);
473 TIFFClose(t.tiff);
474
475 if(ok == 1)
476 {
477 img->loader = LOADER_TIFF;
478 return DT_IMAGEIO_OK;
479 }
480 else
481 return DT_IMAGEIO_FILE_CORRUPTED;
482 }
483
dt_imageio_tiff_read_profile(const char * filename,uint8_t ** out)484 int dt_imageio_tiff_read_profile(const char *filename, uint8_t **out)
485 {
486 TIFF *tiff = NULL;
487 uint32_t profile_len = 0;
488 uint8_t *profile = NULL;
489 uint16_t photometric;
490
491 if(!(filename && *filename && out)) return 0;
492
493 #ifdef _WIN32
494 wchar_t *wfilename = g_utf8_to_utf16(filename, -1, NULL, NULL, NULL);
495 tiff = TIFFOpenW(wfilename, "rb");
496 g_free(wfilename);
497 #else
498 tiff = TIFFOpen(filename, "rb");
499 #endif
500
501 if(tiff == NULL) return 0;
502
503 TIFFGetField(tiff, TIFFTAG_PHOTOMETRIC, &photometric);
504
505 if(photometric == PHOTOMETRIC_CIELAB || photometric == PHOTOMETRIC_ICCLAB)
506 {
507 profile = dt_colorspaces_get_profile(LAB_CONVERSION_PROFILE, "", DT_PROFILE_DIRECTION_OUT | DT_PROFILE_DIRECTION_DISPLAY)->profile;
508
509 cmsSaveProfileToMem(profile, 0, &profile_len);
510 if(profile_len > 0)
511 {
512 *out = (uint8_t *)g_malloc(profile_len);
513 cmsSaveProfileToMem(profile, *out, &profile_len);
514 }
515 }
516 else if(TIFFGetField(tiff, TIFFTAG_ICCPROFILE, &profile_len, &profile))
517 {
518 if(profile_len > 0)
519 {
520 *out = (uint8_t *)g_malloc(profile_len);
521 memcpy(*out, profile, profile_len);
522 }
523 }
524 else
525 profile_len = 0;
526
527 TIFFClose(tiff);
528
529 return profile_len;
530 }
531
532 // modelines: These editor modelines have been set for all relevant files by tools/update_modelines.sh
533 // vim: shiftwidth=2 expandtab tabstop=2 cindent
534 // kate: tab-indents: off; indent-width 2; replace-tabs on; indent-mode cstyle; remove-trailing-spaces modified;
535