Ansel 0.0
A darktable fork - bloat + design vision
Loading...
Searching...
No Matches
iop/lut3d.c
Go to the documentation of this file.
1/*
2 This file is part of darktable,
3 Copyright (C) 2019 Andreas Schneider.
4 Copyright (C) 2019-2020 Heiko Bauke.
5 Copyright (C) 2019 Jakub Filipowicz.
6 Copyright (C) 2019-2022 Pascal Obry.
7 Copyright (C) 2019-2021 Philippe Weyland.
8 Copyright (C) 2019 Tobias Ellinghaus.
9 Copyright (C) 2020 Aldric Renaudin.
10 Copyright (C) 2020, 2023-2026 Aurélien PIERRE.
11 Copyright (C) 2020 Chris Elston.
12 Copyright (C) 2020 David-Tillmann Schaefer.
13 Copyright (C) 2020-2022 Diederik Ter Rahe.
14 Copyright (C) 2020-2021 Hubert Kowalski.
15 Copyright (C) 2020 Marco.
16 Copyright (C) 2020-2021 Ralf Brown.
17 Copyright (C) 2021-2022 Hanno Schwalm.
18 Copyright (C) 2021 Marco Carrarini.
19 Copyright (C) 2022 Martin Bařinka.
20 Copyright (C) 2022 Philipp Lutz.
21 Copyright (C) 2022 Victor Forsiuk.
22 Copyright (C) 2023 Maurizio Paglia.
23 Copyright (C) 2024 Alynx Zhou.
24
25 darktable is free software: you can redistribute it and/or modify
26 it under the terms of the GNU General Public License as published by
27 the Free Software Foundation, either version 3 of the License, or
28 (at your option) any later version.
29
30 darktable is distributed in the hope that it will be useful,
31 but WITHOUT ANY WARRANTY; without even the implied warranty of
32 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
33 GNU General Public License for more details.
34
35 You should have received a copy of the GNU General Public License
36 along with darktable. If not, see <http://www.gnu.org/licenses/>.
37*/
38#ifdef HAVE_CONFIG_H
40#include "common/conf.h"
41#include "config.h"
42#endif
43
44#include "widgets/bauhaus.h"
45#include "system/macros.h"
46#include "system/mem_alloc.h"
47#include "common/logging.h"
49#include <glib/gstdio.h>
50#include "imageio/imageio_png.h"
51#include "common/imagebuf.h"
54#include "develop/iop_profile.h"
55#include "pixel/lut3d.h"
56#include "control/control.h"
57#include "develop/imageop.h"
58#include "develop/imageop_gui.h"
59#include "develop/develop.h"
60#include "widgets/button.h"
61#include "widgets/dialog.h"
62#include "gui/application.h"
63
64#include "iop/iop_api.h"
65
66#include <gtk/gtk.h>
67#include <png.h>
68#include <stdio.h>
69#include <stdlib.h>
70#include <unistd.h>
71#include <dirent.h>
73#include "control/signal.h"
74#if defined (_WIN32)
75#include "win/getdelim.h"
76#endif // defined (_WIN32)
77
79
80#define DT_IOP_LUT3D_MAX_PATHNAME 512
81#define DT_IOP_LUT3D_MAX_LUTNAME 128
82#define DT_IOP_LUT3D_CLUT_LEVEL 48
83#define DT_IOP_LUT3D_MAX_KEYPOINTS 2048
84
86{
87 DT_IOP_SRGB = 0, // $DESCRIPTION: "sRGB"
88 DT_IOP_ARGB, // $DESCRIPTION: "Adobe RGB"
89 DT_IOP_REC709, // $DESCRIPTION: "gamma Rec709 RGB"
90 DT_IOP_LIN_REC709, // $DESCRIPTION: "linear Rec709 RGB"
91 DT_IOP_LIN_REC2020, // $DESCRIPTION: "linear Rec2020 RGB"
92 DT_IOP_ITUR_BT1886, // $DESCRIPTION: "ITU-R BT.1886 (gamma 2.4 Rec709)"
94
96{
97 DT_IOP_TETRAHEDRAL = 0, // $DESCRIPTION: "tetrahedral"
98 DT_IOP_TRILINEAR = 1, // $DESCRIPTION: "trilinear"
99 DT_IOP_PYRAMID = 2, // $DESCRIPTION: "pyramid"
101
103{
105 dt_iop_lut3d_colorspace_t colorspace; // $DEFAULT: DT_IOP_SRGB $DESCRIPTION: "application color space"
106 dt_iop_lut3d_interpolation_t interpolation; // $DEFAULT: DT_IOP_TETRAHEDRAL
107 int nb_keypoints; // $DEFAULT: 0 >0 indicates the presence of compressed lut
111
113{
114 // Whether the deprecation dialog has been shown yet, and for which path, so gui_update() --
115 // which runs on every history change -- raises it once per affected LUT rather than repeatedly.
116 // The flag is separate from the path on purpose: an edit that carries keypoints inside the
117 // history has no filepath at all, and testing the path alone would compare "" against the
118 // zero-initialised buffer, match, and silently skip the warning for precisely the oldest edits.
119 gboolean gmz_warned;
126
133
134const char invalid_filepath_prefix[] = "INVALID >> ";
135
137{
139 float *clut; // cube lut pointer
140 uint16_t level; // cube_size
142
150
151
152const char *name()
153{
154 return _("lut 3D");
155}
156
157const char **description(struct dt_iop_module_t *self)
158{
159 return dt_iop_set_description(self, _("perform color space corrections and apply look"),
160 _("corrective or creative"),
161 _("linear, RGB, display-referred"),
162 _("defined by profile, RGB"),
163 _("linear or non-linear, RGB, display-referred"));
164}
165
170
172{
173 return IOP_GROUP_COLOR;
174}
175
177{
178 return IOP_CS_RGB;
179}
180
181int legacy_params(dt_iop_module_t *self, const void *const old_params, const int old_version, void *new_params,
182 const int new_version)
183{
184 if(old_version == 1 && new_version == 3)
185 {
186 typedef struct dt_iop_lut3d_params_v1_t
187 {
188 char filepath[DT_IOP_LUT3D_MAX_PATHNAME];
189 int colorspace;
190 int interpolation;
191 } dt_iop_lut3d_params_v1_t;
192
193 dt_iop_lut3d_params_v1_t *o = (dt_iop_lut3d_params_v1_t *)old_params;
195 g_strlcpy(n->filepath, o->filepath, sizeof(n->filepath));
196 n->colorspace = o->colorspace;
197 n->interpolation = o->interpolation;
198 n->nb_keypoints = 0;
199 memset(&n->c_clut, 0, sizeof(n->c_clut));
200 memset(&n->lutname, 0, sizeof(n->lutname));
201 return 0;
202 }
203 if(old_version == 2 && new_version == 3)
204 {
205 typedef struct dt_iop_lut3d_params_v2_t
206 {
207 char filepath[DT_IOP_LUT3D_MAX_PATHNAME];
208 int colorspace;
209 int interpolation;
210 int nb_keypoints; // >0 indicates the presence of compressed lut
211 char c_clut[DT_IOP_LUT3D_MAX_KEYPOINTS*2*3];
212 char lutname[DT_IOP_LUT3D_MAX_LUTNAME];
213 uint32_t gmic_version;
214 } dt_iop_lut3d_params_v2_t;
215
216 dt_iop_lut3d_params_v2_t *o = (dt_iop_lut3d_params_v2_t *)old_params;
218 memcpy(n, o, sizeof(dt_iop_lut3d_params_t));
219 return 0;
220 }
221
222 return 1;
223}
224uint16_t calculate_clut_haldclut(dt_iop_lut3d_params_t *const p, const char *const filepath, float **clut)
225{
227 if(read_header(filepath, &png))
228 {
229 fprintf(stderr, "[lut3d] invalid png file %s\n", filepath);
230 dt_control_log(_("invalid png file %s"), filepath);
231 return 0;
232 }
233 dt_print(DT_DEBUG_DEV, "[lut3d] png: width=%d, height=%d, color_type=%d, bit_depth=%d\n", png.width,
234 png.height, png.color_type, png.bit_depth);
235 if (png.bit_depth !=8 && png.bit_depth != 16)
236 {
237 fprintf(stderr, "[lut3d] png bit-depth %d not supported\n", png.bit_depth);
238 dt_control_log(_("png bit-depth %d not supported"), png.bit_depth);
239 fclose(png.f);
240 png_destroy_read_struct(&png.png_ptr, &png.info_ptr, NULL);
241 return 0;
242 }
243
244 // check the file sizes
245 uint16_t level = 2;
246 while(level * level * level < png.width) ++level;
247
248 if(level * level * level != png.width)
249 {
250 if (png.height == 2)
251 {
252 fprintf(stderr, "[lut3d] this Ansel build is not compatible with compressed clut\n");
253 dt_control_log(_("this Ansel build is not compatible with compressed clut"));
254 }
255 else
256 {
257 fprintf(stderr, "[lut3d] invalid level in png file %d %d\n", level, png.width);
258 dt_control_log(_("invalid level in png file %d %d"), level, png.width);
259 }
260 fclose(png.f);
261 png_destroy_read_struct(&png.png_ptr, &png.info_ptr, NULL);
262 return 0;
263 }
264
265 level *= level; // to be equivalent to cube level
266 if(level > 256)
267 {
268 fprintf(stderr, "[lut3d] error - LUT 3D size %d > 256\n", level);
269 dt_control_log(_("error - lut 3D size %d exceeds the maximum supported"), level);
270 fclose(png.f);
271 png_destroy_read_struct(&png.png_ptr, &png.info_ptr, NULL);
272 return 0;
273 }
274 const size_t buf_size = (size_t)png.height * png_get_rowbytes(png.png_ptr, png.info_ptr);
275 dt_print(DT_DEBUG_DEV, "[lut3d] allocating %" G_GSIZE_FORMAT " bytes for png file\n", buf_size);
276 uint8_t *buf = NULL;
277 buf = dt_pixelpipe_cache_alloc_align_cache(buf_size, 0);
278 if(IS_NULL_PTR(buf))
279 {
280 fprintf(stderr, "[lut3d] error allocating buffer for png lut\n");
281 dt_control_log(_("error allocating buffer for png lut"));
282 fclose(png.f);
283 png_destroy_read_struct(&png.png_ptr, &png.info_ptr, NULL);
284 return 0;
285 }
286 if (read_image(&png, buf))
287 {
288 fprintf(stderr, "[lut3d] error - could not read png image `%s'\n", filepath);
289 dt_control_log(_("error - could not read png image %s"), filepath);
291 return 0;
292 }
293 const size_t buf_size_lut = (size_t)png.height * png.height * 3;
294 dt_print(DT_DEBUG_DEV, "[lut3d] allocating %" G_GSIZE_FORMAT " floats for png lut - level %d\n", buf_size_lut, level);
295 float *lclut = dt_pixelpipe_cache_alloc_align_cache(sizeof(float) * buf_size_lut, 0);
296 if(IS_NULL_PTR(lclut))
297 {
298 fprintf(stderr, "[lut3d] error - allocating buffer for png lut\n");
299 dt_control_log(_("error - allocating buffer for png lut"));
301 return 0;
302 }
303 // get clut values
304 const float norm = 1.0f / (powf(2.f, png.bit_depth) - 1.0f);
305 if (png.bit_depth == 8)
306 {
307 for (size_t i = 0; i < buf_size_lut; ++i)
308 lclut[i] = (float)buf[i] * norm;
309 }
310 else
311 {
312 for (size_t i = 0; i < buf_size_lut; ++i)
313 lclut[i] = (256.0f * (float)buf[2*i] + (float)buf[2*i+1]) * norm;
314 }
316 *clut = lclut;
317 return level;
318}
319
320// provided by @rabauke, atof replaces strtod & sccanf which are locale dependent
321double dt_atof(const char *str)
322{
323 if (strncmp(str, "nan", 3) == 0 || strncmp(str, "NAN", 3) == 0)
324 return NAN;
325 double integral_result = 0;
326 double fractional_result = 0;
327 double sign = 1;
328 if (*str == '+')
329 {
330 str++;
331 sign = +1;
332 } else if (*str == '-')
333 {
334 str++;
335 sign = -1;
336 }
337 if (strncmp(str, "inf", 3) == 0 || strncmp(str, "INF", 3) == 0)
338 return sign * INFINITY;
339 // search for end of integral part and parse from
340 // right to left for numerical stability
341 const char * istr_back = str;
342 while (*str >= '0' && *str <= '9')
343 str++;
344 const char * istr_2 = str;
345 double imultiplier = 1;
346 while (istr_2 != istr_back)
347 {
348 --istr_2;
349 integral_result += (*istr_2 - '0') * imultiplier;
350 imultiplier *= 10;
351 }
352 if (*str == '.')
353 {
354 str++;
355 // search for end of fractional part and parse from
356 // right to left for numerical stability
357 const char * fstr_back = str;
358 while (*str >= '0' && *str <= '9')
359 str++;
360 const char * fstr_2 = str;
361 double fmultiplier = 1;
362 while (fstr_2 != fstr_back)
363 {
364 --fstr_2;
365 fractional_result += (*fstr_2 - '0') * fmultiplier;
366 fmultiplier *= 10;
367 }
368 fractional_result /= fmultiplier;
369 }
370 double result = sign * (integral_result + fractional_result);
371 if (*str == 'e' || *str == 'E')
372 {
373 str++;
374 double power_sign = 1;
375 if (*str == '+')
376 {
377 str++;
378 power_sign = +1;
379 }
380 else if (*str == '-')
381 {
382 str++;
383 power_sign = -1;
384 }
385 double power = 0;
386 while (*str >= '0' && *str <= '9')
387 {
388 power *= 10;
389 power += *str - '0';
390 str++;
391 }
392 if (power_sign > 0)
393 result *= pow(10, power);
394 else
395 result /= pow(10, power);
396 }
397 return result;
398}
399
400// return max 3 tokens from the line (separator = ' ' and token length = 50)
401// if nb tokens > 3, the 3rd one captures the last input
402uint8_t parse_cube_line(char *line, char (*token)[50])
403{
404 const int max_token_len = 50;
405 uint8_t i = 0;
406 uint8_t c = 0;
407 char *t = &token[0][0];
408 char *l = line;
409
410 while (*l != 0 && i < max_token_len)
411 {
412 if (*l == '#' || *l == '\n' || *l == '\r')
413 { // end of useful part of the line
414 if (i > 0)
415 {
416 *t = 0;
417 c++;
418 return c;
419 }
420 else
421 {
422 *t = 0;
423 return c;
424 }
425 }
426 if (*l == ' ' || *l == '\t')
427 { // separator
428 if (i > 0)
429 {
430 *t = 0;
431 c++;
432 i = 0;
433 t = &token[c > 2 ? 2 : c][0];
434 }
435 }
436 else
437 { // capture info
438 *t = *l;
439 t++;
440 i++;
441 }
442 l++;
443 // sometimes the last lf is missing
444 if (*l == 0)
445 {
446 *t = 0;
447 c++;
448 return c;
449 }
450 }
451 token[0][max_token_len - 1] = 0;
452 token[1][max_token_len - 1] = 0;
453 token[2][max_token_len - 1] = 0;
454 return c;
455}
456
457uint16_t calculate_clut_cube(const char *const filepath, float **clut)
458{
459 FILE *cube_file;
460 char *line = NULL;
461 size_t len = 0;
462 ssize_t read;
463 char token[3][50];
464 uint16_t level = 0;
465 float *lclut = NULL;
466 uint32_t i = 0;
467 size_t buf_size = 0;
468 uint32_t out_of_range_nb = 0;
469
470 if(!(cube_file = g_fopen(filepath, "r")))
471 {
472 fprintf(stderr, "[lut3d] invalid cube file: %s\n", filepath);
473 dt_control_log(_("error - invalid cube file: %s"), filepath);
474 return 0;
475 }
476 while ((read = getline(&line, &len, cube_file)) != -1)
477 {
478 const uint8_t nb_token = parse_cube_line(line, token);
479 if (nb_token)
480 {
481 if (token[0][0] == 'T') continue;
482 else if (strcmp("DOMAIN_MIN", token[0]) == 0)
483 {
484 if (strtod(token[1], NULL) != 0.0f)
485 {
486 fprintf(stderr, "[lut3d] DOMAIN MIN <> 0.0 is not supported\n");
487 dt_control_log(_("DOMAIN MIN <> 0.0 is not supported"));
489 dt_free(line);
490 fclose(cube_file);
491 }
492 }
493 else if (strcmp("DOMAIN_MAX", token[0]) == 0)
494 {
495 if (strtod(token[1], NULL) != 1.0f)
496 {
497 fprintf(stderr, "[lut3d] DOMAIN MAX <> 1.0 is not supported\n");
498 dt_control_log(_("DOMAIN MAX <> 1.0 is not supported"));
500 dt_free(line);
501 fclose(cube_file);
502 }
503 }
504 else if (strcmp("LUT_1D_SIZE", token[0]) == 0)
505 {
506 fprintf(stderr, "[lut3d] 1D cube lut is not supported\n");
507 dt_control_log(_("[1D cube lut is not supported"));
508 dt_free(line);
509 fclose(cube_file);
510 return 0;
511 }
512 else if (strcmp("LUT_3D_SIZE", token[0]) == 0)
513 {
514 level = atoll(token[1]);
515 if(level > 256)
516 {
517 fprintf(stderr, "[lut3d] error - LUT 3D size %d > 256\n", level);
518 dt_control_log(_("error - lut 3D size %d exceeds the maximum supported"), level);
519 dt_free(line);
520 fclose(cube_file);
521 return 0;
522 }
523 buf_size = level * level * level * 3;
524 dt_print(DT_DEBUG_DEV, "[lut3d] allocating %" G_GSIZE_FORMAT " bytes for cube lut - level %d\n", buf_size, level);
525 lclut = dt_pixelpipe_cache_alloc_align_cache(sizeof(float) * buf_size, 0);
526 if(IS_NULL_PTR(lclut))
527 {
528 fprintf(stderr, "[lut3d] error - allocating buffer for cube lut\n");
529 dt_control_log(_("error - allocating buffer for cube lut"));
530 dt_free(line);
531 fclose(cube_file);
532 return 0;
533 }
534 }
535 else if (nb_token == 3)
536 {
537 if (!level)
538 {
539 fprintf(stderr, "[lut3d] error - cube lut size is not defined\n");
540 dt_control_log(_("error - cube lut size is not defined"));
541 dt_free(line);
542 fclose(cube_file);
543 return 0;
544 }
545 for (int j=0; j < 3; j++)
546 {
547 lclut[i+j] = dt_atof(token[j]);
548 if(isnan(lclut[i+j]))
549 {
550 fprintf(stderr, "[lut3d] error - invalid number line %d\n", (int)i/3);
551 dt_control_log(_("error - cube lut invalid number line %d"), (int)i/3);
552 dt_free(line);
553 fclose(cube_file);
554 return 0;
555 }
556 else if(lclut[i+j] < 0.0 || lclut[i+j] > 1.0)
557 out_of_range_nb++;
558 }
559 i += 3;
560 }
561 }
562 }
563 if (i != buf_size || i == 0)
564 {
565 fprintf(stderr, "[lut3d] error - cube lut lines number %d is not correct, should be %d\n",
566 (int)i/3, (int)buf_size/3);
567 dt_control_log(_("error - cube lut lines number %d is not correct, should be %d"),
568 (int)i/3, (int)buf_size/3);
570 dt_free(line);
571 fclose(cube_file);
572 return 0;
573 }
574 if(out_of_range_nb)
575 {
576 fprintf(stderr, "[lut3d] warning - %d out of range values [0,1]\n", out_of_range_nb);
577 dt_control_log(_("warning - cube lut %d out of range values [0,1]"), out_of_range_nb);
578 }
579 *clut = lclut;
580 dt_free(line);
581 fclose(cube_file);
582 return level;
583}
584
585uint16_t calculate_clut_3dl(const char *const filepath, float **clut)
586{
587 FILE *cube_file;
588 char *line = NULL;
589 size_t len = 0;
590 ssize_t read;
591 char token[3][50];
592 uint16_t level = 0;
593 float *lclut = NULL;
594 int max_value = 0;
595 uint32_t i = 0;
596 size_t buf_size = 0;
597
598 if(!(cube_file = g_fopen(filepath, "r")))
599 {
600 fprintf(stderr, "[lut3d] invalid 3dl file: %s\n", filepath);
601 dt_control_log(_("error - invalid 3dl file: %s"), filepath);
602 return 0;
603 }
604 while ((read = getline(&line, &len, cube_file)) != -1)
605 {
606 const uint8_t nb_token = parse_cube_line(line, token);
607 if (nb_token)
608 {
609 if (!level)
610 {
611 if (nb_token > 3)
612 {
613 // we assume the shaper is linear and gives the size of the cube (level)
614 const int min_shaper = atoll(token[0]);
615 const int max_shaper = atoll(token[2]);
616 if (max_shaper > min_shaper)
617 {
618 level = nb_token; // max nb_token = 50 < 256
619 if(max_shaper < 128)
620 {
621 fprintf(stderr, "[lut3d] error - the maximum shaper lut value %d is too low\n", max_shaper);
622 dt_control_log(_("error - the maximum shaper lut value %d is too low"), max_shaper);
623 dt_free(line);
624 fclose(cube_file);
625 return 0;
626 }
627 buf_size = level * level * level * 3;
628 dt_print(DT_DEBUG_DEV, "[lut3d] allocating %" G_GSIZE_FORMAT " bytes for cube lut - level %d\n", buf_size, level);
629 lclut = dt_pixelpipe_cache_alloc_align_cache(sizeof(float) * buf_size, 0);
630 if(IS_NULL_PTR(lclut))
631 {
632 fprintf(stderr, "[lut3d] error - allocating buffer for cube lut\n");
633 dt_control_log(_("error - allocating buffer for cube lut"));
634 dt_free(line);
635 fclose(cube_file);
636 return 0;
637 }
638 }
639 }
640 }
641 else if (nb_token == 3)
642 {
643 if (!level)
644 {
645 fprintf(stderr, "[lut3d] error - cube lut size is not defined\n");
646 dt_control_log(_("error - cube lut size is not defined"));
647 dt_free(line);
648 fclose(cube_file);
649 return 0;
650 }
651 // indexing starts with blue instead of red. need to restore the right index
652 const uint32_t level2 = level * level;
653 const uint32_t red = i / level2;
654 const uint32_t rr = i - red * level2;
655 const uint32_t green = rr / level;
656 const uint32_t blue = rr - green * level;
657 const uint32_t k = red + level * green + level2 * blue;
658 for (int j=0; j < 3; j++)
659 {
660 const uint32_t value = atoll(token[j]);
661 lclut[k*3+j] = (float)value;
662 if (value > max_value)
663 max_value = value;
664 }
665 i++;
666 if (i * 3 > buf_size)
667 break;
668 }
669 }
670 }
671 if (i * 3 != buf_size || i == 0)
672 {
673 fprintf(stderr, "[lut3d] error - cube lut lines number is not correct\n");
674 dt_control_log(_("error - cube lut lines number is not correct"));
676 dt_free(line);
677 fclose(cube_file);
678 return 0;
679 }
680 dt_free(line);
681 fclose(cube_file);
682
683 // search bit depth: min 2^x > max_value
684 int inorm = 1;
685 while ((inorm < max_value) && (inorm < 65536)) // bit depth 16
686 inorm <<= 1;
687 if (inorm < 128) // bit depth 7
688 {
689 fprintf(stderr, "[lut3d] error - the maximum lut value does not match any valid bit depth\n");
690 dt_control_log(_("error - the maximum lut value does not match any valid bit depth"));
692 return 0;
693 }
694 const float norm = 1.0f / (float)(inorm - 1);
695 // normalize the lut
696 for (i =0; i < buf_size; i++)
697 lclut[i] = CLAMP(lclut[i] * norm, 0.0f, 1.0f);
698 *clut = lclut;
699 return level;
700}
701
702#ifdef HAVE_OPENCL
703int process_cl(struct dt_iop_module_t *self, const dt_dev_pixelpipe_t *pipe, const dt_dev_pixelpipe_iop_t *piece, cl_mem dev_in, cl_mem dev_out)
704{
705 const dt_iop_roi_t *const roi_in = &piece->roi_in;
708 cl_int err = CL_SUCCESS;
709 const float *const clut = (float *)d->clut;
710 const int level = d->level;
711 const int kernel = (d->params.interpolation == DT_IOP_TETRAHEDRAL) ? gd->kernel_lut3d_tetrahedral
712 : (d->params.interpolation == DT_IOP_TRILINEAR) ? gd->kernel_lut3d_trilinear
714 const int colorspace
715 = (d->params.colorspace == DT_IOP_SRGB) ? DT_COLORSPACE_SRGB
716 : (d->params.colorspace == DT_IOP_REC709) ? DT_COLORSPACE_REC709
717 : (d->params.colorspace == DT_IOP_ARGB) ? DT_COLORSPACE_ADOBERGB
718 : (d->params.colorspace == DT_IOP_LIN_REC709) ? DT_COLORSPACE_LIN_REC709
719 : (d->params.colorspace == DT_IOP_ITUR_BT1886) ? DT_COLORSPACE_ITUR_BT1886
721 const dt_iop_order_iccprofile_info_t *const lut_profile
722 = dt_colorspaces_add_profile(colorspace, "", INTENT_PERCEPTUAL);
723 const dt_iop_order_iccprofile_info_t *const work_profile
725 gboolean transform = (!IS_NULL_PTR(work_profile) && !IS_NULL_PTR(lut_profile)) ? TRUE : FALSE;
726 cl_mem clut_cl = NULL;
727 const int devid = pipe->devid;
728 const int width = roi_in->width;
729 const int height = roi_in->height;
730 const size_t sizes[] = { ROUNDUPDWD(width, devid), ROUNDUPDHT(height, devid), 1 };
731
732 if (clut && level)
733 {
734 clut_cl = dt_opencl_copy_host_to_device_constant(devid, sizeof(float) * 3 * level * level * level, (void *)clut);
735 if(IS_NULL_PTR(clut_cl))
736 {
737 fprintf(stderr, "[lut3d process_cl] error allocating memory\n");
738 err = CL_MEM_OBJECT_ALLOCATION_FAILURE;
739 goto cleanup;
740 }
741 if (transform)
742 {
743 const int success = dt_ioppr_transform_image_colorspace_rgb_cl(devid, dev_in, dev_out, width, height,
744 work_profile, lut_profile, "work profile to LUT profile");
745 if (!success)
747 }
748 if (transform)
749 dt_opencl_set_kernel_arg(devid, kernel, 0, sizeof(cl_mem), (void *)&dev_out);
750 else
751 dt_opencl_set_kernel_arg(devid, kernel, 0, sizeof(cl_mem), (void *)&dev_in);
752 dt_opencl_set_kernel_arg(devid, kernel, 1, sizeof(cl_mem), (void *)&dev_out);
753 dt_opencl_set_kernel_arg(devid, kernel, 2, sizeof(int), (void *)&width);
754 dt_opencl_set_kernel_arg(devid, kernel, 3, sizeof(int), (void *)&height);
755 dt_opencl_set_kernel_arg(devid, kernel, 4, sizeof(cl_mem), (void *)&clut_cl);
756 dt_opencl_set_kernel_arg(devid, kernel, 5, sizeof(int), (void *)&level);
757 err = dt_opencl_enqueue_kernel_2d(devid, kernel, sizes);
758 if (transform)
760 lut_profile, work_profile, "LUT profile to work profile");
761 }
762 else
763 { // no lut: identity kernel
764 dt_opencl_set_kernel_arg(devid, gd->kernel_lut3d_none, 0, sizeof(cl_mem), (void *)&dev_in);
765 dt_opencl_set_kernel_arg(devid, gd->kernel_lut3d_none, 1, sizeof(cl_mem), (void *)&dev_out);
766 dt_opencl_set_kernel_arg(devid, gd->kernel_lut3d_none, 2, sizeof(int), (void *)&width);
767 dt_opencl_set_kernel_arg(devid, gd->kernel_lut3d_none, 3, sizeof(int), (void *)&height);
768 err = dt_opencl_enqueue_kernel_2d(devid, gd->kernel_lut3d_none, sizes);
769 }
770 if(err != CL_SUCCESS)
771 {
772 fprintf(stderr, "[lut3d process_cl] error %i enqueue kernel\n", err);
773 goto cleanup;
774 }
775
776cleanup:
778
779 if(err != CL_SUCCESS) dt_print(DT_DEBUG_OPENCL, "[opencl_lut3d] couldn't enqueue kernel! %d\n", err);
780 return (err == CL_SUCCESS) ? TRUE : FALSE;
781}
782#endif
783
784int process(struct dt_iop_module_t *self, const dt_dev_pixelpipe_t *pipe, const dt_dev_pixelpipe_iop_t *piece, const void *const ibuf, void *const obuf)
785{
786 const dt_iop_roi_t *const roi_in = &piece->roi_in;
788 const int width = roi_in->width;
789 const int height = roi_in->height;
790 const int ch = piece->dsc_in.channels;
791 const float *const clut = (float *)d->clut;
792 const uint16_t level = d->level;
793 const int interpolation = d->params.interpolation;
794 const int colorspace
795 = (d->params.colorspace == DT_IOP_SRGB) ? DT_COLORSPACE_SRGB
796 : (d->params.colorspace == DT_IOP_REC709) ? DT_COLORSPACE_REC709
797 : (d->params.colorspace == DT_IOP_ARGB) ? DT_COLORSPACE_ADOBERGB
798 : (d->params.colorspace == DT_IOP_LIN_REC709) ? DT_COLORSPACE_LIN_REC709
799 : (d->params.colorspace == DT_IOP_ITUR_BT1886) ? DT_COLORSPACE_ITUR_BT1886
801 const dt_iop_order_iccprofile_info_t *const lut_profile
802 = dt_colorspaces_add_profile(colorspace, "", INTENT_PERCEPTUAL);
803 const dt_iop_order_iccprofile_info_t *const work_profile
805 const gboolean transform = (!IS_NULL_PTR(work_profile) && !IS_NULL_PTR(lut_profile)) ? TRUE : FALSE;
806 if (!IS_NULL_PTR(clut))
807 {
808 if (transform)
809 {
811 work_profile, lut_profile, "work profile to LUT profile");
812 dt_lut3d_apply(obuf, obuf, (size_t)width * height, clut, level, 1.f,
813 (dt_lut3d_interpolation_t)interpolation);
815 lut_profile, work_profile, "LUT profile to work profile");
816 }
817 else
818 {
819 dt_lut3d_apply(ibuf, obuf, (size_t)width * height, clut, level, 1.f,
820 (dt_lut3d_interpolation_t)interpolation);
821 }
822 }
823 else // no clut
824 {
826 }
827 return 0;
828}
829
830void filepath_set_unix_separator(char *filepath)
831{ // use the unix separator as it works also on windows
832 const int len = strlen(filepath);
833 for(int i=0; i<len; ++i)
834 if (filepath[i]=='\\') filepath[i] = '/';
835}
836
838{
839 const int program = 28; // rgbcurve.cl, from programs.conf
842 module->data = gd;
843 gd->kernel_lut3d_tetrahedral = dt_opencl_create_kernel(program, "lut3d_tetrahedral");
844 gd->kernel_lut3d_trilinear = dt_opencl_create_kernel(program, "lut3d_trilinear");
845 gd->kernel_lut3d_pyramid = dt_opencl_create_kernel(program, "lut3d_pyramid");
846 gd->kernel_lut3d_none = dt_opencl_create_kernel(program, "lut3d_none");
847
848}
849
859
860/* --- Deprecated: G'MIC compressed LUTs (.gmz) ------------------------------------------------
861 *
862 * Reading a .gmz meant linking the whole G'MIC library for three functions, and G'MIC brings a
863 * 7.4 MB shared object plus ~50 transitive ones -- among them libcurl, OpenSSL, Kerberos and an
864 * X11 display stack -- into a photo editor. Decompressing the format is not a matter of parsing
865 * it either: the keypoints it stores are reconstructed either by a dense RBF solve or by a
866 * multiscale diffusion PDE, both implemented in G'MIC's own scripting language. That is a lot of
867 * machinery, and a lot of attack surface, for one LUT container.
868 *
869 * So .gmz is gone, and G'MIC with it. Every collection that ships .gmz also ships .cube, and
870 * G'MIC's own CLI converts one to the other in a single command; the module documentation spells
871 * out the procedure. See DT_LUT3D_GMZ_DOC_URL.
872 *
873 * Two things mark an edit as affected, and both must be caught: a filepath still pointing at a
874 * .gmz, and -- for edits made before the removal -- keypoints serialised straight into the
875 * history by an older version, which is what nb_keypoints > 0 means. The params keep those fields
876 * so that existing histories still deserialise; they are simply never read any more.
877 */
878#define DT_LUT3D_GMZ_DOC_URL "https://ansel.photos/en/doc/views/darkroom/modules/lut-3d/"
879
880static gboolean _params_need_gmz(const dt_iop_lut3d_params_t *const p)
881{
882 if(p->nb_keypoints > 0) return TRUE;
883 return p->filepath[0] && (g_str_has_suffix(p->filepath, ".gmz") || g_str_has_suffix(p->filepath, ".GMZ"));
884}
885
886static int calculate_clut(dt_iop_lut3d_params_t *const p, float **clut)
887{
888 uint16_t level = 0;
889 const char *filepath = p->filepath;
890 gchar *lutfolder = dt_conf_get_string("plugins/darkroom/lut3d/def_path");
891 if (filepath[0] && lutfolder[0])
892 {
893 char *fullpath = g_build_filename(lutfolder, filepath, NULL);
894 if (g_str_has_suffix (filepath, ".png") || g_str_has_suffix (filepath, ".PNG"))
895 {
896 level = calculate_clut_haldclut(p, fullpath, clut);
897 }
898 else if (g_str_has_suffix (filepath, ".cube") || g_str_has_suffix (filepath, ".CUBE"))
899 {
900 level = calculate_clut_cube(fullpath, clut);
901 }
902 else if (g_str_has_suffix (filepath, ".3dl") || g_str_has_suffix (filepath, ".3DL"))
903 {
904 level = calculate_clut_3dl(fullpath, clut);
905 }
906 dt_free(fullpath);
907 }
908 dt_free(lutfolder);
909 return level;
910}
911
912
915{
918
919 if (strcmp(p->filepath, d->params.filepath) != 0 || strcmp(p->lutname, d->params.lutname) != 0 )
920 { // new clut file
921 if (d->clut)
922 { // reset current clut if any
924 d->clut = NULL;
925 d->level = 0;
926 }
927
929 {
930 // Reached from every pipe -- darkroom, thumbnails, export -- and it is deliberately the
931 // only notice outside the darkroom: one modal per thumbnail over a lighttable grid would
932 // be unusable, so there the image has to be named in the log instead. Guarded by the
933 // filepath comparison above, so it is printed when the LUT changes, not once per frame.
934 fprintf(stderr,
935 "[lut3d] %s: compressed G'MIC LUTs (.gmz) are no longer supported, so no LUT was "
936 "applied to this image. Convert it to .cube -- see %s\n",
937 (pipe && pipe->dev) ? pipe->dev->image_storage.filename : "(unknown image)",
939 d->level = 0;
940 }
941 else
942 d->level = calculate_clut(p, &d->clut);
943 }
944 memcpy(&d->params, p, sizeof(dt_iop_lut3d_params_t));
945}
946
948{
949 piece->data = dt_calloc_align(sizeof(dt_iop_lut3d_data_t));
950 piece->data_size = sizeof(dt_iop_lut3d_data_t);
952 memcpy(&d->params, self->default_params, sizeof(dt_iop_lut3d_params_t));
953 d->clut = NULL;
954 d->level = 0;
955 d->params.filepath[0] = '\0';
956}
957
959{
960 /* init_pipe() may have failed to allocate, and cleanup runs regardless. */
961 if(IS_NULL_PTR(piece->data)) return;
964 d->clut = NULL;
965 d->level = 0;
966 dt_free_align(piece->data);
967 piece->data = NULL;
968}
969
970static void filepath_callback(GtkWidget *widget, dt_iop_module_t *self)
971{
972 if(dt_gui_widgets_suppressed()) return;
974 char filepath[DT_IOP_LUT3D_MAX_PATHNAME];
975 g_strlcpy(filepath, dt_bauhaus_combobox_get_text(widget), sizeof(filepath));
976 fprintf(stdout, "filepath: %s\n", filepath);
977
978 if (!g_str_has_prefix(filepath, invalid_filepath_prefix))
979 {
981 g_strlcpy(p->filepath, filepath, sizeof(p->filepath));
982 dt_dev_add_history_item(self->dev, self, TRUE, TRUE);
983 }
984}
985
986
987// remove root lut folder from path
988static void remove_root_from_path(const char *const lutfolder, char *const filepath)
989{
990 const int j = strlen(lutfolder) + 1;
991 int i;
992 for(i = 0; filepath[i+j] != '\0'; i++)
993 filepath[i] = filepath[i+j];
994 filepath[i] = '\0';
995}
996
997gboolean check_extension(char *filename)
998{
999 gboolean res = FALSE;
1000 if (!filename || !filename[0]) return res;
1001 char *p = g_strrstr(filename,".");
1002 if (IS_NULL_PTR(p)) return res;
1003 char *fext = g_ascii_strdown(g_strdup(p), -1);
1004 if (!g_strcmp0(fext, ".png") || !g_strcmp0(fext, ".cube") || !g_strcmp0(fext, ".3dl") ) res = TRUE;
1005 dt_free(fext);
1006 return res;
1007}
1008
1009static gint array_str_cmp(gconstpointer a, gconstpointer b)
1010{
1011 return g_strcmp0(((dt_bauhaus_combobox_entry_t *)a)->label, ((dt_bauhaus_combobox_entry_t *)b)->label);
1012}
1013
1014// update filepath combobox with all files in the current folder
1015static void update_filepath_combobox(dt_iop_lut3d_gui_data_t *g, char *filepath, char *lutfolder)
1016{
1017 if (!filepath[0])
1018 dt_bauhaus_combobox_clear(g->filepath);
1019 else if (!dt_bauhaus_combobox_set_from_text(g->filepath, filepath))
1020 {
1021 // new folder -> update the files list
1022 char *relativepath = g_path_get_dirname(filepath);
1023 char *folder = g_build_filename(lutfolder, relativepath, NULL);
1024 struct dirent *dir;
1025 DIR *d = opendir(folder);
1026 if(!IS_NULL_PTR(d))
1027 {
1028 dt_bauhaus_combobox_clear(g->filepath);
1029 while ((dir = readdir(d)) != NULL)
1030 {
1031 char *file = dir->d_name;
1032 if (check_extension(file))
1033 {
1034 char *ofilepath = (strcmp(relativepath, ".") != 0)
1035 ? g_build_filename(relativepath, file, NULL)
1036 : g_strdup(file);
1037 filepath_set_unix_separator(ofilepath);
1038 dt_bauhaus_combobox_add(g->filepath, ofilepath);
1039 dt_free(ofilepath);
1040 }
1041 }
1042 dt_bauhaus_widget_t *w = DT_BAUHAUS_WIDGET(g->filepath);
1043 dt_bauhaus_combobox_data_t *combo_data = &w->data.combobox;
1044 g_ptr_array_sort(combo_data->entries, array_str_cmp);
1045 closedir(d);
1046 }
1047 if(!dt_bauhaus_combobox_set_from_text(g->filepath, filepath))
1048 { // file may have disappeared - show it
1049 char *invalidfilepath = g_strconcat(invalid_filepath_prefix, filepath, NULL);
1050 dt_bauhaus_combobox_add(g->filepath, invalidfilepath);
1051 dt_bauhaus_combobox_set_from_text(g->filepath, invalidfilepath);
1052 dt_free(invalidfilepath);
1053 }
1054 dt_free(relativepath);
1055 dt_free(folder);
1056 }
1057}
1058
1059static void button_clicked(GtkWidget *widget, dt_iop_module_t *self)
1060{
1063 gchar* lutfolder = dt_conf_get_string("plugins/darkroom/lut3d/def_path");
1064 if (strlen(lutfolder) == 0)
1065 {
1066 fprintf(stderr, "[lut3d] Lut root folder not defined\n");
1067 dt_control_log(_("lut root folder not defined"));
1068 dt_free(lutfolder);
1069 return;
1070 }
1072 GtkFileChooserNative *filechooser = gtk_file_chooser_native_new(
1073 _("select lut file"), GTK_WINDOW(win), GTK_FILE_CHOOSER_ACTION_OPEN,
1074 _("_select"), _("_cancel"));
1075 gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(filechooser), FALSE);
1076
1077 char *composed = g_build_filename(lutfolder, p->filepath, NULL);
1078 if (strlen(p->filepath) == 0 || g_access(composed, F_OK) == -1)
1079 gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(filechooser), lutfolder);
1080 else
1081 gtk_file_chooser_select_filename(GTK_FILE_CHOOSER(filechooser), composed);
1082 dt_free(composed);
1083
1084 GtkFileFilter* filter = GTK_FILE_FILTER(gtk_file_filter_new());
1085 gtk_file_filter_add_pattern(filter, "*.png");
1086 gtk_file_filter_add_pattern(filter, "*.PNG");
1087 gtk_file_filter_add_pattern(filter, "*.cube");
1088 gtk_file_filter_add_pattern(filter, "*.CUBE");
1089 gtk_file_filter_add_pattern(filter, "*.3dl");
1090 gtk_file_filter_add_pattern(filter, "*.3DL");
1091 gtk_file_filter_set_name(filter, _("hald cluts (png) or 3D lut (cube or 3dl)"));
1092 gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(filechooser), filter);
1093 gtk_file_chooser_set_filter(GTK_FILE_CHOOSER(filechooser), filter);
1094
1095 // let this option to allow the user to see the actual content of the folder
1096 // but any selected file with ext <> png or cube will be ignored
1097 filter = GTK_FILE_FILTER(gtk_file_filter_new());
1098 gtk_file_filter_add_pattern(filter, "*");
1099 gtk_file_filter_set_name(filter, _("all files"));
1100 gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(filechooser), filter);
1101
1102 if(gtk_native_dialog_run(GTK_NATIVE_DIALOG(filechooser)) == GTK_RESPONSE_ACCEPT)
1103 {
1104 gchar *filepath = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(filechooser));
1105 if (strcmp(lutfolder, filepath) < 0)
1106 {
1107 remove_root_from_path(lutfolder, filepath);
1109 update_filepath_combobox(g, filepath, lutfolder);
1110 }
1111 else if (!filepath[0])// file chosen outside of root folder
1112 {
1113 fprintf(stderr, "[lut3d] select file outside Lut root folder is not allowed\n");
1114 dt_control_log(_("select file outside Lut root folder is not allowed"));
1115 }
1116 dt_free(filepath);
1117 gtk_widget_set_sensitive(g->filepath, p->filepath[0]);
1118 g_strlcpy(p->filepath, dt_bauhaus_combobox_get_text(g->filepath), sizeof(p->filepath));
1119 dt_dev_add_history_item(self->dev, self, TRUE, TRUE);
1120 }
1121 dt_free(lutfolder);
1122 g_object_unref(filechooser);
1123}
1124
1126{
1127 if(IS_NULL_PTR(self)) return;
1129 if(IS_NULL_PTR(g) || IS_NULL_PTR(g->colorspace)) return;
1130 GList *iop_order_list = self->dev->iop_order_list;
1131 const int order_lut3d = dt_ioppr_get_iop_order(iop_order_list, self->op, self->multi_priority);
1132 const int order_colorin = dt_ioppr_get_iop_order(iop_order_list, "colorin", -1);
1133 const int order_colorout = dt_ioppr_get_iop_order(iop_order_list, "colorout", -1);
1134 if(order_lut3d < order_colorin || order_lut3d > order_colorout)
1135 {
1136 gtk_widget_hide(g->colorspace);
1137 }
1138 else
1139 {
1140 gtk_widget_show(g->colorspace);
1141 }
1142}
1143
1144/* Raised only from gui_update(), which means only in the darkroom: module GUIs are not
1145 * instantiated for lighttable thumbnails or for export, so there is no path by which this can
1146 * fire once per image over a grid. Outside the darkroom the notice is the log line in
1147 * commit_params(), which names the image instead. */
1149{
1151 if(IS_NULL_PTR(win)) return;
1152
1153 GtkWidget *dialog = gtk_message_dialog_new(
1154 GTK_WINDOW(win), GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_WARNING, GTK_BUTTONS_CLOSE,
1155 _("This edit uses a compressed G'MIC LUT (.gmz), which Ansel no longer supports.\n\n"
1156 "No LUT is being applied to this image, so it will not look as it did. Convert the LUT "
1157 "to the .cube format and select it again -- the documentation gives the command.\n\n"
1158 "%s"),
1159 p->filepath[0] ? p->filepath : _("(the LUT was stored inside this edit)"));
1160 gtk_window_set_title(GTK_WINDOW(dialog), _("3D LUT: unsupported file format"));
1161
1162 gtk_dialog_add_button(GTK_DIALOG(dialog), _("open documentation"), GTK_RESPONSE_HELP);
1163 if(gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_HELP)
1164 gtk_show_uri_on_window(GTK_WINDOW(win), DT_LUT3D_GMZ_DOC_URL, GDK_CURRENT_TIME, NULL);
1165
1166 GtkWindow *parent = gtk_window_get_transient_for(GTK_WINDOW(dialog));
1167 gtk_widget_destroy(dialog);
1168 dt_gui_refocus_parent(parent);
1169}
1170
1172{
1173 if(IS_NULL_PTR(self)) return;
1175 if(IS_NULL_PTR(g)) return;
1177 if(IS_NULL_PTR(p)) return;
1178 gchar *lutfolder = dt_conf_get_string("plugins/darkroom/lut3d/def_path");
1179 if (!lutfolder[0])
1180 {
1181 gtk_widget_set_sensitive(g->hbox, FALSE);
1182 gtk_widget_set_sensitive(g->filepath, FALSE);
1183 dt_bauhaus_combobox_clear(g->filepath);
1184 }
1185 else
1186 {
1187 gtk_widget_set_sensitive(g->hbox, TRUE);
1188 gtk_widget_set_sensitive(g->filepath, p->filepath[0]);
1189 update_filepath_combobox(g, p->filepath, lutfolder);
1190 }
1191 dt_free(lutfolder);
1192
1194
1195 if(_params_need_gmz(p) && (!g->gmz_warned || strcmp(g->gmz_warned_for, p->filepath) != 0))
1196 {
1197 g->gmz_warned = TRUE;
1198 g_strlcpy(g->gmz_warned_for, p->filepath, sizeof(g->gmz_warned_for));
1200 }
1201}
1202
1203void module_moved_callback(gpointer instance, dt_iop_module_t *self)
1204{
1206}
1207
1209{
1211
1212 self->gui->widget = gtk_box_new(GTK_ORIENTATION_VERTICAL, DT_GUI_BOX_SPACING);
1213
1214 g->hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, DT_GUI_BOX_SPACING);
1216 gtk_widget_set_tooltip_text(button, _("select a png (haldclut)"
1217 ", a cube or a 3dl file "
1218 "CAUTION: 3D lut folder must be set in preferences/processing before choosing the lut file"));
1219 gtk_box_pack_start(GTK_BOX(g->hbox), button, FALSE, FALSE, 0);
1220 g_signal_connect(G_OBJECT(button), "clicked", G_CALLBACK(button_clicked), self);
1221
1223 dt_bauhaus_combobox_set_entries_ellipsis(g->filepath, PANGO_ELLIPSIZE_MIDDLE);
1224 gtk_box_pack_start(GTK_BOX(g->hbox), g->filepath, TRUE, TRUE, 0);
1225 gtk_widget_set_tooltip_text(g->filepath,
1226 _("the file path (relative to lut folder) is saved with image (and not the lut data themselves)"));
1227 g_signal_connect(G_OBJECT(g->filepath), "value-changed", G_CALLBACK(filepath_callback), self);
1228
1229 gtk_box_pack_start(GTK_BOX(self->gui->widget), GTK_WIDGET(g->hbox), TRUE, TRUE, 0);
1230
1231
1232 g->colorspace = dt_bauhaus_combobox_from_params(self, "colorspace");
1233 gtk_widget_set_tooltip_text(g->colorspace, _("select the color space in which the LUT has to be applied"));
1234
1235 g->interpolation = dt_bauhaus_combobox_from_params(self, N_("interpolation"));
1236 gtk_widget_set_tooltip_text(g->interpolation, _("select the interpolation method"));
1237
1239 G_CALLBACK(module_moved_callback), self);
1240}
1241
1248
1249// clang-format off
1250// modelines: These editor modelines have been set for all relevant files by tools/update_modelines.py
1251// vim: shiftwidth=2 expandtab tabstop=2 cindent
1252// kate: tab-indents: off; indent-width 2; replace-tabs on; indent-mode cstyle; remove-trailing-spaces modified;
1253// clang-format on
GtkWidget * dt_gui_main_window(void)
#define TRUE
Definition ashift_lsd.c:162
#define FALSE
Definition ashift_lsd.c:158
void cleanup(dt_imageio_module_format_t *self)
Definition avif.c:170
void dt_bauhaus_combobox_clear(GtkWidget *widget)
Definition bauhaus.c:1997
void dt_bauhaus_combobox_set_entries_ellipsis(GtkWidget *widget, PangoEllipsizeMode ellipis)
Definition bauhaus.c:1872
const char * dt_bauhaus_combobox_get_text(GtkWidget *widget)
Definition bauhaus.c:1970
GtkWidget * dt_bauhaus_combobox_new(dt_bauhaus_t *bh, dt_gui_module_t *self)
Definition bauhaus.c:1698
gboolean dt_bauhaus_combobox_set_from_text(GtkWidget *widget, const char *text)
Definition bauhaus.c:2100
void dt_bauhaus_combobox_add(GtkWidget *widget, const char *text)
Definition bauhaus.c:1824
#define DT_BAUHAUS_WIDGET(obj)
Definition bauhaus.h:62
GtkWidget * dtgtk_button_new(DTGTKCairoPaintIconFunc paint, gint paintflags, void *paintdata)
Definition button.c:135
static void transform(float *x, float *o, const float *m, const float t_h, const float t_v)
Definition clipping.c:487
@ IOP_CS_RGB
dt_iop_order_iccprofile_info_t * dt_colorspaces_add_profile(const dt_colorspaces_color_profile_type_t profile_type, const char *profile_filename, const int intent)
Find-or-build the derived matrix/LUT data for a profile identity, memoised.
void dt_ioppr_transform_image_colorspace_rgb(const float *const restrict image_in, float *const restrict image_out, const int width, const int height, const dt_iop_order_iccprofile_info_t *const profile_info_from, const dt_iop_order_iccprofile_info_t *const profile_info_to, const char *message)
int dt_ioppr_transform_image_colorspace_rgb_cl(const int devid, cl_mem dev_img_in, cl_mem dev_img_out, const int width, const int height, const dt_iop_order_iccprofile_info_t *const profile_info_from, const dt_iop_order_iccprofile_info_t *const profile_info_to, const char *message)
OpenCL counterpart of dt_ioppr_transform_image_colorspace_rgb().
const int t
The colour-profile module's API: which profiles exist, and how to apply one.
struct _GtkWidget GtkWidget
GtkWidget, opaque, spelled exactly as GTK spells it.
Definition colorspaces.h:98
gchar * dt_conf_get_string(const char *name)
Read the stored string for name as a private copy.
void dt_control_log(const char *msg,...)
Definition control.c:824
struct dt_bauhaus_t * dt_bauhaus_get_global(void)
Definition darktable.c:646
#define dt_dev_add_history_item(dev, module, enable, redraw)
void dt_iop_params_t
Definition dev_history.h:43
__DT_CLONE_TARGETS__ dt_iop_order_iccprofile_info_t * dt_ioppr_get_iop_work_profile_info(struct dt_iop_module_t *module, GList *iop_list)
void dt_gui_refocus_parent(GtkWindow *parent)
Definition dialog.c:64
GtkWidget * folder
destination folder chooser
const int res
Definition dtpthread.h:351
ssize_t getline(char **lineptr, size_t *n, FILE *stream)
Definition getdelim.c:157
#define DT_GUI_MODULE(x)
static void dt_iop_image_copy_by_size(float *const __restrict__ out, const float *const __restrict__ in, const size_t width, const size_t height, const size_t ch)
Definition imagebuf.h:91
int read_header(const char *filename, dt_imageio_png_t *png)
Definition imageio_png.c:44
const char ** dt_iop_set_description(dt_iop_module_t *module, const char *main_text, const char *purpose, const char *input, const char *process, const char *output)
Definition imageop.c:1893
@ IOP_FLAGS_INCLUDE_IN_STYLES
Definition imageop.h:185
@ IOP_FLAGS_SUPPORTS_BLENDING
Definition imageop.h:186
@ IOP_GROUP_COLOR
Definition imageop.h:158
GtkWidget * dt_bauhaus_combobox_from_params(dt_iop_module_t *self, const char *param)
#define IOP_GUI_FREE
Definition imageop_gui.h:96
static dt_iop_gui_data_t * dt_iop_gui_data(const struct dt_iop_module_t *m)
The module's GUI data blob, NULL-safe for headless callers: IOP process() implementations read it for...
Definition imageop_gui.h:81
#define IOP_GUI_ALLOC(module)
Definition imageop_gui.h:93
static float kernel(const float *x, const float *y)
void commit_params(struct dt_iop_module_t *self, dt_iop_params_t *p1, dt_dev_pixelpipe_t *pipe, dt_dev_pixelpipe_iop_t *piece)
Definition iop/lut3d.c:913
static int calculate_clut(dt_iop_lut3d_params_t *const p, float **clut)
Definition iop/lut3d.c:886
static gboolean _params_need_gmz(const dt_iop_lut3d_params_t *const p)
Definition iop/lut3d.c:880
const char ** description(struct dt_iop_module_t *self)
Definition iop/lut3d.c:157
int process(struct dt_iop_module_t *self, const dt_dev_pixelpipe_t *pipe, const dt_dev_pixelpipe_iop_t *piece, const void *const ibuf, void *const obuf)
Definition iop/lut3d.c:784
int default_group()
Definition iop/lut3d.c:171
uint16_t calculate_clut_3dl(const char *const filepath, float **clut)
Definition iop/lut3d.c:585
const char invalid_filepath_prefix[]
Definition iop/lut3d.c:134
void gui_update(dt_iop_module_t *self)
Refresh GUI controls from current params and configuration.
Definition iop/lut3d.c:1171
gboolean check_extension(char *filename)
Definition iop/lut3d.c:997
dt_iop_lut3d_interpolation_t
Definition iop/lut3d.c:96
@ DT_IOP_PYRAMID
Definition iop/lut3d.c:99
@ DT_IOP_TRILINEAR
Definition iop/lut3d.c:98
@ DT_IOP_TETRAHEDRAL
Definition iop/lut3d.c:97
#define DT_IOP_LUT3D_MAX_KEYPOINTS
Definition iop/lut3d.c:83
void init_pipe(struct dt_iop_module_t *self, dt_dev_pixelpipe_t *pipe, dt_dev_pixelpipe_iop_t *piece)
Definition iop/lut3d.c:947
double dt_atof(const char *str)
Definition iop/lut3d.c:321
uint8_t parse_cube_line(char *line, char(*token)[50])
Definition iop/lut3d.c:402
const char * name()
Definition iop/lut3d.c:152
void module_moved_callback(gpointer instance, dt_iop_module_t *self)
Definition iop/lut3d.c:1203
static void filepath_callback(GtkWidget *widget, dt_iop_module_t *self)
Definition iop/lut3d.c:970
void gui_init(dt_iop_module_t *self)
Definition iop/lut3d.c:1208
static void button_clicked(GtkWidget *widget, dt_iop_module_t *self)
Definition iop/lut3d.c:1059
void filepath_set_unix_separator(char *filepath)
Definition iop/lut3d.c:830
dt_iop_lut3d_colorspace_t
Definition iop/lut3d.c:86
@ DT_IOP_SRGB
Definition iop/lut3d.c:87
@ DT_IOP_ITUR_BT1886
Definition iop/lut3d.c:92
@ DT_IOP_LIN_REC2020
Definition iop/lut3d.c:91
@ DT_IOP_ARGB
Definition iop/lut3d.c:88
@ DT_IOP_REC709
Definition iop/lut3d.c:89
@ DT_IOP_LIN_REC709
Definition iop/lut3d.c:90
static void update_filepath_combobox(dt_iop_lut3d_gui_data_t *g, char *filepath, char *lutfolder)
Definition iop/lut3d.c:1015
#define DT_IOP_LUT3D_MAX_PATHNAME
Definition iop/lut3d.c:80
void gui_cleanup(dt_iop_module_t *self)
Definition iop/lut3d.c:1242
static gint array_str_cmp(gconstpointer a, gconstpointer b)
Definition iop/lut3d.c:1009
void cleanup_global(dt_iop_module_so_t *module)
Definition iop/lut3d.c:850
int default_colorspace(dt_iop_module_t *self, dt_dev_pixelpipe_t *pipe, const dt_dev_pixelpipe_iop_t *piece)
Definition iop/lut3d.c:176
static void _warn_gmz_deprecated(const dt_iop_lut3d_params_t *const p)
Definition iop/lut3d.c:1148
int flags()
Definition iop/lut3d.c:166
uint16_t calculate_clut_haldclut(dt_iop_lut3d_params_t *const p, const char *const filepath, float **clut)
Definition iop/lut3d.c:224
void cleanup_pipe(struct dt_iop_module_t *self, dt_dev_pixelpipe_t *pipe, dt_dev_pixelpipe_iop_t *piece)
Definition iop/lut3d.c:958
dt_lut3d_cols_t
Definition iop/lut3d.c:128
@ DT_LUT3D_COL_NAME
Definition iop/lut3d.c:129
@ DT_LUT3D_NUM_COLS
Definition iop/lut3d.c:131
@ DT_LUT3D_COL_VISIBLE
Definition iop/lut3d.c:130
static void _show_hide_colorspace(dt_iop_module_t *self)
Definition iop/lut3d.c:1125
uint16_t calculate_clut_cube(const char *const filepath, float **clut)
Definition iop/lut3d.c:457
#define DT_IOP_LUT3D_MAX_LUTNAME
Definition iop/lut3d.c:81
void init_global(dt_iop_module_so_t *module)
Definition iop/lut3d.c:837
static void remove_root_from_path(const char *const lutfolder, char *const filepath)
Definition iop/lut3d.c:988
int process_cl(struct dt_iop_module_t *self, const dt_dev_pixelpipe_t *pipe, const dt_dev_pixelpipe_iop_t *piece, cl_mem dev_in, cl_mem dev_out)
Definition iop/lut3d.c:703
#define DT_LUT3D_GMZ_DOC_URL
Definition iop/lut3d.c:878
int legacy_params(dt_iop_module_t *self, const void *const old_params, const int old_version, void *new_params, const int new_version)
Definition iop/lut3d.c:181
int dt_ioppr_get_iop_order(GList *iop_order_list, const char *op_name, const int multi_priority)
Return the iop_order for a given operation/instance pair.
Definition iop_order.c:913
int read_image(dt_imageio_module_data_t *jpg_tmp, uint8_t *out)
Definition jpeg.c:430
@ DT_DEBUG_OPENCL
Definition logging.h:57
@ DT_DEBUG_DEV
Definition logging.h:53
void dt_print(dt_debug_thread_t thread, const char *msg,...) __attribute__((format(printf
Print to stdout when thread is enabled, prefixed with seconds since startup.
float *const restrict const size_t k
float *const restrict const size_t const size_t ch
dt_lut3d_interpolation_t
Definition lut3d.h:26
#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_align(ptr)
Release memory from dt_alloc_align() and set ptr to NULL.
Definition mem_alloc.h:214
static void * dt_calloc_align(size_t size)
dt_alloc_align() followed by a zero fill.
Definition mem_alloc.h:225
#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 DT_MODULE_INTROSPECTION(MODVER, PARAMSTYPE)
DT_MODULE() for a module whose params struct is introspected.
int dt_opencl_enqueue_kernel_2d(const int dev, const int kernel, const size_t *sizes)
Definition opencl.c:2554
int dt_opencl_create_kernel(const int prog, const char *name)
Definition opencl.c:2448
void * dt_opencl_copy_host_to_device_constant(const int devid, const size_t size, void *host)
Definition opencl.c:2750
void dt_opencl_free_kernel(const int kernel)
Definition opencl.c:2491
int dt_opencl_set_kernel_arg(const int dev, const int kernel, const int num, const size_t size, const void *arg)
Definition opencl.c:2545
void dt_opencl_release_mem_object(cl_mem mem)
Definition opencl.c:2805
#define ROUNDUPDHT(a, b)
Definition opencl.h:86
#define ROUNDUPDWD(a, b)
Definition opencl.h:85
void dt_lut3d_apply(const float *const in, float *const out, const size_t pixel_nb, const float *const clut, const uint16_t level, const float normalization, const dt_lut3d_interpolation_t interpolation)
Apply one interpolation model over a packed RGB CLUT.
#define dt_pixelpipe_cache_alloc_align_cache(size, id)
#define dt_pixelpipe_cache_free_align(mem)
@ DT_COLORSPACE_ADOBERGB
@ DT_COLORSPACE_SRGB
sRGB – registered TWICE, and the two entries are not interchangeable.
@ DT_COLORSPACE_REC709
@ DT_COLORSPACE_LIN_REC2020
@ DT_COLORSPACE_ITUR_BT1886
@ DT_COLORSPACE_LIN_REC709
#define DT_DEBUG_CONTROL_SIGNAL_DISCONNECT(ctlsig, cb, user_data)
Definition signal.h:407
struct dt_control_signal_t * dt_control_signal_get_global(void)
Definition darktable.c:616
@ DT_SIGNAL_DEVELOP_MODULE_MOVED
This signal is raised when order of modules in pipeline is changed.
Definition signal.h:221
#define DT_DEBUG_CONTROL_SIGNAL_CONNECT(ctlsig, signal, cb, user_data)
Definition signal.h:396
static const dt_aligned_pixel_simd_t sign
Definition simd.h:118
static const dt_aligned_pixel_simd_t value
Definition simd.h:144
Definition bauhaus.h:132
dt_bauhaus_data_t data
Definition bauhaus.h:218
dt_iop_buffer_dsc_t dsc_in
struct dt_iop_module_t *void * data
struct dt_develop_t * dev
GList * iop_order_list
Definition develop.h:275
dt_image_t image_storage
Definition develop.h:225
GList * iop
Definition develop.h:269
char filename[DT_MAX_FILENAME_LEN]
Definition image.h:386
png_infop info_ptr
Definition png.c:73
png_structp png_ptr
Definition png.c:72
FILE * f
Definition png.c:71
unsigned int channels
Definition format.h:83
dt_iop_lut3d_params_t params
Definition iop/lut3d.c:138
GtkWidget * interpolation
Definition iop/lut3d.c:124
char c_clut[2048 *2 *3]
Definition iop/lut3d.c:108
dt_iop_lut3d_colorspace_t colorspace
Definition iop/lut3d.c:105
dt_iop_lut3d_interpolation_t interpolation
Definition iop/lut3d.c:106
GtkWidget * widget
Definition imageop_gui.h:47
dt_iop_global_data_t * data
Definition imageop.h:238
dt_iop_params_t * default_params
Definition imageop.h:333
struct dt_iop_module_gui_t * gui
Definition imageop.h:346
struct dt_develop_t * dev
Definition imageop.h:311
dt_iop_global_data_t * global_data
Definition imageop.h:337
dt_dev_operation_t op
Definition imageop.h:259
dt_iop_params_t * params
Definition imageop.h:333
A profile reduced to the arithmetic the pixel loop can run: two matrices and six tone-curve LUTs,...
Region of interest passed through the pixelpipe.
Definition format.h:49
int width
Definition format.h:50
int height
Definition format.h:50
dt_bauhaus_combobox_data_t combobox
Definition bauhaus.h:162
gboolean dt_gui_widgets_suppressed(void)
#define DT_GUI_BOX_SPACING
void dtgtk_cairo_paint_directory(cairo_t *cr, gint x, gint y, gint w, gint h, gint flags, void *data)
@ CPF_NONE