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