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