Ansel 0.0
A darktable fork - bloat + design vision
Loading...
Searching...
No Matches
imageio_tiff.c
Go to the documentation of this file.
1/*
2 This file is part of darktable,
3 Copyright (C) 2010-2011, 2014 Henrik Andersson.
4 Copyright (C) 2010-2012, 2014 johannes hanika.
5 Copyright (C) 2011 Jonathan A. Kollasch.
6 Copyright (C) 2011-2012, 2014, 2016-2018 Tobias Ellinghaus.
7 Copyright (C) 2012 Richard Wonka.
8 Copyright (C) 2012-2014, 2019 Ulrich Pegelow.
9 Copyright (C) 2013-2014, 2016 Roman Lebedev.
10 Copyright (C) 2014 Edouard Gomez.
11 Copyright (C) 2014 Pascal de Bruijn.
12 Copyright (C) 2015 Pedro Côrte-Real.
13 Copyright (C) 2017 luzpaz.
14 Copyright (C) 2019 Edgardo Hoszowski.
15 Copyright (C) 2020 Aurélien PIERRE.
16 Copyright (C) 2020 Hubert Kowalski.
17 Copyright (C) 2020-2021 Miloš Komarčević.
18 Copyright (C) 2020-2021 Pascal Obry.
19 Copyright (C) 2022 Martin Bařinka.
20 Copyright (C) 2022 Philipp Lutz.
21 Copyright (C) 2023 Alynx Zhou.
22
23 darktable is free software: you can redistribute it and/or modify
24 it under the terms of the GNU General Public License as published by
25 the Free Software Foundation, either version 3 of the License, or
26 (at your option) any later version.
27
28 darktable is distributed in the hope that it will be useful,
29 but WITHOUT ANY WARRANTY; without even the implied warranty of
30 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
31 GNU General Public License for more details.
32
33 You should have received a copy of the GNU General Public License
34 along with darktable. If not, see <http://www.gnu.org/licenses/>.
35*/
37#include "imageio_tiff.h"
38#include "system/macros.h"
39#include "system/mem_alloc.h"
40#include "common/logging.h"
42#include "metadata/exif.h"
43#include "develop/develop.h"
44
45#include <inttypes.h>
46#include <memory.h>
47#include <stdio.h>
48#include <strings.h>
49#include <tiffio.h>
50
51#define LAB_CONVERSION_PROFILE DT_COLORSPACE_LIN_REC2020
52
53typedef struct tiff_t
54{
55 TIFF *tiff;
56 uint32_t width;
57 uint32_t height;
58 uint16_t bpp;
59 uint16_t spp;
60 uint16_t sampleformat;
61 uint32_t scanlinesize;
63 float *mipbuf;
64 tdata_t buf;
66
67typedef union fp32_t
68{
69 uint32_t u;
70 float f;
72
73static inline float _half_to_float(uint16_t h)
74{
75 /* see https://en.wikipedia.org/wiki/Half-precision_floating-point_format#Exponent_encoding
76 and https://en.wikipedia.org/wiki/Single-precision_floating-point_format#Exponent_encoding */
77
78 /* TODO: use intrinsics when possible */
79
80 /* from https://gist.github.com/rygorous/2156668 */
81 static const fp32_t magic = { 113 << 23 };
82 static const uint32_t shifted_exp = 0x7c00 << 13; // exponent mask after shift
83 fp32_t o;
84
85 o.u = (h & 0x7fff) << 13; // exponent/mantissa bits
86 uint32_t exp = shifted_exp & o.u; // just the exponent
87 o.u += (127 - 15) << 23; // exponent adjust
88
89 // handle exponent special cases
90 if (exp == shifted_exp) // Inf/NaN?
91 o.u += (128 - 16) << 23; // extra exp adjust
92 else if (exp == 0) // Zero/Denormal?
93 {
94 o.u += 1 << 23; // extra exp adjust
95 o.f -= magic.f; // renormalize
96 }
97
98 o.u |= (h & 0x8000) << 16; // sign bit
99 return o.f;
100}
101
102static inline int _read_chunky_8(tiff_t *t)
103{
104 for(uint32_t row = 0; row < t->height; row++)
105 {
106 uint8_t *in = ((uint8_t *)t->buf);
107 float *out = ((float *)t->mipbuf) + (size_t)4 * row * t->width;
108
109 /* read scanline */
110 if(TIFFReadScanline(t->tiff, in, row, 0) == -1) return -1;
111
112 for(uint32_t i = 0; i < t->width; i++, in += t->spp, out += 4)
113 {
114 /* set rgb to first sample from scanline */
115 out[0] = ((float)in[0]) * (1.0f / 255.0f);
116
117 if(t->spp == 1)
118 {
119 out[1] = out[2] = out[0];
120 }
121 else
122 {
123 out[1] = ((float)in[1]) * (1.0f / 255.0f);
124 out[2] = ((float)in[2]) * (1.0f / 255.0f);
125 }
126
127 out[3] = 0;
128 }
129 }
130
131 return 1;
132}
133
134static inline int _read_chunky_16(tiff_t *t)
135{
136 for(uint32_t row = 0; row < t->height; row++)
137 {
138 uint16_t *in = ((uint16_t *)t->buf);
139 float *out = ((float *)t->mipbuf) + (size_t)4 * row * t->width;
140
141 /* read scanline */
142 if(TIFFReadScanline(t->tiff, in, row, 0) == -1) return -1;
143
144 for(uint32_t i = 0; i < t->width; i++, in += t->spp, out += 4)
145 {
146 out[0] = ((float)in[0]) * (1.0f / 65535.0f);
147
148 if(t->spp == 1)
149 {
150 out[1] = out[2] = out[0];
151 }
152 else
153 {
154 out[1] = ((float)in[1]) * (1.0f / 65535.0f);
155 out[2] = ((float)in[2]) * (1.0f / 65535.0f);
156 }
157
158 out[3] = 0;
159 }
160 }
161
162 return 1;
163}
164
165static inline int _read_chunky_h(tiff_t *t)
166{
167 for(uint32_t row = 0; row < t->height; row++)
168 {
169 uint16_t *in = ((uint16_t *)t->buf);
170 float *out = ((float *)t->mipbuf) + (size_t)4 * row * t->width;
171
172 /* read scanline */
173 if(TIFFReadScanline(t->tiff, in, row, 0) == -1) return -1;
174
175 for(uint32_t i = 0; i < t->width; i++, in += t->spp, out += 4)
176 {
177 out[0] = _half_to_float(in[0]);
178
179 if(t->spp == 1)
180 {
181 out[1] = out[2] = out[0];
182 }
183 else
184 {
185 out[1] = _half_to_float(in[1]);
186 out[2] = _half_to_float(in[2]);
187 }
188
189 out[3] = 0;
190 }
191 }
192
193 return 1;
194}
195
196static inline int _read_chunky_f(tiff_t *t)
197{
198 for(uint32_t row = 0; row < t->height; row++)
199 {
200 float *in = ((float *)t->buf);
201 float *out = ((float *)t->mipbuf) + (size_t)4 * row * t->width;
202
203 /* read scanline */
204 if(TIFFReadScanline(t->tiff, in, row, 0) == -1) return -1;
205
206 for(uint32_t i = 0; i < t->width; i++, in += t->spp, out += 4)
207 {
208 out[0] = in[0];
209
210 if(t->spp == 1)
211 {
212 out[1] = out[2] = out[0];
213 }
214 else
215 {
216 out[1] = in[1];
217 out[2] = in[2];
218 }
219
220 out[3] = 0;
221 }
222 }
223
224 return 1;
225}
226
227static inline int _read_chunky_8_Lab(tiff_t *t, uint16_t photometric)
228{
231 const cmsHTRANSFORM xform = cmsCreateTransform(Lab, TYPE_LabA_FLT, output_profile, TYPE_RGBA_FLT, INTENT_PERCEPTUAL, 0);
232
233 for(uint32_t row = 0; row < t->height; row++)
234 {
235 uint8_t *in = ((uint8_t *)t->buf);
236 float *output = ((float *)t->mipbuf) + (size_t)4 * row * t->width;
237 float *out = output;
238
239 /* read scanline */
240 if(TIFFReadScanline(t->tiff, in, row, 0) == -1) goto failed;
241
242 for(uint32_t i = 0; i < t->width; i++, in += t->spp, out += 4)
243 {
244 out[0] = ((float)in[0]) * (100.0f/255.0f);
245
246 if(t->spp == 1)
247 {
248 out[1] = out[2] = 0;
249 }
250 else
251 {
252 if(photometric == PHOTOMETRIC_CIELAB)
253 {
254 out[1] = ((float)((int8_t)in[1]));
255 out[2] = ((float)((int8_t)in[2]));
256 }
257 else // photometric == PHOTOMETRIC_ICCLAB
258 {
259 out[1] = ((float)(in[1])) - 128.0f;
260 out[2] = ((float)(in[2])) - 128.0f;
261 }
262 }
263
264 out[3] = 0;
265 }
266
267 cmsDoTransform(xform, output, output, t->width);
268 }
269
270 cmsDeleteTransform(xform);
271
272 return 1;
273
274failed:
275 cmsDeleteTransform(xform);
276 return -1;
277}
278
279
280static inline int _read_chunky_16_Lab(tiff_t *t, uint16_t photometric)
281{
284 const cmsHTRANSFORM xform = cmsCreateTransform(Lab, TYPE_LabA_FLT, output_profile, TYPE_RGBA_FLT, INTENT_PERCEPTUAL, 0);
285 const float range = (photometric == PHOTOMETRIC_CIELAB) ? 65535.0f : 65280.0f;
286
287 for(uint32_t row = 0; row < t->height; row++)
288 {
289 uint16_t *in = ((uint16_t *)t->buf);
290 float *output = ((float *)t->mipbuf) + (size_t)4 * row * t->width;
291 float *out = output;
292
293 /* read scanline */
294 if(TIFFReadScanline(t->tiff, in, row, 0) == -1) goto failed;
295
296 for(uint32_t i = 0; i < t->width; i++, in += t->spp, out += 4)
297 {
298 out[0] = ((float)in[0]) * (100.0f/range);
299
300 if(t->spp == 1)
301 {
302 out[1] = out[2] = 0;
303 }
304 else
305 {
306 if(photometric == PHOTOMETRIC_CIELAB)
307 {
308 out[1] = ((float)((int16_t)in[1])) / 256.0f;
309 out[2] = ((float)((int16_t)in[2])) / 256.0f;
310 }
311 else // photometric == PHOTOMETRIC_ICCLAB
312 {
313 out[1] = (((float)(in[1])) - 32768.0f) / 256.0f;
314 out[2] = (((float)(in[2])) - 32768.0f) / 256.0f;
315 }
316 }
317
318 out[3] = 0;
319 }
320
321 cmsDoTransform(xform, output, output, t->width);
322 }
323
324 cmsDeleteTransform(xform);
325
326 return 1;
327
328failed:
329 cmsDeleteTransform(xform);
330 return -1;
331}
332
333
334static void _warning_error_handler(const char *type, const char* module, const char* fmt, va_list ap)
335{
336 fprintf(stderr, "[tiff_open] %s: %s: ", type, module);
337 vfprintf(stderr, fmt, ap);
338 fprintf(stderr, "\n");
339}
340
341static void _warning_handler(const char* module, const char* fmt, va_list ap)
342{
344 {
345 _warning_error_handler("warning", module, fmt, ap);
346 }
347}
348
349static void _error_handler(const char* module, const char* fmt, va_list ap)
350{
351 _warning_error_handler("error", module, fmt, ap);
352}
353
355{
356 // doing this once would be enough, but our imageio reading code is
357 // compiled into dt's core and doesn't have an init routine.
358 TIFFSetWarningHandler(_warning_handler);
359 TIFFSetErrorHandler(_error_handler);
360
361 const char *ext = filename + strlen(filename);
362 while(*ext != '.' && ext > filename) ext--;
363 if(strncmp(ext, ".tif", 4) && strncmp(ext, ".TIF", 4) && strncmp(ext, ".tiff", 5)
364 && strncmp(ext, ".TIFF", 5))
366 if(!img->exif_inited) (void)dt_exif_read(img, filename);
367
368 tiff_t t;
369 uint16_t config;
370 uint16_t photometric;
371 uint16_t inkset;
372
373 t.image = img;
374
375#ifdef _WIN32
376 wchar_t *wfilename = g_utf8_to_utf16(filename, -1, NULL, NULL, NULL);
377 t.tiff = TIFFOpenW(wfilename, "rb");
378 dt_free(wfilename);
379#else
380 t.tiff = TIFFOpen(filename, "rb");
381#endif
382
383 if(IS_NULL_PTR(t.tiff)) return DT_IMAGEIO_FILE_CORRUPTED;
384
385 TIFFGetField(t.tiff, TIFFTAG_IMAGEWIDTH, &t.width);
386 TIFFGetField(t.tiff, TIFFTAG_IMAGELENGTH, &t.height);
387 TIFFGetField(t.tiff, TIFFTAG_BITSPERSAMPLE, &t.bpp);
388 TIFFGetField(t.tiff, TIFFTAG_SAMPLESPERPIXEL, &t.spp);
389 TIFFGetFieldDefaulted(t.tiff, TIFFTAG_SAMPLEFORMAT, &t.sampleformat);
390 TIFFGetField(t.tiff, TIFFTAG_PLANARCONFIG, &config);
391 TIFFGetField(t.tiff, TIFFTAG_PHOTOMETRIC, &photometric);
392 TIFFGetField(t.tiff, TIFFTAG_INKSET, &inkset);
393
394 if(inkset == INKSET_CMYK || inkset == INKSET_MULTIINK)
395 {
396 fprintf(stderr, "[tiff_open] error: CMYK (or multiink) TIFFs are not supported.\n");
397 TIFFClose(t.tiff);
399 }
400
401 if(TIFFRasterScanlineSize(t.tiff) != TIFFScanlineSize(t.tiff)) return DT_IMAGEIO_FILE_CORRUPTED;
402
403 t.scanlinesize = TIFFScanlineSize(t.tiff);
404
405 dt_print(DT_DEBUG_IMAGEIO, "[tiff_open] %dx%d %dbpp, %d samples per pixel.\n", t.width, t.height, t.bpp, t.spp);
406
407 // we only support 8/16 and 32 bits per pixel formats.
408 if(t.bpp != 8 && t.bpp != 16 && t.bpp != 32)
409 {
410 TIFFClose(t.tiff);
412 }
413
414 /* we only support 1,3 or 4 samples per pixel */
415 if(t.spp != 1 && t.spp != 3 && t.spp != 4)
416 {
417 TIFFClose(t.tiff);
419 }
420
421 /* don't depend on planar config if spp == 1 */
422 if(t.spp > 1 && config != PLANARCONFIG_CONTIG)
423 {
424 fprintf(stderr, "[tiff_open] error: PlanarConfiguration other than chunky is not supported.\n");
425 TIFFClose(t.tiff);
427 }
428
429 /* initialize cached image buffer */
430 t.image->width = t.width;
431 t.image->height = t.height;
432
433 t.image->dsc.channels = 4;
434 t.image->dsc.datatype = TYPE_FLOAT;
435 t.image->dsc.bpp = 4 * sizeof(float);
436 t.image->dsc.cst = IOP_CS_RGB;
437 t.image->dsc.filters = 0u;
438
439 // flag the image buffer properly depending on sample format
440 if(t.sampleformat == SAMPLEFORMAT_IEEEFP)
441 {
442 // HDR TIFF
443 t.image->flags &= ~DT_IMAGE_LDR;
444 t.image->flags |= DT_IMAGE_HDR;
445 }
446 else
447 {
448 // LDR TIFF
449 t.image->flags |= DT_IMAGE_LDR;
450 t.image->flags &= ~DT_IMAGE_HDR;
451 }
452
453 if(photometric == PHOTOMETRIC_CIELAB || photometric == PHOTOMETRIC_ICCLAB)
454 t.image->dsc.cst = IOP_CS_LAB;
455
456 t.image->flags &= ~DT_IMAGE_RAW;
457 t.image->flags &= ~DT_IMAGE_S_RAW;
458 t.image->loader = LOADER_TIFF;
459
460 if(IS_NULL_PTR(mbuf))
461 {
462 TIFFClose(t.tiff);
463 return DT_IMAGEIO_OK;
464 }
465
466 t.mipbuf = (float *)dt_mipmap_cache_alloc(mbuf, t.image);
467 if(IS_NULL_PTR(t.mipbuf))
468 {
469 fprintf(stderr, "[tiff_open] error: could not alloc full buffer for image `%s'\n", t.image->filename);
470 TIFFClose(t.tiff);
472 }
473
474 if((t.buf = _TIFFmalloc(t.scanlinesize)) == NULL)
475 {
476 TIFFClose(t.tiff);
478 }
479
480 int ok = 1;
481
482 if((photometric == PHOTOMETRIC_CIELAB || photometric == PHOTOMETRIC_ICCLAB) && t.bpp == 8 && t.sampleformat == SAMPLEFORMAT_UINT)
483 {
484 ok = _read_chunky_8_Lab(&t, photometric);
485 t.image->dsc.cst = IOP_CS_LAB;
486 }
487 else if((photometric == PHOTOMETRIC_CIELAB || photometric == PHOTOMETRIC_ICCLAB) && t.bpp == 16 && t.sampleformat == SAMPLEFORMAT_UINT)
488 {
489 ok = _read_chunky_16_Lab(&t, photometric);
490 t.image->dsc.cst = IOP_CS_LAB;
491 }
492 else if(t.bpp == 8 && t.sampleformat == SAMPLEFORMAT_UINT)
493 ok = _read_chunky_8(&t);
494 else if(t.bpp == 16 && t.sampleformat == SAMPLEFORMAT_UINT)
495 ok = _read_chunky_16(&t);
496 else if(t.bpp == 16 && t.sampleformat == SAMPLEFORMAT_IEEEFP)
497 ok = _read_chunky_h(&t);
498 else if(t.bpp == 32 && t.sampleformat == SAMPLEFORMAT_IEEEFP)
499 ok = _read_chunky_f(&t);
500 else
501 {
502 fprintf(stderr, "[tiff_open] error: not a supported tiff image format.\n");
503 ok = 0;
504 }
505
506 _TIFFfree(t.buf);
507 TIFFClose(t.tiff);
508
509 if(ok == 1)
510 {
511 return DT_IMAGEIO_OK;
512 }
513 else
515}
516
517int dt_imageio_tiff_read_profile(const char *filename, uint8_t **out)
518{
519 TIFF *tiff = NULL;
520 uint32_t profile_len = 0;
521 uint8_t *profile = NULL;
522 uint16_t photometric;
523
524 if(!(filename && *filename && out)) return 0;
525
526#ifdef _WIN32
527 wchar_t *wfilename = g_utf8_to_utf16(filename, -1, NULL, NULL, NULL);
528 tiff = TIFFOpenW(wfilename, "rb");
529 dt_free(wfilename);
530#else
531 tiff = TIFFOpen(filename, "rb");
532#endif
533
534 if(IS_NULL_PTR(tiff)) return 0;
535
536 TIFFGetField(tiff, TIFFTAG_PHOTOMETRIC, &photometric);
537
538 if(photometric == PHOTOMETRIC_CIELAB || photometric == PHOTOMETRIC_ICCLAB)
539 {
541
542 cmsSaveProfileToMem(profile, 0, &profile_len);
543 if(profile_len > 0)
544 {
545 *out = (uint8_t *)g_malloc(profile_len);
546 cmsSaveProfileToMem(profile, *out, &profile_len);
547 }
548 }
549 else if(TIFFGetField(tiff, TIFFTAG_ICCPROFILE, &profile_len, &profile))
550 {
551 if(profile_len > 0)
552 {
553 *out = (uint8_t *)g_malloc(profile_len);
554 memcpy(*out, profile, profile_len);
555 }
556 }
557 else
558 profile_len = 0;
559
560 TIFFClose(tiff);
561
562 return profile_len;
563}
564
565/* ---- Embedded-preview decoding, from a memory blob ------------------------
566 *
567 * A raw file that embeds a JPEG preview is handled by libjpeg in
568 * dt_imageio_large_thumbnail(); one that embeds a TIFF preview lands here.
569 * These previews are small, self-contained images, so the whole blob is
570 * already in memory and libtiff reads it through TIFFClientOpen with the five
571 * callbacks below -- no temporary file, and no second image library.
572 *
573 * Sample format and bit depth are libtiff's problem, not ours: the RGBA
574 * interface converts whatever the preview holds into 8-bit RGBA, and the cases
575 * it cannot convert are refused up front by TIFFRGBAImageOK(). See the depth
576 * note in dt_imageio_tiff_decode_blob(). */
577
578typedef struct _tiff_blob_t
579{
580 const uint8_t *data;
581 tmsize_t size;
582 // Held as toff_t, libtiff's file-offset type, rather than tmsize_t: a seek past the end is
583 // legal (see _blob_seek) and must record the position asked for, which a corrupt offset in
584 // the file's own tags can put far beyond the blob. _blob_read() is what bounds it.
585 toff_t pos;
587
588static tmsize_t _blob_read(thandle_t handle, void *buffer, tmsize_t size)
589{
590 _tiff_blob_t *blob = (_tiff_blob_t *)handle;
591 // At or past the end reads as empty rather than as an error -- this is the single place the
592 // extent of the data is enforced, so _blob_seek() does not have to refuse anything.
593 if(size <= 0 || blob->pos >= (toff_t)blob->size) return 0;
594 const tmsize_t available = (tmsize_t)((toff_t)blob->size - blob->pos);
595 const tmsize_t n = (size < available) ? size : available;
596 memcpy(buffer, blob->data + (size_t)blob->pos, (size_t)n);
597 blob->pos += (toff_t)n;
598 return n;
599}
600
601// Read-only: libtiff still requires a write callback, and refusing every write
602// is what makes the handle read-only rather than silently corrupting anything.
603static tmsize_t _blob_write(thandle_t handle, void *buffer, tmsize_t size)
604{
605 return 0;
606}
607
608static toff_t _blob_seek(thandle_t handle, toff_t offset, int whence)
609{
610 _tiff_blob_t *blob = (_tiff_blob_t *)handle;
611 toff_t base = 0;
612 switch(whence)
613 {
614 case SEEK_SET: base = 0; break;
615 case SEEK_CUR: base = blob->pos; break;
616 case SEEK_END: base = (toff_t)blob->size; break;
617 default: return (toff_t)-1;
618 }
619 const toff_t target = base + offset;
620 if(target < base) return (toff_t)-1; // wrapped: the caller asked for something absurd
621
622 // Seeking beyond the end is legal and must succeed, exactly as lseek(2) does: libtiff probes
623 // an offset before deciding whether to read it, and expects the new position back rather than
624 // an error. Refusing here would turn a routine probe into a fatal read failure. The end of the
625 // data is enforced by _blob_read(), which returns 0 bytes from any position at or past it.
626 blob->pos = target;
627 return target;
628}
629
630static int _blob_close(thandle_t handle)
631{
632 return 0;
633}
634
635static toff_t _blob_size(thandle_t handle)
636{
637 return (toff_t)((_tiff_blob_t *)handle)->size;
638}
639
640// No memory-mapped path: the blob is already a plain host buffer, and claiming
641// otherwise would hand libtiff a mapping it would try to unmap.
642static int _blob_map(thandle_t handle, void **base, toff_t *size)
643{
644 return 0;
645}
646
647static void _blob_unmap(thandle_t handle, void *base, toff_t size)
648{
649}
650
651gboolean dt_imageio_tiff_decode_blob(const uint8_t *const blob, const size_t bufsize, uint8_t **out,
652 int32_t *width, int32_t *height)
653{
654 if(IS_NULL_PTR(blob) || bufsize == 0 || IS_NULL_PTR(out) || IS_NULL_PTR(width) || IS_NULL_PTR(height))
655 return FALSE;
656
657 // Same handlers the file path installs, so a malformed preview is reported
658 // through our log instead of libtiff's default stderr chatter.
659 TIFFSetWarningHandler(_warning_handler);
660 TIFFSetErrorHandler(_error_handler);
661
662 _tiff_blob_t handle = { .data = blob, .size = (tmsize_t)bufsize, .pos = 0 };
663 TIFF *tiff = TIFFClientOpen("embedded-preview", "rm", (thandle_t)&handle, _blob_read, _blob_write,
665 if(IS_NULL_PTR(tiff)) return FALSE;
666
667 gboolean ok = FALSE;
668 uint32_t w = 0, h = 0;
669 if(!TIFFGetField(tiff, TIFFTAG_IMAGEWIDTH, &w) || !TIFFGetField(tiff, TIFFTAG_IMAGELENGTH, &h)
670 || w == 0 || h == 0)
671 goto done;
672
673 /* Bit depth is NOT assumed. TIFFReadRGBAImage() normalises 1-, 2-, 4-, 8- and 16-bit samples
674 * down to 8 bits per channel itself, along with the photometric interpretation (palette,
675 * greyscale, YCbCr, CMYK, ...) -- which is the whole reason for using the RGBA interface here
676 * rather than reading strips by hand. What it does NOT handle is 32-bit integer or float
677 * samples, and a few exotic compression/photometric combinations.
678 *
679 * TIFFRGBAImageOK() is libtiff's own predicate for exactly that question and fills in the
680 * reason, so an unsupported preview is refused with a diagnosis instead of failing anonymously
681 * inside the read below. Losing precision to 8 bits is correct here regardless: the caller's
682 * contract is an 8-bit RGBx buffer for a thumbnail. */
683 char why[1024] = { 0 };
684 if(!TIFFRGBAImageOK(tiff, why))
685 {
686 dt_print(DT_DEBUG_IMAGEIO, "[tiff_decode_blob] embedded preview cannot be read as RGBA: %s\n", why);
687 goto done;
688 }
689
690 uint16_t bps = 0;
691 if(TIFFGetField(tiff, TIFFTAG_BITSPERSAMPLE, &bps) && bps != 8)
692 dt_print(DT_DEBUG_IMAGEIO, "[tiff_decode_blob] %u-bit embedded preview, converted to 8-bit\n",
693 (unsigned)bps);
694
695 // TIFFReadRGBAImage indexes its raster with a 32-bit pixel count, so refuse
696 // anything that would overflow it rather than trusting the tags in a file we
697 // did not write.
698 if((uint64_t)w * (uint64_t)h > (uint64_t)0xFFFFFFFFu / 4u) goto done;
699
700 const size_t npixels = (size_t)w * (size_t)h;
701 uint8_t *pixels = (uint8_t *)dt_pixelpipe_cache_alloc_align_cache(sizeof(uint8_t) * 4 * npixels, 0);
702 if(IS_NULL_PTR(pixels)) goto done;
703
704 // ORIENTATION_TOPLEFT so the result is top-down like every other decoder here;
705 // libtiff would otherwise hand back a bottom-up raster. stopOnError = 0: a
706 // partially decodable preview is still worth showing.
707 if(!TIFFReadRGBAImageOriented(tiff, w, h, (uint32_t *)pixels, ORIENTATION_TOPLEFT, 0))
708 {
710 goto done;
711 }
712
713 // libtiff packs each pixel as one host-order uint32 (ABGR); unpack in place to the R, G, B,
714 // unused byte layout the callers expect. Copied out through memcpy rather than read through a
715 // uint32_t* view of a uint8_t buffer: the compiler turns it into the same single load, and it
716 // keeps the loop from resting on an effective-type argument (the allocation has no declared
717 // type, so libtiff's write is what makes it uint32) that a reader would have to reconstruct.
718 for(size_t i = 0; i < npixels; i++)
719 {
720 uint8_t *const dest = pixels + 4 * i;
721 uint32_t px;
722 memcpy(&px, dest, sizeof(px));
723 dest[0] = (uint8_t)TIFFGetR(px);
724 dest[1] = (uint8_t)TIFFGetG(px);
725 dest[2] = (uint8_t)TIFFGetB(px);
726 dest[3] = 0;
727 }
728
729 *out = pixels;
730 *width = (int32_t)w;
731 *height = (int32_t)h;
732 ok = TRUE;
733
734done:
735 TIFFClose(tiff);
736 return ok;
737}
738
739// clang-format off
740// modelines: These editor modelines have been set for all relevant files by tools/update_modelines.py
741// vim: shiftwidth=2 expandtab tabstop=2 cindent
742// kate: tab-indents: off; indent-width 2; replace-tabs on; indent-mode cstyle; remove-trailing-spaces modified;
743// clang-format on
#define TRUE
Definition ashift_lsd.c:162
#define FALSE
Definition ashift_lsd.c:158
typedef void((*dt_cache_allocate_t)(void *userdata, dt_cache_entry_t *entry))
@ IOP_CS_RGB
@ IOP_CS_LAB
const int t
const dt_colorspaces_color_profile_t * dt_colorspaces_get_profile(dt_colorspaces_color_profile_type_t type, const char *filename, dt_colorspaces_profile_role_t role)
Resolve a profile identity to its registered entry.
The colour-profile module's API: which profiles exist, and how to apply one.
static dt_aligned_pixel_t Lab
const dt_colormatrix_t dt_aligned_pixel_t out
static const int row
int dt_exif_read(dt_image_t *img, const char *path)
Definition exif.cc:1994
What a photograph says about itself: the EXIF, IPTC and XMP tags a camera and a cataloguer write,...
@ TYPE_FLOAT
Definition format.h:56
dt_imageio_retval_t
Definition image.h:91
@ DT_IMAGEIO_OK
Definition image.h:92
@ DT_IMAGEIO_CACHE_FULL
Definition image.h:95
@ DT_IMAGEIO_FILE_CORRUPTED
Definition image.h:94
@ DT_IMAGE_HDR
Definition image.h:126
@ DT_IMAGE_LDR
Definition image.h:122
@ LOADER_TIFF
Definition image.h:284
static tmsize_t _blob_write(thandle_t handle, void *buffer, tmsize_t size)
static int _blob_map(thandle_t handle, void **base, toff_t *size)
static void _error_handler(const char *module, const char *fmt, va_list ap)
static int _read_chunky_8(tiff_t *t)
static void _warning_handler(const char *module, const char *fmt, va_list ap)
#define LAB_CONVERSION_PROFILE
static toff_t _blob_size(thandle_t handle)
static int _read_chunky_16(tiff_t *t)
static toff_t _blob_seek(thandle_t handle, toff_t offset, int whence)
static void _blob_unmap(thandle_t handle, void *base, toff_t size)
static int _read_chunky_h(tiff_t *t)
int dt_imageio_tiff_read_profile(const char *filename, uint8_t **out)
static void _warning_error_handler(const char *type, const char *module, const char *fmt, va_list ap)
static int _read_chunky_8_Lab(tiff_t *t, uint16_t photometric)
static int _blob_close(thandle_t handle)
static int _read_chunky_f(tiff_t *t)
static tmsize_t _blob_read(thandle_t handle, void *buffer, tmsize_t size)
dt_imageio_retval_t dt_imageio_open_tiff(dt_image_t *img, const char *filename, dt_mipmap_buffer_t *mbuf)
gboolean dt_imageio_tiff_decode_blob(const uint8_t *const blob, const size_t bufsize, uint8_t **out, int32_t *width, int32_t *height)
Decode a TIFF held in memory into 8-bit RGBx, for the previews raw files embed.
static int _read_chunky_16_Lab(tiff_t *t, uint16_t photometric)
static float _half_to_float(uint16_t h)
_lib_location_type_t type
Definition location.c:1
@ DT_DEBUG_IMAGEIO
Definition logging.h:68
int32_t dt_get_debug_flags(void)
Definition darktable.c:2085
void dt_print(dt_debug_thread_t thread, const char *msg,...) __attribute__((format(printf
Print to stdout when thread is enabled, prefixed with seconds since startup.
#define IS_NULL_PTR(p)
C is way too permissive with !=, == and if(var) checks, which can mean too many things depending on w...
Definition macros.h:96
#define dt_free(ptr)
g_free() ptr and set it to NULL, skipping both if it is already NULL.
Definition mem_alloc.h:171
void * dt_mipmap_cache_alloc(dt_mipmap_buffer_t *buf, const dt_image_t *img)
uint32_t width
Definition mipmap_cache.c:0
uint32_t height
Definition mipmap_cache.c:1
size_t size
Definition mipmap_cache.c:3
#define dt_pixelpipe_cache_alloc_align_cache(size, id)
#define dt_pixelpipe_cache_free_align(mem)
@ DT_COLORSPACE_LAB
@ DT_PROFILE_ROLE_OUTPUT
Listed in the output/export-profile combo (colorout, export).
@ DT_PROFILE_ROLE_MONITOR
Eligible for the monitor-profile menu.
@ DT_PROFILE_ROLE_ANY
All four roles, in registration order.
unsigned __int64 uint64_t
Definition strptime.c:75
const uint8_t * data
tmsize_t size
cmsHPROFILE profile
the actual profile; NULL for the three category entries
int32_t exif_inited
Definition image.h:365
dt_image_t * image
float * mipbuf
uint16_t spp
uint32_t width
TIFF * tiff
uint32_t scanlinesize
uint32_t height
uint16_t bpp
uint16_t sampleformat
tdata_t buf
float f
uint32_t u