Ansel 0.0
A darktable fork - bloat + design vision
Loading...
Searching...
No Matches
io.c
Go to the documentation of this file.
1/*
2 This file is part of the Ansel project.
3 Copyright (C) 2026 Aurélien PIERRE.
4 Copyright (C) 2026 Guillaume Stutin.
5
6 Ansel is free software: you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation, either version 3 of the License, or
9 (at your option) any later version.
10
11 Ansel is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with Ansel. If not, see <http://www.gnu.org/licenses/>.
18*/
19
20#include "system/macros.h"
21#include "system/mem_alloc.h"
22#include "system/openmp.h"
23#include "iop/drawlayer/io.h"
24
26#include "common/image.h"
29#include "control/jobs.h"
30#include "iop/drawlayer/cache.h"
31
32#include <glib/gstdio.h>
33#include <math.h>
34#include <stdlib.h>
35#include <string.h>
36#include <tiffio.h>
37
43static inline float _clamp01(const float value)
44{
45 return fminf(fmaxf(value, 0.0f), 1.0f);
46}
47
49{
50 uint32_t u;
51 float f;
53
55static inline float _half_to_float(const uint16_t h)
56{
57 static const dt_drawlayer_io_fp32_t magic = { 113u << 23 };
58 static const uint32_t shifted_exp = 0x7c00u << 13;
60
61 out.u = (h & 0x7fffu) << 13;
62 const uint32_t exp = shifted_exp & out.u;
63 out.u += (127u - 15u) << 23;
64
65 if(exp == shifted_exp)
66 out.u += (128u - 16u) << 23;
67 else if(exp == 0)
68 {
69 out.u += 1u << 23;
70 out.f -= magic.f;
71 }
72
73 out.u |= (h & 0x8000u) << 16;
74 return out.f;
75}
76
78static inline uint16_t _float_to_half(float value)
79{
80 dt_drawlayer_io_fp32_t in = { .f = value };
81 const uint32_t sign = (in.u >> 16) & 0x8000u;
82 const uint32_t exponent = (in.u >> 23) & 0xffu;
83 uint32_t mantissa = in.u & 0x007fffffu;
84
85 if(exponent == 0xffu)
86 {
87 if(mantissa) return (uint16_t)(sign | 0x7e00u);
88 return (uint16_t)(sign | 0x7c00u);
89 }
90
91 const int32_t half_exponent = (int32_t)exponent - 127 + 15;
92
93 if(half_exponent >= 0x1f) return (uint16_t)(sign | 0x7c00u);
94 if(half_exponent <= 0)
95 {
96 if(half_exponent < -10) return (uint16_t)sign;
97
98 mantissa |= 0x00800000u;
99 const uint32_t shift = (uint32_t)(1 - half_exponent);
100 uint32_t half_mantissa = mantissa >> (shift + 13);
101 const uint32_t round_bit = 1u << (shift + 12);
102
103 if((mantissa & round_bit) && ((mantissa & (round_bit - 1u)) || (half_mantissa & 1u)))
104 half_mantissa++;
105
106 return (uint16_t)(sign | half_mantissa);
107 }
108
109 uint32_t half_exp = (uint32_t)half_exponent << 10;
110 uint32_t half_mantissa = mantissa >> 13;
111
112 if((mantissa & 0x00001000u) && ((mantissa & 0x00001fffu) || (half_mantissa & 1u)))
113 {
114 half_mantissa++;
115
116 if(half_mantissa == 0x0400u)
117 {
118 half_mantissa = 0;
119 half_exp += 0x0400u;
120 if(half_exp >= 0x7c00u) return (uint16_t)(sign | 0x7c00u);
121 }
122 }
123
124 return (uint16_t)(sign | half_exp | half_mantissa);
125}
126
128static inline void _clear_transparent_half(uint16_t *pixels, const size_t pixel_count)
129{
130 if(IS_NULL_PTR(pixels)) return;
131 memset(pixels, 0, pixel_count * 4 * sizeof(uint16_t));
132}
133
135static inline void _load_half_pixel_rgba(const uint16_t *src, float rgba[4])
136{
137 rgba[0] = _half_to_float(src[0]);
138 rgba[1] = _half_to_float(src[1]);
139 rgba[2] = _half_to_float(src[2]);
140 rgba[3] = _half_to_float(src[3]);
141}
142
151
152static gboolean dt_drawlayer_layer_name_non_empty(const char *name)
153{
154 if(IS_NULL_PTR(name)) return FALSE;
155 char tmp[DT_DRAWLAYER_IO_NAME_SIZE] = { 0 };
156 g_strlcpy(tmp, name, sizeof(tmp));
157 g_strstrip(tmp);
158 return tmp[0] != '\0';
159}
160
161static int64_t dt_drawlayer_sidecar_timestamp_from_path(const char *path)
162{
163 if(IS_NULL_PTR(path) || path[0] == '\0' || !g_file_test(path, G_FILE_TEST_EXISTS)) return 0;
164
165 GStatBuf st = { 0 };
166 if(g_stat(path, &st) != 0) return 0;
167 return (int64_t)st.st_mtime;
168}
169
170static gboolean _export_pre_module_fullres_to_tiff(const int32_t imgid, const char *filter, const char *path)
171{
172 if(imgid <= 0 || IS_NULL_PTR(path) || path[0] == '\0') return FALSE;
173
175 if(IS_NULL_PTR(format)) return FALSE;
176
177 dt_imageio_module_data_t *format_params = format->get_params(format);
178 if(IS_NULL_PTR(format_params)) return FALSE;
179
180 format_params->max_width = 0;
181 format_params->max_height = 0;
182 format_params->width = 0;
183 format_params->height = 0;
184 format_params->style[0] = '\0';
185
186 if(format->params_size(format) >= sizeof(drawlayer_tiff_export_params_t))
187 {
188 drawlayer_tiff_export_params_t *const tiff_params = (drawlayer_tiff_export_params_t *)format_params;
189 tiff_params->bpp = 32;
190 tiff_params->compress = 0;
191 tiff_params->compresslevel = 0;
192 tiff_params->shortfile = 0;
193 }
194
195 const int rc = dt_imageio_export_with_flags(
196 imgid, path, format, format_params, TRUE, FALSE, TRUE, FALSE, FALSE, FALSE, (filter && filter[0]) ? filter : NULL,
197 FALSE, FALSE, DT_COLORSPACE_NONE, NULL, DT_INTENT_PERCEPTUAL, NULL, NULL, 0, 0, NULL, NULL);
198
199 format->free_params(format, format_params);
200 return rc == 0;
201}
202
204static gboolean _icc_blob_from_profile_key(const char *work_profile, uint8_t **icc_data, uint32_t *icc_len)
205{
206 if(icc_data) *icc_data = NULL;
207 if(!IS_NULL_PTR(icc_len)) *icc_len = 0;
208 if(IS_NULL_PTR(work_profile) || work_profile[0] == '\0' || IS_NULL_PTR(icc_data) || !icc_len) return FALSE;
209
210 const char *sep0 = strchr(work_profile, '|');
211 if(IS_NULL_PTR(sep0)) return FALSE;
212 const char *sep1 = strchr(sep0 + 1, '|');
213 if(IS_NULL_PTR(sep1)) return FALSE;
214
215 char *endptr = NULL;
216 const long type_long = strtol(work_profile, &endptr, 10);
217 if(endptr != sep0) return FALSE;
218
220 const char *filename = sep1 + 1;
222 if(IS_NULL_PTR(profile) || IS_NULL_PTR(profile->profile)) return FALSE;
223
224 cmsUInt32Number len = 0;
225 cmsSaveProfileToMem(profile->profile, NULL, &len);
226 if(len == 0) return FALSE;
227
228 uint8_t *buf = g_malloc(len);
229 if(IS_NULL_PTR(buf)) return FALSE;
230
231 if(!cmsSaveProfileToMem(profile->profile, buf, &len) || len == 0)
232 {
233 dt_free(buf);
234 return FALSE;
235 }
236
237 *icc_data = buf;
238 *icc_len = (uint32_t)len;
239 return TRUE;
240}
241
243static gboolean _set_directory_tags(TIFF *tiff, const uint32_t width, const uint32_t height, const char *name,
244 const char *work_profile)
245{
246 const uint16_t extrasamples[] = { EXTRASAMPLE_ASSOCALPHA };
247
248 if(!TIFFSetField(tiff, TIFFTAG_IMAGEWIDTH, width)) return FALSE;
249 if(!TIFFSetField(tiff, TIFFTAG_IMAGELENGTH, height)) return FALSE;
250 if(!TIFFSetField(tiff, TIFFTAG_BITSPERSAMPLE, 16)) return FALSE;
251 if(!TIFFSetField(tiff, TIFFTAG_SAMPLEFORMAT, SAMPLEFORMAT_IEEEFP)) return FALSE;
252 if(!TIFFSetField(tiff, TIFFTAG_SAMPLESPERPIXEL, 4)) return FALSE;
253 if(!TIFFSetField(tiff, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG)) return FALSE;
254 if(!TIFFSetField(tiff, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_RGB)) return FALSE;
255 if(!TIFFSetField(tiff, TIFFTAG_EXTRASAMPLES, 1, extrasamples)) return FALSE;
256
257 if(!TIFFSetField(tiff, TIFFTAG_COMPRESSION, COMPRESSION_ADOBE_DEFLATE)
258 && !TIFFSetField(tiff, TIFFTAG_COMPRESSION, COMPRESSION_DEFLATE)
259 && !TIFFSetField(tiff, TIFFTAG_COMPRESSION, COMPRESSION_LZW))
260 TIFFSetField(tiff, TIFFTAG_COMPRESSION, COMPRESSION_NONE);
261
262 TIFFSetField(tiff, TIFFTAG_ROWSPERSTRIP, TIFFDefaultStripSize(tiff, 0));
263 if(!IS_NULL_PTR(name) && name[0]) TIFFSetField(tiff, TIFFTAG_PAGENAME, name);
264 if(!IS_NULL_PTR(work_profile) && work_profile[0]) TIFFSetField(tiff, TIFFTAG_IMAGEDESCRIPTION, work_profile);
265 return TRUE;
266}
267
269static gboolean _read_scanline_rgba(TIFF *tiff, const uint32_t width, const uint32_t row, uint16_t *out)
270{
271 uint16_t bpp = 0;
272 uint16_t spp = 0;
273 uint16_t sampleformat = SAMPLEFORMAT_UINT;
274
275 TIFFGetField(tiff, TIFFTAG_BITSPERSAMPLE, &bpp);
276 TIFFGetField(tiff, TIFFTAG_SAMPLESPERPIXEL, &spp);
277 TIFFGetFieldDefaulted(tiff, TIFFTAG_SAMPLEFORMAT, &sampleformat);
278
279 if(spp != 4) return FALSE;
280
281 const tsize_t scanline = TIFFScanlineSize(tiff);
282 tdata_t buffer = _TIFFmalloc((tsize_t)scanline);
283 if(IS_NULL_PTR(buffer)) return FALSE;
284
285 const int ok = TIFFReadScanline(tiff, buffer, row, 0);
286 if(ok == -1)
287 {
288 _TIFFfree(buffer);
289 return FALSE;
290 }
291
292 if(bpp == 16 && sampleformat == SAMPLEFORMAT_IEEEFP)
293 memcpy(out, buffer, (size_t)width * 4 * sizeof(uint16_t));
294 else
295 {
296 _TIFFfree(buffer);
297 return FALSE;
298 }
299
300 _TIFFfree(buffer);
301 return TRUE;
302}
303
305static gboolean _write_scanline_rgba(TIFF *tiff, const uint32_t row, const uint16_t *in)
306{
307 return TIFFWriteScanline(tiff, (tdata_t)in, row, 0) != -1;
308}
309
311static void _overlay_patch_row_rgba(uint16_t *dst_row, const uint32_t width, const int offset_x,
312 const int raw_y, const dt_drawlayer_io_patch_t *patch)
313{
314 if(IS_NULL_PTR(dst_row) || IS_NULL_PTR(patch) || IS_NULL_PTR(patch->pixels) || raw_y < patch->y || raw_y >= patch->y + patch->height) return;
315
316 const int dst_x0 = MAX(0, patch->x - offset_x);
317 const int dst_x1 = MIN((int)width, patch->x + patch->width - offset_x);
318 if(dst_x0 >= dst_x1) return;
319
320 const float *patch_row = patch->pixels + 4 * (size_t)(raw_y - patch->y) * patch->width;
322 for(int dst_x = dst_x0; dst_x < dst_x1; dst_x++)
323 {
324 const float *src_pixel = patch_row + 4 * (size_t)(dst_x + offset_x - patch->x);
325 dst_row[4 * dst_x + 0] = _float_to_half(src_pixel[0]);
326 dst_row[4 * dst_x + 1] = _float_to_half(src_pixel[1]);
327 dst_row[4 * dst_x + 2] = _float_to_half(src_pixel[2]);
328 dst_row[4 * dst_x + 3] = _float_to_half(src_pixel[3]);
329 }
330}
331
333static void _scan_directories(TIFF *tiff, const char *target_name, const int target_order,
335{
336 memset(info, 0, sizeof(*info));
337 info->index = -1;
338
339 if(IS_NULL_PTR(tiff)) return;
340 if(!TIFFSetDirectory(tiff, 0)) return;
341
342 const gboolean has_target_name = (target_name && target_name[0] != '\0');
343 const gboolean has_target_order = (target_order >= 0);
344 gboolean exact_found = FALSE;
345 gboolean named_found = FALSE;
346 gboolean order_found = FALSE;
347 dt_drawlayer_io_layer_info_t exact = { 0 };
348 dt_drawlayer_io_layer_info_t named = { 0 };
349 dt_drawlayer_io_layer_info_t ordered = { 0 };
350 exact.index = -1;
351 named.index = -1;
352 ordered.index = -1;
353
354 int index = 0;
355 do
356 {
357 char *page_name = NULL;
358 char *page_profile = NULL;
359 uint32_t width = 0;
360 uint32_t height = 0;
361 TIFFGetField(tiff, TIFFTAG_IMAGEWIDTH, &width);
362 TIFFGetField(tiff, TIFFTAG_IMAGELENGTH, &height);
363 TIFFGetField(tiff, TIFFTAG_PAGENAME, &page_name);
364 TIFFGetField(tiff, TIFFTAG_IMAGEDESCRIPTION, &page_profile);
365
366 const gboolean order_match = (has_target_order && index == target_order);
367 const gboolean name_match = (has_target_name && !g_strcmp0(page_name ? page_name : "", target_name));
368 const gboolean exact_match = (name_match && order_match);
369
370 if(exact_match && !exact_found)
371 {
372 exact_found = TRUE;
373 exact.found = TRUE;
374 exact.index = index;
375 exact.width = width;
376 exact.height = height;
377 g_strlcpy(exact.name, page_name ? page_name : "", sizeof(exact.name));
378 g_strlcpy(exact.work_profile, page_profile ? page_profile : "", sizeof(exact.work_profile));
379 }
380
381 if(name_match && !named_found)
382 {
383 named_found = TRUE;
384 named.found = TRUE;
385 named.index = index;
386 named.width = width;
387 named.height = height;
388 g_strlcpy(named.name, page_name ? page_name : "", sizeof(named.name));
389 g_strlcpy(named.work_profile, page_profile ? page_profile : "", sizeof(named.work_profile));
390 }
391
392 if(order_match && !order_found)
393 {
394 order_found = TRUE;
395 ordered.found = TRUE;
396 ordered.index = index;
397 ordered.width = width;
398 ordered.height = height;
399 g_strlcpy(ordered.name, page_name ? page_name : "", sizeof(ordered.name));
400 g_strlcpy(ordered.work_profile, page_profile ? page_profile : "", sizeof(ordered.work_profile));
401 }
402
403 index++;
404 } while(TIFFReadDirectory(tiff));
405
406 if(exact_found)
407 {
408 info->found = TRUE;
409 info->index = exact.index;
410 info->width = exact.width;
411 info->height = exact.height;
412 g_strlcpy(info->name, exact.name, sizeof(info->name));
413 g_strlcpy(info->work_profile, exact.work_profile, sizeof(info->work_profile));
414 }
415 else if(named_found)
416 {
417 info->found = TRUE;
418 info->index = named.index;
419 info->width = named.width;
420 info->height = named.height;
421 g_strlcpy(info->name, named.name, sizeof(info->name));
422 g_strlcpy(info->work_profile, named.work_profile, sizeof(info->work_profile));
423 }
424 else if(order_found)
425 {
426 info->found = TRUE;
427 info->index = ordered.index;
428 info->width = ordered.width;
429 info->height = ordered.height;
430 g_strlcpy(info->name, ordered.name, sizeof(info->name));
431 g_strlcpy(info->work_profile, ordered.work_profile, sizeof(info->work_profile));
432 }
433
434 info->count = index;
435 TIFFSetDirectory(tiff, 0);
436}
437
439static gboolean _write_page(TIFF *dst, TIFF *src, const int src_dir, const char *name, const char *work_profile,
440 const dt_drawlayer_io_patch_t *patch, const int layer_width, const int layer_height)
441{
442 uint32_t width = patch ? (uint32_t)patch->width : (uint32_t)layer_width;
443 uint32_t height = patch ? (uint32_t)patch->height : (uint32_t)layer_height;
444 const char *page_profile = work_profile;
445 uint8_t *icc_profile = NULL;
446 uint32_t icc_profile_len = 0;
447
448 if(!IS_NULL_PTR(src))
449 {
450 if(!TIFFSetDirectory(src, (tdir_t)src_dir)) return FALSE;
451 if(IS_NULL_PTR(patch))
452 {
453 TIFFGetField(src, TIFFTAG_IMAGEWIDTH, &width);
454 TIFFGetField(src, TIFFTAG_IMAGELENGTH, &height);
455 }
456 if(IS_NULL_PTR(name))
457 {
458 char *src_name = NULL;
459 TIFFGetField(src, TIFFTAG_PAGENAME, &src_name);
460 name = src_name;
461 }
462 if(IS_NULL_PTR(page_profile))
463 {
464 char *src_profile = NULL;
465 TIFFGetField(src, TIFFTAG_IMAGEDESCRIPTION, &src_profile);
466 page_profile = src_profile;
467 }
468 }
469
470 if(!_set_directory_tags(dst, width, height, name, page_profile)) return FALSE;
471 if(page_profile && page_profile[0] && _icc_blob_from_profile_key(page_profile, &icc_profile, &icc_profile_len))
472 TIFFSetField(dst, TIFFTAG_ICCPROFILE, icc_profile_len, icc_profile);
473
474 uint16_t *src_row = g_malloc_n((gsize)width * 4, sizeof(uint16_t));
475 uint16_t *dst_row = g_malloc_n((gsize)width * 4, sizeof(uint16_t));
476 if(IS_NULL_PTR(src_row) || IS_NULL_PTR(dst_row))
477 {
478 dt_free(icc_profile);
479 dt_free(src_row);
480 dt_free(dst_row);
481 return FALSE;
482 }
483
484 const int offset_x = (layer_width - (int)width) / 2;
485 const int offset_y = (layer_height - (int)height) / 2;
486
487 for(uint32_t y = 0; y < height; y++)
488 {
489 if(!IS_NULL_PTR(src))
490 {
491 if(!_read_scanline_rgba(src, width, y, src_row))
492 {
493 dt_free(icc_profile);
494 dt_free(src_row);
495 dt_free(dst_row);
496 return FALSE;
497 }
498 memcpy(dst_row, src_row, (size_t)width * 4 * sizeof(uint16_t));
499 }
500 else
501 _clear_transparent_half(dst_row, (size_t)width);
502
503 if(!IS_NULL_PTR(patch))
504 {
505 const int layer_y = (int)y + offset_y;
506 _overlay_patch_row_rgba(dst_row, width, offset_x, layer_y, patch);
507 }
508
509 if(!_write_scanline_rgba(dst, y, dst_row))
510 {
511 dt_free(icc_profile);
512 dt_free(src_row);
513 dt_free(dst_row);
514 return FALSE;
515 }
516 }
517
518 dt_free(icc_profile);
519 dt_free(src_row);
520 dt_free(dst_row);
521 return TIFFWriteDirectory(dst) != 0;
522}
523
525static gboolean _rewrite_sidecar(const char *path, const char *target_name, const int target_order,
526 const char *work_profile, const dt_drawlayer_io_patch_t *patch, const int layer_width,
527 const int layer_height, const gboolean delete_target, const int insert_order,
528 int *final_order)
529{
530 if(!IS_NULL_PTR(final_order)) *final_order = -1;
531
532 TIFF *src = NULL;
534 memset(&info, 0, sizeof(info));
535 info.index = -1;
536
537 if(g_file_test(path, G_FILE_TEST_EXISTS))
538 {
539 src = TIFFOpen(path, "rb");
540 if(IS_NULL_PTR(src)) return FALSE;
541 _scan_directories(src, target_name, target_order, &info);
542 }
543
544 if(delete_target && (IS_NULL_PTR(src) || !info.found))
545 {
546 if(!IS_NULL_PTR(src)) TIFFClose(src);
547 return TRUE;
548 }
549
550 if(delete_target && !IS_NULL_PTR(src) && info.found && info.count == 1)
551 {
552 TIFFClose(src);
553 return g_unlink(path) == 0;
554 }
555
556 gchar *tmp_path = g_strdup_printf("%s.tmp", path);
557 TIFF *dst = TIFFOpen(tmp_path, "wb");
558 if(IS_NULL_PTR(dst))
559 {
560 if(!IS_NULL_PTR(src)) TIFFClose(src);
561 dt_free(tmp_path);
562 return FALSE;
563 }
564
565 int written_index = 0;
566 gboolean ok = TRUE;
567
568 if(!IS_NULL_PTR(src))
569 {
570 for(int dir = 0; dir < info.count && ok; dir++)
571 {
572 if(!delete_target && !info.found && insert_order >= 0 && written_index == insert_order)
573 {
574 ok = _write_page(dst, NULL, -1, target_name, work_profile, patch, layer_width, layer_height);
575 if(ok && final_order) *final_order = written_index;
576 if(ok) written_index++;
577 }
578
579 const gboolean is_target = info.found && dir == info.index;
580 if(is_target && delete_target) continue;
581
582 const dt_drawlayer_io_patch_t *page_patch = (is_target && !delete_target) ? patch : NULL;
583 const char *page_label = is_target ? target_name : NULL;
584
585 ok = _write_page(dst, src, dir, page_label, is_target ? work_profile : NULL, page_patch, layer_width,
587 if(ok && is_target && !delete_target && final_order) *final_order = written_index;
588 if(ok) written_index++;
589 }
590 }
591
592 if(ok && !delete_target && (IS_NULL_PTR(src) || !info.found))
593 {
594 const gboolean append_at_end = (insert_order < 0 || insert_order >= written_index || !src);
595 if(append_at_end)
596 {
597 ok = _write_page(dst, NULL, -1, target_name, work_profile, patch, layer_width, layer_height);
598 if(ok && !IS_NULL_PTR(final_order)) *final_order = written_index;
599 }
600 }
601
602 TIFFClose(dst);
603 if(!IS_NULL_PTR(src)) TIFFClose(src);
604
605 if(ok)
606 ok = (g_rename(tmp_path, path) == 0);
607 else
608 g_unlink(tmp_path);
609
610 dt_free(tmp_path);
611 return ok;
612}
613
615gboolean dt_drawlayer_io_layer_name_exists(const char *path, const char *candidate, const int ignore_index)
616{
617 if(IS_NULL_PTR(path) || !candidate || candidate[0] == '\0' || !g_file_test(path, G_FILE_TEST_EXISTS)) return FALSE;
618
619 TIFF *tiff = TIFFOpen(path, "rb");
620 if(IS_NULL_PTR(tiff)) return FALSE;
621
622 gboolean exists = FALSE;
623 int index = 0;
624 if(TIFFSetDirectory(tiff, 0))
625 {
626 do
627 {
628 char *page_name = NULL;
629 TIFFGetField(tiff, TIFFTAG_PAGENAME, &page_name);
630 if(index != ignore_index && !g_strcmp0(page_name ? page_name : "", candidate))
631 {
632 exists = TRUE;
633 break;
634 }
635 index++;
636 } while(TIFFReadDirectory(tiff));
637 }
638
639 TIFFClose(tiff);
640 return exists;
641}
642
644gboolean dt_drawlayer_io_sidecar_path(const int32_t imgid, char *path, const size_t path_size)
645{
646 gboolean from_cache = FALSE;
647 if(IS_NULL_PTR(path) || path_size == 0) return FALSE;
648 dt_image_full_path(imgid, path, path_size, &from_cache, __FUNCTION__);
649 if(path[0] == '\0') return FALSE;
650 g_strlcat(path, ".ansel.tiff", path_size);
651 return TRUE;
652}
653
655gboolean dt_drawlayer_io_find_layer(const char *path, const char *target_name, const int target_order,
657{
658 if(!IS_NULL_PTR(info)) memset(info, 0, sizeof(*info));
659 if(IS_NULL_PTR(path) || !g_file_test(path, G_FILE_TEST_EXISTS)) return FALSE;
660
661 TIFF *tiff = TIFFOpen(path, "rb");
662 if(IS_NULL_PTR(tiff)) return FALSE;
663 _scan_directories(tiff, target_name, target_order, info);
664 TIFFClose(tiff);
665 return info && info->found;
666}
667
669gboolean dt_drawlayer_io_load_layer(const char *path, const char *target_name, const int target_order,
670 const int layer_width, const int layer_height, dt_drawlayer_io_patch_t *patch)
671{
672 if(IS_NULL_PTR(patch) || IS_NULL_PTR(patch->pixels) || patch->width <= 0 || patch->height <= 0) return FALSE;
673 dt_drawlayer_cache_clear_transparent_float(patch->pixels, (size_t)patch->width * patch->height);
674
675 if(!g_file_test(path, G_FILE_TEST_EXISTS)) return TRUE;
676
677 TIFF *tiff = TIFFOpen(path, "rb");
678 if(IS_NULL_PTR(tiff)) return FALSE;
679
681 _scan_directories(tiff, target_name, target_order, &info);
682 if(!info.found)
683 {
684 TIFFClose(tiff);
685 return TRUE;
686 }
687
688 if(!TIFFSetDirectory(tiff, (tdir_t)info.index))
689 {
690 TIFFClose(tiff);
691 return FALSE;
692 }
693
694 uint16_t *row = g_malloc_n((gsize)info.width * 4, sizeof(uint16_t));
695 if(IS_NULL_PTR(row))
696 {
697 TIFFClose(tiff);
698 return FALSE;
699 }
700
701 const int offset_x = (layer_width - (int)info.width) / 2;
702 const int offset_y = (layer_height - (int)info.height) / 2;
703
704 for(int py = 0; py < patch->height; py++)
705 {
706 const int layer_y = patch->y + py;
707 const int src_y = layer_y - offset_y;
708 if(src_y < 0 || src_y >= (int)info.height) continue;
709 if(!_read_scanline_rgba(tiff, info.width, (uint32_t)src_y, row))
710 {
711 dt_free(row);
712 TIFFClose(tiff);
713 return FALSE;
714 }
715
716 for(int px = 0; px < patch->width; px++)
717 {
718 const int layer_x = patch->x + px;
719 const int src_x = layer_x - offset_x;
720 if(src_x < 0 || src_x >= (int)info.width) continue;
721 float *dst = patch->pixels + 4 * ((size_t)py * patch->width + px);
722 _load_half_pixel_rgba(row + 4 * src_x, dst);
723 }
724 }
725
726 dt_free(row);
727 TIFFClose(tiff);
728 return TRUE;
729}
730
732gboolean dt_drawlayer_io_load_flat_rgba(const char *path, float **pixels, int *width, int *height)
733{
734 if(pixels) *pixels = NULL;
735 if(width) *width = 0;
736 if(height) *height = 0;
737 if(IS_NULL_PTR(path) || IS_NULL_PTR(pixels) || !width || IS_NULL_PTR(height) || !g_file_test(path, G_FILE_TEST_EXISTS)) return FALSE;
738
739 TIFF *tiff = TIFFOpen(path, "rb");
740 if(IS_NULL_PTR(tiff)) return FALSE;
741
742 uint32_t w = 0, h = 0;
743 uint16_t spp = 0, bpp = 0, sampleformat = SAMPLEFORMAT_UINT;
744 TIFFGetField(tiff, TIFFTAG_IMAGEWIDTH, &w);
745 TIFFGetField(tiff, TIFFTAG_IMAGELENGTH, &h);
746 TIFFGetFieldDefaulted(tiff, TIFFTAG_SAMPLESPERPIXEL, &spp);
747 TIFFGetFieldDefaulted(tiff, TIFFTAG_BITSPERSAMPLE, &bpp);
748 TIFFGetFieldDefaulted(tiff, TIFFTAG_SAMPLEFORMAT, &sampleformat);
749
750 if(w == 0 || h == 0 || spp < 1)
751 {
752 TIFFClose(tiff);
753 return FALSE;
754 }
755
756 float *out = g_malloc_n((size_t)w * h * 4, sizeof(float));
757 if(IS_NULL_PTR(out))
758 {
759 TIFFClose(tiff);
760 return FALSE;
761 }
763
764 const tsize_t scanline = TIFFScanlineSize(tiff);
765 tdata_t row = _TIFFmalloc((tsize_t)scanline);
766 if(IS_NULL_PTR(row))
767 {
768 dt_free(out);
769 TIFFClose(tiff);
770 return FALSE;
771 }
772
773 gboolean ok = TRUE;
774 for(uint32_t y = 0; y < h && ok; y++)
775 {
776 if(TIFFReadScanline(tiff, row, y, 0) == -1)
777 {
778 ok = FALSE;
779 break;
780 }
781
782 for(uint32_t x = 0; x < w; x++)
783 {
784 float r = 0.0f, g = 0.0f, b = 0.0f, a = 1.0f;
785
786 if(bpp == 32 && sampleformat == SAMPLEFORMAT_IEEEFP)
787 {
788 const float *src = (const float *)row + (size_t)x * spp;
789 if(spp >= 3)
790 {
791 r = src[0];
792 g = src[1];
793 b = src[2];
794 }
795 else
796 {
797 r = g = b = src[0];
798 }
799 if(spp >= 4) a = src[3];
800 }
801 else if(bpp == 16 && sampleformat == SAMPLEFORMAT_IEEEFP)
802 {
803 const uint16_t *src = (const uint16_t *)row + (size_t)x * spp;
804 if(spp >= 3)
805 {
806 r = _half_to_float(src[0]);
807 g = _half_to_float(src[1]);
808 b = _half_to_float(src[2]);
809 }
810 else
811 {
812 r = g = b = _half_to_float(src[0]);
813 }
814 if(spp >= 4) a = _half_to_float(src[3]);
815 }
816 else if(bpp == 16)
817 {
818 const uint16_t *src = (const uint16_t *)row + (size_t)x * spp;
819 if(spp >= 3)
820 {
821 r = src[0] / 65535.0f;
822 g = src[1] / 65535.0f;
823 b = src[2] / 65535.0f;
824 }
825 else
826 {
827 r = g = b = src[0] / 65535.0f;
828 }
829 if(spp >= 4) a = src[3] / 65535.0f;
830 }
831 else if(bpp == 8)
832 {
833 const uint8_t *src = (const uint8_t *)row + (size_t)x * spp;
834 if(spp >= 3)
835 {
836 r = src[0] / 255.0f;
837 g = src[1] / 255.0f;
838 b = src[2] / 255.0f;
839 }
840 else
841 {
842 r = g = b = src[0] / 255.0f;
843 }
844 if(spp >= 4) a = src[3] / 255.0f;
845 }
846 else
847 {
848 ok = FALSE;
849 break;
850 }
851
852 float *dst = out + 4 * ((size_t)y * w + x);
853 dst[0] = r;
854 dst[1] = g;
855 dst[2] = b;
856 dst[3] = _clamp01(a);
857 }
858 }
859
860 _TIFFfree(row);
861 TIFFClose(tiff);
862 if(!ok)
863 {
864 dt_free(out);
865 return FALSE;
866 }
867
868 *pixels = out;
869 *width = (int)w;
870 *height = (int)h;
871 return TRUE;
872}
873
875gboolean dt_drawlayer_io_store_layer(const char *path, const char *target_name, const int target_order,
876 const char *work_profile, const dt_drawlayer_io_patch_t *patch,
877 const int layer_width, const int layer_height, const gboolean delete_target,
878 int *final_order)
879{
880 if(IS_NULL_PTR(target_name) || target_name[0] == '\0') return FALSE;
881 return _rewrite_sidecar(path, target_name, target_order, work_profile, patch, layer_width, layer_height,
882 delete_target, -1, final_order);
883}
884
886gboolean dt_drawlayer_io_insert_layer(const char *path, const char *target_name, const int insert_after_order,
887 const char *work_profile, const dt_drawlayer_io_patch_t *patch,
888 const int layer_width, const int layer_height, int *final_order)
889{
890 if(IS_NULL_PTR(target_name) || target_name[0] == '\0') return FALSE;
891 const int insert_order = (insert_after_order >= 0) ? (insert_after_order + 1) : -1;
892 return _rewrite_sidecar(path, target_name, -1, work_profile, patch, layer_width, layer_height, FALSE, insert_order,
893 final_order);
894}
895
897gboolean dt_drawlayer_io_rename_layer(const char *path, const char *current_name, const char *new_name,
898 const char *work_profile, const int layer_width, const int layer_height,
900{
901 if(!IS_NULL_PTR(info)) memset(info, 0, sizeof(*info));
902 if(IS_NULL_PTR(path) || IS_NULL_PTR(current_name) || current_name[0] == '\0' || IS_NULL_PTR(new_name) || new_name[0] == '\0') return FALSE;
903 if(!g_file_test(path, G_FILE_TEST_EXISTS)) return FALSE;
904
905 dt_drawlayer_io_layer_info_t current = { 0 };
906 if(!dt_drawlayer_io_find_layer(path, current_name, -1, &current)) return FALSE;
907 if(dt_drawlayer_io_layer_name_exists(path, new_name, current.index)) return FALSE;
908
909 int final_order = current.index;
910 if(!dt_drawlayer_io_store_layer(path, new_name, current.index, work_profile, NULL, layer_width, layer_height, FALSE,
911 &final_order))
912 return FALSE;
913
914 if(!IS_NULL_PTR(info))
915 {
916 *info = current;
917 info->found = TRUE;
918 info->index = final_order;
919 g_strlcpy(info->name, new_name, sizeof(info->name));
920 if(!IS_NULL_PTR(work_profile) && work_profile[0] != '\0')
921 g_strlcpy(info->work_profile, work_profile, sizeof(info->work_profile));
922 }
923 return TRUE;
924}
925
927gboolean dt_drawlayer_io_delete_layer(const char *path, const char *target_name, const int layer_width,
928 const int layer_height)
929{
930 if(IS_NULL_PTR(path) || IS_NULL_PTR(target_name) || target_name[0] == '\0') return FALSE;
931 if(!g_file_test(path, G_FILE_TEST_EXISTS)) return FALSE;
932
933 dt_drawlayer_io_layer_info_t info = { 0 };
934 if(!dt_drawlayer_io_find_layer(path, target_name, -1, &info)) return FALSE;
935
936 return dt_drawlayer_io_store_layer(path, target_name, info.index, NULL, NULL, layer_width, layer_height, TRUE,
937 NULL);
938}
939
941void dt_drawlayer_io_make_unique_name_plain(const char *path, const char *requested, char *name, const size_t name_size)
942{
943 if(IS_NULL_PTR(name) || name_size == 0) return;
944 name[0] = '\0';
945 if(!IS_NULL_PTR(requested)) g_strlcpy(name, requested, name_size);
946 g_strstrip(name);
947 if(name[0] == '\0') return;
948 if(!dt_drawlayer_io_layer_name_exists(path, name, -1)) return;
949
950 char base[DT_DRAWLAYER_IO_NAME_SIZE] = { 0 };
951 g_strlcpy(base, name, sizeof(base));
952
953 for(int suffix = 2; suffix < 100000; suffix++)
954 {
955 g_snprintf(name, name_size, "%.*s %d", MAX((int)name_size - 12, 1), base, suffix);
956 if(!dt_drawlayer_io_layer_name_exists(path, name, -1)) return;
957 }
958}
959
961gboolean dt_drawlayer_io_list_layer_names(const char *path, char ***names, int *count)
962{
963 if(names) *names = NULL;
964 if(!IS_NULL_PTR(count)) *count = 0;
965 if(IS_NULL_PTR(path) || IS_NULL_PTR(names) || !count || !g_file_test(path, G_FILE_TEST_EXISTS)) return FALSE;
966
967 TIFF *tiff = TIFFOpen(path, "rb");
968 if(IS_NULL_PTR(tiff)) return FALSE;
969
970 GPtrArray *arr = g_ptr_array_new_with_free_func(g_free);
971 if(TIFFSetDirectory(tiff, 0))
972 {
973 do
974 {
975 char *page_name = NULL;
976 TIFFGetField(tiff, TIFFTAG_PAGENAME, &page_name);
977 g_ptr_array_add(arr, g_strdup(page_name ? page_name : ""));
978 } while(TIFFReadDirectory(tiff));
979 }
980 TIFFClose(tiff);
981
982 *count = (int)arr->len;
983 if(arr->len == 0)
984 {
985 g_ptr_array_free(arr, TRUE);
986 *names = NULL;
987 return TRUE;
988 }
989
990 const guint layer_count = arr->len;
991 char **out = (char **)g_ptr_array_free(arr, FALSE);
992 out = g_renew(char *, out, layer_count + 1);
993 out[layer_count] = NULL;
994 *names = out;
995 return TRUE;
996}
997
1000{
1001 if(IS_NULL_PTR(names) || !*names) return;
1002 const int n = (!IS_NULL_PTR(count) && *count > 0) ? *count : 0;
1003 if(n > 0)
1004 {
1005 for(int i = 0; i < n; i++) dt_free((*names)[i]);
1006 }
1007 else
1008 {
1009 for(int i = 0; (*names)[i]; i++) dt_free((*names)[i]);
1010 }
1011 dt_free(*names);
1012 if(!IS_NULL_PTR(count)) *count = 0;
1013}
1014
1016{
1019 if(IS_NULL_PTR(params)) return 0;
1020
1022 result->imgid = params->imgid;
1023 g_strlcpy(result->initiator_layer_name, params->initiator_layer_name, sizeof(result->initiator_layer_name));
1024 result->initiator_layer_order = params->initiator_layer_order;
1025 g_strlcpy(result->message, _("failed to create background layer from input"), sizeof(result->message));
1026
1027 gchar *tmp_path = NULL;
1028 const int tmp_fd = g_file_open_tmp("ansel-drawlayer-bg-XXXXXX.tiff", &tmp_path, NULL);
1029 if(tmp_fd >= 0) g_close(tmp_fd, NULL);
1030
1031 float *export_pixels = NULL;
1032 int export_w = 0;
1033 int export_h = 0;
1034 dt_drawlayer_io_patch_t bg_patch = { 0 };
1035
1036 do
1037 {
1038 if(tmp_fd < 0 || IS_NULL_PTR(tmp_path)) break;
1039 if(!_export_pre_module_fullres_to_tiff(params->imgid, params->filter, tmp_path)) break;
1040 if(!dt_drawlayer_io_load_flat_rgba(tmp_path, &export_pixels, &export_w, &export_h)) break;
1041 if(IS_NULL_PTR(export_pixels) || export_w <= 0 || export_h <= 0) break;
1042
1043 bg_patch.width = params->layer_width;
1044 bg_patch.height = params->layer_height;
1045 bg_patch.x = 0;
1046 bg_patch.y = 0;
1048 (size_t)params->layer_width * params->layer_height * 4 * sizeof(float), "drawlayer bg layer");
1049 if(IS_NULL_PTR(bg_patch.pixels)) break;
1050 dt_drawlayer_cache_clear_transparent_float(bg_patch.pixels, (size_t)params->layer_width * params->layer_height);
1051
1052 const int clip_x0 = MAX(params->dst_x, 0);
1053 const int clip_y0 = MAX(params->dst_y, 0);
1054 const int clip_x1 = MIN(params->dst_x + export_w, params->layer_width);
1055 const int clip_y1 = MIN(params->dst_y + export_h, params->layer_height);
1056 if(clip_x1 <= clip_x0 || clip_y1 <= clip_y0) break;
1057
1058 const int copy_w = clip_x1 - clip_x0;
1059 const int copy_h = clip_y1 - clip_y0;
1060 const int src_x0 = clip_x0 - params->dst_x;
1061 const int src_y0 = clip_y0 - params->dst_y;
1062 for(int y = 0; y < copy_h; y++)
1063 {
1064 const float *src = export_pixels + 4 * ((size_t)(src_y0 + y) * export_w + src_x0);
1065 float *dst = bg_patch.pixels + 4 * ((size_t)(clip_y0 + y) * params->layer_width + clip_x0);
1066 memcpy(dst, src, (size_t)copy_w * 4 * sizeof(float));
1067 }
1068
1069 char bg_name[DT_DRAWLAYER_IO_NAME_SIZE] = { 0 };
1070 dt_drawlayer_io_make_unique_name_plain(params->sidecar_path, params->requested_bg_name, bg_name,
1071 sizeof(bg_name));
1072 if(!dt_drawlayer_layer_name_non_empty(bg_name)) break;
1073
1074 int final_order = -1;
1075 if(!dt_drawlayer_io_insert_layer(params->sidecar_path, bg_name, params->insert_after_order,
1076 params->work_profile, &bg_patch, params->layer_width, params->layer_height,
1077 &final_order))
1078 break;
1079
1080 dt_drawlayer_io_layer_info_t io_info = { 0 };
1081 if(!dt_drawlayer_io_find_layer(params->sidecar_path, bg_name, final_order, &io_info)) break;
1082
1083 result->success = TRUE;
1085 g_strlcpy(result->created_bg_name, bg_name, sizeof(result->created_bg_name));
1086 g_snprintf(result->message, sizeof(result->message), _("created background layer `%s'"), bg_name);
1087 } while(0);
1088
1089 if(!IS_NULL_PTR(export_pixels)) dt_free(export_pixels);
1090 if(bg_patch.pixels) dt_drawlayer_cache_free_temp_buffer((void **)&bg_patch.pixels, "drawlayer bg layer");
1091 if(tmp_path)
1092 {
1093 g_unlink(tmp_path);
1094 dt_free(tmp_path);
1095 }
1096
1097 if(params->done_idle)
1098 g_main_context_invoke(NULL, params->done_idle, result);
1099 else
1100 dt_free(result);
1101 return 0;
1102}
#define TRUE
Definition ashift_lsd.c:162
#define FALSE
Definition ashift_lsd.c:158
static const float x
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.
const dt_colormatrix_t dt_aligned_pixel_t out
static const int row
void dt_image_full_path(const int32_t imgid, char *pathname, size_t pathname_len, gboolean *from_cache, const char *calling_func)
Get the full path of an image out of the database.
GHashTable * names
GtkWidget* -> the name the application knows it by.
int bpp
dt_imageio_retval_t dt_imageio_export_with_flags(const int32_t imgid, const char *filename, dt_imageio_module_format_t *format, dt_imageio_module_data_t *format_params, const gboolean ignore_exif, const gboolean display_byteorder, const gboolean high_quality, const double scale_factor, const gboolean thumbnail_export, const gboolean ignore_history, const char *filter, const gboolean copy_metadata, const gboolean export_masks, dt_colorspaces_color_profile_type_t icc_type, const gchar *icc_filename, dt_iop_color_intent_t icc_intent, dt_imageio_module_storage_t *storage, dt_imageio_module_data_t *storage_params, int num, int total, dt_export_metadata_t *metadata, dt_atomic_int *shutdown)
dt_imageio_module_format_t * dt_imageio_get_format_by_name(const char *name)
int32_t dt_drawlayer_io_background_layer_job_run(dt_job_t *job)
Worker entrypoint for async "create background from input" sidecar jobs.
Definition io.c:1015
gboolean dt_drawlayer_io_layer_name_exists(const char *path, const char *candidate, const int ignore_index)
Test if a layer name already exists in sidecar TIFF.
Definition io.c:615
gboolean dt_drawlayer_io_find_layer(const char *path, const char *target_name, const int target_order, dt_drawlayer_io_layer_info_t *info)
Find target layer metadata in sidecar TIFF.
Definition io.c:655
void dt_drawlayer_io_make_unique_name_plain(const char *path, const char *requested, char *name, const size_t name_size)
Create unique layer name without fallback source.
Definition io.c:941
static gboolean _write_scanline_rgba(TIFF *tiff, const uint32_t row, const uint16_t *in)
Write one half-float RGBA scanline into TIFF page.
Definition io.c:305
static float _clamp01(const float value)
Clamp scalar to [0,1].
Definition io.c:43
gboolean dt_drawlayer_io_insert_layer(const char *path, const char *target_name, const int insert_after_order, const char *work_profile, const dt_drawlayer_io_patch_t *patch, const int layer_width, const int layer_height, int *final_order)
Insert a new layer after given order in sidecar TIFF.
Definition io.c:886
gboolean dt_drawlayer_io_sidecar_path(const int32_t imgid, char *path, const size_t path_size)
Build absolute sidecar path from image id.
Definition io.c:644
static gboolean _rewrite_sidecar(const char *path, const char *target_name, const int target_order, const char *work_profile, const dt_drawlayer_io_patch_t *patch, const int layer_width, const int layer_height, const gboolean delete_target, const int insert_order, int *final_order)
Rewrite sidecar with optional update/insert/delete of one target layer.
Definition io.c:525
static void _load_half_pixel_rgba(const uint16_t *src, float rgba[4])
Load one half-float RGBA texel to float RGBA.
Definition io.c:135
static int64_t dt_drawlayer_sidecar_timestamp_from_path(const char *path)
Definition io.c:161
static float _half_to_float(const uint16_t h)
Convert IEEE-754 binary16 to float32.
Definition io.c:55
static gboolean _set_directory_tags(TIFF *tiff, const uint32_t width, const uint32_t height, const char *name, const char *work_profile)
Write TIFF directory tags for one RGBA half-float page.
Definition io.c:243
static gboolean dt_drawlayer_layer_name_non_empty(const char *name)
Definition io.c:152
static gboolean _icc_blob_from_profile_key(const char *work_profile, uint8_t **icc_data, uint32_t *icc_len)
Resolve embedded ICC blob from serialized work-profile key.
Definition io.c:204
static void _clear_transparent_half(uint16_t *pixels, const size_t pixel_count)
Clear uint16 RGBA row/buffer to transparent black.
Definition io.c:128
gboolean dt_drawlayer_io_rename_layer(const char *path, const char *current_name, const char *new_name, const char *work_profile, const int layer_width, const int layer_height, dt_drawlayer_io_layer_info_t *info)
Rename one existing layer page while preserving its directory payload.
Definition io.c:897
gboolean dt_drawlayer_io_list_layer_names(const char *path, char ***names, int *count)
List all TIFF layer page names.
Definition io.c:961
gboolean dt_drawlayer_io_load_layer(const char *path, const char *target_name, const int target_order, const int layer_width, const int layer_height, dt_drawlayer_io_patch_t *patch)
Load one sidecar layer patch into float RGBA destination patch.
Definition io.c:669
static void _overlay_patch_row_rgba(uint16_t *dst_row, const uint32_t width, const int offset_x, const int raw_y, const dt_drawlayer_io_patch_t *patch)
Overlay one clipped float patch span into a half-float TIFF row.
Definition io.c:311
gboolean dt_drawlayer_io_delete_layer(const char *path, const char *target_name, const int layer_width, const int layer_height)
Delete one existing layer page from the sidecar TIFF.
Definition io.c:927
gboolean dt_drawlayer_io_load_flat_rgba(const char *path, float **pixels, int *width, int *height)
Load first TIFF page as flat RGBA float image.
Definition io.c:732
static uint16_t _float_to_half(float value)
Convert float32 to IEEE-754 binary16 with rounding.
Definition io.c:78
static void _scan_directories(TIFF *tiff, const char *target_name, const int target_order, dt_drawlayer_io_layer_info_t *info)
Scan TIFF directories and resolve best match for name/order target.
Definition io.c:333
gboolean dt_drawlayer_io_store_layer(const char *path, const char *target_name, const int target_order, const char *work_profile, const dt_drawlayer_io_patch_t *patch, const int layer_width, const int layer_height, const gboolean delete_target, int *final_order)
Store/update/delete one layer entry in sidecar TIFF.
Definition io.c:875
static gboolean _export_pre_module_fullres_to_tiff(const int32_t imgid, const char *filter, const char *path)
Definition io.c:170
static gboolean _read_scanline_rgba(TIFF *tiff, const uint32_t width, const uint32_t row, uint16_t *out)
Read one TIFF scanline into half-float RGBA buffer.
Definition io.c:269
static gboolean _write_page(TIFF *dst, TIFF *src, const int src_dir, const char *name, const char *work_profile, const dt_drawlayer_io_patch_t *patch, const int layer_width, const int layer_height)
Write one output page, optionally copying from source and patch overlay.
Definition io.c:439
void dt_drawlayer_io_free_layer_names(char ***names, int *count)
Free array returned by dt_drawlayer_io_list_layer_names.
Definition io.c:999
TIFF sidecar I/O API for drawlayer layers.
#define DT_DRAWLAYER_IO_NAME_SIZE
Definition io.h:32
Patch/cache helpers for drawlayer process and preview buffers.
void * dt_control_job_get_params(const _dt_job_t *job)
Definition jobs.c:132
static const dt_iop_drawlayer_params_t * params
Definition layers.h:80
static const dt_dev_pixelpipe_t const dt_dev_pixelpipe_iop_t int int * layer_height
Definition layers.h:51
static const dt_dev_pixelpipe_t const dt_dev_pixelpipe_iop_t int * layer_width
Definition layers.h:50
_lib_location_type_t type
Definition location.c:1
#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
uint32_t width
Definition mipmap_cache.c:0
uint32_t height
Definition mipmap_cache.c:1
#define __OMP_FOR_SIMD__(...)
Definition openmp.h:97
const char * name
Definition pdf.h:90
@ DT_INTENT_PERCEPTUAL
dt_colorspaces_color_profile_type_t
@ DT_COLORSPACE_NONE
No profile / "take it from the image settings". Never matches a list entry.
@ DT_PROFILE_ROLE_ANY
All four roles, in registration order.
static const dt_aligned_pixel_simd_t sign
Definition simd.h:118
static const dt_aligned_pixel_simd_t value
Definition simd.h:144
static const dt_aligned_pixel_simd_t exponent
Definition simd.h:127
void * dt_drawlayer_cache_alloc_temp_buffer(const size_t bytes, const char *name)
Allocate temporary aligned cache buffer.
void dt_drawlayer_cache_free_temp_buffer(void **buffer, const char *name)
Free temporary aligned cache buffer.
void dt_drawlayer_cache_clear_transparent_float(float *pixels, const size_t pixel_count)
Fill float RGBA buffer with transparent black.
const float r
dt_imageio_module_data_t global
Definition io.c:145
One registered profile: its identity, its LCMS handle, and where it sits in each combo box.
cmsHPROFILE profile
the actual profile; NULL for the three category entries
Parameters owned by the async "create background from input" job.
Definition io.h:61
Result posted back to the UI after background-layer creation.
Definition io.h:79
Metadata returned when probing one layer directory in sidecar TIFF.
Definition io.h:47
char work_profile[256]
Definition io.h:54
Float RGBA patch used by drawlayer I/O routines.
Definition io.h:37
#define MIN(a, b)
Definition thinplate.c:32
#define MAX(a, b)
Definition thinplate.c:29