Ansel 0.0
A darktable fork - bloat + design vision
Loading...
Searching...
No Matches
colorprimaries.c
Go to the documentation of this file.
1/*
2 This file is part of Ansel,
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 darktable. If not, see <http://www.gnu.org/licenses/>.
17*/
18
19#ifdef HAVE_CONFIG_H
20#include "config.h"
21#endif
22
23#include <math.h>
24#include <stdlib.h>
25#include <string.h>
26
27#include "bauhaus/bauhaus.h"
29#include "common/darktable.h"
30#include "common/imagebuf.h"
31#include "common/lut_viewer.h"
32#include "control/control.h"
33#include "develop/develop.h"
34#include "develop/imageop.h"
35#include "develop/imageop_gui.h"
37#include "gui/gtk.h"
38#include "iop/iop_api.h"
39
41
42#define DT_IOP_COLORPRIMARIES_NODE_COUNT 6
43#define DT_IOP_COLORPRIMARIES_EDGE_COUNT 6
44#define DT_IOP_COLORPRIMARIES_RADIAL_COUNT (DT_IOP_COLORPRIMARIES_NODE_COUNT + DT_IOP_COLORPRIMARIES_EDGE_COUNT)
45#define DT_IOP_COLORPRIMARIES_BLACK_WHITE_COUNT (2 * DT_IOP_COLORPRIMARIES_NODE_COUNT)
46#define DT_IOP_COLORPRIMARIES_AXIS_ANCHORS 64
47#define DT_IOP_COLORPRIMARIES_MAX_ANCHORS \
48 (DT_IOP_COLORPRIMARIES_NODE_COUNT + DT_IOP_COLORPRIMARIES_EDGE_COUNT + DT_IOP_COLORPRIMARIES_RADIAL_COUNT \
49 + DT_IOP_COLORPRIMARIES_BLACK_WHITE_COUNT + DT_IOP_COLORPRIMARIES_AXIS_ANCHORS)
50#define DT_IOP_COLORPRIMARIES_VIEWER_CONTROL_NODES \
51 (DT_IOP_COLORPRIMARIES_NODE_COUNT + DT_IOP_COLORPRIMARIES_EDGE_COUNT + DT_IOP_COLORPRIMARIES_RADIAL_COUNT \
52 + DT_IOP_COLORPRIMARIES_BLACK_WHITE_COUNT)
53#define DT_IOP_COLORPRIMARIES_SQRT3 1.7320508075688772f
54#define DT_IOP_COLORPRIMARIES_INV_SQRT2 0.7071067811865475f
55
57{
58 DT_IOP_COLORPRIMARIES_TETRAHEDRAL = 0, // $DESCRIPTION: "tetrahedral"
59 DT_IOP_COLORPRIMARIES_TRILINEAR = 1, // $DESCRIPTION: "trilinear"
60 DT_IOP_COLORPRIMARIES_PYRAMID = 2, // $DESCRIPTION: "pyramid"
62
72
78
80{
81 float white_level; // $MIN: -2.0 $MAX: 16.0 $DEFAULT: 1.0 $DESCRIPTION: "white level"
82 float gamut_coverage; // $MIN: 0.0 $MAX: 100.0 $DEFAULT: 67.0 $DESCRIPTION: "gamut coverage"
83 float sigma_L; // $MIN: 1.0 $MAX: 100.0 $DEFAULT: 100.0 $DESCRIPTION: "brightness smoothing"
84 float sigma_rho; // $MIN: 0.01 $MAX: 2.0 $DEFAULT: 0.70710678 $DESCRIPTION: "saturation smoothing"
85 float sigma_theta; // $MIN: 0.01 $MAX: 6.28318531 $DEFAULT: 0.70710678 $DESCRIPTION: "hue smoothing"
86 float neutral_protection; // $MIN: 0.0 $MAX: 2.0 $DEFAULT: 0.0 $DESCRIPTION: "neutral protection"
87 dt_iop_colorprimaries_interpolation_t interpolation; // $DEFAULT: DT_IOP_COLORPRIMARIES_TETRAHEDRAL $DESCRIPTION: "interpolation"
88 float hue[DT_IOP_COLORPRIMARIES_NODE_COUNT]; // $MIN: -180.0 $MAX: 180.0 $DEFAULT: 0.0 $DESCRIPTION: "hue"
89 float saturation[DT_IOP_COLORPRIMARIES_NODE_COUNT]; // $MIN: -100.0 $MAX: 100.0 $DEFAULT: 0.0 $DESCRIPTION: "saturation"
90 float brightness[DT_IOP_COLORPRIMARIES_NODE_COUNT]; // $MIN: -0.25 $MAX: 0.25 $DEFAULT: 0.0 $DESCRIPTION: "brightness"
92
94{
95 float *clut;
96 uint16_t clut_level;
101 // Params that `clut` was last built from, for this piece alone. The LUT is expensive
102 // enough to be worth not rebuilding on every commit, but it must never be shared across
103 // pieces/instances: see the multi-instance cache-bleed writeup for this module.
105 gboolean clut_valid;
107
109{
110 // Truly global, read-only-after-init state only. Do NOT add a shared LUT/params cache
111 // here: this struct is the same instance for every module instance, every piece, and
112 // every pipe (full/preview/thumbnail/export/virtual-preview) of colorprimaries in the
113 // whole application -- there is exactly one of it, not one per instance. A memoized LUT
114 // stored here can only ever hold one instance's content at a time, and gets silently
115 // overwritten by whichever piece/instance last committed -- including disabled instances,
116 // since commit_params runs for disabled pieces too.
122
146
147const char *name()
148{
149 return _("color primaries");
150}
151
152const char *aliases()
153{
154 return _("RGB primaries|primary colors");
155}
156
157const char **description(struct dt_iop_module_t *self)
158{
159 return dt_iop_set_description(self,
160 _("edit RGB/CYM primary control nodes in dt UCS and interpolate their RGB shifts "
161 "through a cylindrical local field"),
162 _("creative"), _("linear, RGB, display-referred"), _("linear, RGB"),
163 _("linear, RGB, display-referred"));
164}
165
167{
168 return IOP_GROUP_COLOR;
169}
170
175
177{
178 return IOP_CS_RGB;
179}
180
181static void _update_gui_lut_cache(dt_iop_module_t *self);
182
191
192static inline const char *_node_name(const dt_iop_colorprimaries_node_t node)
193{
194 switch(node)
195 {
197 return _("yellow");
199 return _("green");
201 return _("cyan");
203 return _("blue");
205 return _("magenta");
207 default:
208 return _("red");
209 }
210}
211
213{
214 memset(p, 0, sizeof(*p));
215 p->white_level = 1.f;
216 p->gamut_coverage = 67.f;
217 p->sigma_L = 100.f;
219 p->sigma_theta = DT_IOP_COLORPRIMARIES_INV_SQRT2;
220 p->neutral_protection = 0.f;
221 p->interpolation = DT_IOP_COLORPRIMARIES_TETRAHEDRAL;
222}
223
224static inline gboolean _lut_fields_equal(const dt_iop_colorprimaries_params_t *const a,
225 const dt_iop_colorprimaries_params_t *const b)
226{
227 return !memcmp(a, b, sizeof(*a));
228}
229
231{
232 switch(node)
233 {
235 RGB[0] = 1.f;
236 RGB[1] = 1.f;
237 RGB[2] = 0.f;
238 break;
240 RGB[0] = 0.f;
241 RGB[1] = 1.f;
242 RGB[2] = 0.f;
243 break;
245 RGB[0] = 0.f;
246 RGB[1] = 1.f;
247 RGB[2] = 1.f;
248 break;
250 RGB[0] = 0.f;
251 RGB[1] = 0.f;
252 RGB[2] = 1.f;
253 break;
255 RGB[0] = 1.f;
256 RGB[1] = 0.f;
257 RGB[2] = 1.f;
258 break;
260 default:
261 RGB[0] = 1.f;
262 RGB[1] = 0.f;
263 RGB[2] = 0.f;
264 break;
265 }
266 RGB[3] = 0.f;
267}
268
269static inline void _node_base_rgb(const dt_iop_colorprimaries_node_t node, const float gamut_coverage, dt_aligned_pixel_t RGB)
270{
271 dt_aligned_pixel_t corner = { 0.f };
272 _node_corner_rgb(node, corner);
273
274 const float mu = (corner[0] + corner[1] + corner[2]) / 3.f;
275 for_each_channel(c, aligned(RGB, corner)) RGB[c] = mu + gamut_coverage * (corner[c] - mu);
276 RGB[3] = 0.f;
277}
278
279static inline float _mix_hue_delta_weighted(const float hue_a, const float hue_b, const float weight_a,
280 const float weight_b)
281{
282 const float angle_a = hue_a * M_PI_F / 180.f;
283 const float angle_b = hue_b * M_PI_F / 180.f;
284 return atan2f(weight_a * sinf(angle_a) + weight_b * sinf(angle_b),
285 weight_a * cosf(angle_a) + weight_b * cosf(angle_b));
286}
287
288static inline float _hsb_distance(const dt_aligned_pixel_t a, const dt_aligned_pixel_t b)
289{
290 const float dh = dt_colorrings_wrap_pi(a[0] - b[0]);
291 const float ds = a[1] - b[1];
292 const float db = a[2] - b[2];
293 return sqrtf(dh * dh + ds * ds + db * db);
294}
295
296static inline void _black_white_rgb(const gboolean white, dt_aligned_pixel_t RGB)
297{
298 RGB[0] = white ? 1.f : 0.f;
299 RGB[1] = white ? 1.f : 0.f;
300 RGB[2] = white ? 1.f : 0.f;
301 RGB[3] = 0.f;
302}
303
304static void _halfway_to_axis_rgb(const dt_aligned_pixel_t source_rgb, dt_aligned_pixel_t halfway_rgb)
305{
306 float L = 0.f;
307 float rho = 0.f;
308 float theta = 0.f;
309 dt_colorrings_rgb_to_gray_cyl(source_rgb, &L, &rho, &theta);
310 dt_colorrings_gray_basis_to_rgb(L, 0.5f * rho * cosf(theta), 0.5f * rho * sinf(theta), halfway_rgb);
311 halfway_rgb[3] = 0.f;
312}
313
314static gboolean _build_anchor_from_source_rgb(const dt_aligned_pixel_t source_rgb, const float hue_delta,
315 const float saturation_delta, const float brightness_delta,
316 const dt_iop_order_iccprofile_info_t *const lut_profile,
317 dt_colorrings_sparse_anchor_t *const anchor)
318{
319 const float white = dt_colorrings_graph_white();
320 dt_aligned_pixel_t source_hsb = { 0.f };
321 dt_aligned_pixel_t source_axis = { 0.f };
322 dt_aligned_pixel_t target_axis = { 0.f };
323 float source_L = 0.f;
324 float source_rho = 0.f;
325 float source_theta = 0.f;
326 float source_axis_L = 0.f;
327 float target_axis_L = 0.f;
328 float unused_rho = 0.f;
329 float unused_theta = 0.f;
330 float source_brightness = 0.f;
331 float target_brightness = 0.f;
332 float requested_scale = 1.f;
333
334 dt_colorrings_profile_rgb_to_dt_ucs_hsb(source_rgb, white, lut_profile, source_hsb);
335 dt_colorrings_rgb_to_gray_cyl(source_rgb, &source_L, &source_rho, &source_theta);
336
337 if(source_rho <= 1e-6f) return FALSE;
338
350 source_brightness = CLAMP(source_hsb[2], 0.f, 1.f);
351 target_brightness = CLAMP(source_brightness + brightness_delta, 0.f, 1.f);
352 dt_colorrings_brightness_to_axis_rgb(source_brightness, white, lut_profile, source_axis);
353 dt_colorrings_rgb_to_gray_cyl(source_axis, &source_axis_L, &unused_rho, &unused_theta);
354 dt_colorrings_brightness_to_axis_rgb(target_brightness, white, lut_profile, target_axis);
355 dt_colorrings_rgb_to_gray_cyl(target_axis, &target_axis_L, &unused_rho, &unused_theta);
356
357 if(source_hsb[1] > 1e-6f)
358 requested_scale = fmaxf(source_hsb[1] + saturation_delta, 0.f) / source_hsb[1];
359
360 anchor->L = source_L;
361 anchor->rho = source_rho;
362 anchor->theta = source_theta;
363 anchor->delta_L = target_axis_L - source_axis_L;
364 anchor->chroma_scale = requested_scale;
365 anchor->delta_theta = dt_colorrings_wrap_pi(hue_delta);
366 anchor->weight = 1.f;
367 return TRUE;
368}
369
371 const float hue_delta, const float saturation_delta,
372 const float brightness_delta,
373 const dt_iop_order_iccprofile_info_t *const lut_profile,
374 dt_colorrings_sparse_anchor_t *const anchor,
375 dt_aligned_pixel_t halfway_rgb)
376{
377 dt_aligned_pixel_t source_hsb = { 0.f };
378 dt_aligned_pixel_t axis_rgb = { 0.f };
379 dt_aligned_pixel_t axis_hsb = { 0.f };
380 dt_aligned_pixel_t halfway_hsb = { 0.f };
381 float distance_source = 0.f;
382 float distance_axis = 0.f;
383 float weight_source = 0.5f;
384 float source_L = 0.f;
385 float source_rho = 0.f;
386 float source_theta = 0.f;
387
388 dt_colorrings_rgb_to_gray_cyl(source_rgb, &source_L, &source_rho, &source_theta);
389 if(source_rho <= 1e-6f) return FALSE;
390
391 _halfway_to_axis_rgb(source_rgb, halfway_rgb);
392 dt_colorrings_gray_axis_rgb_from_L(source_L, axis_rgb);
393
394 dt_colorrings_profile_rgb_to_dt_ucs_hsb(source_rgb, dt_colorrings_graph_white(), lut_profile, source_hsb);
395 dt_colorrings_profile_rgb_to_dt_ucs_hsb(axis_rgb, dt_colorrings_graph_white(), lut_profile, axis_hsb);
396 dt_colorrings_profile_rgb_to_dt_ucs_hsb(halfway_rgb, dt_colorrings_graph_white(), lut_profile, halfway_hsb);
397
404 axis_hsb[0] = source_hsb[0];
405 distance_source = _hsb_distance(halfway_hsb, source_hsb);
406 distance_axis = _hsb_distance(halfway_hsb, axis_hsb);
407 if(distance_source + distance_axis > 1e-6f)
408 weight_source = distance_axis / (distance_source + distance_axis);
409
410 return _build_anchor_from_source_rgb(halfway_rgb, weight_source * hue_delta, weight_source * saturation_delta,
411 weight_source * brightness_delta, lut_profile, anchor);
412}
413
415 const dt_aligned_pixel_t extreme_rgb,
416 const float hue_delta, const float saturation_delta,
417 const float brightness_delta,
418 const dt_iop_order_iccprofile_info_t *const lut_profile,
419 dt_colorrings_sparse_anchor_t *const anchor,
420 dt_aligned_pixel_t halfway_rgb)
421{
422 dt_aligned_pixel_t source_hsb = { 0.f };
423 dt_aligned_pixel_t extreme_hsb = { 0.f };
424 dt_aligned_pixel_t halfway_hsb = { 0.f };
425 float distance_source = 0.f;
426 float distance_extreme = 0.f;
427 float weight_source = 0.5f;
428
429 for(int c = 0; c < 3; c++) halfway_rgb[c] = 0.5f * (source_rgb[c] + extreme_rgb[c]);
430 halfway_rgb[3] = 0.f;
431
432 dt_colorrings_profile_rgb_to_dt_ucs_hsb(source_rgb, dt_colorrings_graph_white(), lut_profile, source_hsb);
433 dt_colorrings_profile_rgb_to_dt_ucs_hsb(extreme_rgb, dt_colorrings_graph_white(), lut_profile, extreme_hsb);
434 dt_colorrings_profile_rgb_to_dt_ucs_hsb(halfway_rgb, dt_colorrings_graph_white(), lut_profile, halfway_hsb);
435
436 extreme_hsb[0] = source_hsb[0];
437 distance_source = _hsb_distance(halfway_hsb, source_hsb);
438 distance_extreme = _hsb_distance(halfway_hsb, extreme_hsb);
439 if(distance_source + distance_extreme > 1e-6f)
440 weight_source = distance_extreme / (distance_source + distance_extreme);
441
442 return _build_anchor_from_source_rgb(halfway_rgb, weight_source * hue_delta, weight_source * saturation_delta,
443 weight_source * brightness_delta, lut_profile, anchor);
444}
445
446static void _apply_anchor_to_rgb(const dt_aligned_pixel_t source_rgb, const dt_colorrings_sparse_anchor_t *const anchor,
447 dt_aligned_pixel_t target_rgb)
448{
449 dt_aligned_pixel_t axis = { 0.f };
450 float source_L = 0.f;
451 float source_rho = 0.f;
452 float source_theta = 0.f;
453
454 dt_colorrings_rgb_to_gray_cyl(source_rgb, &source_L, &source_rho, &source_theta);
455 dt_colorrings_gray_basis_to_rgb(source_L + anchor->delta_L,
456 source_rho * anchor->chroma_scale * cosf(source_theta + anchor->delta_theta),
457 source_rho * anchor->chroma_scale * sinf(source_theta + anchor->delta_theta),
458 target_rgb);
459 dt_colorrings_gray_axis_rgb_from_L(source_L + anchor->delta_L, axis);
460 dt_colorrings_project_to_cube_shell(axis, target_rgb);
461 target_rgb[3] = 0.f;
462}
463
465 const dt_iop_order_iccprofile_info_t *const lut_profile,
466 dt_colorrings_sparse_anchor_t *const anchor)
467{
468 dt_aligned_pixel_t source_rgb = { 0.f };
469 _node_base_rgb(node, CLAMP(p->gamut_coverage * 0.01f, 0.f, 1.f), source_rgb);
470 return _build_anchor_from_source_rgb(source_rgb, p->hue[node] * M_PI_F / 180.f, p->saturation[node] * 0.01f,
471 p->brightness[node], lut_profile, anchor);
472}
473
475 const dt_iop_order_iccprofile_info_t *const lut_profile, dt_aligned_pixel_t source_rgb,
476 float *const hue_delta, float *const saturation_delta,
477 float *const brightness_delta)
478{
479 dt_aligned_pixel_t source_a = { 0.f };
480 dt_aligned_pixel_t source_b = { 0.f };
481 dt_aligned_pixel_t hsb_a = { 0.f };
482 dt_aligned_pixel_t hsb_b = { 0.f };
483 dt_aligned_pixel_t hsb_mid = { 0.f };
484 float distance_a = 0.f;
485 float distance_b = 0.f;
486 float weight_a = 0.5f;
487 float weight_b = 0.5f;
488
489 _node_base_rgb(edge.a, CLAMP(p->gamut_coverage * 0.01f, 0.f, 1.f), source_a);
490 _node_base_rgb(edge.b, CLAMP(p->gamut_coverage * 0.01f, 0.f, 1.f), source_b);
491
503 for_each_channel(c, aligned(source_a, source_b, source_rgb)) source_rgb[c] = 0.5f * (source_a[c] + source_b[c]);
504 source_rgb[3] = 0.f;
507 dt_colorrings_profile_rgb_to_dt_ucs_hsb(source_rgb, dt_colorrings_graph_white(), lut_profile, hsb_mid);
508
509 distance_a = _hsb_distance(hsb_mid, hsb_a);
510 distance_b = _hsb_distance(hsb_mid, hsb_b);
511 if(distance_a + distance_b > 1e-6f)
512 {
513 weight_a = distance_b / (distance_a + distance_b);
514 weight_b = distance_a / (distance_a + distance_b);
515 }
516
517 *hue_delta = _mix_hue_delta_weighted(p->hue[edge.a], p->hue[edge.b], weight_a, weight_b);
518 *saturation_delta = (weight_a * p->saturation[edge.a] + weight_b * p->saturation[edge.b]) * 0.01f;
519 *brightness_delta = weight_a * p->brightness[edge.a] + weight_b * p->brightness[edge.b];
520 return TRUE;
521}
522
523static inline void _store_viewer_control_node(dt_lut_viewer_control_node_t *const control_nodes, int *const count,
524 const dt_aligned_pixel_t input_rgb, const dt_aligned_pixel_t output_rgb)
525{
526 for(int c = 0; c < 3; c++)
527 {
528 control_nodes[*count].input_rgb[c] = input_rgb[c];
529 control_nodes[*count].output_rgb[c] = output_rgb[c];
530 }
531 (*count)++;
532}
533
534static inline void _append_anchor(dt_colorrings_sparse_anchor_t *const anchors, int *const anchor_count,
535 const dt_colorrings_sparse_anchor_t *const anchor)
536{
537 anchors[*anchor_count] = *anchor;
538 (*anchor_count)++;
539}
540
542 const dt_iop_order_iccprofile_info_t *const lut_profile,
543 dt_colorrings_sparse_anchor_t *const anchor)
544{
545 dt_aligned_pixel_t source_rgb = { 0.f };
546 float hue_delta = 0.f;
547 float saturation_delta = 0.f;
548 float brightness_delta = 0.f;
549
550 if(!_build_edge_edit(p, edge, lut_profile, source_rgb, &hue_delta, &saturation_delta, &brightness_delta))
551 return FALSE;
552
553 return _build_anchor_from_source_rgb(source_rgb, hue_delta, saturation_delta, brightness_delta, lut_profile, anchor);
554}
555
558 const dt_iop_order_iccprofile_info_t *const lut_profile,
559 dt_colorrings_sparse_anchor_t *const anchor,
560 dt_aligned_pixel_t midpoint_rgb)
561{
562 dt_aligned_pixel_t source_rgb = { 0.f };
563 _node_base_rgb(node, CLAMP(p->gamut_coverage * 0.01f, 0.f, 1.f), source_rgb);
564 return _build_halfway_radial_anchor_from_source_rgb(source_rgb, p->hue[node] * M_PI_F / 180.f,
565 p->saturation[node] * 0.01f, p->brightness[node],
566 lut_profile, anchor, midpoint_rgb);
567}
568
571 const gboolean toward_white,
572 const dt_iop_order_iccprofile_info_t *const lut_profile,
573 dt_colorrings_sparse_anchor_t *const anchor,
574 dt_aligned_pixel_t midpoint_rgb)
575{
576 dt_aligned_pixel_t source_rgb = { 0.f };
577 dt_aligned_pixel_t extreme_rgb = { 0.f };
578 _node_base_rgb(node, CLAMP(p->gamut_coverage * 0.01f, 0.f, 1.f), source_rgb);
579 _black_white_rgb(toward_white, extreme_rgb);
580 return _build_halfway_extreme_anchor_from_source_rgb(source_rgb, extreme_rgb, p->hue[node] * M_PI_F / 180.f,
581 p->saturation[node] * 0.01f, p->brightness[node],
582 lut_profile, anchor, midpoint_rgb);
583}
584
587 const dt_iop_order_iccprofile_info_t *const lut_profile,
588 dt_colorrings_sparse_anchor_t *const anchor,
589 dt_aligned_pixel_t midpoint_rgb)
590{
591 dt_aligned_pixel_t source_rgb = { 0.f };
592 float hue_delta = 0.f;
593 float saturation_delta = 0.f;
594 float brightness_delta = 0.f;
595
596 if(!_build_edge_edit(p, edge, lut_profile, source_rgb, &hue_delta, &saturation_delta, &brightness_delta))
597 return FALSE;
598
599 return _build_halfway_radial_anchor_from_source_rgb(source_rgb, hue_delta, saturation_delta, brightness_delta,
600 lut_profile, anchor, midpoint_rgb);
601}
602
604 const dt_iop_order_iccprofile_info_t *const lut_profile,
605 dt_lut_viewer_control_node_t *const control_nodes)
606{
607 int count = 0;
608
609 if(IS_NULL_PTR(control_nodes) || IS_NULL_PTR(lut_profile)) return 0;
610
611 for(int node = 0; node < DT_IOP_COLORPRIMARIES_NODE_COUNT; node++)
612 {
613 dt_colorrings_sparse_anchor_t anchor = { 0.f };
614 dt_aligned_pixel_t source_rgb = { 0.f };
615 dt_aligned_pixel_t target_rgb = { 0.f };
616
617 _node_base_rgb((dt_iop_colorprimaries_node_t)node, CLAMP(p->gamut_coverage * 0.01f, 0.f, 1.f), source_rgb);
618 if(!_build_node_anchor(p, (dt_iop_colorprimaries_node_t)node, lut_profile, &anchor)) continue;
619 _apply_anchor_to_rgb(source_rgb, &anchor, target_rgb);
620 _store_viewer_control_node(control_nodes, &count, source_rgb, target_rgb);
621 }
622
623 for(int edge = 0; edge < DT_IOP_COLORPRIMARIES_EDGE_COUNT; edge++)
624 {
625 dt_colorrings_sparse_anchor_t anchor = { 0.f };
626 dt_aligned_pixel_t source_rgb = { 0.f };
627 dt_aligned_pixel_t target_rgb = { 0.f };
628 float hue_delta = 0.f;
629 float saturation_delta = 0.f;
630 float brightness_delta = 0.f;
631
632 if(!_build_edge_edit(p, _chroma_edges[edge], lut_profile, source_rgb, &hue_delta, &saturation_delta, &brightness_delta))
633 continue;
634 if(!_build_anchor_from_source_rgb(source_rgb, hue_delta, saturation_delta, brightness_delta, lut_profile, &anchor))
635 continue;
636 _apply_anchor_to_rgb(source_rgb, &anchor, target_rgb);
637 _store_viewer_control_node(control_nodes, &count, source_rgb, target_rgb);
638 }
639
640 for(int white = 0; white < 2; white++)
641 for(int node = 0; node < DT_IOP_COLORPRIMARIES_NODE_COUNT; node++)
642 {
643 dt_colorrings_sparse_anchor_t anchor = { 0.f };
644 dt_aligned_pixel_t midway_rgb = { 0.f };
645 dt_aligned_pixel_t target_rgb = { 0.f };
646
647 if(!_build_node_black_white_midpoint_anchor(p, (dt_iop_colorprimaries_node_t)node, white != 0, lut_profile, &anchor,
648 midway_rgb))
649 continue;
650 _apply_anchor_to_rgb(midway_rgb, &anchor, target_rgb);
651 _store_viewer_control_node(control_nodes, &count, midway_rgb, target_rgb);
652 }
653
654 for(int node = 0; node < DT_IOP_COLORPRIMARIES_NODE_COUNT; node++)
655 {
656 dt_colorrings_sparse_anchor_t anchor = { 0.f };
657 dt_aligned_pixel_t inner_source_rgb = { 0.f };
658 dt_aligned_pixel_t inner_target_rgb = { 0.f };
659
660 if(!_build_node_radial_midpoint_anchor(p, (dt_iop_colorprimaries_node_t)node, lut_profile, &anchor, inner_source_rgb))
661 continue;
662 _apply_anchor_to_rgb(inner_source_rgb, &anchor, inner_target_rgb);
663 _store_viewer_control_node(control_nodes, &count, inner_source_rgb, inner_target_rgb);
664 }
665
666 for(int edge = 0; edge < DT_IOP_COLORPRIMARIES_EDGE_COUNT; edge++)
667 {
668 dt_colorrings_sparse_anchor_t anchor = { 0.f };
669 dt_aligned_pixel_t inner_source_rgb = { 0.f };
670 dt_aligned_pixel_t inner_target_rgb = { 0.f };
671 if(!_build_edge_radial_midpoint_anchor(p, _chroma_edges[edge], lut_profile, &anchor, inner_source_rgb))
672 continue;
673 _apply_anchor_to_rgb(inner_source_rgb, &anchor, inner_target_rgb);
674 _store_viewer_control_node(control_nodes, &count, inner_source_rgb, inner_target_rgb);
675 }
676
677 return count;
678}
679
681 const dt_iop_order_iccprofile_info_t *const lut_profile, dt_aligned_pixel_t HSB)
682{
683 dt_aligned_pixel_t RGB = { 0.f };
684 _node_base_rgb(node, CLAMP(p->gamut_coverage * 0.01f, 0.f, 1.f), RGB);
686}
687
689 const dt_iop_order_iccprofile_info_t *const lut_profile, dt_aligned_pixel_t HSB)
690{
691 dt_aligned_pixel_t source_hsb = { 0.f };
692 _node_source_hsb(p, node, lut_profile, source_hsb);
693
694 HSB[0] = dt_colorrings_wrap_hue_pi(source_hsb[0] + p->hue[node] * M_PI_F / 180.f);
695 HSB[1] = CLAMP(source_hsb[1] + p->saturation[node] * 0.01f, 0.f, 1.f);
696 HSB[2] = CLAMP(source_hsb[2] + p->brightness[node], 0.f, 1.f);
697 HSB[3] = 0.f;
698}
699
701 const dt_iop_order_iccprofile_info_t *lut_profile)
702{
703 const gboolean log_perf = (darktable.unmuted & DT_DEBUG_PERF) != 0;
704 const double start = log_perf ? dt_get_wtime() : 0.0;
705 const size_t clut_size = (size_t)DT_COLORRINGS_CLUT_LEVEL * DT_COLORRINGS_CLUT_LEVEL * DT_COLORRINGS_CLUT_LEVEL * 3u;
706 const float inv_sigma_L = 1.f / fmaxf(p->sigma_L * 0.01f, 1e-6f);
707 const float inv_sigma_rho = 1.f / fmaxf(p->sigma_rho, 1e-6f);
708 const float inv_sigma_theta = 1.f / fmaxf(p->sigma_theta, 1e-6f);
710 int anchor_count = 0;
711
712 if(IS_NULL_PTR(d->clut)) d->clut = dt_alloc_align_float(clut_size);
713 d->lut_profile = (dt_iop_order_iccprofile_info_t *)lut_profile;
714
723 for(int node = 0; node < DT_IOP_COLORPRIMARIES_NODE_COUNT; node++)
724 {
725 dt_colorrings_sparse_anchor_t anchor = { 0.f };
726 if(_build_node_anchor(p, (dt_iop_colorprimaries_node_t)node, lut_profile, &anchor))
727 _append_anchor(anchors, &anchor_count, &anchor);
728 }
729
730 for(int edge = 0; edge < DT_IOP_COLORPRIMARIES_EDGE_COUNT; edge++)
731 {
732 dt_colorrings_sparse_anchor_t anchor = { 0.f };
733 if(_build_edge_anchor(p, _chroma_edges[edge], lut_profile, &anchor))
734 _append_anchor(anchors, &anchor_count, &anchor);
735 }
736
737 for(int node = 0; node < DT_IOP_COLORPRIMARIES_NODE_COUNT; node++)
738 {
739 dt_colorrings_sparse_anchor_t inner_anchor = { 0.f };
740 dt_aligned_pixel_t inner_source_rgb = { 0.f };
741 if(_build_node_radial_midpoint_anchor(p, (dt_iop_colorprimaries_node_t)node, lut_profile, &inner_anchor,
742 inner_source_rgb))
743 _append_anchor(anchors, &anchor_count, &inner_anchor);
744 }
745
746 for(int edge = 0; edge < DT_IOP_COLORPRIMARIES_EDGE_COUNT; edge++)
747 {
748 dt_colorrings_sparse_anchor_t inner_anchor = { 0.f };
749 dt_aligned_pixel_t inner_source_rgb = { 0.f };
750 if(_build_edge_radial_midpoint_anchor(p, _chroma_edges[edge], lut_profile, &inner_anchor, inner_source_rgb))
751 _append_anchor(anchors, &anchor_count, &inner_anchor);
752 }
753
754 for(int white = 0; white < 2; white++)
755 for(int node = 0; node < DT_IOP_COLORPRIMARIES_NODE_COUNT; node++)
756 {
757 dt_colorrings_sparse_anchor_t extreme_anchor = { 0.f };
758 dt_aligned_pixel_t midway_rgb = { 0.f };
760 &extreme_anchor, midway_rgb))
761 _append_anchor(anchors, &anchor_count, &extreme_anchor);
762 }
763
764 for(int sample = 0; sample < DT_IOP_COLORPRIMARIES_AXIS_ANCHORS; sample++)
765 {
766 const float value = (float)sample / (float)(DT_IOP_COLORPRIMARIES_AXIS_ANCHORS - 1);
767 anchors[anchor_count].L = value * DT_IOP_COLORPRIMARIES_SQRT3;
768 anchors[anchor_count].rho = 0.f;
769 anchors[anchor_count].theta = 0.f;
770 anchors[anchor_count].delta_L = 0.f;
771 anchors[anchor_count].chroma_scale = 1.f;
772 anchors[anchor_count].delta_theta = 0.f;
773 anchors[anchor_count].weight = 1.f / (float)DT_IOP_COLORPRIMARIES_AXIS_ANCHORS;
774 anchor_count++;
775 }
776
777 dt_colorrings_fill_lut_sparse_local_field(d->clut, DT_COLORRINGS_CLUT_LEVEL, anchors, anchor_count, inv_sigma_L,
778 inv_sigma_rho, inv_sigma_theta, fmaxf(p->neutral_protection, 1e-6f));
779 d->clut_level = DT_COLORRINGS_CLUT_LEVEL;
780
781 if(log_perf)
782 dt_print(DT_DEBUG_PERF, "[colorprimaries] build_clut level=%u anchors=%d total=%.3fms\n", d->clut_level,
783 anchor_count, 1000.0 * (dt_get_wtime() - start));
784}
785
788{
791 const dt_iop_order_iccprofile_info_t *lut_profile
793 : NULL;
795
796 if(IS_NULL_PTR(lut_profile) || IS_NULL_PTR(work_profile))
797 {
798 // Leave d->clut's allocation (if any) alone -- it is this piece's own buffer now,
799 // not a borrowed pointer. clut_level == 0 already makes process()/process_cl() treat
800 // it as not-ready; freeing/reallocating it is cleanup_pipe's job.
801 d->clut_level = 0;
802 d->clut_valid = FALSE;
803 d->white_level = 1.f;
804 d->lut_profile = NULL;
805 d->work_profile = NULL;
806 d->interpolation = DT_LUT3D_INTERP_TETRAHEDRAL;
807 return;
808 }
809
810 // This LUT is expensive enough to be worth memoizing, but the cache must be scoped to
811 // this piece alone -- see the dt_iop_colorprimaries_global_data_t comment for why a
812 // shared cache silently bleeds one instance's content into another's.
813 if(!d->clut_valid || !_lut_fields_equal(&d->built_params, p))
814 {
815 _build_clut(d, p, lut_profile);
816 d->built_params = *p;
817 d->clut_valid = TRUE;
818 }
819
820 d->white_level = exp2f(p->white_level);
821 d->lut_profile = (dt_iop_order_iccprofile_info_t *)lut_profile;
822 d->work_profile = (dt_iop_order_iccprofile_info_t *)work_profile;
823 d->interpolation = (dt_lut3d_interpolation_t)p->interpolation;
824}
825
827{
829 piece->data = d;
831 d->white_level = 1.f;
832 d->interpolation = DT_LUT3D_INTERP_TETRAHEDRAL;
833}
834
836{
838 dt_free_align(d->clut);
839 d->clut = NULL;
840 dt_free_align(piece->data);
841 piece->data = NULL;
842}
843
844#ifdef HAVE_OPENCL
845int process_cl(struct dt_iop_module_t *self, const dt_dev_pixelpipe_t *pipe, const dt_dev_pixelpipe_iop_t *piece,
846 cl_mem dev_in, cl_mem dev_out)
847{
850 const int width = piece->roi_in.width;
851 const int height = piece->roi_in.height;
852 const int devid = pipe->devid;
853 const size_t sizes[] = { ROUNDUPDWD(width, devid), ROUNDUPDHT(height, devid), 1 };
854 const float white_level = fmaxf(d->white_level, 1e-6f);
855 const float normalize = 1.f / white_level;
856 const float denormalize = white_level;
857 const float black = 0.f;
858 cl_mem clut_cl = NULL;
859 cl_int err = CL_SUCCESS;
860 const int kernel = (d->interpolation == DT_LUT3D_INTERP_TRILINEAR) ? gd->kernel_lut3d_trilinear
861 : (d->interpolation == DT_LUT3D_INTERP_PYRAMID) ? gd->kernel_lut3d_pyramid
863
864 if(IS_NULL_PTR(d->clut) || d->clut_level == 0 || IS_NULL_PTR(d->lut_profile) || IS_NULL_PTR(d->work_profile)) return FALSE;
865
866 dt_opencl_set_kernel_arg(devid, gd->kernel_exposure, 0, sizeof(cl_mem), &dev_in);
867 dt_opencl_set_kernel_arg(devid, gd->kernel_exposure, 1, sizeof(cl_mem), &dev_out);
868 dt_opencl_set_kernel_arg(devid, gd->kernel_exposure, 2, sizeof(int), &width);
869 dt_opencl_set_kernel_arg(devid, gd->kernel_exposure, 3, sizeof(int), &height);
870 dt_opencl_set_kernel_arg(devid, gd->kernel_exposure, 4, sizeof(float), &black);
871 dt_opencl_set_kernel_arg(devid, gd->kernel_exposure, 5, sizeof(float), &normalize);
872 err = dt_opencl_enqueue_kernel_2d(devid, gd->kernel_exposure, sizes);
873 if(err != CL_SUCCESS) goto cleanup;
874
875 if(!dt_ioppr_transform_image_colorspace_rgb_cl(devid, dev_out, dev_out, width, height, d->work_profile,
876 d->lut_profile, "colorequal work to HLG Rec2020"))
877 {
878 err = CL_INVALID_OPERATION;
879 goto cleanup;
880 }
881
882 clut_cl = dt_opencl_copy_host_to_device_constant(devid, sizeof(float) * 3 * d->clut_level * d->clut_level * d->clut_level,
883 d->clut);
884 if(IS_NULL_PTR(clut_cl))
885 {
886 err = CL_MEM_OBJECT_ALLOCATION_FAILURE;
887 goto cleanup;
888 }
889
890 dt_opencl_set_kernel_arg(devid, kernel, 0, sizeof(cl_mem), &dev_out);
891 dt_opencl_set_kernel_arg(devid, kernel, 1, sizeof(cl_mem), &dev_out);
892 dt_opencl_set_kernel_arg(devid, kernel, 2, sizeof(int), &width);
893 dt_opencl_set_kernel_arg(devid, kernel, 3, sizeof(int), &height);
894 dt_opencl_set_kernel_arg(devid, kernel, 4, sizeof(cl_mem), &clut_cl);
895 dt_opencl_set_kernel_arg(devid, kernel, 5, sizeof(int), &d->clut_level);
896 err = dt_opencl_enqueue_kernel_2d(devid, kernel, sizes);
897 if(err != CL_SUCCESS) goto cleanup;
898
899 if(!dt_ioppr_transform_image_colorspace_rgb_cl(devid, dev_out, dev_out, width, height, d->lut_profile,
900 d->work_profile, "colorequal HLG Rec2020 to work"))
901 {
902 err = CL_INVALID_OPERATION;
903 goto cleanup;
904 }
905
906 dt_opencl_set_kernel_arg(devid, gd->kernel_exposure, 0, sizeof(cl_mem), &dev_out);
907 dt_opencl_set_kernel_arg(devid, gd->kernel_exposure, 1, sizeof(cl_mem), &dev_out);
908 dt_opencl_set_kernel_arg(devid, gd->kernel_exposure, 2, sizeof(int), &width);
909 dt_opencl_set_kernel_arg(devid, gd->kernel_exposure, 3, sizeof(int), &height);
910 dt_opencl_set_kernel_arg(devid, gd->kernel_exposure, 4, sizeof(float), &black);
911 dt_opencl_set_kernel_arg(devid, gd->kernel_exposure, 5, sizeof(float), &denormalize);
912 err = dt_opencl_enqueue_kernel_2d(devid, gd->kernel_exposure, sizes);
913
914cleanup:
916 if(err != CL_SUCCESS) dt_print(DT_DEBUG_OPENCL, "[opencl_colorequal] couldn't enqueue kernel! %d\n", err);
917 return err == CL_SUCCESS;
918}
919#endif
920
922int process(struct dt_iop_module_t *self, const dt_dev_pixelpipe_t *pipe, const dt_dev_pixelpipe_iop_t *piece,
923 const void *const ibuf, void *const obuf)
924{
926 const int width = piece->roi_in.width;
927 const int height = piece->roi_in.height;
928 const int ch = piece->dsc_in.channels;
929
930 if(IS_NULL_PTR(d->clut) || d->clut_level == 0 || IS_NULL_PTR(d->lut_profile) || IS_NULL_PTR(d->work_profile))
931 {
933 return 0;
934 }
935
936 const float white_level = fmaxf(d->white_level, 1e-6f);
938 for(size_t k = 0; k < (size_t)width * height; k++)
939 {
940 const float *in = (const float *)ibuf + k * ch;
941 float *out = (float *)obuf + k * ch;
942 out[0] = in[0] / white_level;
943 out[1] = in[1] / white_level;
944 out[2] = in[2] / white_level;
945 if(ch > 3) out[3] = in[3];
946 }
947
948 dt_ioppr_transform_image_colorspace_rgb((float *)obuf, (float *)obuf, width, height, d->work_profile, d->lut_profile,
949 "colorprimaries work to HLG Rec2020");
950 dt_lut3d_apply((float *)obuf, (float *)obuf, (size_t)width * height, d->clut, d->clut_level, 1.f, d->interpolation);
951 dt_ioppr_transform_image_colorspace_rgb((float *)obuf, (float *)obuf, width, height, d->lut_profile, d->work_profile,
952 "colorprimaries HLG Rec2020 to work");
954 for(size_t k = 0; k < (size_t)width * height; k++)
955 {
956 float *out = (float *)obuf + k * ch;
957 out[0] *= white_level;
958 out[1] *= white_level;
959 out[2] *= white_level;
960 }
961
962 return 0;
963}
964
967{
969 if(IS_NULL_PTR(work_profile))
970 {
971 memset(Ych, 0, sizeof(dt_aligned_pixel_t));
972 return;
973 }
974
975 dt_colorrings_profile_rgb_to_Ych(RGB, work_profile, Ych);
976}
977
978static void _set_slider_stop_from_hsb(GtkWidget *slider, const float stop, const dt_aligned_pixel_t HSB,
979 const dt_iop_order_iccprofile_info_t *display_profile)
980{
981 dt_aligned_pixel_t RGB = { 0.f };
983 dt_bauhaus_slider_set_stop(slider, stop, RGB[0], RGB[1], RGB[2]);
984}
985
986static void _set_slider_stop_from_profile_rgb(GtkWidget *slider, const float stop, const dt_aligned_pixel_t RGB,
987 const dt_iop_order_iccprofile_info_t *lut_profile,
988 const dt_iop_order_iccprofile_info_t *display_profile)
989{
990 dt_aligned_pixel_t display_rgb = { 0.f };
991 dt_colorrings_profile_rgb_to_display_rgb(RGB, lut_profile, display_profile, display_rgb);
992 dt_bauhaus_slider_set_stop(slider, stop, display_rgb[0], display_rgb[1], display_rgb[2]);
993}
994
996{
999 const dt_iop_order_iccprofile_info_t *lut_profile
1001 : NULL;
1002 const dt_iop_order_iccprofile_info_t *display_profile
1004 : NULL;
1005
1006 if(IS_NULL_PTR(lut_profile)) return;
1007
1013 for(int node = 0; node < DT_IOP_COLORPRIMARIES_NODE_COUNT; node++)
1014 {
1015 dt_aligned_pixel_t source_hsb = { 0.f };
1016 dt_aligned_pixel_t target_hsb = { 0.f };
1017 _node_source_hsb(p, (dt_iop_colorprimaries_node_t)node, lut_profile, source_hsb);
1018 _node_target_hsb(p, (dt_iop_colorprimaries_node_t)node, lut_profile, target_hsb);
1019
1020 dt_bauhaus_slider_clear_stops(g->node_hue[node]);
1021 for(int stop = 0; stop <= 6; stop++)
1022 {
1023 const float hue_shift = -M_PI_F + 2.f * M_PI_F * (float)stop / 6.f;
1024 dt_aligned_pixel_t HSB = { dt_colorrings_wrap_hue_2pi(source_hsb[0] + hue_shift), target_hsb[1], target_hsb[2],
1025 0.f };
1026 _set_slider_stop_from_hsb(g->node_hue[node], (float)stop / 6.f, HSB, display_profile);
1027 }
1028
1029 dt_bauhaus_slider_clear_stops(g->node_saturation[node]);
1030 {
1031 dt_aligned_pixel_t axis_rgb = { 0.f };
1032 dt_aligned_pixel_t target_rgb = { 0.f };
1033 dt_aligned_pixel_t shell_rgb = { 0.f };
1034
1042 dt_colorrings_brightness_to_axis_rgb(target_hsb[2], dt_colorrings_graph_white(), lut_profile, axis_rgb);
1043 dt_colorrings_hsb_to_profile_rgb(target_hsb, dt_colorrings_graph_white(), lut_profile, target_rgb);
1044 memcpy(shell_rgb, target_rgb, sizeof(shell_rgb));
1045 dt_colorrings_project_to_cube_shell(axis_rgb, shell_rgb);
1046
1047 _set_slider_stop_from_profile_rgb(g->node_saturation[node], 0.f, axis_rgb, lut_profile, display_profile);
1048 _set_slider_stop_from_profile_rgb(g->node_saturation[node], 0.5f, target_rgb, lut_profile, display_profile);
1049 _set_slider_stop_from_profile_rgb(g->node_saturation[node], 1.f, shell_rgb, lut_profile, display_profile);
1050 }
1051
1052 dt_bauhaus_slider_clear_stops(g->node_brightness[node]);
1053 _set_slider_stop_from_hsb(g->node_brightness[node], 0.f,
1054 (dt_aligned_pixel_t){ target_hsb[0], target_hsb[1], CLAMP(source_hsb[2] - 0.05f, 0.f, 1.f),
1055 0.f },
1056 display_profile);
1057 _set_slider_stop_from_hsb(g->node_brightness[node], 0.5f,
1058 (dt_aligned_pixel_t){ target_hsb[0], target_hsb[1], source_hsb[2], 0.f }, display_profile);
1059 _set_slider_stop_from_hsb(g->node_brightness[node], 1.f,
1060 (dt_aligned_pixel_t){ target_hsb[0], target_hsb[1], CLAMP(source_hsb[2] + 0.05f, 0.f, 1.f),
1061 0.f },
1062 display_profile);
1063
1064 /* The three sliders in one color node share the same edited hue, saturation
1065 * and brightness. A drag redraws the active slider through GTK events, but
1066 * the two sibling sliders only repaint if we invalidate them explicitly
1067 * after replacing their gradient stops. */
1068 gtk_widget_queue_draw(g->node_hue[node]);
1069 gtk_widget_queue_draw(g->node_saturation[node]);
1070 gtk_widget_queue_draw(g->node_brightness[node]);
1071 }
1072}
1073
1075{
1076 // GUI-thread only (called from gui_changed()/gui_update()): self->params is live GUI
1077 // state and must never be read from a pipeline thread (see commit_params(), which uses
1078 // the historical params it is passed instead). g->viewer_lut is this instance's own gui_data,
1079 // never shared with any other instance's viewer or with the pipeline's piece->data clut.
1082 const dt_iop_order_iccprofile_info_t *lut_profile
1084 : NULL;
1085 const dt_iop_order_iccprofile_info_t *display_profile
1087 : NULL;
1088
1089 if(IS_NULL_PTR(g->viewer)) return;
1090
1091 if(!g->viewer_lut_dirty && g->viewer_lut_valid && _lut_fields_equal(&g->viewer_lut_params, p)
1092 && g->viewer_display_profile == display_profile)
1093 return;
1094
1095 if(IS_NULL_PTR(lut_profile))
1096 {
1097 dt_lut_viewer_set_lut(g->viewer, NULL, 0, NULL, NULL, NULL);
1098 dt_lut_viewer_set_control_nodes(g->viewer, NULL, 0);
1099 g->viewer_lut_dirty = FALSE;
1100 g->viewer_lut_valid = FALSE;
1101 g->viewer_display_profile = display_profile;
1102 g->viewer_control_node_count = 0;
1103 return;
1104 }
1105
1106 _build_clut(&g->viewer_lut, p, lut_profile);
1107 g->viewer_lut_params = *p;
1108 g->viewer_lut.lut_profile = (dt_iop_order_iccprofile_info_t *)lut_profile;
1109 g->viewer_control_node_count
1110 = _build_viewer_control_nodes(p, lut_profile, g->viewer_control_nodes);
1111 dt_lut_viewer_set_lut(g->viewer, g->viewer_lut.clut, g->viewer_lut.clut_level, NULL, g->viewer_lut.lut_profile,
1112 display_profile);
1113 dt_lut_viewer_set_control_nodes(g->viewer, g->viewer_control_nodes, g->viewer_control_node_count);
1114
1115 g->viewer_lut_dirty = FALSE;
1116 g->viewer_lut_valid = TRUE;
1117 g->viewer_display_profile = display_profile;
1118
1119 dt_lut_viewer_queue_draw(g->viewer);
1120}
1121
1122void gui_changed(dt_iop_module_t *self, GtkWidget *w, void *previous)
1123{
1125 if(IS_NULL_PTR(g)) return;
1128}
1129
1131{
1134
1136 dt_bauhaus_slider_set(g->white_level, p->white_level);
1137 dt_bauhaus_slider_set(g->gamut_coverage, p->gamut_coverage);
1138 dt_bauhaus_slider_set(g->sigma_L, p->sigma_L);
1139 dt_bauhaus_slider_set(g->sigma_rho, p->sigma_rho);
1140 dt_bauhaus_slider_set(g->sigma_theta, p->sigma_theta);
1141 dt_bauhaus_slider_set(g->neutral_protection, p->neutral_protection);
1142 dt_bauhaus_combobox_set(g->interpolation, p->interpolation);
1143
1144 for(int node = 0; node < DT_IOP_COLORPRIMARIES_NODE_COUNT; node++)
1145 {
1146 dt_bauhaus_slider_set(g->node_hue[node], p->hue[node]);
1147 dt_bauhaus_slider_set(g->node_saturation[node], p->saturation[node]);
1148 dt_bauhaus_slider_set(g->node_brightness[node], p->brightness[node]);
1149 }
1151
1152 gui_changed(self, NULL, NULL);
1153}
1154
1156{
1158 dt_free_align(g->viewer_lut.clut);
1159 g->viewer_lut.clut = NULL;
1160 dt_lut_viewer_destroy(&g->viewer);
1162}
1163
1164static GtkWidget *_new_section_label(GtkWidget *box, const char *label)
1165{
1166 GtkWidget *section = dt_ui_section_label_new(label);
1167 gtk_widget_set_hexpand(section, TRUE);
1168 gtk_box_pack_start(GTK_BOX(box), section, FALSE, FALSE, 0);
1169 return section;
1170}
1171
1174{
1177 const dt_iop_module_t *sampled_module = piece && piece->module ? piece->module : self;
1178
1179 if(picker != g->white_level) return;
1180 if(sampled_module->picked_color_max[0] < sampled_module->picked_color_min[0]) return;
1181
1182 dt_aligned_pixel_t max_Ych = { 0.f };
1183 _pipe_rgb_to_Ych(self, pipe, (const float *)sampled_module->picked_color_max, max_Ych);
1185 p->white_level = log2f(fmaxf(max_Ych[0], 1e-6f));
1186 dt_bauhaus_slider_set(g->white_level, p->white_level);
1188
1190}
1191
1192void autoset(struct dt_iop_module_t *self, const struct dt_dev_pixelpipe_t *pipe,
1193 const struct dt_dev_pixelpipe_iop_t *piece, const void *i)
1194{
1195 const dt_iop_order_iccprofile_info_t *const work_profile = dt_ioppr_get_pipe_current_profile_info(self, pipe);
1196 if(IS_NULL_PTR(work_profile) || piece->dsc_in.channels != 4) return;
1197
1199 const dt_iop_roi_t *const roi_out = &piece->roi_out;
1200 const float *const restrict in = (const float *)i;
1201 float max_Y = 0.0f;
1202
1203 __OMP_PARALLEL_FOR__(reduction(max:max_Y))
1204 for(size_t k = 0; k < (size_t)roi_out->width * roi_out->height * 4; k += 4)
1205 {
1206 dt_aligned_pixel_t Ych = { 0.f };
1207 dt_colorrings_profile_rgb_to_Ych(in + k, work_profile, Ych);
1208 if(isfinite(Ych[0]))
1209 max_Y = fmaxf(max_Y, Ych[0]);
1210 }
1211
1212 p->white_level = log2f(fmaxf(max_Y, 1e-6f));
1213}
1214
1216{
1219
1220 g->viewer_lut_dirty = TRUE;
1221 g->viewer_lut_valid = FALSE;
1222 g->viewer_lut.clut = NULL;
1223 g->viewer_lut.clut_level = 0;
1224 g->viewer_lut.lut_profile = NULL;
1225 g->viewer_lut.work_profile = NULL;
1226 g->preview_signal_connected = FALSE;
1227 g->viewer_display_profile = NULL;
1228
1229 self->widget = gtk_box_new(GTK_ORIENTATION_VERTICAL, DT_GUI_BOX_SPACING);
1230
1231 g->tabs = GTK_NOTEBOOK(gtk_notebook_new());
1232 gtk_notebook_set_show_border(g->tabs, FALSE);
1233 gtk_box_pack_start(GTK_BOX(self->widget), GTK_WIDGET(g->tabs), TRUE, TRUE, 0);
1234 // white_level lives on the "options" tab; reset it if still active once the
1235 // user switches away from that tab.
1237
1238 GtkWidget *colors_page = gtk_box_new(GTK_ORIENTATION_VERTICAL, DT_GUI_BOX_SPACING);
1239 GtkWidget *colors_tab = gtk_label_new(_("colors"));
1240 dt_gui_add_class(colors_tab, "dt_modulegroups_tab_label");
1241 gtk_notebook_append_page(g->tabs, colors_page, colors_tab);
1242 gtk_container_child_set(GTK_CONTAINER(g->tabs), colors_page, "tab-expand", TRUE, "tab-fill", TRUE, NULL);
1243
1244 GtkWidget *const module_root = self->widget;
1245 self->widget = colors_page;
1246 for(int node = 0; node < DT_IOP_COLORPRIMARIES_NODE_COUNT; node++)
1247 {
1248 char hue_name[16] = { 0 };
1249 char saturation_name[24] = { 0 };
1250 char brightness_name[24] = { 0 };
1251 snprintf(hue_name, sizeof(hue_name), "hue[%d]", node);
1252 snprintf(saturation_name, sizeof(saturation_name), "saturation[%d]", node);
1253 snprintf(brightness_name, sizeof(brightness_name), "brightness[%d]", node);
1254
1256 g->node_hue[node] = dt_bauhaus_slider_from_params(self, hue_name);
1257 dt_bauhaus_widget_set_label(g->node_hue[node], N_("hue"));
1258 dt_bauhaus_slider_set_format(g->node_hue[node], _("°"));
1259 dt_bauhaus_slider_set_default(g->node_hue[node], defaults->hue[node]);
1260
1261 g->node_saturation[node] = dt_bauhaus_slider_from_params(self, saturation_name);
1262 dt_bauhaus_widget_set_label(g->node_saturation[node], N_("saturation"));
1263 dt_bauhaus_slider_set_format(g->node_saturation[node], _(" %"));
1264 dt_bauhaus_slider_set_default(g->node_saturation[node], defaults->saturation[node]);
1265
1266 g->node_brightness[node] = dt_bauhaus_slider_from_params(self, brightness_name);
1267 dt_bauhaus_widget_set_label(g->node_brightness[node], N_("brightness"));
1268 dt_bauhaus_slider_set_format(g->node_brightness[node], _(" %"));
1269 dt_bauhaus_slider_set_soft_range(g->node_brightness[node], -0.25f, 0.25f);
1270 dt_bauhaus_slider_set_default(g->node_brightness[node], defaults->brightness[node]);
1271 }
1272 self->widget = module_root;
1273
1274 GtkWidget *options_page = gtk_box_new(GTK_ORIENTATION_VERTICAL, DT_GUI_BOX_SPACING);
1275 GtkWidget *options_tab = gtk_label_new(_("options"));
1276 dt_gui_add_class(options_tab, "dt_modulegroups_tab_label");
1277 gtk_notebook_append_page(g->tabs, options_page, options_tab);
1278 gtk_container_child_set(GTK_CONTAINER(g->tabs), options_page, "tab-expand", TRUE, "tab-fill", TRUE, NULL);
1279
1280 self->widget = options_page;
1281 g->white_level = dt_color_picker_new(self, DT_COLOR_PICKER_AREA, dt_bauhaus_slider_from_params(self, "white_level"));
1282 dt_bauhaus_slider_set_default(g->white_level, defaults->white_level);
1283 dt_bauhaus_slider_set_format(g->white_level, _(" EV"));
1284 dt_bauhaus_slider_set_soft_range(g->white_level, -2.f, 2.f);
1285
1286 g->gamut_coverage = dt_bauhaus_slider_from_params(self, "gamut_coverage");
1287 dt_bauhaus_slider_set_default(g->gamut_coverage, defaults->gamut_coverage);
1288 dt_bauhaus_slider_set_format(g->gamut_coverage, _(" %"));
1289 dt_bauhaus_slider_set_soft_range(g->gamut_coverage, 0.f, 100.f);
1290
1291 g->sigma_L = dt_bauhaus_slider_from_params(self, "sigma_L");
1292 dt_bauhaus_slider_set_default(g->sigma_L, defaults->sigma_L);
1293 dt_bauhaus_slider_set_format(g->sigma_L, _(" %"));
1294 dt_bauhaus_slider_set_soft_range(g->sigma_L, 1.f, 100.f);
1295
1296 g->sigma_rho = dt_bauhaus_slider_from_params(self, "sigma_rho");
1297 dt_bauhaus_slider_set_default(g->sigma_rho, defaults->sigma_rho);
1298 dt_bauhaus_slider_set_soft_range(g->sigma_rho, 0.1f, 2.f);
1299
1300 g->sigma_theta = dt_bauhaus_slider_from_params(self, "sigma_theta");
1301 dt_bauhaus_slider_set_default(g->sigma_theta, defaults->sigma_theta);
1302 dt_bauhaus_slider_set_soft_range(g->sigma_theta, 0.05f, 2.f);
1303
1304 g->neutral_protection = dt_bauhaus_slider_from_params(self, "neutral_protection");
1305 dt_bauhaus_slider_set_default(g->neutral_protection, defaults->neutral_protection);
1306 dt_bauhaus_slider_set_soft_range(g->neutral_protection, 0.f, 1.f);
1307
1308 g->interpolation = dt_bauhaus_combobox_from_params(self, "interpolation");
1309 dt_bauhaus_combobox_set_default(g->interpolation, defaults->interpolation);
1310 self->widget = module_root;
1311
1312 g->viewer = dt_lut_viewer_new(DT_GUI_MODULE(self));
1313 if(g->viewer)
1314 gtk_box_pack_start(GTK_BOX(options_page), dt_lut_viewer_get_widget(g->viewer), TRUE, TRUE, 0);
1315
1316 gtk_widget_show_all(self->widget);
1317 gui_update(self);
1318}
1319
1321{
1322 dt_iop_colorprimaries_global_data_t *gd = malloc(sizeof(*gd));
1323 memset(gd, 0, sizeof(*gd));
1324 module->data = gd;
1325
1326#ifdef HAVE_OPENCL
1327 const int lut_program = 28; // lut3d.cl, from programs.conf
1328 const int basic_program = 2; // basic.cl, from programs.conf
1329 gd->kernel_lut3d_tetrahedral = dt_opencl_create_kernel(lut_program, "lut3d_tetrahedral");
1330 gd->kernel_lut3d_trilinear = dt_opencl_create_kernel(lut_program, "lut3d_trilinear");
1331 gd->kernel_lut3d_pyramid = dt_opencl_create_kernel(lut_program, "lut3d_pyramid");
1332 gd->kernel_exposure = dt_opencl_create_kernel(basic_program, "exposure");
1333#endif
1334}
1335
1347
1349{
1350 dt_iop_default_init(module);
1352 memcpy(module->params, module->default_params, module->params_size);
1353}
1354
1355// clang-format off
1356// modelines: These editor modelines have been set for all relevant files by tools/update_modelines.py
1357// vim: shiftwidth=2 expandtab tabstop=2 cindent
1358// kate: tab-indents: off; indent-width 2; replace-tabs on; indent-mode cstyle; remove-trailing-spaces modified;
1359// clang-format on
#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:164
void dt_bauhaus_slider_set_soft_range(GtkWidget *widget, float soft_min, float soft_max)
Definition bauhaus.c:1650
void dt_bauhaus_slider_clear_stops(GtkWidget *widget)
Definition bauhaus.c:2367
void dt_bauhaus_slider_set_default(GtkWidget *widget, float def)
Definition bauhaus.c:1643
void dt_bauhaus_slider_set_stop(GtkWidget *widget, float stop, float r, float g, float b)
Definition bauhaus.c:2375
void dt_bauhaus_combobox_set_default(GtkWidget *widget, int def)
Definition bauhaus.c:1552
void dt_bauhaus_slider_set(GtkWidget *widget, float pos)
Definition bauhaus.c:3537
void dt_bauhaus_combobox_set(GtkWidget *widget, const int pos)
Definition bauhaus.c:2304
void dt_bauhaus_widget_set_label(GtkWidget *widget, const char *label)
Definition bauhaus.c:1656
void dt_bauhaus_slider_set_format(GtkWidget *widget, const char *format)
Definition bauhaus.c:3613
int width
Definition bilateral.h:1
int height
Definition bilateral.h:1
static __DT_CLONE_TARGETS__ void normalize(float *const buffer, const size_t width, const size_t height, const float norm)
Definition blurs.c:347
static const dt_aligned_pixel_simd_t const dt_adaptation_t const float p
@ IOP_CS_RGB
GtkWidget * dt_color_picker_new(dt_iop_module_t *module, dt_iop_color_picker_kind_t kind, GtkWidget *w)
@ DT_COLOR_PICKER_AREA
void dt_colorrings_profile_rgb_to_dt_ucs_hsb(const dt_aligned_pixel_t RGB, const float white, const dt_iop_order_iccprofile_info_t *profile, dt_aligned_pixel_t HSB)
void dt_colorrings_profile_rgb_to_display_rgb(const dt_aligned_pixel_t RGB, const dt_iop_order_iccprofile_info_t *profile, const dt_iop_order_iccprofile_info_t *display_profile, dt_aligned_pixel_t display_rgb)
void dt_colorrings_fill_lut_sparse_local_field(float *lut, const int level, const dt_colorrings_sparse_anchor_t *const anchors, const int anchor_count, const float inv_sigma_L, const float inv_sigma_rho, const float inv_sigma_theta, const float rho0)
void dt_colorrings_hsb_to_profile_rgb(const dt_aligned_pixel_t HSB, const float white, const dt_iop_order_iccprofile_info_t *profile, dt_aligned_pixel_t RGB)
void dt_colorrings_gray_basis_to_rgb(const float L, const float u, const float v, float rgb[3])
void dt_colorrings_gray_axis_rgb_from_L(const float L, dt_aligned_pixel_t RGB)
void dt_colorrings_rgb_to_gray_cyl(const float rgb[3], float *L, float *rho, float *theta)
float dt_colorrings_wrap_hue_pi(float hue)
float dt_colorrings_wrap_hue_2pi(float hue)
void dt_colorrings_project_to_cube_shell(const dt_aligned_pixel_t axis, dt_aligned_pixel_t RGB)
void dt_colorrings_hsb_to_display_rgb(const dt_aligned_pixel_t HSB, const float white, const dt_iop_order_iccprofile_info_t *display_profile, dt_aligned_pixel_t RGB)
float dt_colorrings_wrap_pi(float x)
void dt_colorrings_profile_rgb_to_Ych(const dt_aligned_pixel_t RGB, const dt_iop_order_iccprofile_info_t *profile, dt_aligned_pixel_t Ych)
float dt_colorrings_graph_white(void)
void dt_colorrings_brightness_to_axis_rgb(const float brightness, const float white, const dt_iop_order_iccprofile_info_t *profile, dt_aligned_pixel_t RGB)
#define DT_COLORRINGS_CLUT_LEVEL
static void _refresh_slider_gradients(dt_iop_module_t *self)
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)
static const char * _node_name(const dt_iop_colorprimaries_node_t node)
void init(dt_iop_module_t *module)
static void _append_anchor(dt_colorrings_sparse_anchor_t *const anchors, int *const anchor_count, const dt_colorrings_sparse_anchor_t *const anchor)
const char ** description(struct dt_iop_module_t *self)
int default_group()
static void _apply_anchor_to_rgb(const dt_aligned_pixel_t source_rgb, const dt_colorrings_sparse_anchor_t *const anchor, dt_aligned_pixel_t target_rgb)
static float _hsb_distance(const dt_aligned_pixel_t a, const dt_aligned_pixel_t b)
static void _node_target_hsb(const dt_iop_colorprimaries_params_t *const p, const dt_iop_colorprimaries_node_t node, const dt_iop_order_iccprofile_info_t *const lut_profile, dt_aligned_pixel_t HSB)
static void _store_viewer_control_node(dt_lut_viewer_control_node_t *const control_nodes, int *const count, const dt_aligned_pixel_t input_rgb, const dt_aligned_pixel_t output_rgb)
void gui_update(dt_iop_module_t *self)
static void _black_white_rgb(const gboolean white, dt_aligned_pixel_t RGB)
static void _node_corner_rgb(const dt_iop_colorprimaries_node_t node, dt_aligned_pixel_t RGB)
static gboolean _lut_fields_equal(const dt_iop_colorprimaries_params_t *const a, const dt_iop_colorprimaries_params_t *const b)
static int _build_viewer_control_nodes(const dt_iop_colorprimaries_params_t *const p, const dt_iop_order_iccprofile_info_t *const lut_profile, dt_lut_viewer_control_node_t *const control_nodes)
const char * aliases()
static void _set_slider_stop_from_profile_rgb(GtkWidget *slider, const float stop, const dt_aligned_pixel_t RGB, const dt_iop_order_iccprofile_info_t *lut_profile, const dt_iop_order_iccprofile_info_t *display_profile)
static gboolean _build_edge_radial_midpoint_anchor(const dt_iop_colorprimaries_params_t *const p, const dt_iop_colorprimaries_edge_t edge, const dt_iop_order_iccprofile_info_t *const lut_profile, dt_colorrings_sparse_anchor_t *const anchor, dt_aligned_pixel_t midpoint_rgb)
void init_pipe(struct dt_iop_module_t *self, dt_dev_pixelpipe_t *pipe, dt_dev_pixelpipe_iop_t *piece)
static gboolean _build_halfway_radial_anchor_from_source_rgb(const dt_aligned_pixel_t source_rgb, const float hue_delta, const float saturation_delta, const float brightness_delta, const dt_iop_order_iccprofile_info_t *const lut_profile, dt_colorrings_sparse_anchor_t *const anchor, dt_aligned_pixel_t halfway_rgb)
static GtkWidget * _new_section_label(GtkWidget *box, const char *label)
static gboolean _build_halfway_extreme_anchor_from_source_rgb(const dt_aligned_pixel_t source_rgb, const dt_aligned_pixel_t extreme_rgb, const float hue_delta, const float saturation_delta, const float brightness_delta, const dt_iop_order_iccprofile_info_t *const lut_profile, dt_colorrings_sparse_anchor_t *const anchor, dt_aligned_pixel_t halfway_rgb)
static gboolean _build_anchor_from_source_rgb(const dt_aligned_pixel_t source_rgb, const float hue_delta, const float saturation_delta, const float brightness_delta, const dt_iop_order_iccprofile_info_t *const lut_profile, dt_colorrings_sparse_anchor_t *const anchor)
const char * name()
static void _halfway_to_axis_rgb(const dt_aligned_pixel_t source_rgb, dt_aligned_pixel_t halfway_rgb)
void gui_init(dt_iop_module_t *self)
static void _node_base_rgb(const dt_iop_colorprimaries_node_t node, const float gamut_coverage, dt_aligned_pixel_t RGB)
void gui_changed(dt_iop_module_t *self, GtkWidget *w, void *previous)
#define DT_IOP_COLORPRIMARIES_EDGE_COUNT
void gui_cleanup(dt_iop_module_t *self)
static gboolean _build_node_black_white_midpoint_anchor(const dt_iop_colorprimaries_params_t *const p, const dt_iop_colorprimaries_node_t node, const gboolean toward_white, const dt_iop_order_iccprofile_info_t *const lut_profile, dt_colorrings_sparse_anchor_t *const anchor, dt_aligned_pixel_t midpoint_rgb)
static void _update_gui_lut_cache(dt_iop_module_t *self)
void cleanup_global(dt_iop_module_so_t *module)
static gboolean _build_edge_edit(const dt_iop_colorprimaries_params_t *const p, const dt_iop_colorprimaries_edge_t edge, const dt_iop_order_iccprofile_info_t *const lut_profile, dt_aligned_pixel_t source_rgb, float *const hue_delta, float *const saturation_delta, float *const brightness_delta)
int default_colorspace(dt_iop_module_t *self, dt_dev_pixelpipe_t *pipe, const dt_dev_pixelpipe_iop_t *piece)
static gboolean _build_node_anchor(const dt_iop_colorprimaries_params_t *const p, const dt_iop_colorprimaries_node_t node, const dt_iop_order_iccprofile_info_t *const lut_profile, dt_colorrings_sparse_anchor_t *const anchor)
int flags()
dt_iop_colorprimaries_node_t
@ DT_IOP_COLORPRIMARIES_BLUE
@ DT_IOP_COLORPRIMARIES_GREEN
@ DT_IOP_COLORPRIMARIES_MAGENTA
@ DT_IOP_COLORPRIMARIES_YELLOW
@ DT_IOP_COLORPRIMARIES_CYAN
@ DT_IOP_COLORPRIMARIES_RED
#define DT_IOP_COLORPRIMARIES_SQRT3
#define DT_IOP_COLORPRIMARIES_INV_SQRT2
dt_iop_colorprimaries_interpolation_t
@ DT_IOP_COLORPRIMARIES_TETRAHEDRAL
@ DT_IOP_COLORPRIMARIES_PYRAMID
@ DT_IOP_COLORPRIMARIES_TRILINEAR
void cleanup_pipe(struct dt_iop_module_t *self, dt_dev_pixelpipe_t *pipe, dt_dev_pixelpipe_iop_t *piece)
static gboolean _build_edge_anchor(const dt_iop_colorprimaries_params_t *const p, const dt_iop_colorprimaries_edge_t edge, const dt_iop_order_iccprofile_info_t *const lut_profile, dt_colorrings_sparse_anchor_t *const anchor)
static void _init_default_params(dt_iop_colorprimaries_params_t *p)
static gboolean _build_node_radial_midpoint_anchor(const dt_iop_colorprimaries_params_t *const p, const dt_iop_colorprimaries_node_t node, const dt_iop_order_iccprofile_info_t *const lut_profile, dt_colorrings_sparse_anchor_t *const anchor, dt_aligned_pixel_t midpoint_rgb)
static float _mix_hue_delta_weighted(const float hue_a, const float hue_b, const float weight_a, const float weight_b)
static void _node_source_hsb(const dt_iop_colorprimaries_params_t *const p, const dt_iop_colorprimaries_node_t node, const dt_iop_order_iccprofile_info_t *const lut_profile, dt_aligned_pixel_t HSB)
#define DT_IOP_COLORPRIMARIES_NODE_COUNT
static void _pipe_rgb_to_Ych(dt_iop_module_t *self, dt_dev_pixelpipe_t *pipe, const dt_aligned_pixel_t RGB, dt_aligned_pixel_t Ych)
void init_global(dt_iop_module_so_t *module)
__DT_CLONE_TARGETS__ 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)
void autoset(struct dt_iop_module_t *self, const struct dt_dev_pixelpipe_t *pipe, const struct dt_dev_pixelpipe_iop_t *piece, const void *i)
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)
static const dt_iop_colorprimaries_edge_t _chroma_edges[6]
void color_picker_apply(dt_iop_module_t *self, GtkWidget *picker, dt_dev_pixelpipe_t *pipe, dt_dev_pixelpipe_iop_t *piece)
static void _build_clut(dt_iop_colorprimaries_data_t *d, const dt_iop_colorprimaries_params_t *p, const dt_iop_order_iccprofile_info_t *lut_profile)
#define DT_IOP_COLORPRIMARIES_VIEWER_CONTROL_NODES
#define DT_IOP_COLORPRIMARIES_MAX_ANCHORS
static void _set_slider_stop_from_hsb(GtkWidget *slider, const float stop, const dt_aligned_pixel_t HSB, const dt_iop_order_iccprofile_info_t *display_profile)
#define DT_IOP_COLORPRIMARIES_AXIS_ANCHORS
@ DT_INTENT_PERCEPTUAL
Definition colorspaces.h:64
@ DT_COLORSPACE_HLG_REC2020
const float max
const dt_colormatrix_t dt_aligned_pixel_t out
static dt_aligned_pixel_t RGB
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.
darktable_t darktable
Definition darktable.c:183
void dt_print(dt_debug_thread_t thread, const char *msg,...)
Definition darktable.c:1600
#define dt_free_align(ptr)
Definition darktable.h:503
static void * dt_calloc_align(size_t size)
Definition darktable.h:510
@ DT_DEBUG_OPENCL
Definition darktable.h:744
@ DT_DEBUG_PERF
Definition darktable.h:741
#define dt_gui_freeze_begin()
Definition darktable.h:900
#define dt_gui_freeze_end()
Definition darktable.h:901
#define for_each_channel(_var,...)
Definition darktable.h:684
static float * dt_alloc_align_float(size_t pixels)
Definition darktable.h:516
#define dt_free(ptr)
Definition darktable.h:478
#define DT_MODULE_INTROSPECTION(MODVER, PARAMSTYPE)
Definition darktable.h:151
#define __DT_CLONE_TARGETS__
Definition darktable.h:379
#define __OMP_PARALLEL_FOR__(...)
Definition darktable.h:270
static const dt_aligned_pixel_simd_t value
Definition darktable.h:599
static double dt_get_wtime(void)
Definition darktable.h:976
#define IS_NULL_PTR(p)
C is way too permissive with !=, == and if(var) checks, which can mean too many things depending on w...
Definition darktable.h:293
#define M_PI_F
#define dt_dev_add_history_item(dev, module, enable, redraw)
void dt_iop_params_t
Definition dev_history.h:42
void dt_gui_add_class(GtkWidget *widget, const gchar *class_name)
Definition gtk.c:133
void dt_ui_notebook_set_picker_owner(GtkNotebook *notebook, gpointer owner)
Register an opaque owner for a GtkNotebook's page switches, and relay every "switch_page" as DT_SIGNA...
Definition gtk.c:2500
static GtkWidget * dt_ui_section_label_new(const gchar *str)
Definition gtk.h:469
#define DT_GUI_BOX_SPACING
Definition gtk.h:109
#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:87
void dt_iop_default_init(dt_iop_module_t *module)
Definition imageop.c:321
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:3220
#define IOP_GUI_FREE
Definition imageop.h:641
@ IOP_FLAGS_INCLUDE_IN_STYLES
Definition imageop.h:196
@ IOP_FLAGS_SUPPORTS_BLENDING
Definition imageop.h:197
@ IOP_GROUP_COLOR
Definition imageop.h:169
#define IOP_GUI_ALLOC(module)
Definition imageop.h:638
GtkWidget * dt_bauhaus_slider_from_params(dt_iop_module_t *self, const char *param)
Definition imageop_gui.c:77
GtkWidget * dt_bauhaus_combobox_from_params(dt_iop_module_t *self, const char *param)
static float kernel(const float *x, const float *y)
dt_iop_order_iccprofile_info_t * dt_ioppr_get_pipe_output_profile_info(const struct dt_dev_pixelpipe_t *pipe)
dt_iop_order_iccprofile_info_t * dt_ioppr_add_profile_info_to_list(struct dt_develop_t *dev, const dt_colorspaces_color_profile_type_t profile_type, const char *profile_filename, const int intent)
dt_iop_order_iccprofile_info_t * dt_ioppr_get_pipe_current_profile_info(dt_iop_module_t *module, const struct dt_dev_pixelpipe_t *pipe)
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)
float *const restrict const size_t k
float *const restrict const size_t const size_t ch
dt_lut3d_interpolation_t
Definition lut3d.h:25
@ DT_LUT3D_INTERP_PYRAMID
Definition lut3d.h:28
@ DT_LUT3D_INTERP_TETRAHEDRAL
Definition lut3d.h:26
@ DT_LUT3D_INTERP_TRILINEAR
Definition lut3d.h:27
void dt_lut_viewer_destroy(dt_lut_viewer_t **viewer)
void dt_lut_viewer_queue_draw(dt_lut_viewer_t *viewer)
void dt_lut_viewer_set_control_nodes(dt_lut_viewer_t *viewer, const dt_lut_viewer_control_node_t *control_nodes, size_t control_node_count)
GtkWidget * dt_lut_viewer_get_widget(dt_lut_viewer_t *viewer)
dt_lut_viewer_t * dt_lut_viewer_new(dt_gui_module_t *module)
void dt_lut_viewer_set_lut(dt_lut_viewer_t *viewer, const float *clut, uint16_t level, dt_pthread_rwlock_t *clut_lock, const dt_iop_order_iccprofile_info_t *lut_profile, const dt_iop_order_iccprofile_info_t *display_profile)
float dt_aligned_pixel_t[4]
int dt_opencl_enqueue_kernel_2d(const int dev, const int kernel, const size_t *sizes)
Definition opencl.c:2164
int dt_opencl_create_kernel(const int prog, const char *name)
Definition opencl.c:2058
void * dt_opencl_copy_host_to_device_constant(const int devid, const size_t size, void *host)
Definition opencl.c:2360
void dt_opencl_free_kernel(const int kernel)
Definition opencl.c:2101
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:2155
void dt_opencl_release_mem_object(cl_mem mem)
Definition opencl.c:2415
#define ROUNDUPDHT(a, b)
Definition opencl.h:82
#define ROUNDUPDWD(a, b)
Definition opencl.h:81
struct _GtkWidget GtkWidget
Definition splash.h:29
return noise *sigma mu
int32_t unmuted
Definition darktable.h:788
struct dt_develop_t * develop
Definition darktable.h:798
dt_iop_buffer_dsc_t dsc_in
struct dt_iop_module_t *void * data
struct dt_dev_pixelpipe_t * preview_pipe
Definition develop.h:247
unsigned int channels
Definition format.h:54
dt_iop_order_iccprofile_info_t * lut_profile
dt_lut3d_interpolation_t interpolation
dt_iop_colorprimaries_params_t built_params
dt_iop_order_iccprofile_info_t * work_profile
dt_iop_colorprimaries_node_t a
dt_iop_colorprimaries_node_t b
dt_lut_viewer_control_node_t viewer_control_nodes[(6+6+(6+6)+(2 *6))]
const dt_iop_order_iccprofile_info_t * viewer_display_profile
dt_iop_colorprimaries_data_t viewer_lut
dt_iop_colorprimaries_params_t viewer_lut_params
dt_iop_colorprimaries_interpolation_t interpolation
dt_iop_global_data_t * data
Definition imageop.h:263
dt_iop_params_t * default_params
Definition imageop.h:344
GtkWidget * widget
Definition imageop.h:374
struct dt_develop_t * dev
Definition imageop.h:333
dt_iop_gui_data_t * gui_data
Definition imageop.h:348
dt_iop_global_data_t * global_data
Definition imageop.h:351
dt_aligned_pixel_t picked_color_min
Definition imageop.h:309
dt_aligned_pixel_t picked_color_max
Definition imageop.h:309
int32_t params_size
Definition imageop.h:346
dt_iop_params_t * params
Definition imageop.h:344
Region of interest passed through the pixelpipe.
Definition imageop.h:72