Ansel 0.0
A darktable fork - bloat + design vision
Loading...
Searching...
No Matches
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-2011 johannes hanika.
5 Copyright (C) 2011, 2013-2014 Pascal de Bruijn.
6 Copyright (C) 2011-2020 Tobias Ellinghaus.
7 Copyright (C) 2011 Togan Muftuoglu.
8 Copyright (C) 2012 James C. McPherson.
9 Copyright (C) 2012-2015, 2020-2021 Pascal Obry.
10 Copyright (C) 2012 Richard Wonka.
11 Copyright (C) 2012 Simon Spannagel.
12 Copyright (C) 2013 Jérémy Rosen.
13 Copyright (C) 2013 Thomas Pryds.
14 Copyright (C) 2013-2014 Ulrich Pegelow.
15 Copyright (C) 2014 Edouard Gomez.
16 Copyright (C) 2014, 2016 Roman Lebedev.
17 Copyright (C) 2017 luzpaz.
18 Copyright (C) 2018 Simon Raffeiner.
19 Copyright (C) 2019, 2022, 2025-2026 Aurélien PIERRE.
20 Copyright (C) 2019 Sam Smith.
21 Copyright (C) 2020 Diederik Ter Rahe.
22 Copyright (C) 2020 Hanno Schwalm.
23 Copyright (C) 2020-2021 Hubert Kowalski.
24 Copyright (C) 2020-2022 Miloš Komarčević.
25 Copyright (C) 2020-2021 Ralf Brown.
26 Copyright (C) 2022 Martin Bařinka.
27
28 darktable is free software: you can redistribute it and/or modify
29 it under the terms of the GNU General Public License as published by
30 the Free Software Foundation, either version 3 of the License, or
31 (at your option) any later version.
32
33 darktable is distributed in the hope that it will be useful,
34 but WITHOUT ANY WARRANTY; without even the implied warranty of
35 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
36 GNU General Public License for more details.
37
38 You should have received a copy of the GNU General Public License
39 along with darktable. If not, see <http://www.gnu.org/licenses/>.
40*/
41#include "widgets/bauhaus.h"
42#include "develop/imageop.h"
44#include "system/macros.h"
45#include "system/mem_alloc.h"
48#include "metadata/exif.h"
51#include "math/math.h"
52#include "common/conf.h"
56#include <inttypes.h>
57#include <stddef.h>
58#include <stdio.h>
59#include <stdlib.h>
60#include <tiffio.h>
62
63// it would be nice to save space by storing the masks as single channel float data,
64// but at least GIMP can't open TIFF files where not all layers have the same format.
65#define MASKS_USE_SAME_FORMAT
66
67DT_MODULE(3)
68
78
86
87
88int write_image(dt_imageio_module_data_t *d_tmp, const char *filename, const void *in_void,
89 dt_colorspaces_color_profile_type_t over_type, const char *over_filename,
90 void *exif, int exif_len, int32_t imgid, int num, int total, dt_dev_pixelpipe_t *pipe,
91 const gboolean export_masks)
92{
93 const dt_imageio_tiff_t *d = (dt_imageio_tiff_t *)d_tmp;
94
95 uint8_t *profile = NULL;
96 uint32_t profile_len = 0;
97
98 TIFF *tif = NULL;
99
100 void *rowdata = NULL;
101
102 gboolean free_mask = FALSE;
103 float *raster_mask = NULL;
104#ifdef _WIN32
105 wchar_t *wfilename = g_utf8_to_utf16(filename, -1, NULL, NULL, NULL);
106#endif
107 int rc = 1; // default to error
108
109 if(imgid > 0)
110 {
111 cmsHPROFILE out_profile = dt_colorspaces_get_output_profile(imgid, &over_type, over_filename)->profile;
112 cmsSaveProfileToMem(out_profile, 0, &profile_len);
113 if(profile_len > 0)
114 {
115 profile = malloc(profile_len);
116 if(IS_NULL_PTR(profile))
117 {
118 rc = 1;
119 goto exit;
120 }
121 cmsSaveProfileToMem(out_profile, profile, &profile_len);
122 }
123 }
124
125 uint16_t n_pages = 1;
126 // only when masks are to be stored we check for extra pages!
127 if(export_masks && pipe)
128 {
129 for(GList *iter = pipe->nodes; iter; iter = g_list_next(iter))
130 {
131 const dt_dev_pixelpipe_iop_t *piece = (dt_dev_pixelpipe_iop_t *)iter->data;
132 if(piece->enabled)
133 n_pages += g_hash_table_size(piece->module->raster_mask.source.masks);
134 }
135 }
136
137 // Create little endian tiff image
138#ifdef _WIN32
139 tif = TIFFOpenW(wfilename, "wl");
140#else
141 tif = TIFFOpen(filename, "wl");
142#endif
143
144 if(IS_NULL_PTR(tif))
145 {
146 rc = 1;
147 goto exit;
148 }
149
150 if(n_pages > 1)
151 {
152 TIFFSetField(tif, TIFFTAG_SUBFILETYPE, FILETYPE_PAGE);
153 TIFFSetField(tif, TIFFTAG_PAGENAME, _("image"));
154 TIFFSetField(tif, TIFFTAG_PAGENUMBER, 0, n_pages);
155 }
156 else
157 TIFFSetField(tif, TIFFTAG_SUBFILETYPE, 0);
158
159 TIFFSetField(tif, TIFFTAG_DOCUMENTNAME, filename);
160
161 // http://partners.adobe.com/public/developer/en/tiff/TIFFphotoshop.pdf (dated 2002)
162 // "A proprietary ZIP/Flate compression code (0x80b2) has been used by some"
163 // "software vendors. This code should be considered obsolete. We recommend"
164 // "that TIFF implementations recognize and read the obsolete code but only"
165 // "write the official compression code (0x0008)."
166 // http://www.awaresystems.be/imaging/tiff/tifftags/compression.html
167 // http://www.awaresystems.be/imaging/tiff/tifftags/predictor.html
168 if(d->compress == 1)
169 {
170 TIFFSetField(tif, TIFFTAG_COMPRESSION, COMPRESSION_ADOBE_DEFLATE);
171 TIFFSetField(tif, TIFFTAG_PREDICTOR, PREDICTOR_NONE);
172 TIFFSetField(tif, TIFFTAG_ZIPQUALITY, (uint16_t)d->compresslevel);
173 }
174 else if(d->compress == 2)
175 {
176 TIFFSetField(tif, TIFFTAG_COMPRESSION, COMPRESSION_ADOBE_DEFLATE);
177 if(d->bpp == 32)
178 TIFFSetField(tif, TIFFTAG_PREDICTOR, PREDICTOR_FLOATINGPOINT);
179 else
180 TIFFSetField(tif, TIFFTAG_PREDICTOR, PREDICTOR_HORIZONTAL);
181 TIFFSetField(tif, TIFFTAG_ZIPQUALITY, (uint16_t)d->compresslevel);
182 }
183
184 if(!IS_NULL_PTR(profile))
185 {
186 TIFFSetField(tif, TIFFTAG_ICCPROFILE, (uint32_t)profile_len, profile);
187 }
188
189/* Howto check for a grayscale image?
190 We test every pixel for differences between the rgb channels using specific thresholds
191 for every precision. If there is such a pixel we keep it as an rgb image, otherwise
192 it's safe to assume a grayscale.
193 As there might be pipeline errors at the border we leave them alone.
194 After these checks layers can be used later on.
195*/
196 uint16_t layers = 3; // default are rgb images
197
198 int shortmode = 0;
199 if(dt_conf_key_exists("plugins/imageio/format/tiff/shortfile"))
200 shortmode = dt_conf_get_int("plugins/imageio/format/tiff/shortfile");
201
202 if((d->global.height > 4) && (d->global.width > 4) && shortmode)
203 {
204 layers = 1; // let's now assume a grayscale
205 if(d->bpp == 32)
206 {
207 for(int y = 1; y < d->global.height-1; y++)
208 {
209 float *in = (float *)in_void + (size_t)4 * y * d->global.width;
210 for(int x = 1; x < d->global.width-1; x++, in += 4)
211 {
212 if((fabsf(fmaxf(in[0], 0.001f) / fmaxf(in[1], 0.001f)) > 1.01f) ||
213 (fabsf(fmaxf(in[0], 0.001f) / fmaxf(in[2], 0.001f)) > 1.01f) ||
214 (fabsf(fmaxf(in[1], 0.001f) / fmaxf(in[2], 0.001f)) > 1.01f))
215 {
216 layers = 3;
217 goto checkdone;
218 }
219 }
220 }
221 }
222 else if(d->bpp == 16)
223 {
224 for(int y = 1; y < d->global.height-1; y++)
225 {
226 uint16_t *in = (uint16_t *)in_void + (size_t)4 * y * d->global.width;
227 for(int x = 1; x < d->global.width-1; x++, in += 4)
228 {
229 if((abs(in[0] - in[1]) > 100) ||
230 (abs(in[0] - in[2]) > 100) ||
231 (abs(in[1] - in[2]) > 100))
232 {
233 layers = 3;
234 goto checkdone;
235 }
236 }
237 }
238 }
239 else
240 {
241 for(int y = 1; y < d->global.height-1; y++)
242 {
243 uint8_t *in = (uint8_t *)in_void + (size_t)4 * y * d->global.width;
244 for(int x = 1; x < d->global.width-1; x++, in += 4)
245 {
246 if((abs(in[0] - in[1]) > 5) ||
247 (abs(in[0] - in[2]) > 5) ||
248 (abs(in[1] - in[2]) > 5))
249 {
250 layers = 3;
251 goto checkdone;
252 }
253 }
254 }
255 }
256
257 }
258 checkdone:
259 if(layers == 1)
260 dt_control_log(_("will export as a grayscale image"));
261
262 TIFFSetField(tif, TIFFTAG_SAMPLESPERPIXEL, layers);
263 TIFFSetField(tif, TIFFTAG_BITSPERSAMPLE, (uint16_t)d->bpp);
264 TIFFSetField(tif, TIFFTAG_SAMPLEFORMAT, (d->bpp == 32) ? SAMPLEFORMAT_IEEEFP : SAMPLEFORMAT_UINT);
265 TIFFSetField(tif, TIFFTAG_IMAGEWIDTH, (uint32_t)d->global.width);
266 TIFFSetField(tif, TIFFTAG_IMAGELENGTH, (uint32_t)d->global.height);
267 if(layers == 3)
268 TIFFSetField(tif, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_RGB);
269 else
270 TIFFSetField(tif, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_MINISBLACK);
271
272 TIFFSetField(tif, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG);
273 TIFFSetField(tif, TIFFTAG_ORIENTATION, ORIENTATION_TOPLEFT);
274 TIFFSetField(tif, TIFFTAG_ROWSPERSTRIP, TIFFDefaultStripSize(tif, 0));
275
276 const int resolution = dt_conf_get_int("metadata/resolution");
277 TIFFSetField(tif, TIFFTAG_XRESOLUTION, (float)resolution);
278 TIFFSetField(tif, TIFFTAG_YRESOLUTION, (float)resolution);
279 TIFFSetField(tif, TIFFTAG_RESOLUTIONUNIT, RESUNIT_INCH);
280
281 const size_t rowsize = (d->global.width * layers) * d->bpp / 8;
282 if((rowdata = malloc(rowsize)) == NULL)
283 {
284 rc = 1;
285 goto exit;
286 }
287
288 if(d->bpp == 32)
289 {
290 for(int y = 0; y < d->global.height; y++)
291 {
292 float *in = (float *)in_void + (size_t)4 * y * d->global.width;
293 float *out = (float *)rowdata;
294
295 for(int x = 0; x < d->global.width; x++, in += 4, out += layers)
296 {
297 memcpy(out, in, sizeof(float) * layers);
298 }
299
300 if(TIFFWriteScanline(tif, rowdata, y, 0) == -1)
301 {
302 rc = 1;
303 goto exit;
304 }
305 }
306 }
307 else if(d->bpp == 16)
308 {
309 for(int y = 0; y < d->global.height; y++)
310 {
311 uint16_t *in = (uint16_t *)in_void + (size_t)4 * y * d->global.width;
312 uint16_t *out = (uint16_t *)rowdata;
313
314 for(int x = 0; x < d->global.width; x++, in += 4, out += layers)
315 {
316 memcpy(out, in, sizeof(uint16_t) * layers);
317 }
318
319 if(TIFFWriteScanline(tif, rowdata, y, 0) == -1)
320 {
321 rc = 1;
322 goto exit;
323 }
324 }
325 }
326 else
327 {
328 for(int y = 0; y < d->global.height; y++)
329 {
330 uint8_t *in = (uint8_t *)in_void + (size_t)4 * y * d->global.width;
331 uint8_t *out = (uint8_t *)rowdata;
332
333 for(int x = 0; x < d->global.width; x++, in += 4, out += layers)
334 {
335 memcpy(out, in, sizeof(uint8_t) * layers);
336 }
337
338 if(TIFFWriteScanline(tif, rowdata, y, 0) == -1)
339 {
340 rc = 1;
341 goto exit;
342 }
343 }
344 }
345
346 rc = 0;
347
348 // close the file before adding exif data
349 if(tif)
350 {
351 TIFFClose(tif);
352 tif = NULL;
353 }
354 if(exif)
355 {
356 rc = dt_exif_write_blob(exif, exif_len, filename, d->compress > 0);
357 // Until we get symbolic error status codes, if rc is 1, return 0
358 rc = (rc == 1) ? 0 : 1;
359 }
360
361 // exiv2 doesn't support multi page tiffs. so we have to write in two steps. :-(
362
363 if(rc == 0 && n_pages > 1)
364 {
365#ifdef _WIN32
366 tif = TIFFOpenW(wfilename, "al");
367#else
368 tif = TIFFOpen(filename, "al");
369#endif
370
371 if(IS_NULL_PTR(tif))
372 {
373 rc = 1;
374 goto exit;
375 }
376
377 // add masks
378 float missing_raster_mask[8 * 8] = { 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,
379 0.0, 0.0, 1.0, 1.0, 1.0, 1.0, 0.0, 0.0,
380 0.0, 1.0, 0.0, 1.0, 1.0, 0.0, 1.0, 0.0,
381 0.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 0.0,
382 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0,
383 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,
384 0.0, 0.0, 1.0, 1.0, 1.0, 1.0, 0.0, 0.0,
385 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0 };
386 static const size_t missing_raster_mask_w = 8, missing_raster_mask_h = 8;
387 uint16_t page = 1;
388 for(GList *iter = pipe->nodes; iter; iter = g_list_next(iter))
389 {
391
392 GHashTableIter rm_iter;
393 gpointer key, value;
394
395 // The global cache remains authoritative even when the provider image was
396 // an exact hit and this pipeline node did not run locally.
397 g_hash_table_iter_init(&rm_iter, piece->module->raster_mask.source.masks);
398 while(g_hash_table_iter_next(&rm_iter, &key, &value))
399 {
400 if(free_mask) dt_pixelpipe_cache_free_align(raster_mask);
401 raster_mask = dt_dev_get_raster_mask(pipe, piece->module, GPOINTER_TO_INT(key), NULL, NULL);
402 free_mask = !IS_NULL_PTR(raster_mask);
403
404
405 size_t w = d->global.width, h = d->global.height;
406 if(IS_NULL_PTR(raster_mask))
407 {
408 // this should never happen
409 w = missing_raster_mask_w;
410 h = missing_raster_mask_h;
411 raster_mask = missing_raster_mask;
412 free_mask = FALSE;
413 }
414
415 TIFFSetField(tif, TIFFTAG_SUBFILETYPE, FILETYPE_PAGE);
416 TIFFSetField(tif, TIFFTAG_PAGENUMBER, page, n_pages);
417
418 const char *pagename = g_hash_table_lookup(piece->module->raster_mask.source.masks, key);
419 if(pagename)
420 TIFFSetField(tif, TIFFTAG_PAGENAME, pagename);
421 else
422 TIFFSetField(tif, TIFFTAG_PAGENAME, piece->module->name());
423
424 if(d->compress == 1)
425 {
426 TIFFSetField(tif, TIFFTAG_COMPRESSION, COMPRESSION_ADOBE_DEFLATE);
427 TIFFSetField(tif, TIFFTAG_PREDICTOR, PREDICTOR_NONE);
428 TIFFSetField(tif, TIFFTAG_ZIPQUALITY, (uint16_t)d->compresslevel);
429 }
430 else if(d->compress == 2)
431 {
432 TIFFSetField(tif, TIFFTAG_COMPRESSION, COMPRESSION_ADOBE_DEFLATE);
433 if(d->bpp == 32)
434 TIFFSetField(tif, TIFFTAG_PREDICTOR, PREDICTOR_FLOATINGPOINT);
435 else
436 TIFFSetField(tif, TIFFTAG_PREDICTOR, PREDICTOR_HORIZONTAL);
437 TIFFSetField(tif, TIFFTAG_ZIPQUALITY, (uint16_t)d->compresslevel);
438 }
439
440 TIFFSetField(tif, TIFFTAG_XRESOLUTION, (float)resolution);
441 TIFFSetField(tif, TIFFTAG_YRESOLUTION, (float)resolution);
442 TIFFSetField(tif, TIFFTAG_RESOLUTIONUNIT, RESUNIT_INCH);
443
444 TIFFSetField(tif, TIFFTAG_IMAGEWIDTH, (uint32_t)w);
445 TIFFSetField(tif, TIFFTAG_IMAGELENGTH, (uint32_t)h);
446 TIFFSetField(tif, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG);
447 TIFFSetField(tif, TIFFTAG_ORIENTATION, ORIENTATION_TOPLEFT);
448
449#ifdef MASKS_USE_SAME_FORMAT
450 TIFFSetField(tif, TIFFTAG_SAMPLESPERPIXEL, layers);
451 TIFFSetField(tif, TIFFTAG_BITSPERSAMPLE, (uint16_t)d->bpp);
452 TIFFSetField(tif, TIFFTAG_SAMPLEFORMAT, (d->bpp == 32) ? SAMPLEFORMAT_IEEEFP : SAMPLEFORMAT_UINT);
453 if(layers == 3)
454 TIFFSetField(tif, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_RGB);
455 else
456 TIFFSetField(tif, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_MINISBLACK);
457 TIFFSetField(tif, TIFFTAG_ROWSPERSTRIP, TIFFDefaultStripSize(tif, 0));
458
459 if(w != d->global.width)
460 {
461 dt_free(rowdata);
462 const size_t _rowsize = (w * layers) * d->bpp / 8;
463 rowdata = malloc(_rowsize);
464 }
465
466 if(d->bpp == 32)
467 {
468 for(int y = 0; y < h; y++)
469 {
470 const float *in = raster_mask + (size_t)y * w;
471 float *out = (float *)rowdata;
472
473 for(int x = 0; x < w; x++, out += layers)
474 {
475 for(int c = 0; c < layers; c++)
476 out[c] = in[x];
477 }
478
479 if(TIFFWriteScanline(tif, rowdata, y, 0) == -1)
480 {
481 rc = 1;
482 goto exit;
483 }
484 }
485 }
486 else if(d->bpp == 16)
487 {
488 for(int y = 0; y < h; y++)
489 {
490 const float *in = raster_mask + (size_t)y * w;
491 uint16_t *out = (uint16_t *)rowdata;
492
493 for(int x = 0; x < w; x++, out += layers)
494 {
495 for(int c = 0; c < layers; c++)
496 out[c] = CLIP(in[x]) * 65535.0f + 0.5f;
497 }
498
499 if(TIFFWriteScanline(tif, rowdata, y, 0) == -1)
500 {
501 rc = 1;
502 goto exit;
503 }
504 }
505 }
506 else
507 {
508 for(int y = 0; y < h; y++)
509 {
510 const float *in = raster_mask + (size_t)y * w;
511 uint8_t *out = (uint8_t *)rowdata;
512
513 for(int x = 0; x < w; x++, out += layers)
514 {
515 for(int c = 0; c < layers; c++)
516 out[c] = CLIP(in[x]) * 255.0f + 0.5f;
517 }
518
519 if(TIFFWriteScanline(tif, rowdata, y, 0) == -1)
520 {
521 rc = 1;
522 goto exit;
523 }
524 }
525 }
526#else // MASKS_USE_SAME_FORMAT
527 TIFFSetField(tif, TIFFTAG_SAMPLESPERPIXEL, 1);
528 TIFFSetField(tif, TIFFTAG_BITSPERSAMPLE, 32);
529 TIFFSetField(tif, TIFFTAG_SAMPLEFORMAT, SAMPLEFORMAT_IEEEFP);
530 if(d->compress == 2) // override predictor set above assuming MASKS_USE_SAME_FORMAT
531 TIFFSetField(tif, TIFFTAG_PREDICTOR, PREDICTOR_FLOATINGPOINT);
532 TIFFSetField(tif, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_MINISBLACK);
533 TIFFSetField(tif, TIFFTAG_ROWSPERSTRIP, TIFFDefaultStripSize(tif, 0));
534
535 for(int y = 0; y < h; y++)
536 {
537 const float *in = raster_mask + (size_t)y * w;
538 if(TIFFWriteScanline(tif, (void *)in, y, 0) == -1)
539 {
540 rc = 1;
541 goto exit;
542 }
543 }
544#endif // MASKS_USE_SAME_FORMAT
545
546 page++;
547
548 if(page < n_pages)
549 {
550 TIFFWriteDirectory(tif);
551 }
552 } // for all raster masks
553 } // for all pipe nodes
554
555 // success
556 rc = 0;
557 } // if more than 1 page, i.e., there are masks
558
559exit:
560 if(tif)
561 {
562 TIFFClose(tif);
563 tif = NULL;
564 }
565 dt_free(profile);
566 dt_free(rowdata);
567#ifdef _WIN32
568 dt_free(wfilename);
569#endif
570 if(free_mask)
572
573 return rc;
574}
575
576#if 0
577int dt_imageio_tiff_read_header(const char *filename, dt_imageio_tiff_t *tiff)
578{
579 tiff->handle = TIFFOpen(filename, "rl");
580 if( tiff->handle )
581 {
582 TIFFGetField(tiff->handle, TIFFTAG_IMAGEWIDTH, &tiff->width);
583 TIFFGetField(tiff->handle, TIFFTAG_IMAGELENGTH, &tiff->height);
584 }
585 return 1;
586}
587
588int dt_imageio_tiff_read(dt_imageio_tiff_t *tiff, uint8_t *out)
589{
590 TIFFClose(tiff->handle);
591 return 1;
592}
593#endif
594
596{
597 return sizeof(dt_imageio_tiff_t) - sizeof(TIFF *);
598}
599
600void *legacy_params(dt_imageio_module_format_t *self, const void *const old_params,
601 const size_t old_params_size, const int old_version, const int new_version,
602 size_t *new_size)
603{
604 if(old_version == 1 && new_version == 3)
605 {
606 typedef struct dt_imageio_tiff_v1_t
607 {
608 int max_width, max_height;
609 int width, height;
610 char style[128];
611 int bpp;
612 int compress;
613 TIFF *handle;
614 } dt_imageio_tiff_v1_t;
615
616 const dt_imageio_tiff_v1_t *o = (dt_imageio_tiff_v1_t *)old_params;
618
619 n->global.max_width = o->max_width;
620 n->global.max_height = o->max_height;
621 n->global.width = o->width;
622 n->global.height = o->height;
623 g_strlcpy(n->global.style, o->style, sizeof(o->style));
624 n->bpp = o->bpp;
625 n->compress = o->compress == 3 ? 2 : o->compress; // drop redundant float case
626 n->compresslevel = 6;
627 n->handle = o->handle;
628 *new_size = self->params_size(self);
629 return n;
630 }
631 else if(old_version == 2 && new_version == 3)
632 {
633 typedef struct dt_imageio_tiff_v2_t
634 {
635 int max_width, max_height;
636 int width, height;
637 char style[128];
638 gboolean style_append;
639 int bpp;
640 int compress;
641 TIFF *handle;
642 } dt_imageio_tiff_v2_t;
643
644 const dt_imageio_tiff_v2_t *o = (dt_imageio_tiff_v2_t *)old_params;
646
647 n->global.max_width = o->max_width;
648 n->global.max_height = o->max_height;
649 n->global.width = o->width;
650 n->global.height = o->height;
651 g_strlcpy(n->global.style, o->style, sizeof(o->style));
652 n->bpp = o->bpp;
653 n->compress = o->compress == 3 ? 2 : o->compress; // drop redundant float case
654 n->compresslevel = 6;
655 n->handle = o->handle;
656 *new_size = self->params_size(self);
657 return n;
658 }
659 return NULL;
660}
661
663{
665 d->bpp = dt_conf_get_int("plugins/imageio/format/tiff/bpp");
666 if(d->bpp != 16 && d->bpp != 32)
667 d->bpp = 8;
668
669 // Drop redundant float case from existing config
670 // TODO: Move to legacy eventually
671 d->compress = dt_conf_get_int("plugins/imageio/format/tiff/compress");
672 if(d->compress == 3)
673 {
674 d->compress = 2;
675 dt_conf_set_int("plugins/imageio/format/tiff/compress", d->compress);
676 }
677
678 // TIFF compression level might actually be zero, handle this
679 if(!dt_conf_key_exists("plugins/imageio/format/tiff/compresslevel"))
680 d->compresslevel = 6;
681 else
682 {
683 d->compresslevel = dt_conf_get_int("plugins/imageio/format/tiff/compresslevel");
684 if(d->compresslevel < 0 || d->compresslevel > 9) d->compresslevel = 6;
685 }
686
687 // TIFF shortfile
688 if(!dt_conf_key_exists("plugins/imageio/format/tiff/shortfile"))
689 d->shortfile = 0;
690 else
691 {
692 d->shortfile = dt_conf_get_int("plugins/imageio/format/tiff/shortfile");
693 }
694
695 return d;
696}
697
699{
700 dt_free(params);
701}
702
703int set_params(dt_imageio_module_format_t *self, const void *params, const int size)
704{
705 if(size != self->params_size(self)) return 1;
706 const dt_imageio_tiff_t *d = (dt_imageio_tiff_t *)params;
708
709 if(d->bpp == 16)
710 dt_bauhaus_combobox_set(g->bpp, 1);
711 else if(d->bpp == 32)
712 dt_bauhaus_combobox_set(g->bpp, 2);
713 else // (d->bpp == 8)
714 dt_bauhaus_combobox_set(g->bpp, 0);
715
716 dt_bauhaus_combobox_set(g->compress, d->compress);
717
718 dt_bauhaus_slider_set(g->compresslevel, d->compresslevel);
719
720 dt_bauhaus_combobox_set(g->shortfiles, d->shortfile);
721 return 0;
722}
723
725{
726 return ((dt_imageio_tiff_t *)p)->bpp;
727}
728
730{
731 int ret = IMAGEIO_RGB;
732
733 if(((dt_imageio_tiff_t *)p)->bpp == 8)
734 ret |= IMAGEIO_INT8;
735 else if(((dt_imageio_tiff_t *)p)->bpp == 16)
736 ret |= IMAGEIO_INT16;
737 else if(((dt_imageio_tiff_t *)p)->bpp == 32)
738 ret |= IMAGEIO_FLOAT;
739
740 return ret;
741}
742
744{
745 return "image/tiff";
746}
747
749{
750 return "tif";
751}
752
753const char *name()
754{
755 return _("TIFF (8/16/32-bit)");
756}
757
758static void bpp_combobox_changed(GtkWidget *widget, gpointer user_data)
759{
760 const int bpp = dt_bauhaus_combobox_get(widget);
761
762 if(bpp == 1)
763 dt_conf_set_int("plugins/imageio/format/tiff/bpp", 16);
764 else if(bpp == 2)
765 dt_conf_set_int("plugins/imageio/format/tiff/bpp", 32);
766 else // (bpp == 0)
767 dt_conf_set_int("plugins/imageio/format/tiff/bpp", 8);
768}
769
770static void shortfile_combobox_changed(GtkWidget *widget, gpointer user_data)
771{
772 const int mode = dt_bauhaus_combobox_get(widget);
773 dt_conf_set_int("plugins/imageio/format/tiff/shortfile", mode);
774}
775
776static void compress_combobox_changed(GtkWidget *widget, gpointer user_data)
777{
778 const int compress = dt_bauhaus_combobox_get(widget);
779 dt_conf_set_int("plugins/imageio/format/tiff/compress", compress);
780
781 if(compress == 0)
782 gtk_widget_set_sensitive(GTK_WIDGET(user_data), FALSE);
783 else
784 gtk_widget_set_sensitive(GTK_WIDGET(user_data), TRUE);
785
786}
787
788static void compress_level_changed(GtkWidget *slider, gpointer user_data)
789{
790 const int compresslevel = (int)dt_bauhaus_slider_get(slider);
791 dt_conf_set_int("plugins/imageio/format/tiff/compresslevel", compresslevel);
792}
793
795{
796}
797
801
803{
805 self->gui_data = (void *)gui;
806
807 const int bpp = dt_conf_get_int("plugins/imageio/format/tiff/bpp");
808
809 // Drop redundant float case from existing config
810 // TODO: Move to legacy eventually
811 int compress = dt_conf_get_int("plugins/imageio/format/tiff/compress");
812 if(compress == 3)
813 {
814 compress = 2;
815 dt_conf_set_int("plugins/imageio/format/tiff/compress", compress);
816 }
817
818 int shortmode = 0;
819 if(dt_conf_key_exists("plugins/imageio/format/tiff/shortfile"))
820 shortmode = dt_conf_get_int("plugins/imageio/format/tiff/shortfile");
821
822 // TIFF compression level might actually be zero!
823 int compresslevel = 6;
824 if(dt_conf_key_exists("plugins/imageio/format/tiff/compresslevel"))
825 compresslevel = dt_conf_get_int("plugins/imageio/format/tiff/compresslevel");
826
827 self->widget = gtk_box_new(GTK_ORIENTATION_VERTICAL, DT_GUI_BOX_SPACING);
828
829 // Bit depth combo box
831 dt_bauhaus_widget_set_label(gui->bpp, N_("bit depth"));
832 dt_bauhaus_combobox_add(gui->bpp, _("8 bit"));
833 dt_bauhaus_combobox_add(gui->bpp, _("16 bit"));
834 dt_bauhaus_combobox_add(gui->bpp, _("32 bit (float)"));
835 if(bpp == 16)
837 else if(bpp == 32)
839 else // (bpp == 8)
841 gtk_box_pack_start(GTK_BOX(self->widget), gui->bpp, TRUE, TRUE, 0);
842 g_signal_connect(G_OBJECT(gui->bpp), "value-changed", G_CALLBACK(bpp_combobox_changed), NULL);
843
844 // Compression method combo box
846 dt_bauhaus_widget_set_label(gui->compress, N_("compression"));
847 dt_bauhaus_combobox_add(gui->compress, _("uncompressed"));
848 dt_bauhaus_combobox_add(gui->compress, _("deflate"));
849 dt_bauhaus_combobox_add(gui->compress, _("deflate with predictor"));
850 dt_bauhaus_combobox_set(gui->compress, compress);
851 gtk_box_pack_start(GTK_BOX(self->widget), gui->compress, TRUE, TRUE, 0);
852
853 // Compression level slider
855 dt_confgen_get_int("plugins/imageio/format/tiff/compresslevel", DT_MIN),
856 dt_confgen_get_int("plugins/imageio/format/tiff/compresslevel", DT_MAX),
857 1,
858 dt_confgen_get_int("plugins/imageio/format/tiff/compresslevel", DT_DEFAULT),
859 0);
860 dt_bauhaus_widget_set_label(gui->compresslevel, N_("compression level"));
861 dt_bauhaus_slider_set(gui->compresslevel, compresslevel);
862 gtk_box_pack_start(GTK_BOX(self->widget), GTK_WIDGET(gui->compresslevel), TRUE, TRUE, 0);
863 g_signal_connect(G_OBJECT(gui->compresslevel), "value-changed", G_CALLBACK(compress_level_changed), NULL);
864
865 g_signal_connect(G_OBJECT(gui->compress), "value-changed", G_CALLBACK(compress_combobox_changed), (gpointer)gui->compresslevel);
866
867 if(compress == 0)
868 gtk_widget_set_sensitive(gui->compresslevel, FALSE);
869
870 // shortfile option combo box
872 dt_bauhaus_widget_set_label(gui->shortfiles, N_("b&w image"));
873 dt_bauhaus_combobox_add(gui->shortfiles, _("write rgb colors"));
874 dt_bauhaus_combobox_add(gui->shortfiles, _("write grayscale"));
875 dt_bauhaus_combobox_set(gui->shortfiles, shortmode);
876 gtk_box_pack_start(GTK_BOX(self->widget), gui->shortfiles, TRUE, TRUE, 0);
877 g_signal_connect(G_OBJECT(gui->shortfiles), "value-changed", G_CALLBACK(shortfile_combobox_changed), NULL);
878}
879
881{
882 dt_free(self->gui_data);
883}
884
886{
888 dt_bauhaus_combobox_set(gui->bpp, 0); //8bpp
889 dt_bauhaus_slider_set(gui->compresslevel, dt_confgen_get_int("plugins/imageio/format/tiff/compresslevel", DT_DEFAULT));
890 dt_bauhaus_combobox_set(gui->shortfiles, dt_confgen_get_int("plugins/imageio/format/tiff/shortfile", DT_DEFAULT));
891}
892
897
898// clang-format off
899// modelines: These editor modelines have been set for all relevant files by tools/update_modelines.py
900// vim: shiftwidth=2 expandtab tabstop=2 cindent
901// kate: tab-indents: off; indent-width 2; replace-tabs on; indent-mode cstyle; remove-trailing-spaces modified;
902// clang-format on
#define TRUE
Definition ashift_lsd.c:162
#define FALSE
Definition ashift_lsd.c:158
float dt_bauhaus_slider_get(GtkWidget *widget)
Definition bauhaus.c:3280
int dt_bauhaus_combobox_get(GtkWidget *widget)
Definition bauhaus.c:2136
void dt_bauhaus_slider_set(GtkWidget *widget, float pos)
Definition bauhaus.c:3331
void dt_bauhaus_combobox_set(GtkWidget *widget, const int pos)
Definition bauhaus.c:2090
void dt_bauhaus_widget_set_label(GtkWidget *widget, const char *label)
Definition bauhaus.c:1504
GtkWidget * dt_bauhaus_slider_new_with_range(dt_bauhaus_t *bh, dt_gui_module_t *self, float min, float max, float step, float defval, int digits)
Definition bauhaus.c:1632
GtkWidget * dt_bauhaus_combobox_new(dt_bauhaus_t *bh, dt_gui_module_t *self)
Definition bauhaus.c:1698
void dt_bauhaus_combobox_add(GtkWidget *widget, const char *text)
Definition bauhaus.c:1824
static const float x
The colour-profile module's API: which profiles exist, and how to apply one.
struct _GtkWidget GtkWidget
GtkWidget, opaque, spelled exactly as GTK spells it.
Definition colorspaces.h:98
const dt_colormatrix_t dt_aligned_pixel_t out
int dt_conf_key_exists(const char *key)
Does key have a value?
void dt_conf_set_int(const char *name, int val)
int dt_conf_get_int(const char *name)
Integer for name, clamped to the bounds declared in the XML.
int dt_confgen_get_int(const char *name, dt_confgen_value_kind_t kind)
@ DT_DEFAULT
Definition conf.h:135
@ DT_MAX
Definition conf.h:137
@ DT_MIN
Definition conf.h:136
void dt_control_log(const char *msg,...)
Definition control.c:824
struct dt_bauhaus_t * dt_bauhaus_get_global(void)
Definition darktable.c:646
int dt_exif_write_blob(uint8_t *blob, uint32_t size, const char *path, const int compressed)
Definition exif.cc:2057
What a photograph says about itself: the EXIF, IPTC and XMP tags a camera and a cataloguer write,...
#define DT_GUI_MODULE(x)
int bpp
@ IMAGEIO_INT16
@ IMAGEIO_RGB
@ IMAGEIO_INT8
@ IMAGEIO_FLOAT
@ FORMAT_FLAGS_SUPPORT_LAYERS
@ FORMAT_FLAGS_SUPPORT_XMP
const dt_colorspaces_color_profile_t * dt_colorspaces_get_output_profile(const int32_t imgid, dt_colorspaces_color_profile_type_t *over_type, const char *over_filename)
#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 CLIP(x)
Definition math.h:83
#define dt_free(ptr)
g_free() ptr and set it to NULL, skipping both if it is already NULL.
Definition mem_alloc.h:171
char * key
uint32_t width
Definition mipmap_cache.c:0
uint32_t height
Definition mipmap_cache.c:1
size_t size
Definition mipmap_cache.c:3
dt_mipmap_buffer_dsc_flags flags
Definition mipmap_cache.c:4
#define DT_MODULE(MODVER)
Instantiate the version handshake every module must export. Use once, at file scope,...
#define dt_pixelpipe_cache_free_align(mem)
float * dt_dev_get_raster_mask(dt_dev_pixelpipe_t *pipe, const struct dt_iop_module_t *raster_mask_source, const int raster_mask_id, const struct dt_iop_module_t *target_module, int *error)
Retrieve a provider mask from the global cache and transform it to a consumer.
dt_colorspaces_color_profile_type_t
static const dt_aligned_pixel_simd_t value
Definition simd.h:144
cmsHPROFILE profile
the actual profile; NULL for the three category entries
struct dt_iop_module_t *void * data
GModule *GtkWidget * widget
GtkWidget * shortfiles
Definition tiff.c:84
GtkWidget * compresslevel
Definition tiff.c:83
GtkWidget * compress
Definition tiff.c:82
GtkWidget * bpp
Definition tiff.c:81
dt_imageio_module_data_t global
Definition tiff.c:71
int compresslevel
Definition tiff.c:74
TIFF * handle
Definition tiff.c:76
static void shortfile_combobox_changed(GtkWidget *widget, gpointer user_data)
Definition tiff.c:770
const char * mime(dt_imageio_module_data_t *data)
Definition tiff.c:743
int write_image(dt_imageio_module_data_t *d_tmp, const char *filename, const void *in_void, dt_colorspaces_color_profile_type_t over_type, const char *over_filename, void *exif, int exif_len, int32_t imgid, int num, int total, dt_dev_pixelpipe_t *pipe, const gboolean export_masks)
Definition tiff.c:88
size_t params_size(dt_imageio_module_format_t *self)
Definition tiff.c:595
void gui_reset(dt_imageio_module_format_t *self)
Definition tiff.c:885
void gui_init(dt_imageio_module_format_t *self)
Definition tiff.c:802
const char * extension(dt_imageio_module_data_t *data)
Definition tiff.c:748
static void compress_level_changed(GtkWidget *slider, gpointer user_data)
Definition tiff.c:788
int set_params(dt_imageio_module_format_t *self, const void *params, const int size)
Definition tiff.c:703
const char * name()
Definition tiff.c:753
void cleanup(dt_imageio_module_format_t *self)
Definition tiff.c:798
int levels(dt_imageio_module_data_t *p)
Definition tiff.c:729
void * legacy_params(dt_imageio_module_format_t *self, const void *const old_params, const size_t old_params_size, const int old_version, const int new_version, size_t *new_size)
Definition tiff.c:600
void free_params(dt_imageio_module_format_t *self, dt_imageio_module_data_t *params)
Definition tiff.c:698
static void compress_combobox_changed(GtkWidget *widget, gpointer user_data)
Definition tiff.c:776
void init(dt_imageio_module_format_t *self)
Definition tiff.c:794
void * get_params(dt_imageio_module_format_t *self)
Definition tiff.c:662
void gui_cleanup(dt_imageio_module_format_t *self)
Definition tiff.c:880
static void bpp_combobox_changed(GtkWidget *widget, gpointer user_data)
Definition tiff.c:758
Telling the user something happened.
#define DT_GUI_BOX_SPACING