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