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)))
103 half_mantissa++;
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 {
113 half_mantissa++;
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 _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));
156 g_strstrip(tmp);
157 return tmp[0] != '\0';
158}
159
160static int64_t _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 {
187 drawlayer_tiff_export_params_t *const tiff_params = (drawlayer_tiff_export_params_t *)format_params;
188 tiff_params->bpp = 32;
189 tiff_params->compress = 0;
190 tiff_params->compresslevel = 0;
191 tiff_params->shortfile = 0;
192 }
193
194 const int rc = dt_imageio_export_with_flags(
195 imgid, path, format, format_params, TRUE, FALSE, TRUE, FALSE, FALSE, (filter && filter[0]) ? filter : NULL,
196 FALSE, FALSE, DT_COLORSPACE_NONE, NULL, DT_INTENT_PERCEPTUAL, NULL, NULL, 0, 0, NULL, 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;
250 if(!TIFFSetField(tiff, TIFFTAG_SAMPLEFORMAT, SAMPLEFORMAT_IEEEFP)) return FALSE;
251 if(!TIFFSetField(tiff, TIFFTAG_SAMPLESPERPIXEL, 4)) return FALSE;
252 if(!TIFFSetField(tiff, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG)) return FALSE;
253 if(!TIFFSetField(tiff, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_RGB)) return FALSE;
254 if(!TIFFSetField(tiff, TIFFTAG_EXTRASAMPLES, 1, extrasamples)) return FALSE;
255
256 if(!TIFFSetField(tiff, TIFFTAG_COMPRESSION, COMPRESSION_ADOBE_DEFLATE)
257 && !TIFFSetField(tiff, TIFFTAG_COMPRESSION, COMPRESSION_DEFLATE)
258 && !TIFFSetField(tiff, TIFFTAG_COMPRESSION, COMPRESSION_LZW))
259 TIFFSetField(tiff, TIFFTAG_COMPRESSION, COMPRESSION_NONE);
260
261 TIFFSetField(tiff, TIFFTAG_ROWSPERSTRIP, TIFFDefaultStripSize(tiff, 0));
262 if(!IS_NULL_PTR(name) && name[0]) TIFFSetField(tiff, TIFFTAG_PAGENAME, name);
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
274 TIFFGetField(tiff, TIFFTAG_BITSPERSAMPLE, &bpp);
275 TIFFGetField(tiff, TIFFTAG_SAMPLESPERPIXEL, &spp);
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 };
347 dt_drawlayer_io_layer_info_t named = { 0 };
348 dt_drawlayer_io_layer_info_t ordered = { 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;
360 TIFFGetField(tiff, TIFFTAG_IMAGEWIDTH, &width);
361 TIFFGetField(tiff, TIFFTAG_IMAGELENGTH, &height);
362 TIFFGetField(tiff, TIFFTAG_PAGENAME, &page_name);
363 TIFFGetField(tiff, TIFFTAG_IMAGEDESCRIPTION, &page_profile);
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
369 if(exact_match && !exact_found)
370 {
371 exact_found = TRUE;
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 {
382 named_found = TRUE;
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
391 if(order_match && !order_found)
392 {
393 order_found = TRUE;
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 {
452 TIFFGetField(src, TIFFTAG_IMAGEWIDTH, &width);
453 TIFFGetField(src, TIFFTAG_IMAGELENGTH, &height);
454 }
455 if(IS_NULL_PTR(name))
456 {
457 char *src_name = NULL;
458 TIFFGetField(src, TIFFTAG_PAGENAME, &src_name);
459 name = src_name;
460 }
461 if(IS_NULL_PTR(page_profile))
462 {
463 char *src_profile = NULL;
464 TIFFGetField(src, TIFFTAG_IMAGEDESCRIPTION, &src_profile);
465 page_profile = src_profile;
466 }
467 }
468
469 if(!_set_directory_tags(dst, width, height, name, page_profile)) return FALSE;
470 if(page_profile && page_profile[0] && _icc_blob_from_profile_key(page_profile, &icc_profile, &icc_profile_len))
471 TIFFSetField(dst, TIFFTAG_ICCPROFILE, icc_profile_len, icc_profile);
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));
475 if(IS_NULL_PTR(src_row) || IS_NULL_PTR(dst_row))
476 {
477 dt_free(icc_profile);
478 dt_free(src_row);
479 dt_free(dst_row);
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 {
492 dt_free(icc_profile);
493 dt_free(src_row);
494 dt_free(dst_row);
495 return FALSE;
496 }
497 memcpy(dst_row, src_row, (size_t)width * 4 * sizeof(uint16_t));
498 }
499 else
500 _clear_transparent_half(dst_row, (size_t)width);
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
508 if(!_write_scanline_rgba(dst, y, dst_row))
509 {
510 dt_free(icc_profile);
511 dt_free(src_row);
512 dt_free(dst_row);
513 return FALSE;
514 }
515 }
516
517 dt_free(icc_profile);
518 dt_free(src_row);
519 dt_free(dst_row);
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{
529 if(!IS_NULL_PTR(final_order)) *final_order = -1;
530
531 TIFF *src = NULL;
533 memset(&info, 0, sizeof(info));
534 info.index = -1;
535
536 if(g_file_test(path, G_FILE_TEST_EXISTS))
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);
560 dt_free(tmp_path);
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);
574 if(ok && final_order) *final_order = written_index;
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
581 const dt_drawlayer_io_patch_t *page_patch = (is_target && !delete_target) ? patch : NULL;
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,
585 layer_height);
586 if(ok && is_target && !delete_target && final_order) *final_order = written_index;
587 if(ok) written_index++;
588 }
589 }
590
591 if(ok && !delete_target && (IS_NULL_PTR(src) || !info.found))
592 {
593 const gboolean append_at_end = (insert_order < 0 || insert_order >= written_index || !src);
594 if(append_at_end)
595 {
596 ok = _write_page(dst, NULL, -1, target_name, work_profile, patch, layer_width, layer_height);
597 if(ok && !IS_NULL_PTR(final_order)) *final_order = written_index;
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
607 g_unlink(tmp_path);
608
609 dt_free(tmp_path);
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;
628 TIFFGetField(tiff, TIFFTAG_PAGENAME, &page_name);
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
643static void _sanitize_requested_layer_name(const char *requested, const char *fallback_name, char *name,
644 const size_t name_size)
645{
646 if(IS_NULL_PTR(name) || name_size == 0) return;
647 name[0] = '\0';
648 if(!IS_NULL_PTR(requested) && requested[0])
649 {
650 gboolean last_was_space = FALSE;
651 size_t out = 0;
652 for(size_t in = 0; requested[in] != '\0' && out + 1 < name_size; in++)
653 {
654 const unsigned char ch = (unsigned char)requested[in];
655 if(g_ascii_isspace(ch))
656 {
657 if(out > 0 && !last_was_space)
658 {
659 name[out++] = ' ';
660 last_was_space = TRUE;
661 }
662 continue;
663 }
664
665 name[out++] = (char)ch;
666 last_was_space = FALSE;
667 }
668 name[out] = '\0';
669 }
670 g_strstrip(name);
671 if(name[0] == '\0' && !IS_NULL_PTR(fallback_name) && fallback_name[0]) g_strlcpy(name, fallback_name, name_size);
672}
673
675gboolean dt_drawlayer_io_sidecar_path(const int32_t imgid, char *path, const size_t path_size)
676{
677 gboolean from_cache = FALSE;
678 if(IS_NULL_PTR(path) || path_size == 0) return FALSE;
679 dt_image_full_path(imgid, path, path_size, &from_cache, __FUNCTION__);
680 if(path[0] == '\0') return FALSE;
681 g_strlcat(path, ".ansel.tiff", path_size);
682 return TRUE;
683}
684
686gboolean dt_drawlayer_io_find_layer(const char *path, const char *target_name, const int target_order,
688{
689 if(!IS_NULL_PTR(info)) memset(info, 0, sizeof(*info));
690 if(IS_NULL_PTR(path) || !g_file_test(path, G_FILE_TEST_EXISTS)) return FALSE;
691
692 TIFF *tiff = TIFFOpen(path, "rb");
693 if(IS_NULL_PTR(tiff)) return FALSE;
694 _scan_directories(tiff, target_name, target_order, info);
695 TIFFClose(tiff);
696 return info && info->found;
697}
698
700gboolean dt_drawlayer_io_load_layer(const char *path, const char *target_name, const int target_order,
701 const int layer_width, const int layer_height, dt_drawlayer_io_patch_t *patch)
702{
703 if(IS_NULL_PTR(patch) || IS_NULL_PTR(patch->pixels) || patch->width <= 0 || patch->height <= 0) return FALSE;
704 dt_drawlayer_cache_clear_transparent_float(patch->pixels, (size_t)patch->width * patch->height);
705
706 if(!g_file_test(path, G_FILE_TEST_EXISTS)) return TRUE;
707
708 TIFF *tiff = TIFFOpen(path, "rb");
709 if(IS_NULL_PTR(tiff)) return FALSE;
710
712 _scan_directories(tiff, target_name, target_order, &info);
713 if(!info.found)
714 {
715 TIFFClose(tiff);
716 return TRUE;
717 }
718
719 if(!TIFFSetDirectory(tiff, (tdir_t)info.index))
720 {
721 TIFFClose(tiff);
722 return FALSE;
723 }
724
725 uint16_t *row = g_malloc_n((gsize)info.width * 4, sizeof(uint16_t));
726 if(IS_NULL_PTR(row))
727 {
728 TIFFClose(tiff);
729 return FALSE;
730 }
731
732 const int offset_x = (layer_width - (int)info.width) / 2;
733 const int offset_y = (layer_height - (int)info.height) / 2;
734
735 for(int py = 0; py < patch->height; py++)
736 {
737 const int layer_y = patch->y + py;
738 const int src_y = layer_y - offset_y;
739 if(src_y < 0 || src_y >= (int)info.height) continue;
740 if(!_read_scanline_rgba(tiff, info.width, (uint32_t)src_y, row))
741 {
742 dt_free(row);
743 TIFFClose(tiff);
744 return FALSE;
745 }
746
747 for(int px = 0; px < patch->width; px++)
748 {
749 const int layer_x = patch->x + px;
750 const int src_x = layer_x - offset_x;
751 if(src_x < 0 || src_x >= (int)info.width) continue;
752 float *dst = patch->pixels + 4 * ((size_t)py * patch->width + px);
753 _load_half_pixel_rgba(row + 4 * src_x, dst);
754 }
755 }
756
757 dt_free(row);
758 TIFFClose(tiff);
759 return TRUE;
760}
761
763gboolean dt_drawlayer_io_load_flat_rgba(const char *path, float **pixels, int *width, int *height)
764{
765 if(pixels) *pixels = NULL;
766 if(width) *width = 0;
767 if(height) *height = 0;
768 if(IS_NULL_PTR(path) || IS_NULL_PTR(pixels) || !width || IS_NULL_PTR(height) || !g_file_test(path, G_FILE_TEST_EXISTS)) return FALSE;
769
770 TIFF *tiff = TIFFOpen(path, "rb");
771 if(IS_NULL_PTR(tiff)) return FALSE;
772
773 uint32_t w = 0, h = 0;
774 uint16_t spp = 0, bpp = 0, sampleformat = SAMPLEFORMAT_UINT;
775 TIFFGetField(tiff, TIFFTAG_IMAGEWIDTH, &w);
776 TIFFGetField(tiff, TIFFTAG_IMAGELENGTH, &h);
777 TIFFGetFieldDefaulted(tiff, TIFFTAG_SAMPLESPERPIXEL, &spp);
778 TIFFGetFieldDefaulted(tiff, TIFFTAG_BITSPERSAMPLE, &bpp);
779 TIFFGetFieldDefaulted(tiff, TIFFTAG_SAMPLEFORMAT, &sampleformat);
780
781 if(w == 0 || h == 0 || spp < 1)
782 {
783 TIFFClose(tiff);
784 return FALSE;
785 }
786
787 float *out = g_malloc_n((size_t)w * h * 4, sizeof(float));
788 if(IS_NULL_PTR(out))
789 {
790 TIFFClose(tiff);
791 return FALSE;
792 }
794
795 const tsize_t scanline = TIFFScanlineSize(tiff);
796 tdata_t row = _TIFFmalloc((tsize_t)scanline);
797 if(IS_NULL_PTR(row))
798 {
799 dt_free(out);
800 TIFFClose(tiff);
801 return FALSE;
802 }
803
804 gboolean ok = TRUE;
805 for(uint32_t y = 0; y < h && ok; y++)
806 {
807 if(TIFFReadScanline(tiff, row, y, 0) == -1)
808 {
809 ok = FALSE;
810 break;
811 }
812
813 for(uint32_t x = 0; x < w; x++)
814 {
815 float r = 0.0f, g = 0.0f, b = 0.0f, a = 1.0f;
816
817 if(bpp == 32 && sampleformat == SAMPLEFORMAT_IEEEFP)
818 {
819 const float *src = (const float *)row + (size_t)x * spp;
820 if(spp >= 3)
821 {
822 r = src[0];
823 g = src[1];
824 b = src[2];
825 }
826 else
827 {
828 r = g = b = src[0];
829 }
830 if(spp >= 4) a = src[3];
831 }
832 else if(bpp == 16 && sampleformat == SAMPLEFORMAT_IEEEFP)
833 {
834 const uint16_t *src = (const uint16_t *)row + (size_t)x * spp;
835 if(spp >= 3)
836 {
837 r = _half_to_float(src[0]);
838 g = _half_to_float(src[1]);
839 b = _half_to_float(src[2]);
840 }
841 else
842 {
843 r = g = b = _half_to_float(src[0]);
844 }
845 if(spp >= 4) a = _half_to_float(src[3]);
846 }
847 else if(bpp == 16)
848 {
849 const uint16_t *src = (const uint16_t *)row + (size_t)x * spp;
850 if(spp >= 3)
851 {
852 r = src[0] / 65535.0f;
853 g = src[1] / 65535.0f;
854 b = src[2] / 65535.0f;
855 }
856 else
857 {
858 r = g = b = src[0] / 65535.0f;
859 }
860 if(spp >= 4) a = src[3] / 65535.0f;
861 }
862 else if(bpp == 8)
863 {
864 const uint8_t *src = (const uint8_t *)row + (size_t)x * spp;
865 if(spp >= 3)
866 {
867 r = src[0] / 255.0f;
868 g = src[1] / 255.0f;
869 b = src[2] / 255.0f;
870 }
871 else
872 {
873 r = g = b = src[0] / 255.0f;
874 }
875 if(spp >= 4) a = src[3] / 255.0f;
876 }
877 else
878 {
879 ok = FALSE;
880 break;
881 }
882
883 float *dst = out + 4 * ((size_t)y * w + x);
884 dst[0] = r;
885 dst[1] = g;
886 dst[2] = b;
887 dst[3] = _clamp01(a);
888 }
889 }
890
891 _TIFFfree(row);
892 TIFFClose(tiff);
893 if(!ok)
894 {
895 dt_free(out);
896 return FALSE;
897 }
898
899 *pixels = out;
900 *width = (int)w;
901 *height = (int)h;
902 return TRUE;
903}
904
906gboolean dt_drawlayer_io_store_layer(const char *path, const char *target_name, const int target_order,
907 const char *work_profile, const dt_drawlayer_io_patch_t *patch,
908 const int layer_width, const int layer_height, const gboolean delete_target,
909 int *final_order)
910{
911 if(IS_NULL_PTR(target_name) || target_name[0] == '\0') return FALSE;
912 return _rewrite_sidecar(path, target_name, target_order, work_profile, patch, layer_width, layer_height,
913 delete_target, -1, final_order);
914}
915
917gboolean dt_drawlayer_io_insert_layer(const char *path, const char *target_name, const int insert_after_order,
918 const char *work_profile, const dt_drawlayer_io_patch_t *patch,
919 const int layer_width, const int layer_height, int *final_order)
920{
921 if(IS_NULL_PTR(target_name) || target_name[0] == '\0') return FALSE;
922 const int insert_order = (insert_after_order >= 0) ? (insert_after_order + 1) : -1;
923 return _rewrite_sidecar(path, target_name, -1, work_profile, patch, layer_width, layer_height, FALSE, insert_order,
924 final_order);
925}
926
928gboolean dt_drawlayer_io_rename_layer(const char *path, const char *current_name, const char *new_name,
929 const char *work_profile, const int layer_width, const int layer_height,
931{
932 if(!IS_NULL_PTR(info)) memset(info, 0, sizeof(*info));
933 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;
934 if(!g_file_test(path, G_FILE_TEST_EXISTS)) return FALSE;
935
936 dt_drawlayer_io_layer_info_t current = { 0 };
937 if(!dt_drawlayer_io_find_layer(path, current_name, -1, &current)) return FALSE;
938 if(dt_drawlayer_io_layer_name_exists(path, new_name, current.index)) return FALSE;
939
940 int final_order = current.index;
941 if(!dt_drawlayer_io_store_layer(path, new_name, current.index, work_profile, NULL, layer_width, layer_height, FALSE,
942 &final_order))
943 return FALSE;
944
945 if(!IS_NULL_PTR(info))
946 {
947 *info = current;
948 info->found = TRUE;
949 info->index = final_order;
950 g_strlcpy(info->name, new_name, sizeof(info->name));
951 if(!IS_NULL_PTR(work_profile) && work_profile[0] != '\0')
952 g_strlcpy(info->work_profile, work_profile, sizeof(info->work_profile));
953 }
954 return TRUE;
955}
956
958gboolean dt_drawlayer_io_delete_layer(const char *path, const char *target_name, const int layer_width,
959 const int layer_height)
960{
961 if(IS_NULL_PTR(path) || IS_NULL_PTR(target_name) || target_name[0] == '\0') return FALSE;
962 if(!g_file_test(path, G_FILE_TEST_EXISTS)) return FALSE;
963
964 dt_drawlayer_io_layer_info_t info = { 0 };
965 if(!dt_drawlayer_io_find_layer(path, target_name, -1, &info)) return FALSE;
966
967 return dt_drawlayer_io_store_layer(path, target_name, info.index, NULL, NULL, layer_width, layer_height, TRUE,
968 NULL);
969}
970
972void dt_drawlayer_io_make_unique_name(const char *path, const char *requested, const char *fallback_name, char *name,
973 const size_t name_size)
974{
975 _sanitize_requested_layer_name(requested, fallback_name, name, name_size);
976 if(name[0] == '\0') return;
977 if(!dt_drawlayer_io_layer_name_exists(path, name, -1)) return;
978
979 char base[DT_DRAWLAYER_IO_NAME_SIZE] = { 0 };
980 g_strlcpy(base, name, sizeof(base));
981
982 for(int suffix = 2; suffix < 100000; suffix++)
983 {
984 g_snprintf(name, name_size, "%.*s %d", MAX((int)name_size - 12, 1), base, suffix);
985 if(!dt_drawlayer_io_layer_name_exists(path, name, -1)) return;
986 }
987}
988
990void dt_drawlayer_io_make_unique_name_plain(const char *path, const char *requested, char *name, const size_t name_size)
991{
992 if(IS_NULL_PTR(name) || name_size == 0) return;
993 name[0] = '\0';
994 if(!IS_NULL_PTR(requested)) g_strlcpy(name, requested, name_size);
995 g_strstrip(name);
996 if(name[0] == '\0') return;
997 if(!dt_drawlayer_io_layer_name_exists(path, name, -1)) return;
998
999 char base[DT_DRAWLAYER_IO_NAME_SIZE] = { 0 };
1000 g_strlcpy(base, name, sizeof(base));
1001
1002 for(int suffix = 2; suffix < 100000; suffix++)
1003 {
1004 g_snprintf(name, name_size, "%.*s %d", MAX((int)name_size - 12, 1), base, suffix);
1005 if(!dt_drawlayer_io_layer_name_exists(path, name, -1)) return;
1006 }
1007}
1008
1010gboolean dt_drawlayer_io_list_layer_names(const char *path, char ***names, int *count)
1011{
1012 if(names) *names = NULL;
1013 if(!IS_NULL_PTR(count)) *count = 0;
1014 if(IS_NULL_PTR(path) || IS_NULL_PTR(names) || !count || !g_file_test(path, G_FILE_TEST_EXISTS)) return FALSE;
1015
1016 TIFF *tiff = TIFFOpen(path, "rb");
1017 if(IS_NULL_PTR(tiff)) return FALSE;
1018
1019 GPtrArray *arr = g_ptr_array_new_with_free_func(g_free);
1020 if(TIFFSetDirectory(tiff, 0))
1021 {
1022 do
1023 {
1024 char *page_name = NULL;
1025 TIFFGetField(tiff, TIFFTAG_PAGENAME, &page_name);
1026 g_ptr_array_add(arr, g_strdup(page_name ? page_name : ""));
1027 } while(TIFFReadDirectory(tiff));
1028 }
1029 TIFFClose(tiff);
1030
1031 *count = (int)arr->len;
1032 if(arr->len == 0)
1033 {
1034 g_ptr_array_free(arr, TRUE);
1035 *names = NULL;
1036 return TRUE;
1037 }
1038
1039 const guint layer_count = arr->len;
1040 char **out = (char **)g_ptr_array_free(arr, FALSE);
1041 out = g_renew(char *, out, layer_count + 1);
1042 out[layer_count] = NULL;
1043 *names = out;
1044 return TRUE;
1045}
1046
1049{
1050 if(IS_NULL_PTR(names) || !*names) return;
1051 const int n = (!IS_NULL_PTR(count) && *count > 0) ? *count : 0;
1052 if(n > 0)
1053 {
1054 for(int i = 0; i < n; i++) dt_free((*names)[i]);
1055 }
1056 else
1057 {
1058 for(int i = 0; (*names)[i]; i++) dt_free((*names)[i]);
1059 }
1060 dt_free(*names);
1061 if(!IS_NULL_PTR(count)) *count = 0;
1062}
1063
1065{
1068 if(IS_NULL_PTR(params)) return 0;
1069
1071 result->imgid = params->imgid;
1072 g_strlcpy(result->initiator_layer_name, params->initiator_layer_name, sizeof(result->initiator_layer_name));
1073 result->initiator_layer_order = params->initiator_layer_order;
1074 g_strlcpy(result->message, _("failed to create background layer from input"), sizeof(result->message));
1075
1076 gchar *tmp_path = NULL;
1077 const int tmp_fd = g_file_open_tmp("ansel-drawlayer-bg-XXXXXX.tiff", &tmp_path, NULL);
1078 if(tmp_fd >= 0) g_close(tmp_fd, NULL);
1079
1080 float *export_pixels = NULL;
1081 int export_w = 0;
1082 int export_h = 0;
1083 dt_drawlayer_io_patch_t bg_patch = { 0 };
1084
1085 do
1086 {
1087 if(tmp_fd < 0 || IS_NULL_PTR(tmp_path)) break;
1088 if(!_export_pre_module_fullres_to_tiff(params->imgid, params->filter, tmp_path)) break;
1089 if(!dt_drawlayer_io_load_flat_rgba(tmp_path, &export_pixels, &export_w, &export_h)) break;
1090 if(IS_NULL_PTR(export_pixels) || export_w <= 0 || export_h <= 0) break;
1091
1092 bg_patch.width = params->layer_width;
1093 bg_patch.height = params->layer_height;
1094 bg_patch.x = 0;
1095 bg_patch.y = 0;
1097 (size_t)params->layer_width * params->layer_height * 4 * sizeof(float), "drawlayer bg layer");
1098 if(IS_NULL_PTR(bg_patch.pixels)) break;
1099 dt_drawlayer_cache_clear_transparent_float(bg_patch.pixels, (size_t)params->layer_width * params->layer_height);
1100
1101 const int clip_x0 = MAX(params->dst_x, 0);
1102 const int clip_y0 = MAX(params->dst_y, 0);
1103 const int clip_x1 = MIN(params->dst_x + export_w, params->layer_width);
1104 const int clip_y1 = MIN(params->dst_y + export_h, params->layer_height);
1105 if(clip_x1 <= clip_x0 || clip_y1 <= clip_y0) break;
1106
1107 const int copy_w = clip_x1 - clip_x0;
1108 const int copy_h = clip_y1 - clip_y0;
1109 const int src_x0 = clip_x0 - params->dst_x;
1110 const int src_y0 = clip_y0 - params->dst_y;
1111 for(int y = 0; y < copy_h; y++)
1112 {
1113 const float *src = export_pixels + 4 * ((size_t)(src_y0 + y) * export_w + src_x0);
1114 float *dst = bg_patch.pixels + 4 * ((size_t)(clip_y0 + y) * params->layer_width + clip_x0);
1115 memcpy(dst, src, (size_t)copy_w * 4 * sizeof(float));
1116 }
1117
1118 char bg_name[DT_DRAWLAYER_IO_NAME_SIZE] = { 0 };
1119 dt_drawlayer_io_make_unique_name_plain(params->sidecar_path, params->requested_bg_name, bg_name,
1120 sizeof(bg_name));
1121 if(!_layer_name_non_empty(bg_name)) break;
1122
1123 int final_order = -1;
1124 if(!dt_drawlayer_io_insert_layer(params->sidecar_path, bg_name, params->insert_after_order,
1125 params->work_profile, &bg_patch, params->layer_width, params->layer_height,
1126 &final_order))
1127 break;
1128
1129 dt_drawlayer_io_layer_info_t io_info = { 0 };
1130 if(!dt_drawlayer_io_find_layer(params->sidecar_path, bg_name, final_order, &io_info)) break;
1131
1132 result->success = TRUE;
1133 result->sidecar_timestamp = _sidecar_timestamp_from_path(params->sidecar_path);
1134 g_strlcpy(result->created_bg_name, bg_name, sizeof(result->created_bg_name));
1135 g_snprintf(result->message, sizeof(result->message), _("created background layer `%s'"), bg_name);
1136 } while(0);
1137
1138 if(!IS_NULL_PTR(export_pixels)) dt_free(export_pixels);
1139 if(bg_patch.pixels) dt_drawlayer_cache_free_temp_buffer((void **)&bg_patch.pixels, "drawlayer bg layer");
1140 if(tmp_path)
1141 {
1142 g_unlink(tmp_path);
1143 dt_free(tmp_path);
1144 }
1145
1146 if(params->done_idle)
1147 g_main_context_invoke(NULL, params->done_idle, result);
1148 else
1149 dt_free(result);
1150 return 0;
1151}
#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, 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)
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:1064
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:686
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:990
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:917
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:675
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:972
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 _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:643
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 _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 _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:928
gboolean dt_drawlayer_io_list_layer_names(const char *path, char ***names, int *count)
List all TIFF layer page names.
Definition io.c:1010
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:700
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:958
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:763
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:906
static gboolean _export_pre_module_fullres_to_tiff(const int32_t imgid, const char *filter, const char *path)
Definition io.c:169
static gboolean _layer_name_non_empty(const char *name)
Definition io.c:151
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:1048
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:131
_lib_location_type_t type
Definition location.c:1
float *const restrict const size_t const size_t ch
#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