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 "system/macros.h"
20#include "system/mem_alloc.h"
21#include "system/openmp.h"
22#include "iop/drawlayer/io.h"
23
25#include "common/image.h"
28#include "control/jobs.h"
29#include "iop/drawlayer/cache.h"
30
31#include <glib/gstdio.h>
32#include <math.h>
33#include <stdlib.h>
34#include <string.h>
35#include <tiffio.h>
36
42static inline float _clamp01(const float value)
43{
44 return fminf(fmaxf(value, 0.0f), 1.0f);
45}
46
48{
49 uint32_t u;
50 float f;
52
54static inline float _half_to_float(const uint16_t h)
55{
56 static const dt_drawlayer_io_fp32_t magic = { 113u << 23 };
57 static const uint32_t shifted_exp = 0x7c00u << 13;
59
60 out.u = (h & 0x7fffu) << 13;
61 const uint32_t exp = shifted_exp & out.u;
62 out.u += (127u - 15u) << 23;
63
64 if(exp == shifted_exp)
65 out.u += (128u - 16u) << 23;
66 else if(exp == 0)
67 {
68 out.u += 1u << 23;
69 out.f -= magic.f;
70 }
71
72 out.u |= (h & 0x8000u) << 16;
73 return out.f;
74}
75
77static inline uint16_t _float_to_half(float value)
78{
79 dt_drawlayer_io_fp32_t in = { .f = value };
80 const uint32_t sign = (in.u >> 16) & 0x8000u;
81 const uint32_t exponent = (in.u >> 23) & 0xffu;
82 uint32_t mantissa = in.u & 0x007fffffu;
83
84 if(exponent == 0xffu)
85 {
86 if(mantissa) return (uint16_t)(sign | 0x7e00u);
87 return (uint16_t)(sign | 0x7c00u);
88 }
89
90 const int32_t half_exponent = (int32_t)exponent - 127 + 15;
91
92 if(half_exponent >= 0x1f) return (uint16_t)(sign | 0x7c00u);
93 if(half_exponent <= 0)
94 {
95 if(half_exponent < -10) return (uint16_t)sign;
96
97 mantissa |= 0x00800000u;
98 const uint32_t shift = (uint32_t)(1 - half_exponent);
99 uint32_t half_mantissa = mantissa >> (shift + 13);
100 const uint32_t round_bit = 1u << (shift + 12);
101
102 if((mantissa & round_bit) && ((mantissa & (round_bit - 1u)) || (half_mantissa & 1u)))
104
105 return (uint16_t)(sign | half_mantissa);
106 }
107
108 uint32_t half_exp = (uint32_t)half_exponent << 10;
109 uint32_t half_mantissa = mantissa >> 13;
110
111 if((mantissa & 0x00001000u) && ((mantissa & 0x00001fffu) || (half_mantissa & 1u)))
112 {
114
115 if(half_mantissa == 0x0400u)
116 {
117 half_mantissa = 0;
118 half_exp += 0x0400u;
119 if(half_exp >= 0x7c00u) return (uint16_t)(sign | 0x7c00u);
120 }
121 }
122
123 return (uint16_t)(sign | half_exp | half_mantissa);
124}
125
127static inline void _clear_transparent_half(uint16_t *pixels, const size_t pixel_count)
128{
129 if(IS_NULL_PTR(pixels)) return;
130 memset(pixels, 0, pixel_count * 4 * sizeof(uint16_t));
131}
132
134static inline void _load_half_pixel_rgba(const uint16_t *src, float rgba[4])
135{
136 rgba[0] = _half_to_float(src[0]);
137 rgba[1] = _half_to_float(src[1]);
138 rgba[2] = _half_to_float(src[2]);
139 rgba[3] = _half_to_float(src[3]);
140}
141
150
151static gboolean dt_drawlayer_layer_name_non_empty(const char *name)
152{
153 if(IS_NULL_PTR(name)) return FALSE;
154 char tmp[DT_DRAWLAYER_IO_NAME_SIZE] = { 0 };
155 g_strlcpy(tmp, name, sizeof(tmp));
157 return tmp[0] != '\0';
158}
159
160static int64_t dt_drawlayer_sidecar_timestamp_from_path(const char *path)
161{
162 if(IS_NULL_PTR(path) || path[0] == '\0' || !g_file_test(path, G_FILE_TEST_EXISTS)) return 0;
163
164 GStatBuf st = { 0 };
165 if(g_stat(path, &st) != 0) return 0;
166 return (int64_t)st.st_mtime;
167}
168
169static gboolean _export_pre_module_fullres_to_tiff(const int32_t imgid, const char *filter, const char *path)
170{
171 if(imgid <= 0 || IS_NULL_PTR(path) || path[0] == '\0') return FALSE;
172
174 if(IS_NULL_PTR(format)) return FALSE;
175
176 dt_imageio_module_data_t *format_params = format->get_params(format);
177 if(IS_NULL_PTR(format_params)) return FALSE;
178
179 format_params->max_width = 0;
180 format_params->max_height = 0;
181 format_params->width = 0;
182 format_params->height = 0;
183 format_params->style[0] = '\0';
184
185 if(format->params_size(format) >= sizeof(drawlayer_tiff_export_params_t))
186 {
188 tiff_params->bpp = 32;
189 tiff_params->compress = 0;
190 tiff_params->compresslevel = 0;
191 tiff_params->shortfile = 0;
192 }
193
195 imgid, path, format, format_params, TRUE, FALSE, TRUE, FALSE, FALSE, (filter && filter[0]) ? filter : NULL,
197
198 format->free_params(format, format_params);
199 return rc == 0;
200}
201
203static gboolean _icc_blob_from_profile_key(const char *work_profile, uint8_t **icc_data, uint32_t *icc_len)
204{
205 if(icc_data) *icc_data = NULL;
206 if(!IS_NULL_PTR(icc_len)) *icc_len = 0;
207 if(IS_NULL_PTR(work_profile) || work_profile[0] == '\0' || IS_NULL_PTR(icc_data) || !icc_len) return FALSE;
208
209 const char *sep0 = strchr(work_profile, '|');
210 if(IS_NULL_PTR(sep0)) return FALSE;
211 const char *sep1 = strchr(sep0 + 1, '|');
212 if(IS_NULL_PTR(sep1)) return FALSE;
213
214 char *endptr = NULL;
215 const long type_long = strtol(work_profile, &endptr, 10);
216 if(endptr != sep0) return FALSE;
217
219 const char *filename = sep1 + 1;
221 if(IS_NULL_PTR(profile) || IS_NULL_PTR(profile->profile)) return FALSE;
222
223 cmsUInt32Number len = 0;
224 cmsSaveProfileToMem(profile->profile, NULL, &len);
225 if(len == 0) return FALSE;
226
227 uint8_t *buf = g_malloc(len);
228 if(IS_NULL_PTR(buf)) return FALSE;
229
230 if(!cmsSaveProfileToMem(profile->profile, buf, &len) || len == 0)
231 {
232 dt_free(buf);
233 return FALSE;
234 }
235
236 *icc_data = buf;
237 *icc_len = (uint32_t)len;
238 return TRUE;
239}
240
242static gboolean _set_directory_tags(TIFF *tiff, const uint32_t width, const uint32_t height, const char *name,
243 const char *work_profile)
244{
245 const uint16_t extrasamples[] = { EXTRASAMPLE_ASSOCALPHA };
246
247 if(!TIFFSetField(tiff, TIFFTAG_IMAGEWIDTH, width)) return FALSE;
248 if(!TIFFSetField(tiff, TIFFTAG_IMAGELENGTH, height)) return FALSE;
249 if(!TIFFSetField(tiff, TIFFTAG_BITSPERSAMPLE, 16)) return FALSE;
251 if(!TIFFSetField(tiff, TIFFTAG_SAMPLESPERPIXEL, 4)) return FALSE;
255
260
263 if(!IS_NULL_PTR(work_profile) && work_profile[0]) TIFFSetField(tiff, TIFFTAG_IMAGEDESCRIPTION, work_profile);
264 return TRUE;
265}
266
268static gboolean _read_scanline_rgba(TIFF *tiff, const uint32_t width, const uint32_t row, uint16_t *out)
269{
270 uint16_t bpp = 0;
271 uint16_t spp = 0;
272 uint16_t sampleformat = SAMPLEFORMAT_UINT;
273
276 TIFFGetFieldDefaulted(tiff, TIFFTAG_SAMPLEFORMAT, &sampleformat);
277
278 if(spp != 4) return FALSE;
279
280 const tsize_t scanline = TIFFScanlineSize(tiff);
281 tdata_t buffer = _TIFFmalloc((tsize_t)scanline);
282 if(IS_NULL_PTR(buffer)) return FALSE;
283
284 const int ok = TIFFReadScanline(tiff, buffer, row, 0);
285 if(ok == -1)
286 {
287 _TIFFfree(buffer);
288 return FALSE;
289 }
290
291 if(bpp == 16 && sampleformat == SAMPLEFORMAT_IEEEFP)
292 memcpy(out, buffer, (size_t)width * 4 * sizeof(uint16_t));
293 else
294 {
295 _TIFFfree(buffer);
296 return FALSE;
297 }
298
299 _TIFFfree(buffer);
300 return TRUE;
301}
302
304static gboolean _write_scanline_rgba(TIFF *tiff, const uint32_t row, const uint16_t *in)
305{
306 return TIFFWriteScanline(tiff, (tdata_t)in, row, 0) != -1;
307}
308
310static void _overlay_patch_row_rgba(uint16_t *dst_row, const uint32_t width, const int offset_x,
311 const int raw_y, const dt_drawlayer_io_patch_t *patch)
312{
313 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;
314
315 const int dst_x0 = MAX(0, patch->x - offset_x);
316 const int dst_x1 = MIN((int)width, patch->x + patch->width - offset_x);
317 if(dst_x0 >= dst_x1) return;
318
319 const float *patch_row = patch->pixels + 4 * (size_t)(raw_y - patch->y) * patch->width;
321 for(int dst_x = dst_x0; dst_x < dst_x1; dst_x++)
322 {
323 const float *src_pixel = patch_row + 4 * (size_t)(dst_x + offset_x - patch->x);
324 dst_row[4 * dst_x + 0] = _float_to_half(src_pixel[0]);
325 dst_row[4 * dst_x + 1] = _float_to_half(src_pixel[1]);
326 dst_row[4 * dst_x + 2] = _float_to_half(src_pixel[2]);
327 dst_row[4 * dst_x + 3] = _float_to_half(src_pixel[3]);
328 }
329}
330
332static void _scan_directories(TIFF *tiff, const char *target_name, const int target_order,
334{
335 memset(info, 0, sizeof(*info));
336 info->index = -1;
337
338 if(IS_NULL_PTR(tiff)) return;
339 if(!TIFFSetDirectory(tiff, 0)) return;
340
341 const gboolean has_target_name = (target_name && target_name[0] != '\0');
342 const gboolean has_target_order = (target_order >= 0);
343 gboolean exact_found = FALSE;
344 gboolean named_found = FALSE;
345 gboolean order_found = FALSE;
346 dt_drawlayer_io_layer_info_t exact = { 0 };
349 exact.index = -1;
350 named.index = -1;
351 ordered.index = -1;
352
353 int index = 0;
354 do
355 {
356 char *page_name = NULL;
357 char *page_profile = NULL;
358 uint32_t width = 0;
359 uint32_t height = 0;
364
365 const gboolean order_match = (has_target_order && index == target_order);
366 const gboolean name_match = (has_target_name && !g_strcmp0(page_name ? page_name : "", target_name));
367 const gboolean exact_match = (name_match && order_match);
368
370 {
372 exact.found = TRUE;
373 exact.index = index;
374 exact.width = width;
375 exact.height = height;
376 g_strlcpy(exact.name, page_name ? page_name : "", sizeof(exact.name));
377 g_strlcpy(exact.work_profile, page_profile ? page_profile : "", sizeof(exact.work_profile));
378 }
379
380 if(name_match && !named_found)
381 {
383 named.found = TRUE;
384 named.index = index;
385 named.width = width;
386 named.height = height;
387 g_strlcpy(named.name, page_name ? page_name : "", sizeof(named.name));
388 g_strlcpy(named.work_profile, page_profile ? page_profile : "", sizeof(named.work_profile));
389 }
390
392 {
394 ordered.found = TRUE;
395 ordered.index = index;
396 ordered.width = width;
397 ordered.height = height;
398 g_strlcpy(ordered.name, page_name ? page_name : "", sizeof(ordered.name));
399 g_strlcpy(ordered.work_profile, page_profile ? page_profile : "", sizeof(ordered.work_profile));
400 }
401
402 index++;
403 } while(TIFFReadDirectory(tiff));
404
405 if(exact_found)
406 {
407 info->found = TRUE;
408 info->index = exact.index;
409 info->width = exact.width;
410 info->height = exact.height;
411 g_strlcpy(info->name, exact.name, sizeof(info->name));
412 g_strlcpy(info->work_profile, exact.work_profile, sizeof(info->work_profile));
413 }
414 else if(named_found)
415 {
416 info->found = TRUE;
417 info->index = named.index;
418 info->width = named.width;
419 info->height = named.height;
420 g_strlcpy(info->name, named.name, sizeof(info->name));
421 g_strlcpy(info->work_profile, named.work_profile, sizeof(info->work_profile));
422 }
423 else if(order_found)
424 {
425 info->found = TRUE;
426 info->index = ordered.index;
427 info->width = ordered.width;
428 info->height = ordered.height;
429 g_strlcpy(info->name, ordered.name, sizeof(info->name));
430 g_strlcpy(info->work_profile, ordered.work_profile, sizeof(info->work_profile));
431 }
432
433 info->count = index;
434 TIFFSetDirectory(tiff, 0);
435}
436
438static gboolean _write_page(TIFF *dst, TIFF *src, const int src_dir, const char *name, const char *work_profile,
439 const dt_drawlayer_io_patch_t *patch, const int layer_width, const int layer_height)
440{
441 uint32_t width = patch ? (uint32_t)patch->width : (uint32_t)layer_width;
442 uint32_t height = patch ? (uint32_t)patch->height : (uint32_t)layer_height;
443 const char *page_profile = work_profile;
444 uint8_t *icc_profile = NULL;
445 uint32_t icc_profile_len = 0;
446
447 if(!IS_NULL_PTR(src))
448 {
449 if(!TIFFSetDirectory(src, (tdir_t)src_dir)) return FALSE;
450 if(IS_NULL_PTR(patch))
451 {
454 }
455 if(IS_NULL_PTR(name))
456 {
457 char *src_name = NULL;
459 name = src_name;
460 }
462 {
463 char *src_profile = NULL;
466 }
467 }
468
472
473 uint16_t *src_row = g_malloc_n((gsize)width * 4, sizeof(uint16_t));
474 uint16_t *dst_row = g_malloc_n((gsize)width * 4, sizeof(uint16_t));
476 {
480 return FALSE;
481 }
482
483 const int offset_x = (layer_width - (int)width) / 2;
484 const int offset_y = (layer_height - (int)height) / 2;
485
486 for(uint32_t y = 0; y < height; y++)
487 {
488 if(!IS_NULL_PTR(src))
489 {
490 if(!_read_scanline_rgba(src, width, y, src_row))
491 {
495 return FALSE;
496 }
497 memcpy(dst_row, src_row, (size_t)width * 4 * sizeof(uint16_t));
498 }
499 else
501
502 if(!IS_NULL_PTR(patch))
503 {
504 const int layer_y = (int)y + offset_y;
505 _overlay_patch_row_rgba(dst_row, width, offset_x, layer_y, patch);
506 }
507
509 {
513 return FALSE;
514 }
515 }
516
520 return TIFFWriteDirectory(dst) != 0;
521}
522
524static gboolean _rewrite_sidecar(const char *path, const char *target_name, const int target_order,
525 const char *work_profile, const dt_drawlayer_io_patch_t *patch, const int layer_width,
526 const int layer_height, const gboolean delete_target, const int insert_order,
527 int *final_order)
528{
530
531 TIFF *src = NULL;
533 memset(&info, 0, sizeof(info));
534 info.index = -1;
535
537 {
538 src = TIFFOpen(path, "rb");
539 if(IS_NULL_PTR(src)) return FALSE;
540 _scan_directories(src, target_name, target_order, &info);
541 }
542
543 if(delete_target && (IS_NULL_PTR(src) || !info.found))
544 {
545 if(!IS_NULL_PTR(src)) TIFFClose(src);
546 return TRUE;
547 }
548
549 if(delete_target && !IS_NULL_PTR(src) && info.found && info.count == 1)
550 {
551 TIFFClose(src);
552 return g_unlink(path) == 0;
553 }
554
555 gchar *tmp_path = g_strdup_printf("%s.tmp", path);
556 TIFF *dst = TIFFOpen(tmp_path, "wb");
557 if(IS_NULL_PTR(dst))
558 {
559 if(!IS_NULL_PTR(src)) TIFFClose(src);
561 return FALSE;
562 }
563
564 int written_index = 0;
565 gboolean ok = TRUE;
566
567 if(!IS_NULL_PTR(src))
568 {
569 for(int dir = 0; dir < info.count && ok; dir++)
570 {
571 if(!delete_target && !info.found && insert_order >= 0 && written_index == insert_order)
572 {
573 ok = _write_page(dst, NULL, -1, target_name, work_profile, patch, layer_width, layer_height);
575 if(ok) written_index++;
576 }
577
578 const gboolean is_target = info.found && dir == info.index;
579 if(is_target && delete_target) continue;
580
582 const char *page_label = is_target ? target_name : NULL;
583
584 ok = _write_page(dst, src, dir, page_label, is_target ? work_profile : NULL, page_patch, layer_width,
587 if(ok) written_index++;
588 }
589 }
590
591 if(ok && !delete_target && (IS_NULL_PTR(src) || !info.found))
592 {
594 if(append_at_end)
595 {
596 ok = _write_page(dst, NULL, -1, target_name, work_profile, patch, layer_width, layer_height);
598 }
599 }
600
601 TIFFClose(dst);
602 if(!IS_NULL_PTR(src)) TIFFClose(src);
603
604 if(ok)
605 ok = (g_rename(tmp_path, path) == 0);
606 else
608
610 return ok;
611}
612
614gboolean dt_drawlayer_io_layer_name_exists(const char *path, const char *candidate, const int ignore_index)
615{
616 if(IS_NULL_PTR(path) || !candidate || candidate[0] == '\0' || !g_file_test(path, G_FILE_TEST_EXISTS)) return FALSE;
617
618 TIFF *tiff = TIFFOpen(path, "rb");
619 if(IS_NULL_PTR(tiff)) return FALSE;
620
621 gboolean exists = FALSE;
622 int index = 0;
623 if(TIFFSetDirectory(tiff, 0))
624 {
625 do
626 {
627 char *page_name = NULL;
629 if(index != ignore_index && !g_strcmp0(page_name ? page_name : "", candidate))
630 {
631 exists = TRUE;
632 break;
633 }
634 index++;
635 } while(TIFFReadDirectory(tiff));
636 }
637
638 TIFFClose(tiff);
639 return exists;
640}
641
643gboolean dt_drawlayer_io_sidecar_path(const int32_t imgid, char *path, const size_t path_size)
644{
645 gboolean from_cache = FALSE;
646 if(IS_NULL_PTR(path) || path_size == 0) return FALSE;
648 if(path[0] == '\0') return FALSE;
649 g_strlcat(path, ".ansel.tiff", path_size);
650 return TRUE;
651}
652
654gboolean dt_drawlayer_io_find_layer(const char *path, const char *target_name, const int target_order,
656{
657 if(!IS_NULL_PTR(info)) memset(info, 0, sizeof(*info));
658 if(IS_NULL_PTR(path) || !g_file_test(path, G_FILE_TEST_EXISTS)) return FALSE;
659
660 TIFF *tiff = TIFFOpen(path, "rb");
661 if(IS_NULL_PTR(tiff)) return FALSE;
662 _scan_directories(tiff, target_name, target_order, info);
663 TIFFClose(tiff);
664 return info && info->found;
665}
666
668gboolean dt_drawlayer_io_load_layer(const char *path, const char *target_name, const int target_order,
669 const int layer_width, const int layer_height, dt_drawlayer_io_patch_t *patch)
670{
671 if(IS_NULL_PTR(patch) || IS_NULL_PTR(patch->pixels) || patch->width <= 0 || patch->height <= 0) return FALSE;
672 dt_drawlayer_cache_clear_transparent_float(patch->pixels, (size_t)patch->width * patch->height);
673
674 if(!g_file_test(path, G_FILE_TEST_EXISTS)) return TRUE;
675
676 TIFF *tiff = TIFFOpen(path, "rb");
677 if(IS_NULL_PTR(tiff)) return FALSE;
678
680 _scan_directories(tiff, target_name, target_order, &info);
681 if(!info.found)
682 {
683 TIFFClose(tiff);
684 return TRUE;
685 }
686
687 if(!TIFFSetDirectory(tiff, (tdir_t)info.index))
688 {
689 TIFFClose(tiff);
690 return FALSE;
691 }
692
693 uint16_t *row = g_malloc_n((gsize)info.width * 4, sizeof(uint16_t));
694 if(IS_NULL_PTR(row))
695 {
696 TIFFClose(tiff);
697 return FALSE;
698 }
699
700 const int offset_x = (layer_width - (int)info.width) / 2;
701 const int offset_y = (layer_height - (int)info.height) / 2;
702
703 for(int py = 0; py < patch->height; py++)
704 {
705 const int layer_y = patch->y + py;
706 const int src_y = layer_y - offset_y;
707 if(src_y < 0 || src_y >= (int)info.height) continue;
708 if(!_read_scanline_rgba(tiff, info.width, (uint32_t)src_y, row))
709 {
710 dt_free(row);
711 TIFFClose(tiff);
712 return FALSE;
713 }
714
715 for(int px = 0; px < patch->width; px++)
716 {
717 const int layer_x = patch->x + px;
718 const int src_x = layer_x - offset_x;
719 if(src_x < 0 || src_x >= (int)info.width) continue;
720 float *dst = patch->pixels + 4 * ((size_t)py * patch->width + px);
722 }
723 }
724
725 dt_free(row);
726 TIFFClose(tiff);
727 return TRUE;
728}
729
731gboolean dt_drawlayer_io_load_flat_rgba(const char *path, float **pixels, int *width, int *height)
732{
733 if(pixels) *pixels = NULL;
734 if(width) *width = 0;
735 if(height) *height = 0;
736 if(IS_NULL_PTR(path) || IS_NULL_PTR(pixels) || !width || IS_NULL_PTR(height) || !g_file_test(path, G_FILE_TEST_EXISTS)) return FALSE;
737
738 TIFF *tiff = TIFFOpen(path, "rb");
739 if(IS_NULL_PTR(tiff)) return FALSE;
740
741 uint32_t w = 0, h = 0;
742 uint16_t spp = 0, bpp = 0, sampleformat = SAMPLEFORMAT_UINT;
747 TIFFGetFieldDefaulted(tiff, TIFFTAG_SAMPLEFORMAT, &sampleformat);
748
749 if(w == 0 || h == 0 || spp < 1)
750 {
751 TIFFClose(tiff);
752 return FALSE;
753 }
754
755 float *out = g_malloc_n((size_t)w * h * 4, sizeof(float));
756 if(IS_NULL_PTR(out))
757 {
758 TIFFClose(tiff);
759 return FALSE;
760 }
762
763 const tsize_t scanline = TIFFScanlineSize(tiff);
764 tdata_t row = _TIFFmalloc((tsize_t)scanline);
765 if(IS_NULL_PTR(row))
766 {
767 dt_free(out);
768 TIFFClose(tiff);
769 return FALSE;
770 }
771
772 gboolean ok = TRUE;
773 for(uint32_t y = 0; y < h && ok; y++)
774 {
775 if(TIFFReadScanline(tiff, row, y, 0) == -1)
776 {
777 ok = FALSE;
778 break;
779 }
780
781 for(uint32_t x = 0; x < w; x++)
782 {
783 float r = 0.0f, g = 0.0f, b = 0.0f, a = 1.0f;
784
785 if(bpp == 32 && sampleformat == SAMPLEFORMAT_IEEEFP)
786 {
787 const float *src = (const float *)row + (size_t)x * spp;
788 if(spp >= 3)
789 {
790 r = src[0];
791 g = src[1];
792 b = src[2];
793 }
794 else
795 {
796 r = g = b = src[0];
797 }
798 if(spp >= 4) a = src[3];
799 }
800 else if(bpp == 16 && sampleformat == SAMPLEFORMAT_IEEEFP)
801 {
802 const uint16_t *src = (const uint16_t *)row + (size_t)x * spp;
803 if(spp >= 3)
804 {
805 r = _half_to_float(src[0]);
806 g = _half_to_float(src[1]);
807 b = _half_to_float(src[2]);
808 }
809 else
810 {
811 r = g = b = _half_to_float(src[0]);
812 }
813 if(spp >= 4) a = _half_to_float(src[3]);
814 }
815 else if(bpp == 16)
816 {
817 const uint16_t *src = (const uint16_t *)row + (size_t)x * spp;
818 if(spp >= 3)
819 {
820 r = src[0] / 65535.0f;
821 g = src[1] / 65535.0f;
822 b = src[2] / 65535.0f;
823 }
824 else
825 {
826 r = g = b = src[0] / 65535.0f;
827 }
828 if(spp >= 4) a = src[3] / 65535.0f;
829 }
830 else if(bpp == 8)
831 {
832 const uint8_t *src = (const uint8_t *)row + (size_t)x * spp;
833 if(spp >= 3)
834 {
835 r = src[0] / 255.0f;
836 g = src[1] / 255.0f;
837 b = src[2] / 255.0f;
838 }
839 else
840 {
841 r = g = b = src[0] / 255.0f;
842 }
843 if(spp >= 4) a = src[3] / 255.0f;
844 }
845 else
846 {
847 ok = FALSE;
848 break;
849 }
850
851 float *dst = out + 4 * ((size_t)y * w + x);
852 dst[0] = r;
853 dst[1] = g;
854 dst[2] = b;
855 dst[3] = _clamp01(a);
856 }
857 }
858
859 _TIFFfree(row);
860 TIFFClose(tiff);
861 if(!ok)
862 {
863 dt_free(out);
864 return FALSE;
865 }
866
867 *pixels = out;
868 *width = (int)w;
869 *height = (int)h;
870 return TRUE;
871}
872
874gboolean dt_drawlayer_io_store_layer(const char *path, const char *target_name, const int target_order,
875 const char *work_profile, const dt_drawlayer_io_patch_t *patch,
876 const int layer_width, const int layer_height, const gboolean delete_target,
877 int *final_order)
878{
879 if(IS_NULL_PTR(target_name) || target_name[0] == '\0') return FALSE;
880 return _rewrite_sidecar(path, target_name, target_order, work_profile, patch, layer_width, layer_height,
882}
883
885gboolean dt_drawlayer_io_insert_layer(const char *path, const char *target_name, const int insert_after_order,
886 const char *work_profile, const dt_drawlayer_io_patch_t *patch,
887 const int layer_width, const int layer_height, int *final_order)
888{
889 if(IS_NULL_PTR(target_name) || target_name[0] == '\0') return FALSE;
890 const int insert_order = (insert_after_order >= 0) ? (insert_after_order + 1) : -1;
891 return _rewrite_sidecar(path, target_name, -1, work_profile, patch, layer_width, layer_height, FALSE, insert_order,
893}
894
896gboolean dt_drawlayer_io_rename_layer(const char *path, const char *current_name, const char *new_name,
897 const char *work_profile, const int layer_width, const int layer_height,
899{
900 if(!IS_NULL_PTR(info)) memset(info, 0, sizeof(*info));
901 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;
902 if(!g_file_test(path, G_FILE_TEST_EXISTS)) return FALSE;
903
904 dt_drawlayer_io_layer_info_t current = { 0 };
905 if(!dt_drawlayer_io_find_layer(path, current_name, -1, &current)) return FALSE;
906 if(dt_drawlayer_io_layer_name_exists(path, new_name, current.index)) return FALSE;
907
908 int final_order = current.index;
909 if(!dt_drawlayer_io_store_layer(path, new_name, current.index, work_profile, NULL, layer_width, layer_height, FALSE,
910 &final_order))
911 return FALSE;
912
913 if(!IS_NULL_PTR(info))
914 {
915 *info = current;
916 info->found = TRUE;
917 info->index = final_order;
918 g_strlcpy(info->name, new_name, sizeof(info->name));
919 if(!IS_NULL_PTR(work_profile) && work_profile[0] != '\0')
920 g_strlcpy(info->work_profile, work_profile, sizeof(info->work_profile));
921 }
922 return TRUE;
923}
924
926gboolean dt_drawlayer_io_delete_layer(const char *path, const char *target_name, const int layer_width,
927 const int layer_height)
928{
929 if(IS_NULL_PTR(path) || IS_NULL_PTR(target_name) || target_name[0] == '\0') return FALSE;
930 if(!g_file_test(path, G_FILE_TEST_EXISTS)) return FALSE;
931
932 dt_drawlayer_io_layer_info_t info = { 0 };
933 if(!dt_drawlayer_io_find_layer(path, target_name, -1, &info)) return FALSE;
934
935 return dt_drawlayer_io_store_layer(path, target_name, info.index, NULL, NULL, layer_width, layer_height, TRUE,
936 NULL);
937}
938
940void dt_drawlayer_io_make_unique_name_plain(const char *path, const char *requested, char *name, const size_t name_size)
941{
942 if(IS_NULL_PTR(name) || name_size == 0) return;
943 name[0] = '\0';
946 if(name[0] == '\0') return;
947 if(!dt_drawlayer_io_layer_name_exists(path, name, -1)) return;
948
949 char base[DT_DRAWLAYER_IO_NAME_SIZE] = { 0 };
950 g_strlcpy(base, name, sizeof(base));
951
952 for(int suffix = 2; suffix < 100000; suffix++)
953 {
954 g_snprintf(name, name_size, "%.*s %d", MAX((int)name_size - 12, 1), base, suffix);
955 if(!dt_drawlayer_io_layer_name_exists(path, name, -1)) return;
956 }
957}
958
960gboolean dt_drawlayer_io_list_layer_names(const char *path, char ***names, int *count)
961{
962 if(names) *names = NULL;
963 if(!IS_NULL_PTR(count)) *count = 0;
964 if(IS_NULL_PTR(path) || IS_NULL_PTR(names) || !count || !g_file_test(path, G_FILE_TEST_EXISTS)) return FALSE;
965
966 TIFF *tiff = TIFFOpen(path, "rb");
967 if(IS_NULL_PTR(tiff)) return FALSE;
968
970 if(TIFFSetDirectory(tiff, 0))
971 {
972 do
973 {
974 char *page_name = NULL;
977 } while(TIFFReadDirectory(tiff));
978 }
979 TIFFClose(tiff);
980
981 *count = (int)arr->len;
982 if(arr->len == 0)
983 {
985 *names = NULL;
986 return TRUE;
987 }
988
989 const guint layer_count = arr->len;
990 char **out = (char **)g_ptr_array_free(arr, FALSE);
991 out = g_renew(char *, out, layer_count + 1);
993 *names = out;
994 return TRUE;
995}
996
999{
1000 if(IS_NULL_PTR(names) || !*names) return;
1001 const int n = (!IS_NULL_PTR(count) && *count > 0) ? *count : 0;
1002 if(n > 0)
1003 {
1004 for(int i = 0; i < n; i++) dt_free((*names)[i]);
1005 }
1006 else
1007 {
1008 for(int i = 0; (*names)[i]; i++) dt_free((*names)[i]);
1009 }
1010 dt_free(*names);
1011 if(!IS_NULL_PTR(count)) *count = 0;
1012}
1013
1015{
1018 if(IS_NULL_PTR(params)) return 0;
1019
1021 result->imgid = params->imgid;
1022 g_strlcpy(result->initiator_layer_name, params->initiator_layer_name, sizeof(result->initiator_layer_name));
1023 result->initiator_layer_order = params->initiator_layer_order;
1024 g_strlcpy(result->message, _("failed to create background layer from input"), sizeof(result->message));
1025
1026 gchar *tmp_path = NULL;
1027 const int tmp_fd = g_file_open_tmp("ansel-drawlayer-bg-XXXXXX.tiff", &tmp_path, NULL);
1028 if(tmp_fd >= 0) g_close(tmp_fd, NULL);
1029
1030 float *export_pixels = NULL;
1031 int export_w = 0;
1032 int export_h = 0;
1034
1035 do
1036 {
1037 if(tmp_fd < 0 || IS_NULL_PTR(tmp_path)) break;
1038 if(!_export_pre_module_fullres_to_tiff(params->imgid, params->filter, tmp_path)) break;
1040 if(IS_NULL_PTR(export_pixels) || export_w <= 0 || export_h <= 0) break;
1041
1042 bg_patch.width = params->layer_width;
1043 bg_patch.height = params->layer_height;
1044 bg_patch.x = 0;
1045 bg_patch.y = 0;
1047 (size_t)params->layer_width * params->layer_height * 4 * sizeof(float), "drawlayer bg layer");
1048 if(IS_NULL_PTR(bg_patch.pixels)) break;
1049 dt_drawlayer_cache_clear_transparent_float(bg_patch.pixels, (size_t)params->layer_width * params->layer_height);
1050
1051 const int clip_x0 = MAX(params->dst_x, 0);
1052 const int clip_y0 = MAX(params->dst_y, 0);
1053 const int clip_x1 = MIN(params->dst_x + export_w, params->layer_width);
1054 const int clip_y1 = MIN(params->dst_y + export_h, params->layer_height);
1055 if(clip_x1 <= clip_x0 || clip_y1 <= clip_y0) break;
1056
1057 const int copy_w = clip_x1 - clip_x0;
1058 const int copy_h = clip_y1 - clip_y0;
1059 const int src_x0 = clip_x0 - params->dst_x;
1060 const int src_y0 = clip_y0 - params->dst_y;
1061 for(int y = 0; y < copy_h; y++)
1062 {
1063 const float *src = export_pixels + 4 * ((size_t)(src_y0 + y) * export_w + src_x0);
1064 float *dst = bg_patch.pixels + 4 * ((size_t)(clip_y0 + y) * params->layer_width + clip_x0);
1065 memcpy(dst, src, (size_t)copy_w * 4 * sizeof(float));
1066 }
1067
1068 char bg_name[DT_DRAWLAYER_IO_NAME_SIZE] = { 0 };
1069 dt_drawlayer_io_make_unique_name_plain(params->sidecar_path, params->requested_bg_name, bg_name,
1070 sizeof(bg_name));
1072
1073 int final_order = -1;
1074 if(!dt_drawlayer_io_insert_layer(params->sidecar_path, bg_name, params->insert_after_order,
1075 params->work_profile, &bg_patch, params->layer_width, params->layer_height,
1076 &final_order))
1077 break;
1078
1080 if(!dt_drawlayer_io_find_layer(params->sidecar_path, bg_name, final_order, &io_info)) break;
1081
1082 result->success = TRUE;
1084 g_strlcpy(result->created_bg_name, bg_name, sizeof(result->created_bg_name));
1085 g_snprintf(result->message, sizeof(result->message), _("created background layer `%s'"), bg_name);
1086 } while(0);
1087
1089 if(bg_patch.pixels) dt_drawlayer_cache_free_temp_buffer((void **)&bg_patch.pixels, "drawlayer bg layer");
1090 if(tmp_path)
1091 {
1094 }
1095
1096 if(params->done_idle)
1097 g_main_context_invoke(NULL, params->done_idle, result);
1098 else
1099 dt_free(result);
1100 return 0;
1101}
#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
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, const double scale_factor, 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)
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:1014
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:614
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:654
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:940
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:304
static float _clamp01(const float value)
Clamp scalar to [0,1].
Definition io.c:42
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:885
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:643
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:524
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:134
static int64_t dt_drawlayer_sidecar_timestamp_from_path(const char *path)
Definition io.c:160
static float _half_to_float(const uint16_t h)
Convert IEEE-754 binary16 to float32.
Definition io.c:54
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:242
static gboolean dt_drawlayer_layer_name_non_empty(const char *name)
Definition io.c:151
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:203
static void _clear_transparent_half(uint16_t *pixels, const size_t pixel_count)
Clear uint16 RGBA row/buffer to transparent black.
Definition io.c:127
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:896
gboolean dt_drawlayer_io_list_layer_names(const char *path, char ***names, int *count)
List all TIFF layer page names.
Definition io.c:960
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:668
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:310
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:926
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:731
static uint16_t _float_to_half(float value)
Convert float32 to IEEE-754 binary16 with rounding.
Definition io.c:77
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:332
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:874
static gboolean _export_pre_module_fullres_to_tiff(const int32_t imgid, const char *filter, const char *path)
Definition io.c:169
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:268
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:438
void dt_drawlayer_io_free_layer_names(char ***names, int *count)
Free array returned by dt_drawlayer_io_list_layer_names.
Definition io.c:998
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:144
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