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
102
115
139
140const char *name()
141{
142 return _("color primaries");
143}
144
145const char *aliases()
146{
147 return _("RGB primaries|primary colors");
148}
149
150const char **description(struct dt_iop_module_t *self)
151{
152 return dt_iop_set_description(self,
153 _("edit RGB/CYM primary control nodes in dt UCS and interpolate their RGB shifts "
154 "through a cylindrical local field"),
155 _("creative"), _("linear, RGB, display-referred"), _("linear, RGB"),
156 _("linear, RGB, display-referred"));
157}
158
160{
161 return IOP_GROUP_COLOR;
162}
163
168
170{
171 return IOP_CS_RGB;
172}
173
174static void _update_gui_lut_cache(dt_iop_module_t *self);
175
184
185static inline const char *_node_name(const dt_iop_colorprimaries_node_t node)
186{
187 switch(node)
188 {
190 return _("yellow");
192 return _("green");
194 return _("cyan");
196 return _("blue");
198 return _("magenta");
200 default:
201 return _("red");
202 }
203}
204
206{
207 memset(p, 0, sizeof(*p));
208 p->white_level = 1.f;
209 p->gamut_coverage = 67.f;
210 p->sigma_L = 100.f;
212 p->sigma_theta = DT_IOP_COLORPRIMARIES_INV_SQRT2;
213 p->neutral_protection = 0.f;
214 p->interpolation = DT_IOP_COLORPRIMARIES_TETRAHEDRAL;
215}
216
217static inline gboolean _lut_fields_equal(const dt_iop_colorprimaries_params_t *const a,
218 const dt_iop_colorprimaries_params_t *const b)
219{
220 return !memcmp(a, b, sizeof(*a));
221}
222
224{
225 switch(node)
226 {
228 RGB[0] = 1.f;
229 RGB[1] = 1.f;
230 RGB[2] = 0.f;
231 break;
233 RGB[0] = 0.f;
234 RGB[1] = 1.f;
235 RGB[2] = 0.f;
236 break;
238 RGB[0] = 0.f;
239 RGB[1] = 1.f;
240 RGB[2] = 1.f;
241 break;
243 RGB[0] = 0.f;
244 RGB[1] = 0.f;
245 RGB[2] = 1.f;
246 break;
248 RGB[0] = 1.f;
249 RGB[1] = 0.f;
250 RGB[2] = 1.f;
251 break;
253 default:
254 RGB[0] = 1.f;
255 RGB[1] = 0.f;
256 RGB[2] = 0.f;
257 break;
258 }
259 RGB[3] = 0.f;
260}
261
262static inline void _node_base_rgb(const dt_iop_colorprimaries_node_t node, const float gamut_coverage, dt_aligned_pixel_t RGB)
263{
264 dt_aligned_pixel_t corner = { 0.f };
265 _node_corner_rgb(node, corner);
266
267 const float mu = (corner[0] + corner[1] + corner[2]) / 3.f;
268 for_each_channel(c, aligned(RGB, corner)) RGB[c] = mu + gamut_coverage * (corner[c] - mu);
269 RGB[3] = 0.f;
270}
271
272static inline float _mix_hue_delta_weighted(const float hue_a, const float hue_b, const float weight_a,
273 const float weight_b)
274{
275 const float angle_a = hue_a * M_PI_F / 180.f;
276 const float angle_b = hue_b * M_PI_F / 180.f;
277 return atan2f(weight_a * sinf(angle_a) + weight_b * sinf(angle_b),
278 weight_a * cosf(angle_a) + weight_b * cosf(angle_b));
279}
280
281static inline float _hsb_distance(const dt_aligned_pixel_t a, const dt_aligned_pixel_t b)
282{
283 const float dh = dt_colorrings_wrap_pi(a[0] - b[0]);
284 const float ds = a[1] - b[1];
285 const float db = a[2] - b[2];
286 return sqrtf(dh * dh + ds * ds + db * db);
287}
288
289static inline void _black_white_rgb(const gboolean white, dt_aligned_pixel_t RGB)
290{
291 RGB[0] = white ? 1.f : 0.f;
292 RGB[1] = white ? 1.f : 0.f;
293 RGB[2] = white ? 1.f : 0.f;
294 RGB[3] = 0.f;
295}
296
297static void _halfway_to_axis_rgb(const dt_aligned_pixel_t source_rgb, dt_aligned_pixel_t halfway_rgb)
298{
299 float L = 0.f;
300 float rho = 0.f;
301 float theta = 0.f;
302 dt_colorrings_rgb_to_gray_cyl(source_rgb, &L, &rho, &theta);
303 dt_colorrings_gray_basis_to_rgb(L, 0.5f * rho * cosf(theta), 0.5f * rho * sinf(theta), halfway_rgb);
304 halfway_rgb[3] = 0.f;
305}
306
307static gboolean _build_anchor_from_source_rgb(const dt_aligned_pixel_t source_rgb, const float hue_delta,
308 const float saturation_delta, const float brightness_delta,
309 const dt_iop_order_iccprofile_info_t *const lut_profile,
310 dt_colorrings_sparse_anchor_t *const anchor)
311{
312 const float white = dt_colorrings_graph_white();
313 dt_aligned_pixel_t source_hsb = { 0.f };
314 dt_aligned_pixel_t source_axis = { 0.f };
315 dt_aligned_pixel_t target_axis = { 0.f };
316 float source_L = 0.f;
317 float source_rho = 0.f;
318 float source_theta = 0.f;
319 float source_axis_L = 0.f;
320 float target_axis_L = 0.f;
321 float unused_rho = 0.f;
322 float unused_theta = 0.f;
323 float source_brightness = 0.f;
324 float target_brightness = 0.f;
325 float requested_scale = 1.f;
326
327 dt_colorrings_profile_rgb_to_dt_ucs_hsb(source_rgb, white, lut_profile, source_hsb);
328 dt_colorrings_rgb_to_gray_cyl(source_rgb, &source_L, &source_rho, &source_theta);
329
330 if(source_rho <= 1e-6f) return FALSE;
331
343 source_brightness = CLAMP(source_hsb[2], 0.f, 1.f);
344 target_brightness = CLAMP(source_brightness + brightness_delta, 0.f, 1.f);
345 dt_colorrings_brightness_to_axis_rgb(source_brightness, white, lut_profile, source_axis);
346 dt_colorrings_rgb_to_gray_cyl(source_axis, &source_axis_L, &unused_rho, &unused_theta);
347 dt_colorrings_brightness_to_axis_rgb(target_brightness, white, lut_profile, target_axis);
348 dt_colorrings_rgb_to_gray_cyl(target_axis, &target_axis_L, &unused_rho, &unused_theta);
349
350 if(source_hsb[1] > 1e-6f)
351 requested_scale = fmaxf(source_hsb[1] + saturation_delta, 0.f) / source_hsb[1];
352
353 anchor->L = source_L;
354 anchor->rho = source_rho;
355 anchor->theta = source_theta;
356 anchor->delta_L = target_axis_L - source_axis_L;
357 anchor->chroma_scale = requested_scale;
358 anchor->delta_theta = dt_colorrings_wrap_pi(hue_delta);
359 anchor->weight = 1.f;
360 return TRUE;
361}
362
364 const float hue_delta, const float saturation_delta,
365 const float brightness_delta,
366 const dt_iop_order_iccprofile_info_t *const lut_profile,
367 dt_colorrings_sparse_anchor_t *const anchor,
368 dt_aligned_pixel_t halfway_rgb)
369{
370 dt_aligned_pixel_t source_hsb = { 0.f };
371 dt_aligned_pixel_t axis_rgb = { 0.f };
372 dt_aligned_pixel_t axis_hsb = { 0.f };
373 dt_aligned_pixel_t halfway_hsb = { 0.f };
374 float distance_source = 0.f;
375 float distance_axis = 0.f;
376 float weight_source = 0.5f;
377 float source_L = 0.f;
378 float source_rho = 0.f;
379 float source_theta = 0.f;
380
381 dt_colorrings_rgb_to_gray_cyl(source_rgb, &source_L, &source_rho, &source_theta);
382 if(source_rho <= 1e-6f) return FALSE;
383
384 _halfway_to_axis_rgb(source_rgb, halfway_rgb);
385 dt_colorrings_gray_axis_rgb_from_L(source_L, axis_rgb);
386
387 dt_colorrings_profile_rgb_to_dt_ucs_hsb(source_rgb, dt_colorrings_graph_white(), lut_profile, source_hsb);
388 dt_colorrings_profile_rgb_to_dt_ucs_hsb(axis_rgb, dt_colorrings_graph_white(), lut_profile, axis_hsb);
389 dt_colorrings_profile_rgb_to_dt_ucs_hsb(halfway_rgb, dt_colorrings_graph_white(), lut_profile, halfway_hsb);
390
397 axis_hsb[0] = source_hsb[0];
398 distance_source = _hsb_distance(halfway_hsb, source_hsb);
399 distance_axis = _hsb_distance(halfway_hsb, axis_hsb);
400 if(distance_source + distance_axis > 1e-6f)
401 weight_source = distance_axis / (distance_source + distance_axis);
402
403 return _build_anchor_from_source_rgb(halfway_rgb, weight_source * hue_delta, weight_source * saturation_delta,
404 weight_source * brightness_delta, lut_profile, anchor);
405}
406
408 const dt_aligned_pixel_t extreme_rgb,
409 const float hue_delta, const float saturation_delta,
410 const float brightness_delta,
411 const dt_iop_order_iccprofile_info_t *const lut_profile,
412 dt_colorrings_sparse_anchor_t *const anchor,
413 dt_aligned_pixel_t halfway_rgb)
414{
415 dt_aligned_pixel_t source_hsb = { 0.f };
416 dt_aligned_pixel_t extreme_hsb = { 0.f };
417 dt_aligned_pixel_t halfway_hsb = { 0.f };
418 float distance_source = 0.f;
419 float distance_extreme = 0.f;
420 float weight_source = 0.5f;
421
422 for(int c = 0; c < 3; c++) halfway_rgb[c] = 0.5f * (source_rgb[c] + extreme_rgb[c]);
423 halfway_rgb[3] = 0.f;
424
425 dt_colorrings_profile_rgb_to_dt_ucs_hsb(source_rgb, dt_colorrings_graph_white(), lut_profile, source_hsb);
426 dt_colorrings_profile_rgb_to_dt_ucs_hsb(extreme_rgb, dt_colorrings_graph_white(), lut_profile, extreme_hsb);
427 dt_colorrings_profile_rgb_to_dt_ucs_hsb(halfway_rgb, dt_colorrings_graph_white(), lut_profile, halfway_hsb);
428
429 extreme_hsb[0] = source_hsb[0];
430 distance_source = _hsb_distance(halfway_hsb, source_hsb);
431 distance_extreme = _hsb_distance(halfway_hsb, extreme_hsb);
432 if(distance_source + distance_extreme > 1e-6f)
433 weight_source = distance_extreme / (distance_source + distance_extreme);
434
435 return _build_anchor_from_source_rgb(halfway_rgb, weight_source * hue_delta, weight_source * saturation_delta,
436 weight_source * brightness_delta, lut_profile, anchor);
437}
438
439static void _apply_anchor_to_rgb(const dt_aligned_pixel_t source_rgb, const dt_colorrings_sparse_anchor_t *const anchor,
440 dt_aligned_pixel_t target_rgb)
441{
442 dt_aligned_pixel_t axis = { 0.f };
443 float source_L = 0.f;
444 float source_rho = 0.f;
445 float source_theta = 0.f;
446
447 dt_colorrings_rgb_to_gray_cyl(source_rgb, &source_L, &source_rho, &source_theta);
448 dt_colorrings_gray_basis_to_rgb(source_L + anchor->delta_L,
449 source_rho * anchor->chroma_scale * cosf(source_theta + anchor->delta_theta),
450 source_rho * anchor->chroma_scale * sinf(source_theta + anchor->delta_theta),
451 target_rgb);
452 dt_colorrings_gray_axis_rgb_from_L(source_L + anchor->delta_L, axis);
453 dt_colorrings_project_to_cube_shell(axis, target_rgb);
454 target_rgb[3] = 0.f;
455}
456
458 const dt_iop_order_iccprofile_info_t *const lut_profile,
459 dt_colorrings_sparse_anchor_t *const anchor)
460{
461 dt_aligned_pixel_t source_rgb = { 0.f };
462 _node_base_rgb(node, CLAMP(p->gamut_coverage * 0.01f, 0.f, 1.f), source_rgb);
463 return _build_anchor_from_source_rgb(source_rgb, p->hue[node] * M_PI_F / 180.f, p->saturation[node] * 0.01f,
464 p->brightness[node], lut_profile, anchor);
465}
466
468 const dt_iop_order_iccprofile_info_t *const lut_profile, dt_aligned_pixel_t source_rgb,
469 float *const hue_delta, float *const saturation_delta,
470 float *const brightness_delta)
471{
472 dt_aligned_pixel_t source_a = { 0.f };
473 dt_aligned_pixel_t source_b = { 0.f };
474 dt_aligned_pixel_t hsb_a = { 0.f };
475 dt_aligned_pixel_t hsb_b = { 0.f };
476 dt_aligned_pixel_t hsb_mid = { 0.f };
477 float distance_a = 0.f;
478 float distance_b = 0.f;
479 float weight_a = 0.5f;
480 float weight_b = 0.5f;
481
482 _node_base_rgb(edge.a, CLAMP(p->gamut_coverage * 0.01f, 0.f, 1.f), source_a);
483 _node_base_rgb(edge.b, CLAMP(p->gamut_coverage * 0.01f, 0.f, 1.f), source_b);
484
496 for_each_channel(c, aligned(source_a, source_b, source_rgb)) source_rgb[c] = 0.5f * (source_a[c] + source_b[c]);
497 source_rgb[3] = 0.f;
500 dt_colorrings_profile_rgb_to_dt_ucs_hsb(source_rgb, dt_colorrings_graph_white(), lut_profile, hsb_mid);
501
502 distance_a = _hsb_distance(hsb_mid, hsb_a);
503 distance_b = _hsb_distance(hsb_mid, hsb_b);
504 if(distance_a + distance_b > 1e-6f)
505 {
506 weight_a = distance_b / (distance_a + distance_b);
507 weight_b = distance_a / (distance_a + distance_b);
508 }
509
510 *hue_delta = _mix_hue_delta_weighted(p->hue[edge.a], p->hue[edge.b], weight_a, weight_b);
511 *saturation_delta = (weight_a * p->saturation[edge.a] + weight_b * p->saturation[edge.b]) * 0.01f;
512 *brightness_delta = weight_a * p->brightness[edge.a] + weight_b * p->brightness[edge.b];
513 return TRUE;
514}
515
516static inline void _store_viewer_control_node(dt_lut_viewer_control_node_t *const control_nodes, int *const count,
517 const dt_aligned_pixel_t input_rgb, const dt_aligned_pixel_t output_rgb)
518{
519 for(int c = 0; c < 3; c++)
520 {
521 control_nodes[*count].input_rgb[c] = input_rgb[c];
522 control_nodes[*count].output_rgb[c] = output_rgb[c];
523 }
524 (*count)++;
525}
526
527static inline void _append_anchor(dt_colorrings_sparse_anchor_t *const anchors, int *const anchor_count,
528 const dt_colorrings_sparse_anchor_t *const anchor)
529{
530 anchors[*anchor_count] = *anchor;
531 (*anchor_count)++;
532}
533
535 const dt_iop_order_iccprofile_info_t *const lut_profile,
536 dt_colorrings_sparse_anchor_t *const anchor)
537{
538 dt_aligned_pixel_t source_rgb = { 0.f };
539 float hue_delta = 0.f;
540 float saturation_delta = 0.f;
541 float brightness_delta = 0.f;
542
543 if(!_build_edge_edit(p, edge, lut_profile, source_rgb, &hue_delta, &saturation_delta, &brightness_delta))
544 return FALSE;
545
546 return _build_anchor_from_source_rgb(source_rgb, hue_delta, saturation_delta, brightness_delta, lut_profile, anchor);
547}
548
551 const dt_iop_order_iccprofile_info_t *const lut_profile,
552 dt_colorrings_sparse_anchor_t *const anchor,
553 dt_aligned_pixel_t midpoint_rgb)
554{
555 dt_aligned_pixel_t source_rgb = { 0.f };
556 _node_base_rgb(node, CLAMP(p->gamut_coverage * 0.01f, 0.f, 1.f), source_rgb);
557 return _build_halfway_radial_anchor_from_source_rgb(source_rgb, p->hue[node] * M_PI_F / 180.f,
558 p->saturation[node] * 0.01f, p->brightness[node],
559 lut_profile, anchor, midpoint_rgb);
560}
561
564 const gboolean toward_white,
565 const dt_iop_order_iccprofile_info_t *const lut_profile,
566 dt_colorrings_sparse_anchor_t *const anchor,
567 dt_aligned_pixel_t midpoint_rgb)
568{
569 dt_aligned_pixel_t source_rgb = { 0.f };
570 dt_aligned_pixel_t extreme_rgb = { 0.f };
571 _node_base_rgb(node, CLAMP(p->gamut_coverage * 0.01f, 0.f, 1.f), source_rgb);
572 _black_white_rgb(toward_white, extreme_rgb);
573 return _build_halfway_extreme_anchor_from_source_rgb(source_rgb, extreme_rgb, p->hue[node] * M_PI_F / 180.f,
574 p->saturation[node] * 0.01f, p->brightness[node],
575 lut_profile, anchor, midpoint_rgb);
576}
577
580 const dt_iop_order_iccprofile_info_t *const lut_profile,
581 dt_colorrings_sparse_anchor_t *const anchor,
582 dt_aligned_pixel_t midpoint_rgb)
583{
584 dt_aligned_pixel_t source_rgb = { 0.f };
585 float hue_delta = 0.f;
586 float saturation_delta = 0.f;
587 float brightness_delta = 0.f;
588
589 if(!_build_edge_edit(p, edge, lut_profile, source_rgb, &hue_delta, &saturation_delta, &brightness_delta))
590 return FALSE;
591
592 return _build_halfway_radial_anchor_from_source_rgb(source_rgb, hue_delta, saturation_delta, brightness_delta,
593 lut_profile, anchor, midpoint_rgb);
594}
595
597 const dt_iop_order_iccprofile_info_t *const lut_profile,
598 dt_lut_viewer_control_node_t *const control_nodes)
599{
600 int count = 0;
601
602 if(IS_NULL_PTR(control_nodes) || IS_NULL_PTR(lut_profile)) return 0;
603
604 for(int node = 0; node < DT_IOP_COLORPRIMARIES_NODE_COUNT; node++)
605 {
606 dt_colorrings_sparse_anchor_t anchor = { 0.f };
607 dt_aligned_pixel_t source_rgb = { 0.f };
608 dt_aligned_pixel_t target_rgb = { 0.f };
609
610 _node_base_rgb((dt_iop_colorprimaries_node_t)node, CLAMP(p->gamut_coverage * 0.01f, 0.f, 1.f), source_rgb);
611 if(!_build_node_anchor(p, (dt_iop_colorprimaries_node_t)node, lut_profile, &anchor)) continue;
612 _apply_anchor_to_rgb(source_rgb, &anchor, target_rgb);
613 _store_viewer_control_node(control_nodes, &count, source_rgb, target_rgb);
614 }
615
616 for(int edge = 0; edge < DT_IOP_COLORPRIMARIES_EDGE_COUNT; edge++)
617 {
618 dt_colorrings_sparse_anchor_t anchor = { 0.f };
619 dt_aligned_pixel_t source_rgb = { 0.f };
620 dt_aligned_pixel_t target_rgb = { 0.f };
621 float hue_delta = 0.f;
622 float saturation_delta = 0.f;
623 float brightness_delta = 0.f;
624
625 if(!_build_edge_edit(p, _chroma_edges[edge], lut_profile, source_rgb, &hue_delta, &saturation_delta, &brightness_delta))
626 continue;
627 if(!_build_anchor_from_source_rgb(source_rgb, hue_delta, saturation_delta, brightness_delta, lut_profile, &anchor))
628 continue;
629 _apply_anchor_to_rgb(source_rgb, &anchor, target_rgb);
630 _store_viewer_control_node(control_nodes, &count, source_rgb, target_rgb);
631 }
632
633 for(int white = 0; white < 2; white++)
634 for(int node = 0; node < DT_IOP_COLORPRIMARIES_NODE_COUNT; node++)
635 {
636 dt_colorrings_sparse_anchor_t anchor = { 0.f };
637 dt_aligned_pixel_t midway_rgb = { 0.f };
638 dt_aligned_pixel_t target_rgb = { 0.f };
639
640 if(!_build_node_black_white_midpoint_anchor(p, (dt_iop_colorprimaries_node_t)node, white != 0, lut_profile, &anchor,
641 midway_rgb))
642 continue;
643 _apply_anchor_to_rgb(midway_rgb, &anchor, target_rgb);
644 _store_viewer_control_node(control_nodes, &count, midway_rgb, target_rgb);
645 }
646
647 for(int node = 0; node < DT_IOP_COLORPRIMARIES_NODE_COUNT; node++)
648 {
649 dt_colorrings_sparse_anchor_t anchor = { 0.f };
650 dt_aligned_pixel_t inner_source_rgb = { 0.f };
651 dt_aligned_pixel_t inner_target_rgb = { 0.f };
652
653 if(!_build_node_radial_midpoint_anchor(p, (dt_iop_colorprimaries_node_t)node, lut_profile, &anchor, inner_source_rgb))
654 continue;
655 _apply_anchor_to_rgb(inner_source_rgb, &anchor, inner_target_rgb);
656 _store_viewer_control_node(control_nodes, &count, inner_source_rgb, inner_target_rgb);
657 }
658
659 for(int edge = 0; edge < DT_IOP_COLORPRIMARIES_EDGE_COUNT; edge++)
660 {
661 dt_colorrings_sparse_anchor_t anchor = { 0.f };
662 dt_aligned_pixel_t inner_source_rgb = { 0.f };
663 dt_aligned_pixel_t inner_target_rgb = { 0.f };
664 if(!_build_edge_radial_midpoint_anchor(p, _chroma_edges[edge], lut_profile, &anchor, inner_source_rgb))
665 continue;
666 _apply_anchor_to_rgb(inner_source_rgb, &anchor, inner_target_rgb);
667 _store_viewer_control_node(control_nodes, &count, inner_source_rgb, inner_target_rgb);
668 }
669
670 return count;
671}
672
674 const dt_iop_order_iccprofile_info_t *const lut_profile, dt_aligned_pixel_t HSB)
675{
676 dt_aligned_pixel_t RGB = { 0.f };
677 _node_base_rgb(node, CLAMP(p->gamut_coverage * 0.01f, 0.f, 1.f), RGB);
679}
680
682 const dt_iop_order_iccprofile_info_t *const lut_profile, dt_aligned_pixel_t HSB)
683{
684 dt_aligned_pixel_t source_hsb = { 0.f };
685 _node_source_hsb(p, node, lut_profile, source_hsb);
686
687 HSB[0] = dt_colorrings_wrap_hue_pi(source_hsb[0] + p->hue[node] * M_PI_F / 180.f);
688 HSB[1] = CLAMP(source_hsb[1] + p->saturation[node] * 0.01f, 0.f, 1.f);
689 HSB[2] = CLAMP(source_hsb[2] + p->brightness[node], 0.f, 1.f);
690 HSB[3] = 0.f;
691}
692
694 const dt_iop_order_iccprofile_info_t *lut_profile)
695{
696 const gboolean log_perf = (darktable.unmuted & DT_DEBUG_PERF) != 0;
697 const double start = log_perf ? dt_get_wtime() : 0.0;
698 const size_t clut_size = (size_t)DT_COLORRINGS_CLUT_LEVEL * DT_COLORRINGS_CLUT_LEVEL * DT_COLORRINGS_CLUT_LEVEL * 3u;
699 const float inv_sigma_L = 1.f / fmaxf(p->sigma_L * 0.01f, 1e-6f);
700 const float inv_sigma_rho = 1.f / fmaxf(p->sigma_rho, 1e-6f);
701 const float inv_sigma_theta = 1.f / fmaxf(p->sigma_theta, 1e-6f);
703 int anchor_count = 0;
704
705 if(IS_NULL_PTR(d->clut)) d->clut = dt_alloc_align_float(clut_size);
706 d->lut_profile = (dt_iop_order_iccprofile_info_t *)lut_profile;
707
716 for(int node = 0; node < DT_IOP_COLORPRIMARIES_NODE_COUNT; node++)
717 {
718 dt_colorrings_sparse_anchor_t anchor = { 0.f };
719 if(_build_node_anchor(p, (dt_iop_colorprimaries_node_t)node, lut_profile, &anchor))
720 _append_anchor(anchors, &anchor_count, &anchor);
721 }
722
723 for(int edge = 0; edge < DT_IOP_COLORPRIMARIES_EDGE_COUNT; edge++)
724 {
725 dt_colorrings_sparse_anchor_t anchor = { 0.f };
726 if(_build_edge_anchor(p, _chroma_edges[edge], lut_profile, &anchor))
727 _append_anchor(anchors, &anchor_count, &anchor);
728 }
729
730 for(int node = 0; node < DT_IOP_COLORPRIMARIES_NODE_COUNT; node++)
731 {
732 dt_colorrings_sparse_anchor_t inner_anchor = { 0.f };
733 dt_aligned_pixel_t inner_source_rgb = { 0.f };
734 if(_build_node_radial_midpoint_anchor(p, (dt_iop_colorprimaries_node_t)node, lut_profile, &inner_anchor,
735 inner_source_rgb))
736 _append_anchor(anchors, &anchor_count, &inner_anchor);
737 }
738
739 for(int edge = 0; edge < DT_IOP_COLORPRIMARIES_EDGE_COUNT; edge++)
740 {
741 dt_colorrings_sparse_anchor_t inner_anchor = { 0.f };
742 dt_aligned_pixel_t inner_source_rgb = { 0.f };
743 if(_build_edge_radial_midpoint_anchor(p, _chroma_edges[edge], lut_profile, &inner_anchor, inner_source_rgb))
744 _append_anchor(anchors, &anchor_count, &inner_anchor);
745 }
746
747 for(int white = 0; white < 2; white++)
748 for(int node = 0; node < DT_IOP_COLORPRIMARIES_NODE_COUNT; node++)
749 {
750 dt_colorrings_sparse_anchor_t extreme_anchor = { 0.f };
751 dt_aligned_pixel_t midway_rgb = { 0.f };
753 &extreme_anchor, midway_rgb))
754 _append_anchor(anchors, &anchor_count, &extreme_anchor);
755 }
756
757 for(int sample = 0; sample < DT_IOP_COLORPRIMARIES_AXIS_ANCHORS; sample++)
758 {
759 const float value = (float)sample / (float)(DT_IOP_COLORPRIMARIES_AXIS_ANCHORS - 1);
760 anchors[anchor_count].L = value * DT_IOP_COLORPRIMARIES_SQRT3;
761 anchors[anchor_count].rho = 0.f;
762 anchors[anchor_count].theta = 0.f;
763 anchors[anchor_count].delta_L = 0.f;
764 anchors[anchor_count].chroma_scale = 1.f;
765 anchors[anchor_count].delta_theta = 0.f;
766 anchors[anchor_count].weight = 1.f / (float)DT_IOP_COLORPRIMARIES_AXIS_ANCHORS;
767 anchor_count++;
768 }
769
770 dt_colorrings_fill_lut_sparse_local_field(d->clut, DT_COLORRINGS_CLUT_LEVEL, anchors, anchor_count, inv_sigma_L,
771 inv_sigma_rho, inv_sigma_theta, fmaxf(p->neutral_protection, 1e-6f));
772 d->clut_level = DT_COLORRINGS_CLUT_LEVEL;
773
774 if(log_perf)
775 dt_print(DT_DEBUG_PERF, "[colorprimaries] build_clut level=%u anchors=%d total=%.3fms\n", d->clut_level,
776 anchor_count, 1000.0 * (dt_get_wtime() - start));
777}
778
781{
785 const dt_iop_order_iccprofile_info_t *lut_profile
787 : NULL;
789
790 if(IS_NULL_PTR(lut_profile) || IS_NULL_PTR(work_profile))
791 {
792 d->clut = NULL;
793 d->clut_level = 0;
794 d->white_level = 1.f;
795 d->lut_profile = NULL;
796 d->work_profile = NULL;
797 d->interpolation = DT_LUT3D_INTERP_TETRAHEDRAL;
798 return;
799 }
800
802 if(!gd->cache_valid || !_lut_fields_equal(&gd->params, p))
803 {
804 _build_clut(&gd->cache, p, lut_profile);
805 memcpy(&gd->params, p, sizeof(*p));
806 gd->cache_valid = TRUE;
807 gd->cache_generation++;
808 }
809
810 d->clut = gd->cache.clut;
811 d->clut_level = gd->cache.clut_level;
812 d->white_level = exp2f(p->white_level);
813 d->lut_profile = (dt_iop_order_iccprofile_info_t *)lut_profile;
814 d->work_profile = (dt_iop_order_iccprofile_info_t *)work_profile;
815 d->interpolation = (dt_lut3d_interpolation_t)p->interpolation;
817
820}
821
823{
825 piece->data = d;
827 d->white_level = 1.f;
828 d->interpolation = DT_LUT3D_INTERP_TETRAHEDRAL;
829}
830
832{
834 d->clut = NULL;
835 dt_free_align(piece->data);
836 piece->data = NULL;
837}
838
839#ifdef HAVE_OPENCL
840int process_cl(struct dt_iop_module_t *self, const dt_dev_pixelpipe_t *pipe, const dt_dev_pixelpipe_iop_t *piece,
841 cl_mem dev_in, cl_mem dev_out)
842{
845 const int width = piece->roi_in.width;
846 const int height = piece->roi_in.height;
847 const int devid = pipe->devid;
848 const size_t sizes[] = { ROUNDUPDWD(width, devid), ROUNDUPDHT(height, devid), 1 };
849 const float white_level = fmaxf(d->white_level, 1e-6f);
850 const float normalize = 1.f / white_level;
851 const float denormalize = white_level;
852 const float black = 0.f;
853 cl_mem clut_cl = NULL;
854 cl_int err = CL_SUCCESS;
855 const int kernel = (d->interpolation == DT_LUT3D_INTERP_TRILINEAR) ? gd->kernel_lut3d_trilinear
856 : (d->interpolation == DT_LUT3D_INTERP_PYRAMID) ? gd->kernel_lut3d_pyramid
858
859 if(IS_NULL_PTR(d->clut) || d->clut_level == 0 || IS_NULL_PTR(d->lut_profile) || IS_NULL_PTR(d->work_profile)) return FALSE;
860
861 dt_opencl_set_kernel_arg(devid, gd->kernel_exposure, 0, sizeof(cl_mem), &dev_in);
862 dt_opencl_set_kernel_arg(devid, gd->kernel_exposure, 1, sizeof(cl_mem), &dev_out);
863 dt_opencl_set_kernel_arg(devid, gd->kernel_exposure, 2, sizeof(int), &width);
864 dt_opencl_set_kernel_arg(devid, gd->kernel_exposure, 3, sizeof(int), &height);
865 dt_opencl_set_kernel_arg(devid, gd->kernel_exposure, 4, sizeof(float), &black);
866 dt_opencl_set_kernel_arg(devid, gd->kernel_exposure, 5, sizeof(float), &normalize);
867 err = dt_opencl_enqueue_kernel_2d(devid, gd->kernel_exposure, sizes);
868 if(err != CL_SUCCESS) goto cleanup;
869
870 if(!dt_ioppr_transform_image_colorspace_rgb_cl(devid, dev_out, dev_out, width, height, d->work_profile,
871 d->lut_profile, "colorequal work to HLG Rec2020"))
872 {
873 err = CL_INVALID_OPERATION;
874 goto cleanup;
875 }
876
878 clut_cl = dt_opencl_copy_host_to_device_constant(devid, sizeof(float) * 3 * d->clut_level * d->clut_level * d->clut_level,
879 gd->cache.clut);
881 if(IS_NULL_PTR(clut_cl))
882 {
883 err = CL_MEM_OBJECT_ALLOCATION_FAILURE;
884 goto cleanup;
885 }
886
887 dt_opencl_set_kernel_arg(devid, kernel, 0, sizeof(cl_mem), &dev_out);
888 dt_opencl_set_kernel_arg(devid, kernel, 1, sizeof(cl_mem), &dev_out);
889 dt_opencl_set_kernel_arg(devid, kernel, 2, sizeof(int), &width);
890 dt_opencl_set_kernel_arg(devid, kernel, 3, sizeof(int), &height);
891 dt_opencl_set_kernel_arg(devid, kernel, 4, sizeof(cl_mem), &clut_cl);
892 dt_opencl_set_kernel_arg(devid, kernel, 5, sizeof(int), &d->clut_level);
893 err = dt_opencl_enqueue_kernel_2d(devid, kernel, sizes);
894 if(err != CL_SUCCESS) goto cleanup;
895
896 if(!dt_ioppr_transform_image_colorspace_rgb_cl(devid, dev_out, dev_out, width, height, d->lut_profile,
897 d->work_profile, "colorequal HLG Rec2020 to work"))
898 {
899 err = CL_INVALID_OPERATION;
900 goto cleanup;
901 }
902
903 dt_opencl_set_kernel_arg(devid, gd->kernel_exposure, 0, sizeof(cl_mem), &dev_out);
904 dt_opencl_set_kernel_arg(devid, gd->kernel_exposure, 1, sizeof(cl_mem), &dev_out);
905 dt_opencl_set_kernel_arg(devid, gd->kernel_exposure, 2, sizeof(int), &width);
906 dt_opencl_set_kernel_arg(devid, gd->kernel_exposure, 3, sizeof(int), &height);
907 dt_opencl_set_kernel_arg(devid, gd->kernel_exposure, 4, sizeof(float), &black);
908 dt_opencl_set_kernel_arg(devid, gd->kernel_exposure, 5, sizeof(float), &denormalize);
909 err = dt_opencl_enqueue_kernel_2d(devid, gd->kernel_exposure, sizes);
910
911cleanup:
913 if(err != CL_SUCCESS) dt_print(DT_DEBUG_OPENCL, "[opencl_colorequal] couldn't enqueue kernel! %d\n", err);
914 return err == CL_SUCCESS;
915}
916#endif
917
919int process(struct dt_iop_module_t *self, const dt_dev_pixelpipe_t *pipe, const dt_dev_pixelpipe_iop_t *piece,
920 const void *const ibuf, void *const obuf)
921{
924 const int width = piece->roi_in.width;
925 const int height = piece->roi_in.height;
926 const int ch = piece->dsc_in.channels;
927
928 if(IS_NULL_PTR(d->clut) || d->clut_level == 0 || IS_NULL_PTR(d->lut_profile) || IS_NULL_PTR(d->work_profile))
929 {
931 return 0;
932 }
933
934 const float white_level = fmaxf(d->white_level, 1e-6f);
936 for(size_t k = 0; k < (size_t)width * height; k++)
937 {
938 const float *in = (const float *)ibuf + k * ch;
939 float *out = (float *)obuf + k * ch;
940 out[0] = in[0] / white_level;
941 out[1] = in[1] / white_level;
942 out[2] = in[2] / white_level;
943 if(ch > 3) out[3] = in[3];
944 }
945
946 dt_ioppr_transform_image_colorspace_rgb((float *)obuf, (float *)obuf, width, height, d->work_profile, d->lut_profile,
947 "colorprimaries work to HLG Rec2020");
949 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{
1079 const dt_iop_order_iccprofile_info_t *lut_profile
1081 : NULL;
1082 const dt_iop_order_iccprofile_info_t *display_profile
1084 : NULL;
1085 uint64_t cache_generation = 0;
1086
1087 if(IS_NULL_PTR(g->viewer)) return;
1088
1090 cache_generation = gd->cache_generation;
1092
1093 if(!g->viewer_lut_dirty && g->viewer_lut_valid && g->viewer_lut_generation == cache_generation
1094 && g->viewer_display_profile == display_profile)
1095 return;
1096
1097 if(IS_NULL_PTR(lut_profile))
1098 {
1099 dt_lut_viewer_set_lut(g->viewer, NULL, 0, NULL, NULL, NULL);
1100 dt_lut_viewer_set_control_nodes(g->viewer, NULL, 0);
1101 g->viewer_lut_dirty = FALSE;
1102 g->viewer_lut_valid = FALSE;
1103 g->viewer_lut_generation = 0;
1104 g->viewer_display_profile = display_profile;
1105 g->viewer_control_node_count = 0;
1106 return;
1107 }
1108
1110 if(!gd->cache_valid || !_lut_fields_equal(&gd->params, p) || IS_NULL_PTR((&gd->cache)->clut))
1111 {
1112 _build_clut(&gd->cache, p, lut_profile);
1113 memcpy(&gd->params, p, sizeof(*p));
1114 gd->cache_valid = TRUE;
1115 gd->cache_generation++;
1116 }
1117
1118 cache_generation = gd->cache_generation;
1119 g->viewer_lut.clut = gd->cache.clut;
1120 g->viewer_lut.clut_level = gd->cache.clut_level;
1121 g->viewer_lut.lut_profile = (dt_iop_order_iccprofile_info_t *)lut_profile;
1122 g->viewer_control_node_count
1123 = _build_viewer_control_nodes(p, lut_profile, g->viewer_control_nodes);
1124 dt_lut_viewer_set_lut(g->viewer, g->viewer_lut.clut, g->viewer_lut.clut_level, &gd->lock, g->viewer_lut.lut_profile,
1125 display_profile);
1126 dt_lut_viewer_set_control_nodes(g->viewer, g->viewer_control_nodes, g->viewer_control_node_count);
1128
1129 g->viewer_lut_dirty = FALSE;
1130 g->viewer_lut_valid = TRUE;
1131 g->viewer_lut_generation = cache_generation;
1132 g->viewer_display_profile = display_profile;
1133
1134 dt_lut_viewer_queue_draw(g->viewer);
1135}
1136
1137void gui_changed(dt_iop_module_t *self, GtkWidget *w, void *previous)
1138{
1140 if(IS_NULL_PTR(g)) return;
1142}
1143
1145{
1148
1149 ++darktable.gui->reset;
1150 dt_bauhaus_slider_set(g->white_level, p->white_level);
1151 dt_bauhaus_slider_set(g->gamut_coverage, p->gamut_coverage);
1152 dt_bauhaus_slider_set(g->sigma_L, p->sigma_L);
1153 dt_bauhaus_slider_set(g->sigma_rho, p->sigma_rho);
1154 dt_bauhaus_slider_set(g->sigma_theta, p->sigma_theta);
1155 dt_bauhaus_slider_set(g->neutral_protection, p->neutral_protection);
1156 dt_bauhaus_combobox_set(g->interpolation, p->interpolation);
1157
1158 for(int node = 0; node < DT_IOP_COLORPRIMARIES_NODE_COUNT; node++)
1159 {
1160 dt_bauhaus_slider_set(g->node_hue[node], p->hue[node]);
1161 dt_bauhaus_slider_set(g->node_saturation[node], p->saturation[node]);
1162 dt_bauhaus_slider_set(g->node_brightness[node], p->brightness[node]);
1163 }
1164 --darktable.gui->reset;
1165
1166 gui_changed(self, NULL, NULL);
1167}
1168
1170{
1172 g->viewer_lut.clut = NULL;
1173 dt_lut_viewer_destroy(&g->viewer);
1175}
1176
1177static GtkWidget *_new_section_label(GtkWidget *box, const char *label)
1178{
1179 GtkWidget *section = dt_ui_section_label_new(label);
1180 gtk_widget_set_hexpand(section, TRUE);
1181 gtk_box_pack_start(GTK_BOX(box), section, FALSE, FALSE, 0);
1182 return section;
1183}
1184
1187{
1190 const dt_iop_module_t *sampled_module = piece && piece->module ? piece->module : self;
1191
1192 if(picker != g->white_level) return;
1193 if(sampled_module->picked_color_max[0] < sampled_module->picked_color_min[0]) return;
1194
1195 dt_aligned_pixel_t max_Ych = { 0.f };
1196 _pipe_rgb_to_Ych(self, pipe, (const float *)sampled_module->picked_color_max, max_Ych);
1197 ++darktable.gui->reset;
1198 p->white_level = log2f(fmaxf(max_Ych[0], 1e-6f));
1199 dt_bauhaus_slider_set(g->white_level, p->white_level);
1200 --darktable.gui->reset;
1201
1203}
1204
1205void autoset(struct dt_iop_module_t *self, const struct dt_dev_pixelpipe_t *pipe,
1206 const struct dt_dev_pixelpipe_iop_t *piece, const void *i)
1207{
1208 const dt_iop_order_iccprofile_info_t *const work_profile = dt_ioppr_get_pipe_current_profile_info(self, pipe);
1209 if(IS_NULL_PTR(work_profile) || piece->dsc_in.channels != 4) return;
1210
1212 const dt_iop_roi_t *const roi_out = &piece->roi_out;
1213 const float *const restrict in = (const float *)i;
1214 float max_Y = 0.0f;
1215
1216 __OMP_PARALLEL_FOR__(reduction(max:max_Y))
1217 for(size_t k = 0; k < (size_t)roi_out->width * roi_out->height * 4; k += 4)
1218 {
1219 dt_aligned_pixel_t Ych = { 0.f };
1220 dt_colorrings_profile_rgb_to_Ych(in + k, work_profile, Ych);
1221 if(isfinite(Ych[0]))
1222 max_Y = fmaxf(max_Y, Ych[0]);
1223 }
1224
1225 p->white_level = log2f(fmaxf(max_Y, 1e-6f));
1226}
1227
1229{
1232
1233 g->viewer_lut_dirty = TRUE;
1234 g->viewer_lut_valid = FALSE;
1235 g->viewer_lut_generation = 0;
1236 g->viewer_lut.clut = NULL;
1237 g->viewer_lut.clut_level = 0;
1238 g->viewer_lut.lut_profile = NULL;
1239 g->viewer_lut.work_profile = NULL;
1240 g->preview_signal_connected = FALSE;
1241 g->viewer_display_profile = NULL;
1242
1243 self->widget = gtk_box_new(GTK_ORIENTATION_VERTICAL, DT_GUI_BOX_SPACING);
1244
1245 g->tabs = GTK_NOTEBOOK(gtk_notebook_new());
1246 gtk_notebook_set_show_border(g->tabs, FALSE);
1247 gtk_box_pack_start(GTK_BOX(self->widget), GTK_WIDGET(g->tabs), TRUE, TRUE, 0);
1248
1249 GtkWidget *colors_page = gtk_box_new(GTK_ORIENTATION_VERTICAL, DT_GUI_BOX_SPACING);
1250 GtkWidget *colors_tab = gtk_label_new(_("colors"));
1251 dt_gui_add_class(colors_tab, "dt_modulegroups_tab_label");
1252 gtk_notebook_append_page(g->tabs, colors_page, colors_tab);
1253 gtk_container_child_set(GTK_CONTAINER(g->tabs), colors_page, "tab-expand", TRUE, "tab-fill", TRUE, NULL);
1254
1255 GtkWidget *const module_root = self->widget;
1256 self->widget = colors_page;
1257 for(int node = 0; node < DT_IOP_COLORPRIMARIES_NODE_COUNT; node++)
1258 {
1259 char hue_name[16] = { 0 };
1260 char saturation_name[24] = { 0 };
1261 char brightness_name[24] = { 0 };
1262 snprintf(hue_name, sizeof(hue_name), "hue[%d]", node);
1263 snprintf(saturation_name, sizeof(saturation_name), "saturation[%d]", node);
1264 snprintf(brightness_name, sizeof(brightness_name), "brightness[%d]", node);
1265
1267 g->node_hue[node] = dt_bauhaus_slider_from_params(self, hue_name);
1268 dt_bauhaus_widget_set_label(g->node_hue[node], N_("hue"));
1269 dt_bauhaus_slider_set_format(g->node_hue[node], _("°"));
1270 dt_bauhaus_slider_set_default(g->node_hue[node], defaults->hue[node]);
1271
1272 g->node_saturation[node] = dt_bauhaus_slider_from_params(self, saturation_name);
1273 dt_bauhaus_widget_set_label(g->node_saturation[node], N_("saturation"));
1274 dt_bauhaus_slider_set_format(g->node_saturation[node], _(" %"));
1275 dt_bauhaus_slider_set_default(g->node_saturation[node], defaults->saturation[node]);
1276
1277 g->node_brightness[node] = dt_bauhaus_slider_from_params(self, brightness_name);
1278 dt_bauhaus_widget_set_label(g->node_brightness[node], N_("brightness"));
1279 dt_bauhaus_slider_set_format(g->node_brightness[node], _(" %"));
1280 dt_bauhaus_slider_set_soft_range(g->node_brightness[node], -0.25f, 0.25f);
1281 dt_bauhaus_slider_set_default(g->node_brightness[node], defaults->brightness[node]);
1282 }
1283 self->widget = module_root;
1284
1285 GtkWidget *options_page = gtk_box_new(GTK_ORIENTATION_VERTICAL, DT_GUI_BOX_SPACING);
1286 GtkWidget *options_tab = gtk_label_new(_("options"));
1287 dt_gui_add_class(options_tab, "dt_modulegroups_tab_label");
1288 gtk_notebook_append_page(g->tabs, options_page, options_tab);
1289 gtk_container_child_set(GTK_CONTAINER(g->tabs), options_page, "tab-expand", TRUE, "tab-fill", TRUE, NULL);
1290
1291 self->widget = options_page;
1292 g->white_level = dt_color_picker_new(self, DT_COLOR_PICKER_AREA, dt_bauhaus_slider_from_params(self, "white_level"));
1293 dt_bauhaus_slider_set_default(g->white_level, defaults->white_level);
1294 dt_bauhaus_slider_set_format(g->white_level, _(" EV"));
1295 dt_bauhaus_slider_set_soft_range(g->white_level, -2.f, 2.f);
1296
1297 g->gamut_coverage = dt_bauhaus_slider_from_params(self, "gamut_coverage");
1298 dt_bauhaus_slider_set_default(g->gamut_coverage, defaults->gamut_coverage);
1299 dt_bauhaus_slider_set_format(g->gamut_coverage, _(" %"));
1300 dt_bauhaus_slider_set_soft_range(g->gamut_coverage, 0.f, 100.f);
1301
1302 g->sigma_L = dt_bauhaus_slider_from_params(self, "sigma_L");
1303 dt_bauhaus_slider_set_default(g->sigma_L, defaults->sigma_L);
1304 dt_bauhaus_slider_set_format(g->sigma_L, _(" %"));
1305 dt_bauhaus_slider_set_soft_range(g->sigma_L, 1.f, 100.f);
1306
1307 g->sigma_rho = dt_bauhaus_slider_from_params(self, "sigma_rho");
1308 dt_bauhaus_slider_set_default(g->sigma_rho, defaults->sigma_rho);
1309 dt_bauhaus_slider_set_soft_range(g->sigma_rho, 0.1f, 2.f);
1310
1311 g->sigma_theta = dt_bauhaus_slider_from_params(self, "sigma_theta");
1312 dt_bauhaus_slider_set_default(g->sigma_theta, defaults->sigma_theta);
1313 dt_bauhaus_slider_set_soft_range(g->sigma_theta, 0.05f, 2.f);
1314
1315 g->neutral_protection = dt_bauhaus_slider_from_params(self, "neutral_protection");
1316 dt_bauhaus_slider_set_default(g->neutral_protection, defaults->neutral_protection);
1317 dt_bauhaus_slider_set_soft_range(g->neutral_protection, 0.f, 1.f);
1318
1319 g->interpolation = dt_bauhaus_combobox_from_params(self, "interpolation");
1320 dt_bauhaus_combobox_set_default(g->interpolation, defaults->interpolation);
1321 self->widget = module_root;
1322
1323 g->viewer = dt_lut_viewer_new(DT_GUI_MODULE(self));
1324 if(g->viewer)
1325 gtk_box_pack_start(GTK_BOX(options_page), dt_lut_viewer_get_widget(g->viewer), TRUE, TRUE, 0);
1326
1327 gtk_widget_show_all(self->widget);
1328 gui_update(self);
1329}
1330
1332{
1333 dt_iop_colorprimaries_global_data_t *gd = malloc(sizeof(*gd));
1334 memset(gd, 0, sizeof(*gd));
1335 module->data = gd;
1336 dt_pthread_rwlock_init(&gd->lock, NULL);
1337
1338#ifdef HAVE_OPENCL
1339 const int lut_program = 28; // lut3d.cl, from programs.conf
1340 const int basic_program = 2; // basic.cl, from programs.conf
1341 gd->kernel_lut3d_tetrahedral = dt_opencl_create_kernel(lut_program, "lut3d_tetrahedral");
1342 gd->kernel_lut3d_trilinear = dt_opencl_create_kernel(lut_program, "lut3d_trilinear");
1343 gd->kernel_lut3d_pyramid = dt_opencl_create_kernel(lut_program, "lut3d_pyramid");
1344 gd->kernel_exposure = dt_opencl_create_kernel(basic_program, "exposure");
1345#endif
1346}
1347
1362
1364{
1365 dt_iop_default_init(module);
1367 memcpy(module->params, module->default_params, module->params_size);
1368}
1369
1370// clang-format off
1371// modelines: These editor modelines have been set for all relevant files by tools/update_modelines.py
1372// vim: shiftwidth=2 expandtab tabstop=2 cindent
1373// kate: tab-indents: off; indent-width 2; replace-tabs on; indent-mode cstyle; remove-trailing-spaces modified;
1374// 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:1647
void dt_bauhaus_slider_clear_stops(GtkWidget *widget)
Definition bauhaus.c:2364
void dt_bauhaus_slider_set_default(GtkWidget *widget, float def)
Definition bauhaus.c:1640
void dt_bauhaus_slider_set_stop(GtkWidget *widget, float stop, float r, float g, float b)
Definition bauhaus.c:2372
void dt_bauhaus_combobox_set_default(GtkWidget *widget, int def)
Definition bauhaus.c:1551
void dt_bauhaus_slider_set(GtkWidget *widget, float pos)
Definition bauhaus.c:3506
void dt_bauhaus_combobox_set(GtkWidget *widget, const int pos)
Definition bauhaus.c:2301
void dt_bauhaus_widget_set_label(GtkWidget *widget, const char *label)
Definition bauhaus.c:1653
void dt_bauhaus_slider_set_format(GtkWidget *widget, const char *format)
Definition bauhaus.c:3598
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:181
void dt_print(dt_debug_thread_t thread, const char *msg,...)
Definition darktable.c:1542
#define dt_free_align(ptr)
Definition darktable.h:481
static void * dt_calloc_align(size_t size)
Definition darktable.h:488
@ DT_DEBUG_OPENCL
Definition darktable.h:722
@ DT_DEBUG_PERF
Definition darktable.h:719
#define for_each_channel(_var,...)
Definition darktable.h:662
static float * dt_alloc_align_float(size_t pixels)
Definition darktable.h:494
#define dt_free(ptr)
Definition darktable.h:456
#define DT_MODULE_INTROSPECTION(MODVER, PARAMSTYPE)
Definition darktable.h:151
#define __DT_CLONE_TARGETS__
Definition darktable.h:367
#define __OMP_PARALLEL_FOR__(...)
Definition darktable.h:258
static const dt_aligned_pixel_simd_t value
Definition darktable.h:577
static double dt_get_wtime(void)
Definition darktable.h:914
#define IS_NULL_PTR(p)
C is way too permissive with !=, == and if(var) checks, which can mean too many things depending on w...
Definition darktable.h:281
#define M_PI_F
#define dt_dev_add_history_item(dev, module, enable, redraw)
void dt_iop_params_t
Definition dev_history.h:41
#define dt_pthread_rwlock_destroy
Definition dtpthread.h:391
#define dt_pthread_rwlock_wrlock
Definition dtpthread.h:394
#define dt_pthread_rwlock_t
Definition dtpthread.h:389
#define dt_pthread_rwlock_init
Definition dtpthread.h:390
#define dt_pthread_rwlock_unlock
Definition dtpthread.h:392
#define dt_pthread_rwlock_rdlock
Definition dtpthread.h:393
void dt_gui_add_class(GtkWidget *widget, const gchar *class_name)
Definition gtk.c:133
static GtkWidget * dt_ui_section_label_new(const gchar *str)
Definition gtk.h:451
#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:316
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:3141
#define IOP_GUI_FREE
Definition imageop.h:602
@ IOP_FLAGS_INCLUDE_IN_STYLES
Definition imageop.h:166
@ IOP_FLAGS_SUPPORTS_BLENDING
Definition imageop.h:167
@ IOP_GROUP_COLOR
Definition imageop.h:139
#define IOP_GUI_ALLOC(module)
Definition imageop.h:599
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:2136
int dt_opencl_create_kernel(const int prog, const char *name)
Definition opencl.c:2030
void * dt_opencl_copy_host_to_device_constant(const int devid, const size_t size, void *host)
Definition opencl.c:2332
void dt_opencl_free_kernel(const int kernel)
Definition opencl.c:2073
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:2127
void dt_opencl_release_mem_object(cl_mem mem)
Definition opencl.c:2383
#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
unsigned __int64 uint64_t
Definition strptime.c:75
struct dt_gui_gtk_t * gui
Definition darktable.h:775
int32_t unmuted
Definition darktable.h:760
struct dt_develop_t * develop
Definition darktable.h:770
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
int32_t reset
Definition gtk.h:172
unsigned int channels
Definition format.h:54
dt_iop_order_iccprofile_info_t * lut_profile
dt_lut3d_interpolation_t interpolation
dt_iop_order_iccprofile_info_t * work_profile
dt_iop_colorprimaries_node_t a
dt_iop_colorprimaries_node_t b
dt_iop_colorprimaries_data_t cache
dt_iop_colorprimaries_params_t params
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_interpolation_t interpolation
dt_iop_global_data_t * data
Definition imageop.h:233
dt_iop_params_t * default_params
Definition imageop.h:307
GtkWidget * widget
Definition imageop.h:337
struct dt_develop_t * dev
Definition imageop.h:296
dt_iop_gui_data_t * gui_data
Definition imageop.h:311
dt_iop_global_data_t * global_data
Definition imageop.h:314
dt_aligned_pixel_t picked_color_min
Definition imageop.h:272
dt_aligned_pixel_t picked_color_max
Definition imageop.h:272
int32_t params_size
Definition imageop.h:309
dt_iop_params_t * params
Definition imageop.h:307
Region of interest passed through the pixelpipe.
Definition imageop.h:72