Ansel 0.0
A darktable fork - bloat + design vision
Loading...
Searching...
No Matches
colorbalance.c
Go to the documentation of this file.
1#include "widgets/label.h"
2/*
3 This file is part of darktable,
4 Copyright (C) 2013-2016 Roman Lebedev.
5 Copyright (C) 2013-2014, 2016-2019 Tobias Ellinghaus.
6 Copyright (C) 2013-2014 Ulrich Pegelow.
7 Copyright (C) 2015 Pedro Côrte-Real.
8 Copyright (C) 2016 johannes hanika.
9 Copyright (C) 2017 Heiko Bauke.
10 Copyright (C) 2018-2020, 2022-2023, 2025-2026 Aurélien PIERRE.
11 Copyright (C) 2018-2019 Edgardo Hoszowski.
12 Copyright (C) 2018 Matthieu Moy.
13 Copyright (C) 2018 Maurizio Paglia.
14 Copyright (C) 2018-2022 Pascal Obry.
15 Copyright (C) 2018 rawfiner.
16 Copyright (C) 2019 Andreas Schneider.
17 Copyright (C) 2019-2020, 2022 Diederik Ter Rahe.
18 Copyright (C) 2019 Diederik ter Rahe.
19 Copyright (C) 2019 luzpaz.
20 Copyright (C) 2019 msdm.
21 Copyright (C) 2020 Aldric Renaudin.
22 Copyright (C) 2020-2021 Chris Elston.
23 Copyright (C) 2020 Marco.
24 Copyright (C) 2020-2021 Ralf Brown.
25 Copyright (C) 2021 Dan Torop.
26 Copyright (C) 2021 Hubert Kowalski.
27 Copyright (C) 2021 Miloš Komarčević.
28 Copyright (C) 2022 Hanno Schwalm.
29 Copyright (C) 2022 Martin Bařinka.
30 Copyright (C) 2022 Nicolas Auffray.
31 Copyright (C) 2022 Philipp Lutz.
32 Copyright (C) 2022 Victor Forsiuk.
33 Copyright (C) 2023 Luca Zulberti.
34
35 darktable is free software: you can redistribute it and/or modify
36 it under the terms of the GNU General Public License as published by
37 the Free Software Foundation, either version 3 of the License, or
38 (at your option) any later version.
39
40 darktable is distributed in the hope that it will be useful,
41 but WITHOUT ANY WARRANTY; without even the implied warranty of
42 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
43 GNU General Public License for more details.
44
45 You should have received a copy of the GNU General Public License
46 along with darktable. If not, see <http://www.gnu.org/licenses/>.
47*/
48/*
49http://www.youtube.com/watch?v=JVoUgR6bhBc
50 */
51
52#ifdef HAVE_CONFIG_H
53#include "config.h"
54#include "common/conf.h"
55#endif
56// our includes go first:
57#include "widgets/bauhaus.h"
59#include "system/mem_alloc.h"
61#include "metadata/exif.h"
62#include "develop/blend.h"
63#include "develop/imageop.h"
65#include "develop/imageop_gui.h"
66
67#include "gui/presets.h"
69#include "iop/iop_api.h"
70
71//#include <gtk/gtk.h>
72#include <stdlib.h>
73#include "control/signal.h"
74
75// these are not in a state to be useful. but they look nice. too bad i couldn't map the enhanced mode with
76// negative values to the wheels :(
77//#define SHOW_COLOR_WHEELS
78
80
81/*
82
83 Meaning of the values:
84 0 --> 100%
85 -1 --> 0%
86 1 --> 200%
87 */
88
90{
91 LIFT_GAMMA_GAIN = 0, // $DESCRIPTION: "lift, gamma, gain (ProPhoto RGB)"
92 SLOPE_OFFSET_POWER = 1, // $DESCRIPTION: "slope, offset, power (ProPhoto RGB)"
93 LEGACY = 2 // $DESCRIPTION: "lift, gamma, gain (sRGB)"
95
104
112
119
126
128{
129 dt_iop_colorbalance_mode_t mode; // $DEFAULT: SLOPE_OFFSET_POWER
130 float lift[CHANNEL_SIZE], gamma[CHANNEL_SIZE], gain[CHANNEL_SIZE]; // $MIN: 0.0 $MAX: 2.0 $DEFAULT: 1.0
131 float saturation; // $MIN: 0.0 $MAX: 2.0 $DEFAULT: 1.0 $DESCRIPTION: "input saturation"
132 float contrast; // $MIN: 0.01 $MAX: 1.99 $DEFAULT: 1.0
133 float grey; // $MIN: 0.1 $MAX: 100.0 $DEFAULT: 18.0 $DESCRIPTION: "contrast fulcrum"
134 float saturation_out; // $MIN: 0.0 $MAX: 2.0 $DEFAULT: 1.0 $DESCRIPTION: "output saturation"
136
161
168
175
176
177const char *name()
178{
179 return _("color balance (legacy)");
180}
181
182const char *aliases()
183{
184 return _("lift gamma gain|cdl|color grading|contrast|saturation|hue");
185}
186
187const char **description(struct dt_iop_module_t *self)
188{
189 return dt_iop_set_description(self, _("affect color, brightness and contrast"),
190 _("corrective or creative"),
191 _("linear, Lab, scene-referred"),
192 _("non-linear, RGB"),
193 _("non-linear, Lab, scene-referred"));
194}
195
200
202{
203 return IOP_GROUP_COLOR;
204}
205
207{
208 return IOP_CS_LAB;
209}
210
213{
214 default_input_format(self, pipe, piece, dsc);
215 dsc->channels = 4;
216 dsc->datatype = TYPE_FLOAT;
217}
218
219int legacy_params(dt_iop_module_t *self, const void *const old_params, const int old_version, void *new_params,
220 const int new_version)
221{
222 if(old_version == 1 && new_version == 3)
223 {
224 typedef struct dt_iop_colorbalance_params_v1_t
225 {
226 float lift[CHANNEL_SIZE], gamma[CHANNEL_SIZE], gain[CHANNEL_SIZE];
227 } dt_iop_colorbalance_params_v1_t;
228
229 dt_iop_colorbalance_params_v1_t *o = (dt_iop_colorbalance_params_v1_t *)old_params;
232
233 *n = *d; // start with a fresh copy of default parameters
234
235 for(int i = 0; i < CHANNEL_SIZE; i++)
236 {
237 n->lift[i] = o->lift[i];
238 n->gamma[i] = o->gamma[i];
239 n->gain[i] = o->gain[i];
240 }
241 n->mode = LEGACY;
242 return 0;
243 }
244
245 if(old_version == 2 && new_version == 3)
246 {
247 typedef struct dt_iop_colorbalance_params_v2_t
248 {
250 float lift[CHANNEL_SIZE], gamma[CHANNEL_SIZE], gain[CHANNEL_SIZE];
251 float saturation, contrast, grey;
252 } dt_iop_colorbalance_params_v2_t;
253
254 dt_iop_colorbalance_params_v2_t *o = (dt_iop_colorbalance_params_v2_t *)old_params;
257
258 *n = *d; // start with a fresh copy of default parameters
259
260 for(int i = 0; i < CHANNEL_SIZE; i++)
261 {
262 n->lift[i] = o->lift[i];
263 n->gamma[i] = o->gamma[i];
264 n->gain[i] = o->gain[i];
265 }
266 n->mode = o->mode;
267 n->contrast = o->contrast;
268 n->saturation = o->saturation;
269 n->contrast = o->contrast;
270 n->grey = o->grey;
271 return 0;
272 }
273 return 1;
274}
275
276// taken from denoiseprofile.c
277static void add_preset(dt_iop_module_so_t *self, const char *name,
278 const char *pi, const int version, const char *bpi, const int blendop_version)
279{
280 int len, blen;
281 uint8_t *p = dt_exif_xmp_decode(pi, strlen(pi), &len);
282 uint8_t *bp = dt_exif_xmp_decode(bpi, strlen(bpi), &blen);
283 if(blendop_version != dt_develop_blend_version())
284 {
285 // update to current blendop params format
286 void *bp_new = malloc(sizeof(dt_develop_blend_params_t));
287
288 if(dt_develop_blend_legacy_params_from_so(self, bp, blendop_version, bp_new, dt_develop_blend_version(),
289 blen) == 0)
290 {
291 dt_free(bp);
292 bp = bp_new;
293 blen = sizeof(dt_develop_blend_params_t);
294 }
295 else
296 {
297 dt_free(bp);
298 dt_free(bp_new);
299 bp = NULL;
300 }
301 }
302
303 if(p && bp)
304 dt_gui_presets_add_with_blendop(name, self->op, version, p, len, bp, 1);
305 dt_free(bp);
306 dt_free(p);
307}
308
310{
311 // these blobs were exported as dtstyle and copied from there:
312 add_preset(self, _("split-toning teal-orange (2nd instance)"),
313 "gz02eJxjZGBg8HhYZX99cYN9kkCDfdCOOnsGhgZ7ruvN9m8CK+yXFNTaz5w50z5PqBku9u9/PVjNv//9jqfP+NgDAHs0HIc=", 3,
314 "gz05eJxjZWBgYGUAgRNODFDAzszAxMBQ5cwI4Tow4AUNdkBsD8E3gGwue9x8uB6q8s+c8bEF8Z9Y9Nnt2f3bbluCN03tg/EBIBckVg==", 8);
315 add_preset(self, _("split-toning teal-orange (1st instance)"),
316 "gz02eJxjZACBBvugHXX2E3fU219f3GAP4n/TqLFvfd1oL8HZaH/2jI/9prn1cLHUtDSwGgaGCY7//tfbAwBRixpm", 3,
317 "gz04eJxjZWBgYGUAgRNODFDApgwiq5wZIVyHD4E7bBnwggZ7CIYBRiBbBA8fXT1l/P5DX21i+pnA/Pfv8uw6OzzIMq9I5rgtSH//4wii1AMASbIlcw==", 8);
318
319 add_preset(self, _("generic film"),
320 "gz02eJxjZACBBntN5gb7op/19u5AGsSX3dFgr+jYaL+vttb+0NcM+1Pnq+3XyFTZr/rYBJZPS0sD0hMcQDQA29kXSQ==", 3,
321 "gz11eJxjYGBgkGAAgRNODGiAEV0AJ2iwh+CRxQcA5qIZBA==", 8);
322
323 add_preset(self, _("similar to Kodak Portra"),
324 "gz02eJxjZACBBnsQfh3YYK8VU28P43s8rLKP6W+yP/Q1w36deyMYLymoBcsZGxcDaQGHs2d87AGnphWu", 3,
325 "gz11eJxjYGBgkGAAgRNODGiAEV0AJ2iwh+CRxQcA5qIZBA==", 8);
326
327 add_preset(self, _("similar to Kodak Ektar"),
328 "gz02eJxjZACBBvvrixvsrXIb7IN21NnD+CA2iOa6nmxvZFxsX15ebp+e1gaWNwbyGRgEHNLS0uwBE7wWhw==", 3,
329 "gz11eJxjYGBgkGAAgRNODGiAEV0AJ2iwh+CRxQcA5qIZBA==", 8);
330
331 add_preset(self, _("similar to Kodachrome"),
332 "gz02eJxjZACBBvvrixvsrXIb7IN21NnD+CA2iG59HWhvZFxsX15ebp+e1gaWT0tLA9ICDrNmRtoDACjOF7c=", 3,
333 "gz11eJxjYGBgkGAAgRNODGiAEV0AJ2iwh+CRxQcA5qIZBA==", 8);
334}
335
336static inline float CDL(float x, float slope, float offset, float power)
337{
338 float out;
339 out = slope * x + offset;
340 out = (out <= 0.0f) ? 0.0f : powf(out, power);
341 return out;
342}
343
344// see http://www.brucelindbloom.com/Eqn_RGB_XYZ_Matrix.html for the transformation matrices
346int process(struct dt_iop_module_t *self, const dt_dev_pixelpipe_t *pipe, const dt_dev_pixelpipe_iop_t *piece, const void *const ivoid,
347 void *const ovoid)
348{
349 const dt_iop_roi_t *const roi_in = &piece->roi_in;
350 const dt_iop_roi_t *const roi_out = &piece->roi_out;
352 const int ch = 4;
353
354 // these are RGB values!
355 const dt_aligned_pixel_t gain = { d->gain[CHANNEL_RED] * d->gain[CHANNEL_FACTOR],
356 d->gain[CHANNEL_GREEN] * d->gain[CHANNEL_FACTOR],
357 d->gain[CHANNEL_BLUE] * d->gain[CHANNEL_FACTOR] };
358 const float contrast = (d->contrast != 0.0f) ? 1.0f / d->contrast : 1000000.0f,
359 grey = d->grey / 100.0f;
360
361 // For neutral parameters, skip the computations doing x^1 or (x-a)*1 + a to save time
362 const int run_contrast = (d->contrast == 1.0f) ? 0 : 1;
363 const int run_saturation = (d->saturation == 1.0f) ? 0: 1;
364 const int run_saturation_out = (d->saturation_out == 1.0f) ? 0: 1;
365
366 switch (d->mode)
367 {
368 case LEGACY:
369 {
370 // these are RGB values!
371 const dt_aligned_pixel_t lift = { 2.0 - (d->lift[CHANNEL_RED] * d->lift[CHANNEL_FACTOR]),
372 2.0 - (d->lift[CHANNEL_GREEN] * d->lift[CHANNEL_FACTOR]),
373 2.0 - (d->lift[CHANNEL_BLUE] * d->lift[CHANNEL_FACTOR]) },
374 gamma = { d->gamma[CHANNEL_RED] * d->gamma[CHANNEL_FACTOR],
375 d->gamma[CHANNEL_GREEN] * d->gamma[CHANNEL_FACTOR],
376 d->gamma[CHANNEL_BLUE] * d->gamma[CHANNEL_FACTOR] },
377 gamma_inv = { (gamma[0] != 0.0) ? 1.0 / gamma[0] : 1000000.0,
378 (gamma[1] != 0.0) ? 1.0 / gamma[1] : 1000000.0,
379 (gamma[2] != 0.0) ? 1.0 / gamma[2] : 1000000.0 };
381 for(size_t k = 0; k < (size_t)ch * roi_in->width * roi_out->height; k += ch)
382 {
383 float *in = ((float *)ivoid) + k;
384 float *out = ((float *)ovoid) + k;
385
386 // transform the pixel to sRGB:
387 // Lab -> XYZ
388 dt_aligned_pixel_t XYZ = { 0.0f };
389 dt_Lab_to_XYZ(in, XYZ);
390
391 // XYZ -> sRGB
392 dt_aligned_pixel_t rgb = { 0.0f };
394
395 // do the calculation in RGB space
396 for(int c = 0; c < 3; c++)
397 {
398 // lift gamma gain
399 rgb[c] = ((( rgb[c] - 1.0f) * lift[c]) + 1.0f) * gain[c];
400 rgb[c] = (rgb[c] < 0.0f) ? 0.0f : powf(rgb[c], gamma_inv[c]);
401 }
402
403 // transform the result back to Lab
404 // sRGB -> XYZ
405 dt_sRGB_to_XYZ(rgb, XYZ);
406
407 // XYZ -> Lab
409 }
410 break;
411 }
412 case LIFT_GAMMA_GAIN:
413 {
414 // these are RGB values!
415 const dt_aligned_pixel_t lift = { 2.0 - (d->lift[CHANNEL_RED] * d->lift[CHANNEL_FACTOR]),
416 2.0 - (d->lift[CHANNEL_GREEN] * d->lift[CHANNEL_FACTOR]),
417 2.0 - (d->lift[CHANNEL_BLUE] * d->lift[CHANNEL_FACTOR]) },
418 gamma = { d->gamma[CHANNEL_RED] * d->gamma[CHANNEL_FACTOR],
419 d->gamma[CHANNEL_GREEN] * d->gamma[CHANNEL_FACTOR],
420 d->gamma[CHANNEL_BLUE] * d->gamma[CHANNEL_FACTOR] },
421 gamma_inv = { (gamma[0] != 0.0) ? 1.0 / gamma[0] : 1000000.0,
422 (gamma[1] != 0.0) ? 1.0 / gamma[1] : 1000000.0,
423 (gamma[2] != 0.0) ? 1.0 / gamma[2] : 1000000.0 };
425 for(size_t k = 0; k < (size_t)ch * roi_in->width * roi_out->height; k += ch)
426 {
427 float *in = ((float *)ivoid) + k;
428 float *out = ((float *)ovoid) + k;
429
430 // transform the pixel to sRGB:
431 // Lab -> XYZ
432 dt_aligned_pixel_t XYZ = { 0.0f };
433 dt_Lab_to_XYZ(in, XYZ);
434
435 // XYZ -> sRGB
436 dt_aligned_pixel_t rgb = { 0.0f };
438
439 float luma = XYZ[1]; // the Y channel is the relative luminance
440
441 // do the calculation in RGB space
442 for(int c = 0; c < 3; c++)
443 {
444 // main saturation input
445 if (run_saturation) rgb[c] = luma + d->saturation * (rgb[c] - luma);
446
447 // RGB gamma correction
448 rgb[c] = (rgb[c] <= 0.0f) ? 0.0f : powf(rgb[c], 1.0f/2.2f);
449
450 // lift gamma gain
451 rgb[c] = ((( rgb[c] - 1.0f) * lift[c]) + 1.0f) * gain[c];
452 rgb[c] = (rgb[c] <= 0.0f) ? 0.0f : powf(rgb[c], gamma_inv[c] * 2.2f);
453 }
454
455 // main saturation output
456 if (run_saturation_out)
457 {
459 luma = XYZ[1];
460 for(int c = 0; c < 3; c++) rgb[c] = luma + d->saturation_out * (rgb[c] - luma);
461 }
462
463 // fulcrum contrat
464 if (run_contrast) for(int c = 0; c < 3; c++) rgb[c] = (rgb[c] <= 0.0f) ? 0.0f : powf(rgb[c] / grey, contrast) * grey;
465
466 // transform the result back to Lab
467 // sRGB -> XYZ
469
470 // XYZ -> Lab
472 }
473 break;
474 }
476 {
477 // these are RGB values!
478
479 const dt_aligned_pixel_t lift = { ( d->lift[CHANNEL_RED] + d->lift[CHANNEL_FACTOR] - 2.0f),
480 ( d->lift[CHANNEL_GREEN] + d->lift[CHANNEL_FACTOR] - 2.0f),
481 ( d->lift[CHANNEL_BLUE] + d->lift[CHANNEL_FACTOR] - 2.0f)},
482 gamma = { (2.0f - d->gamma[CHANNEL_RED]) * (2.0f - d->gamma[CHANNEL_FACTOR]),
483 (2.0f - d->gamma[CHANNEL_GREEN]) * (2.0f - d->gamma[CHANNEL_FACTOR]),
484 (2.0f - d->gamma[CHANNEL_BLUE]) * (2.0f - d->gamma[CHANNEL_FACTOR])};
486 for(size_t k = 0; k < (size_t)ch * roi_in->width * roi_out->height; k += ch)
487 {
488 float *in = ((float *)ivoid) + k;
489 float *out = ((float *)ovoid) + k;
490
491 // transform the pixel to RGB:
492 // Lab -> XYZ
494 dt_Lab_to_XYZ(in, XYZ);
495
496 // XYZ -> RGB
499
500 float luma = XYZ[1]; // the Y channel is the RGB luminance
501
502 // do the calculation in RGB space
503 for(int c = 0; c < 3; c++)
504 {
505 // main saturation input
506 if (run_saturation) rgb[c] = luma + d->saturation * (rgb[c] - luma);
507
508 // channel CDL
509 rgb[c] = CDL(rgb[c], gain[c], lift[c], gamma[c]);
510 }
511
512 // main saturation output
513 if (run_saturation_out)
514 {
516 luma = XYZ[1];
517 for(int c = 0; c < 3; c++) rgb[c] = luma + d->saturation_out * (rgb[c] - luma);
518 }
519
520 // fulcrum contrat
521 if (run_contrast) for(int c = 0; c < 3; c++) rgb[c] = (rgb[c] <= 0.0f) ? 0.0f : powf(rgb[c] / grey, contrast) * grey;
522
523 // transform the result back to Lab
524 // sRGB -> XYZ
526
527 // XYZ -> Lab
529 }
530 break;
531 }
532 }
533 if(pipe->mask_display & DT_DEV_PIXELPIPE_DISPLAY_MASK) dt_iop_alpha_copy(ivoid, ovoid, roi_out->width, roi_out->height);
534 return 0;
535}
536
537#ifdef HAVE_OPENCL
538int 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)
539{
540 const dt_iop_roi_t *const roi_in = &piece->roi_in;
543
544 cl_int err = -999;
545 const int devid = pipe->devid;
546 const int width = roi_in->width;
547 const int height = roi_in->height;
548 size_t sizes[] = { ROUNDUPDWD(width, devid), ROUNDUPDHT(height, devid), 1 };
549
550 switch (d->mode)
551 {
552 case LEGACY:
553 {
554 const float lift[4] = { 2.0f - (d->lift[CHANNEL_RED] * d->lift[CHANNEL_FACTOR]),
555 2.0f - (d->lift[CHANNEL_GREEN] * d->lift[CHANNEL_FACTOR]),
556 2.0f - (d->lift[CHANNEL_BLUE] * d->lift[CHANNEL_FACTOR]), 0.0f },
557 gamma[4] = { d->gamma[CHANNEL_RED] * d->gamma[CHANNEL_FACTOR],
558 d->gamma[CHANNEL_GREEN] * d->gamma[CHANNEL_FACTOR],
559 d->gamma[CHANNEL_BLUE] * d->gamma[CHANNEL_FACTOR], 0.0f },
560 gamma_inv[4] = { (gamma[0] != 0.0f) ? 1.0f / gamma[0] : 1000000.0f,
561 (gamma[1] != 0.0f) ? 1.0f / gamma[1] : 1000000.0f,
562 (gamma[2] != 0.0f) ? 1.0f / gamma[2] : 1000000.0f, 0.0f },
563 gain[4] = { d->gain[CHANNEL_RED] * d->gain[CHANNEL_FACTOR],
564 d->gain[CHANNEL_GREEN] * d->gain[CHANNEL_FACTOR],
565 d->gain[CHANNEL_BLUE] * d->gain[CHANNEL_FACTOR], 0.0f },
566 contrast = (d->contrast != 0.0f) ? 1.0f / d->contrast : 1000000.0f,
567 grey = d->grey / 100.0f,
568 saturation = d->saturation;
569
570 dt_opencl_set_kernel_arg(devid, gd->kernel_colorbalance, 0, sizeof(cl_mem), (void *)&dev_in);
571 dt_opencl_set_kernel_arg(devid, gd->kernel_colorbalance, 1, sizeof(cl_mem), (void *)&dev_out);
572 dt_opencl_set_kernel_arg(devid, gd->kernel_colorbalance, 2, sizeof(int), (void *)&width);
573 dt_opencl_set_kernel_arg(devid, gd->kernel_colorbalance, 3, sizeof(int), (void *)&height);
574 dt_opencl_set_kernel_arg(devid, gd->kernel_colorbalance, 4, 4 * sizeof(float), (void *)&lift);
575 dt_opencl_set_kernel_arg(devid, gd->kernel_colorbalance, 5, 4 * sizeof(float), (void *)&gain);
576 dt_opencl_set_kernel_arg(devid, gd->kernel_colorbalance, 6, 4 * sizeof(float), (void *)&gamma_inv);
577 dt_opencl_set_kernel_arg(devid, gd->kernel_colorbalance, 7, sizeof(float), (void *)&saturation);
578 dt_opencl_set_kernel_arg(devid, gd->kernel_colorbalance, 8, sizeof(float), (void *)&contrast);
579 dt_opencl_set_kernel_arg(devid, gd->kernel_colorbalance, 9, sizeof(float), (void *)&grey);
580 err = dt_opencl_enqueue_kernel_2d(devid, gd->kernel_colorbalance, sizes);
581 if(err != CL_SUCCESS) goto error;
582 return TRUE;
583
584 break;
585 }
586 case LIFT_GAMMA_GAIN:
587 {
588 const float lift[4] = { 2.0f - (d->lift[CHANNEL_RED] * d->lift[CHANNEL_FACTOR]),
589 2.0f - (d->lift[CHANNEL_GREEN] * d->lift[CHANNEL_FACTOR]),
590 2.0f - (d->lift[CHANNEL_BLUE] * d->lift[CHANNEL_FACTOR]), 0.0f },
591 gamma[4] = { d->gamma[CHANNEL_RED] * d->gamma[CHANNEL_FACTOR],
592 d->gamma[CHANNEL_GREEN] * d->gamma[CHANNEL_FACTOR],
593 d->gamma[CHANNEL_BLUE] * d->gamma[CHANNEL_FACTOR], 0.0f },
594 gamma_inv[4] = { (gamma[0] != 0.0f) ? 1.0f / gamma[0] : 1000000.0f,
595 (gamma[1] != 0.0f) ? 1.0f / gamma[1] : 1000000.0f,
596 (gamma[2] != 0.0f) ? 1.0f / gamma[2] : 1000000.0f, 0.0f },
597 gain[4] = { d->gain[CHANNEL_RED] * d->gain[CHANNEL_FACTOR],
598 d->gain[CHANNEL_GREEN] * d->gain[CHANNEL_FACTOR],
599 d->gain[CHANNEL_BLUE] * d->gain[CHANNEL_FACTOR], 0.0f },
600 contrast = (d->contrast != 0.0f) ? 1.0f / d->contrast : 1000000.0f,
601 grey = d->grey / 100.0f,
602 saturation = d->saturation,
603 saturation_out = d->saturation_out;
604
605 dt_opencl_set_kernel_arg(devid, gd->kernel_colorbalance_lgg, 0, sizeof(cl_mem), (void *)&dev_in);
606 dt_opencl_set_kernel_arg(devid, gd->kernel_colorbalance_lgg, 1, sizeof(cl_mem), (void *)&dev_out);
607 dt_opencl_set_kernel_arg(devid, gd->kernel_colorbalance_lgg, 2, sizeof(int), (void *)&width);
608 dt_opencl_set_kernel_arg(devid, gd->kernel_colorbalance_lgg, 3, sizeof(int), (void *)&height);
609 dt_opencl_set_kernel_arg(devid, gd->kernel_colorbalance_lgg, 4, 4 * sizeof(float), (void *)&lift);
610 dt_opencl_set_kernel_arg(devid, gd->kernel_colorbalance_lgg, 5, 4 * sizeof(float), (void *)&gain);
611 dt_opencl_set_kernel_arg(devid, gd->kernel_colorbalance_lgg, 6, 4 * sizeof(float), (void *)&gamma_inv);
612 dt_opencl_set_kernel_arg(devid, gd->kernel_colorbalance_lgg, 7, sizeof(float), (void *)&saturation);
613 dt_opencl_set_kernel_arg(devid, gd->kernel_colorbalance_lgg, 8, sizeof(float), (void *)&contrast);
614 dt_opencl_set_kernel_arg(devid, gd->kernel_colorbalance_lgg, 9, sizeof(float), (void *)&grey);
615 dt_opencl_set_kernel_arg(devid, gd->kernel_colorbalance_lgg, 10, sizeof(float), (void *)&saturation_out);
617 if(err != CL_SUCCESS) goto error;
618 return TRUE;
619
620 break;
621 }
623 {
624 const float lift[4] = { ( d->lift[CHANNEL_RED] + d->lift[CHANNEL_FACTOR] - 2.0f ),
625 ( d->lift[CHANNEL_GREEN] + d->lift[CHANNEL_FACTOR] - 2.0f ),
626 ( d->lift[CHANNEL_BLUE] + d->lift[CHANNEL_FACTOR] - 2.0f ),
627 0.0f },
628 gamma[4] = { (2.0f - d->gamma[CHANNEL_RED]) * (2.0f - d->gamma[CHANNEL_FACTOR]),
629 (2.0f - d->gamma[CHANNEL_GREEN]) * (2.0f - d->gamma[CHANNEL_FACTOR]),
630 (2.0f - d->gamma[CHANNEL_BLUE]) * (2.0f - d->gamma[CHANNEL_FACTOR]),
631 0.0f },
632 gain[4] = { d->gain[CHANNEL_RED] * d->gain[CHANNEL_FACTOR],
633 d->gain[CHANNEL_GREEN] * d->gain[CHANNEL_FACTOR],
634 d->gain[CHANNEL_BLUE] * d->gain[CHANNEL_FACTOR],
635 0.0f },
636 contrast = (d->contrast != 0.0f) ? 1.0f / d->contrast : 1000000.0f,
637 grey = d->grey / 100.0f,
638 saturation = d->saturation,
639 saturation_out = d->saturation_out;
640
641 dt_opencl_set_kernel_arg(devid, gd->kernel_colorbalance_cdl, 0, sizeof(cl_mem), (void *)&dev_in);
642 dt_opencl_set_kernel_arg(devid, gd->kernel_colorbalance_cdl, 1, sizeof(cl_mem), (void *)&dev_out);
643 dt_opencl_set_kernel_arg(devid, gd->kernel_colorbalance_cdl, 2, sizeof(int), (void *)&width);
644 dt_opencl_set_kernel_arg(devid, gd->kernel_colorbalance_cdl, 3, sizeof(int), (void *)&height);
645 dt_opencl_set_kernel_arg(devid, gd->kernel_colorbalance_cdl, 4, 4 * sizeof(float), (void *)&lift);
646 dt_opencl_set_kernel_arg(devid, gd->kernel_colorbalance_cdl, 5, 4 * sizeof(float), (void *)&gain);
647 dt_opencl_set_kernel_arg(devid, gd->kernel_colorbalance_cdl, 6, 4 * sizeof(float), (void *)&gamma);
648 dt_opencl_set_kernel_arg(devid, gd->kernel_colorbalance_cdl, 7, sizeof(float), (void *)&saturation);
649 dt_opencl_set_kernel_arg(devid, gd->kernel_colorbalance_cdl, 8, sizeof(float), (void *)&contrast);
650 dt_opencl_set_kernel_arg(devid, gd->kernel_colorbalance_cdl, 9, sizeof(float), (void *)&grey);
651 dt_opencl_set_kernel_arg(devid, gd->kernel_colorbalance_cdl, 10, sizeof(float), (void *)&saturation_out);
653 if(err != CL_SUCCESS) goto error;
654 return TRUE;
655
656 break;
657 }
658 }
659
660error:
661 dt_print(DT_DEBUG_OPENCL, "[opencl_colorbalance] couldn't enqueue kernel! %d\n", err);
662 return FALSE;
663}
664#endif
665
666static inline void update_saturation_slider_color(GtkWidget *slider, float hue)
667{
669 if(hue != -1)
670 {
671 hsl2rgb(rgb, hue, 1.0, 0.5);
672 dt_bauhaus_slider_set_stop(slider, 1.0, rgb[0], rgb[1], rgb[2]);
673 hsl2rgb(rgb, hue, 0.0, 0.5);
674 dt_bauhaus_slider_set_stop(slider, 0.0, rgb[0], rgb[1], rgb[2]);
675 gtk_widget_queue_draw(GTK_WIDGET(slider));
676 }
677}
678
679static inline void set_RGB_sliders(GtkWidget *R, GtkWidget *G, GtkWidget *B, float hsl[3], float *p, int mode)
680{
681
682 dt_aligned_pixel_t rgb = { 0.0f };
683 hsl2rgb(rgb, hsl[0], hsl[1], hsl[2]);
684
685 if(hsl[0] != -1)
686 {
687 p[CHANNEL_RED] = rgb[0] * 2.0f;
688 p[CHANNEL_GREEN] = rgb[1] * 2.0f;
689 p[CHANNEL_BLUE] = rgb[2] * 2.0f;
690
696 }
697}
698
699static inline void set_HSL_sliders(GtkWidget *hue, GtkWidget *sat, float RGB[4])
700{
705 dt_aligned_pixel_t RGB_norm = { (RGB[CHANNEL_RED] / 2.0f), (RGB[CHANNEL_GREEN] / 2.0f), (RGB[CHANNEL_BLUE] / 2.0f) };
706
707 float h, s, l;
708 rgb2hsl(RGB_norm, &h, &s, &l);
709
710 if(h != -1.0f)
711 {
712 dt_bauhaus_slider_set(hue, h * 360.0f);
713 dt_bauhaus_slider_set(sat, s * 100.0f);
714 update_saturation_slider_color(GTK_WIDGET(sat), h);
715 gtk_widget_queue_draw(GTK_WIDGET(sat));
716 }
717 else
718 {
719 dt_bauhaus_slider_set(hue, -1.0f);
720 dt_bauhaus_slider_set(sat, 0.0f);
721 gtk_widget_queue_draw(GTK_WIDGET(sat));
722 }
723}
724
726{
728
729 if(g->luma_patches_flags[GAIN] == USER_SELECTED && g->luma_patches_flags[GAMMA] == USER_SELECTED
730 && g->luma_patches_flags[LIFT] == USER_SELECTED)
731 dt_bauhaus_widget_set_label(g->auto_luma, N_("optimize luma from patches"));
732 else
733 dt_bauhaus_widget_set_label(g->auto_luma, N_("optimize luma"));
734
735 if(g->color_patches_flags[GAIN] == USER_SELECTED && g->color_patches_flags[GAMMA] == USER_SELECTED
736 && g->color_patches_flags[LIFT] == USER_SELECTED)
737 dt_bauhaus_widget_set_label(g->auto_color, N_("neutralize colors from patches"));
738 else
739 dt_bauhaus_widget_set_label(g->auto_color, N_("neutralize colors"));
740}
741
742
744{
745 if(dt_gui_widgets_suppressed()) return;
748
749 dt_aligned_pixel_t XYZ = { 0.0f };
750 dt_aligned_pixel_t rgb = { 0.0f };
751 dt_Lab_to_XYZ((const float *)self->picked_color, XYZ);
752 dt_XYZ_to_prophotorgb((const float *)XYZ, rgb);
753
754 const dt_aligned_pixel_t lift
755 = { (p->lift[CHANNEL_RED] + p->lift[CHANNEL_FACTOR] - 2.0f),
756 (p->lift[CHANNEL_GREEN] + p->lift[CHANNEL_FACTOR] - 2.0f),
757 (p->lift[CHANNEL_BLUE] + p->lift[CHANNEL_FACTOR] - 2.0f) },
758 gamma
759 = { p->gamma[CHANNEL_RED] * p->gamma[CHANNEL_FACTOR],
760 p->gamma[CHANNEL_GREEN] * p->gamma[CHANNEL_FACTOR],
761 p->gamma[CHANNEL_BLUE] * p->gamma[CHANNEL_FACTOR] },
762 gain = { p->gain[CHANNEL_RED] * p->gain[CHANNEL_FACTOR], p->gain[CHANNEL_GREEN] * p->gain[CHANNEL_FACTOR],
763 p->gain[CHANNEL_BLUE] * p->gain[CHANNEL_FACTOR] };
764
765 for(int c = 0; c < 3; c++)
766 {
767 rgb[c] = CDL(rgb[c], gain[c], lift[c], 2.0f - gamma[c]);
768 rgb[c] = CLAMP(rgb[c], 0.0f, 1.0f);
769 }
770
771 dt_prophotorgb_to_XYZ((const float *)rgb, XYZ);
772
773 p->grey = XYZ[1] * 100.0f;
774
776 dt_bauhaus_slider_set(g->grey, p->grey);
778
779 dt_dev_add_history_item(self->dev, self, TRUE, TRUE);
780}
781
783{
784 if(dt_gui_widgets_suppressed()) return;
787
788 dt_aligned_pixel_t XYZ = { 0.0f };
789 dt_Lab_to_XYZ((const float *)self->picked_color, XYZ);
790 dt_aligned_pixel_t RGB = { 0.0f };
791 dt_XYZ_to_prophotorgb((const float *)XYZ, RGB);
792
793// Save the patch color for the optimization
794 for(int c = 0; c < 3; ++c) g->color_patches_lift[c] = RGB[c];
795 g->color_patches_flags[LIFT] = USER_SELECTED;
796
797 // Compute the RGB values after the CDL factors
798 for(int c = 0; c < 3; ++c)
799 RGB[c] = CDL(RGB[c], p->gain[CHANNEL_FACTOR], p->lift[CHANNEL_FACTOR] - 1.0f, 2.0f - p->gamma[CHANNEL_FACTOR]);
800
801 // Compute the luminance of the average grey
802 dt_XYZ_to_prophotorgb((const float *)XYZ, RGB);
803
804 // Get the parameter
805 for(int c = 0; c < 3; ++c) RGB[c] = powf(XYZ[1], 1.0f/(2.0f - p->gamma[c+1])) - RGB[c] * p->gain[c+1];
806
807 p->lift[CHANNEL_RED] = RGB[0] + 1.0f;
808 p->lift[CHANNEL_GREEN] = RGB[1] + 1.0f;
809 p->lift[CHANNEL_BLUE] = RGB[2] + 1.0f;
810
812 dt_bauhaus_slider_set(g->lift_r, p->lift[CHANNEL_RED]);
813 dt_bauhaus_slider_set(g->lift_g, p->lift[CHANNEL_GREEN]);
814 dt_bauhaus_slider_set(g->lift_b, p->lift[CHANNEL_BLUE]);
815 set_HSL_sliders(g->hue_lift, g->sat_lift, p->lift);
817
818 dt_dev_add_history_item(self->dev, self, TRUE, TRUE);
819}
820
822{
823 if(dt_gui_widgets_suppressed()) return;
826
827 dt_aligned_pixel_t XYZ = { 0.0f };
828 dt_Lab_to_XYZ((const float *)self->picked_color, XYZ);
829 dt_aligned_pixel_t RGB = { 0.0f };
830 dt_XYZ_to_prophotorgb((const float *)XYZ, RGB);
831
832// Save the patch color for the optimization
833 for(int c = 0; c < 3; ++c) g->color_patches_gamma[c] = RGB[c];
834 g->color_patches_flags[GAMMA] = USER_SELECTED;
835
836 // Compute the RGB values after the CDL factors
837 for(int c = 0; c < 3; ++c)
838 RGB[c] = CDL(RGB[c], p->gain[CHANNEL_FACTOR], p->lift[CHANNEL_FACTOR] - 1.0f, 2.0f - p->gamma[CHANNEL_FACTOR]);
839
840 // Compute the luminance of the average grey
841 dt_XYZ_to_prophotorgb((const float *)XYZ, RGB);
842
843 // Get the parameter
844 for(int c = 0; c < 3; ++c) RGB[c] = logf(XYZ[1])/ logf(RGB[c] * p->gain[c + 1] + p->lift[c + 1] - 1.0f);
845
846 p->gamma[CHANNEL_RED] = CLAMP(2.0 - RGB[0], 0.0001f, 2.0f);
847 p->gamma[CHANNEL_GREEN] = CLAMP(2.0 - RGB[1], 0.0001f, 2.0f);
848 p->gamma[CHANNEL_BLUE] = CLAMP(2.0 - RGB[2], 0.0001f, 2.0f);
849
851 dt_bauhaus_slider_set(g->gamma_r, p->gamma[CHANNEL_RED]);
852 dt_bauhaus_slider_set(g->gamma_g, p->gamma[CHANNEL_GREEN]);
853 dt_bauhaus_slider_set(g->gamma_b, p->gamma[CHANNEL_BLUE]);
854 set_HSL_sliders(g->hue_gamma, g->sat_gamma, p->gamma);
856
857 dt_dev_add_history_item(self->dev, self, TRUE, TRUE);
858}
859
861{
862 if(dt_gui_widgets_suppressed()) return;
865
866 dt_aligned_pixel_t XYZ = { 0.0f };
867 dt_Lab_to_XYZ((const float *)self->picked_color, XYZ);
868 dt_aligned_pixel_t RGB = { 0.0f };
869 dt_XYZ_to_prophotorgb((const float *)XYZ, RGB);
870
871// Save the patch color for the optimization
872 for(int c = 0; c < 3; c++) g->color_patches_gain[c] = RGB[c];
873 g->color_patches_flags[GAIN] = USER_SELECTED;
874
875 // Compute the RGB values after the CDL factors
876 for(int c = 0; c < 3; ++c)
877 RGB[c] = CDL(RGB[c], p->gain[CHANNEL_FACTOR], p->lift[CHANNEL_FACTOR] - 1.0f, 2.0f - p->gamma[CHANNEL_FACTOR]);
878
879 // Compute the luminance of the average grey
880 dt_XYZ_to_prophotorgb((const float *)XYZ, RGB);
881
882 // Get the parameter
883 for(int c = 0; c < 3; ++c) RGB[c] = (powf(XYZ[1], 1.0f/(2.0f - p->gamma[c+1])) - p->lift[c+1] + 1.0f) / MAX(RGB[c], 0.000001f);
884
885 p->gain[CHANNEL_RED] = RGB[0];
886 p->gain[CHANNEL_GREEN] = RGB[1];
887 p->gain[CHANNEL_BLUE] = RGB[2];
888
890 dt_bauhaus_slider_set(g->gain_r, p->gain[CHANNEL_RED]);
891 dt_bauhaus_slider_set(g->gain_g, p->gain[CHANNEL_GREEN]);
892 dt_bauhaus_slider_set(g->gain_b, p->gain[CHANNEL_BLUE]);
893 set_HSL_sliders(g->hue_gain, g->sat_gain, p->gain);
895
896 dt_dev_add_history_item(self->dev, self, TRUE, TRUE);
897}
898
900{
901 if(dt_gui_widgets_suppressed()) return;
904
905 dt_aligned_pixel_t XYZ = { 0.0f };
906 dt_Lab_to_XYZ((const float *)self->picked_color_min, XYZ);
907
908 g->luma_patches[LIFT] = XYZ[1];
909 g->luma_patches_flags[LIFT] = USER_SELECTED;
910
911 dt_aligned_pixel_t RGB = { 0.0f };
912 dt_XYZ_to_prophotorgb((const float *)XYZ, RGB);
913
914 p->lift[CHANNEL_FACTOR] = -p->gain[CHANNEL_FACTOR] * XYZ[1] + 1.0f;
915
917 dt_bauhaus_slider_set(g->lift_factor, p->lift[CHANNEL_FACTOR]);
919
920 dt_dev_add_history_item(self->dev, self, TRUE, TRUE);
921}
922
924{
925 if(dt_gui_widgets_suppressed()) return;
928
929 dt_aligned_pixel_t XYZ = { 0.0f };
930 dt_Lab_to_XYZ((const float *)self->picked_color, XYZ);
931
932 g->luma_patches[GAMMA] = XYZ[1];
933 g->luma_patches_flags[GAMMA] = USER_SELECTED;
934
935 dt_aligned_pixel_t RGB = { 0.0f };
936 dt_XYZ_to_prophotorgb((const float *)XYZ, RGB);
937
938 p->gamma[CHANNEL_FACTOR]
939 = 2.0f - logf(0.1842f) / logf(MAX(p->gain[CHANNEL_FACTOR] * XYZ[1] + p->lift[CHANNEL_FACTOR] - 1.0f, 0.000001f));
940
942 dt_bauhaus_slider_set(g->gamma_factor, p->gamma[CHANNEL_FACTOR]);
944
945 dt_dev_add_history_item(self->dev, self, TRUE, TRUE);
946}
947
949{
950 if(dt_gui_widgets_suppressed()) return;
953
954 dt_aligned_pixel_t XYZ = { 0.0f };
955 dt_Lab_to_XYZ((const float *)self->picked_color_max, XYZ);
956
957 g->luma_patches[GAIN] = XYZ[1];
958 g->luma_patches_flags[GAIN] = USER_SELECTED;
959
960 dt_aligned_pixel_t RGB = { 0.0f };
961 dt_XYZ_to_prophotorgb((const float *)XYZ, RGB);
962
963 p->gain[CHANNEL_FACTOR] = p->lift[CHANNEL_FACTOR] / (XYZ[1]);
964
966 dt_bauhaus_slider_set(g->gain_factor, p->gain[CHANNEL_FACTOR]);
968
969 dt_dev_add_history_item(self->dev, self, TRUE, TRUE);
970}
971
973{
976
977 if(g->color_patches_flags[GAIN] == INVALID || g->color_patches_flags[GAMMA] == INVALID
978 || g->color_patches_flags[LIFT] == INVALID)
979 {
980 /*
981 * Some color patches were not picked by the user. Take a
982 * picture-wide patch for these.
983 */
984 dt_aligned_pixel_t XYZ = { 0.0f };
985 dt_Lab_to_XYZ((const float *)self->picked_color, XYZ);
986 dt_aligned_pixel_t RGB = { 0.0f };
987 dt_XYZ_to_prophotorgb((const float *)XYZ, RGB);
988
989 // Save the patch color for the optimization
990 if(g->color_patches_flags[LIFT] == INVALID)
991 {
992 for(int c = 0; c < 3; c++) g->color_patches_lift[c] = RGB[c];
993 g->color_patches_flags[LIFT] = AUTO_SELECTED;
994 }
995 if(g->color_patches_flags[GAMMA] == INVALID)
996 {
997 for(int c = 0; c < 3; c++) g->color_patches_gamma[c] = RGB[c];
998 g->color_patches_flags[GAMMA] = AUTO_SELECTED;
999 }
1000 if(g->color_patches_flags[GAIN] == INVALID)
1001 {
1002 for(int c = 0; c < 3; c++) g->color_patches_gain[c] = RGB[c];
1003 g->color_patches_flags[GAIN] = AUTO_SELECTED;
1004 }
1005 }
1006
1008
1009 // Build the CDL-corrected samples (after the factors)
1010 dt_aligned_pixel_t samples_lift = { 0.f };
1011 dt_aligned_pixel_t samples_gamma = { 0.f };
1012 dt_aligned_pixel_t samples_gain = { 0.f };
1013
1014 for (int c = 0; c < 3; ++c)
1015 {
1016 samples_lift[c] = CDL(g->color_patches_lift[c], p->gain[CHANNEL_FACTOR], p->lift[CHANNEL_FACTOR] - 1.0f, 2.0f - p->gamma[CHANNEL_FACTOR]);
1017 samples_gamma[c] = CDL(g->color_patches_gamma[c], p->gain[CHANNEL_FACTOR], p->lift[CHANNEL_FACTOR] - 1.0f, 2.0f - p->gamma[CHANNEL_FACTOR]);
1018 samples_gain[c] = CDL(g->color_patches_gain[c], p->gain[CHANNEL_FACTOR], p->lift[CHANNEL_FACTOR] - 1.0f, 2.0f - p->gamma[CHANNEL_FACTOR]);
1019 }
1020
1021 // Get the average patches luma value (= neutral grey equivalents) after the CDL factors
1022 dt_aligned_pixel_t greys = { 0.0 };
1023 dt_aligned_pixel_t XYZ = { 0.0 };
1024 dt_prophotorgb_to_XYZ((const float *)samples_lift, (float *)XYZ);
1025 greys[0] = XYZ[1];
1026 dt_prophotorgb_to_XYZ((const float *)samples_gamma, (float *)XYZ);
1027 greys[1] = XYZ[1];
1028 dt_prophotorgb_to_XYZ((const float *)samples_gain, (float *)XYZ);
1029 greys[2] = XYZ[1];
1030
1031 // Get the current params
1032 dt_aligned_pixel_t RGB_lift = { p->lift[CHANNEL_RED] - 1.0f, p->lift[CHANNEL_GREEN] - 1.0f, p->lift[CHANNEL_BLUE] - 1.0f };
1033 dt_aligned_pixel_t RGB_gamma = { p->gamma[CHANNEL_RED], p->gamma[CHANNEL_GREEN], p->gamma[CHANNEL_BLUE] };
1034 dt_aligned_pixel_t RGB_gain = { p->gain[CHANNEL_RED], p->gain[CHANNEL_GREEN], p->gain[CHANNEL_BLUE] };
1035
1048 for (int runs = 0 ; runs < 1000 ; ++runs)
1049 {
1050 // compute RGB slope/gain (powf(XYZ[1], 1.0f/(2.0f - p->gamma[c+1])) - p->lift[c+1] + 1.0f) / MAX(RGB[c], 0.000001f);
1051 for (int c = 0; c < 3; ++c) RGB_gain[c] = CLAMP((powf(greys[GAIN], 1.0f / (2.0f - RGB_gamma[c])) - RGB_lift[c]) / MAX(samples_gain[c], 0.000001f), 0.75f, 1.25f);
1052 // compute RGB offset/lift powf(XYZ[1], 1.0f/(2.0f - p->gamma[c+1])) - RGB[c] * p->gain[c+1];
1053 for (int c = 0; c < 3; ++c) RGB_lift[c] = CLAMP(powf(greys[LIFT], 1.0f / (2.0f - RGB_gamma[c])) - samples_lift[c] * RGB_gain[c], -0.025f, 0.025f);
1054 // compute power/gamma 2.0f - logf(0.1842f) / logf(MAX(p->gain[CHANNEL_FACTOR] * XYZ[1] + p->lift[CHANNEL_FACTOR] - 1.0f, 0.000001f));
1055 for (int c = 0; c < 3; ++c) RGB_gamma[c] = 2.0f - CLAMP(logf(MAX(greys[GAMMA], 0.000001f)) / logf(MAX(RGB_gain[c] * samples_gamma[c] + RGB_lift[c], 0.000001f)), 0.75f, 1.25f);
1056 }
1057
1058 // save
1059 p->lift[CHANNEL_RED] = RGB_lift[0] + 1.0f;
1060 p->lift[CHANNEL_GREEN] = RGB_lift[1] + 1.0f;
1061 p->lift[CHANNEL_BLUE] = RGB_lift[2] + 1.0f;
1062 p->gamma[CHANNEL_RED] = RGB_gamma[0];
1063 p->gamma[CHANNEL_GREEN] = RGB_gamma[1];
1064 p->gamma[CHANNEL_BLUE] = RGB_gamma[2];
1065 p->gain[CHANNEL_RED] = RGB_gain[0];
1066 p->gain[CHANNEL_GREEN] = RGB_gain[1];
1067 p->gain[CHANNEL_BLUE] = RGB_gain[2];
1068
1070 dt_bauhaus_slider_set(g->lift_r, p->lift[CHANNEL_RED]);
1071 dt_bauhaus_slider_set(g->lift_g, p->lift[CHANNEL_GREEN]);
1072 dt_bauhaus_slider_set(g->lift_b, p->lift[CHANNEL_BLUE]);
1073
1074 dt_bauhaus_slider_set(g->gamma_r, p->gamma[CHANNEL_RED]);
1075 dt_bauhaus_slider_set(g->gamma_g, p->gamma[CHANNEL_GREEN]);
1076 dt_bauhaus_slider_set(g->gamma_b, p->gamma[CHANNEL_BLUE]);
1077
1078 dt_bauhaus_slider_set(g->gain_r, p->gain[CHANNEL_RED]);
1079 dt_bauhaus_slider_set(g->gain_g, p->gain[CHANNEL_GREEN]);
1080 dt_bauhaus_slider_set(g->gain_b, p->gain[CHANNEL_BLUE]);
1081
1082 set_HSL_sliders(g->hue_lift, g->sat_lift, p->lift);
1083 set_HSL_sliders(g->hue_gamma, g->sat_gamma, p->gamma);
1084 set_HSL_sliders(g->hue_gain, g->sat_gain, p->gain);
1086
1087 dt_dev_add_history_item(self->dev, self, TRUE, TRUE);
1088}
1089
1091{
1094
1095 /*
1096 * If some luma patches were not picked by the user, take a
1097 * picture-wide patch for these.
1098 */
1099 if(g->luma_patches_flags[LIFT] == INVALID)
1100 {
1101 dt_aligned_pixel_t XYZ = { 0.0f };
1102 dt_Lab_to_XYZ((const float *)self->picked_color_min, XYZ);
1103 g->luma_patches[LIFT] = XYZ[1];
1104 g->luma_patches_flags[LIFT] = AUTO_SELECTED;
1105 }
1106 if(g->luma_patches_flags[GAMMA] == INVALID)
1107 {
1108 dt_aligned_pixel_t XYZ = { 0.0f };
1109 dt_Lab_to_XYZ((const float *)self->picked_color, XYZ);
1110 g->luma_patches[GAMMA] = XYZ[1];
1111 g->luma_patches_flags[GAMMA] = AUTO_SELECTED;
1112 }
1113 if(g->luma_patches_flags[GAIN] == INVALID)
1114 {
1115 dt_aligned_pixel_t XYZ = { 0.0f };
1116 dt_Lab_to_XYZ((const float *)self->picked_color_max, XYZ);
1117 g->luma_patches[GAIN] = XYZ[1];
1118 g->luma_patches_flags[GAIN] = AUTO_SELECTED;
1119 }
1120
1122
1126 for (int runs = 0 ; runs < 100 ; ++runs)
1127 {
1128 p->gain[CHANNEL_FACTOR] = CLAMP(p->lift[CHANNEL_FACTOR] / g->luma_patches[GAIN], 0.0f, 2.0f);
1129 p->lift[CHANNEL_FACTOR] = CLAMP(-p->gain[CHANNEL_FACTOR] * g->luma_patches[LIFT] + 1.0f, 0.0f, 2.0f);
1130 p->gamma[CHANNEL_FACTOR] = CLAMP(2.0f - logf(0.1842f) / logf(MAX(p->gain[CHANNEL_FACTOR] * g->luma_patches[GAMMA] + p->lift[CHANNEL_FACTOR] - 1.0f, 0.000001f)), 0.0f, 2.0f);
1131 }
1132
1134 dt_bauhaus_slider_set(g->lift_factor, p->lift[CHANNEL_FACTOR]);
1135 dt_bauhaus_slider_set(g->gamma_factor, p->gamma[CHANNEL_FACTOR]);
1136 dt_bauhaus_slider_set(g->gain_factor, p->gain[CHANNEL_FACTOR]);
1138
1139 dt_dev_add_history_item(self->dev, self, TRUE, TRUE);
1140}
1141
1143{
1145 if (picker == g->hue_lift)
1147 else if(picker == g->hue_gamma)
1149 else if(picker == g->hue_gain)
1151 else if(picker == g->lift_factor)
1152 apply_lift_auto(self);
1153 else if(picker == g->gamma_factor)
1154 apply_gamma_auto(self);
1155 else if(picker == g->gain_factor)
1156 apply_gain_auto(self);
1157 else if(picker == g->grey)
1158 apply_autogrey(self);
1159 else if(picker == g->auto_luma)
1160 apply_autoluma(self);
1161 else if(picker == g->auto_color)
1162 apply_autocolor(self);
1163 else
1164 fprintf(stderr, "[colorbalance] unknown color picker\n");
1165
1167}
1168
1170{
1171 const int program = 8; // extended.cl, from programs.conf
1174 module->data = gd;
1175 gd->kernel_colorbalance = dt_opencl_create_kernel(program, "colorbalance");
1176 gd->kernel_colorbalance_lgg = dt_opencl_create_kernel(program, "colorbalance_lgg");
1177 gd->kernel_colorbalance_cdl = dt_opencl_create_kernel(program, "colorbalance_cdl");
1178}
1179
1188
1191{
1194
1195 d->mode = p->mode;
1196
1197 const dt_aligned_pixel_t lift = { p->lift[CHANNEL_RED], p->lift[CHANNEL_GREEN], p->lift[CHANNEL_BLUE] };
1198 const dt_aligned_pixel_t gamma = { p->gamma[CHANNEL_RED], p->gamma[CHANNEL_GREEN], p->gamma[CHANNEL_BLUE] };
1199 const dt_aligned_pixel_t gain = { p->gain[CHANNEL_RED], p->gain[CHANNEL_GREEN], p->gain[CHANNEL_BLUE] };
1200
1201 switch(d->mode)
1202 {
1203 case SLOPE_OFFSET_POWER:
1204 {
1205 // Correct the luminance in RGB parameters so we don't affect it
1207
1209 d->lift[CHANNEL_FACTOR] = p->lift[CHANNEL_FACTOR];
1210 d->lift[CHANNEL_RED] = (p->lift[CHANNEL_RED] - XYZ[1]) + 1.f;
1211 d->lift[CHANNEL_GREEN] = (p->lift[CHANNEL_GREEN] - XYZ[1]) + 1.f;
1212 d->lift[CHANNEL_BLUE] = (p->lift[CHANNEL_BLUE] - XYZ[1]) + 1.f;
1213
1214 dt_prophotorgb_to_XYZ(gamma, XYZ);
1215 d->gamma[CHANNEL_FACTOR] = p->gamma[CHANNEL_FACTOR];
1216 d->gamma[CHANNEL_RED] = (p->gamma[CHANNEL_RED] - XYZ[1]) + 1.f;
1217 d->gamma[CHANNEL_GREEN] = (p->gamma[CHANNEL_GREEN] - XYZ[1]) + 1.f;
1218 d->gamma[CHANNEL_BLUE] = (p->gamma[CHANNEL_BLUE] - XYZ[1]) + 1.f;
1219
1221 d->gain[CHANNEL_FACTOR] = p->gain[CHANNEL_FACTOR];
1222 d->gain[CHANNEL_RED] = (p->gain[CHANNEL_RED] - XYZ[1]) + 1.f;
1223 d->gain[CHANNEL_GREEN] = (p->gain[CHANNEL_GREEN] - XYZ[1]) + 1.f;
1224 d->gain[CHANNEL_BLUE] = (p->gain[CHANNEL_BLUE] - XYZ[1]) + 1.f;
1225
1226 break;
1227 }
1228
1229 case LEGACY:
1230 {
1231 // Luminance is not corrected in lift/gamma/gain for compatibility
1232 for(int i = 0; i < CHANNEL_SIZE; i++)
1233 {
1234 d->lift[i] = p->lift[i];
1235 d->gamma[i] = p->gamma[i];
1236 d->gain[i] = p->gain[i];
1237 }
1238
1239 break;
1240 }
1241
1242 case LIFT_GAMMA_GAIN:
1243 {
1244 // Correct the luminance in RGB parameters so we don't affect it
1246
1248 d->lift[CHANNEL_FACTOR] = p->lift[CHANNEL_FACTOR];
1249 d->lift[CHANNEL_RED] = (p->lift[CHANNEL_RED] - XYZ[1]) + 1.f;
1250 d->lift[CHANNEL_GREEN] = (p->lift[CHANNEL_GREEN] - XYZ[1]) + 1.f;
1251 d->lift[CHANNEL_BLUE] = (p->lift[CHANNEL_BLUE] - XYZ[1]) + 1.f;
1252
1253 dt_prophotorgb_to_XYZ(gamma, XYZ);
1254 d->gamma[CHANNEL_FACTOR] = p->gamma[CHANNEL_FACTOR];
1255 d->gamma[CHANNEL_RED] = (p->gamma[CHANNEL_RED] - XYZ[1]) + 1.f;
1256 d->gamma[CHANNEL_GREEN] = (p->gamma[CHANNEL_GREEN] - XYZ[1]) + 1.f;
1257 d->gamma[CHANNEL_BLUE] = (p->gamma[CHANNEL_BLUE] - XYZ[1]) + 1.f;
1258
1260 d->gain[CHANNEL_FACTOR] = p->gain[CHANNEL_FACTOR];
1261 d->gain[CHANNEL_RED] = (p->gain[CHANNEL_RED] - XYZ[1]) + 1.f;
1262 d->gain[CHANNEL_GREEN] = (p->gain[CHANNEL_GREEN] - XYZ[1]) + 1.f;
1263 d->gain[CHANNEL_BLUE] = (p->gain[CHANNEL_BLUE] - XYZ[1]) + 1.f;
1264
1265 break;
1266 }
1267 }
1268
1269 d->grey = p->grey;
1270 d->saturation = p->saturation;
1271 d->saturation_out = p->saturation_out;
1272 d->contrast = p->contrast;
1273}
1274
1280
1282{
1283 dt_free_align(piece->data);
1284 piece->data = NULL;
1285}
1286
1288{
1289 const int mode = dt_bauhaus_combobox_get(g->mode);
1290 const int control_mode = dt_bauhaus_combobox_get(g->controls);
1291
1292 gtk_widget_set_visible(g->master_box, mode != LEGACY);
1293
1294 dt_conf_set_string("plugins/darkroom/colorbalance/controls",
1295 control_mode == RGBL ? "RGBL" :
1296 control_mode == BOTH ? "BOTH" : "HSL");
1297 gboolean show_rgbl = (control_mode == RGBL) || (control_mode == BOTH);
1298 gboolean show_hsl = (control_mode == HSL) || (control_mode == BOTH);
1299
1300 gtk_widget_set_visible(g->lift_r, show_rgbl);
1301 gtk_widget_set_visible(g->lift_g, show_rgbl);
1302 gtk_widget_set_visible(g->lift_b, show_rgbl);
1303 gtk_widget_set_visible(g->gamma_r, show_rgbl);
1304 gtk_widget_set_visible(g->gamma_g, show_rgbl);
1305 gtk_widget_set_visible(g->gamma_b, show_rgbl);
1306 gtk_widget_set_visible(g->gain_r, show_rgbl);
1307 gtk_widget_set_visible(g->gain_g, show_rgbl);
1308 gtk_widget_set_visible(g->gain_b, show_rgbl);
1309
1310 gtk_widget_set_visible(g->hue_lift, show_hsl);
1311 gtk_widget_set_visible(g->sat_lift, show_hsl);
1312 gtk_widget_set_visible(g->hue_gamma, show_hsl);
1313 gtk_widget_set_visible(g->sat_gamma, show_hsl);
1314 gtk_widget_set_visible(g->hue_gain, show_hsl);
1315 gtk_widget_set_visible(g->sat_gain, show_hsl);
1316
1317 gtk_widget_set_visible(g->optimizer_box, mode == SLOPE_OFFSET_POWER);
1318}
1319
1321{
1324
1325 gui_changed(self, NULL, NULL);
1326}
1327
1329{
1331
1332 for (int k=0; k<LEVELS; k++)
1333 {
1334 g->color_patches_flags[k] = INVALID;
1335 g->luma_patches_flags[k] = INVALID;
1336 }
1338
1339 dt_bauhaus_combobox_set(g->controls, HSL);
1340
1342
1344}
1345
1346static void _configure_slider_blocks(gpointer instance, dt_iop_module_t *self);
1347
1348void gui_changed(dt_iop_module_t *self, GtkWidget *w, void *previous)
1349{
1352
1353 if(IS_NULL_PTR(w) || w == g->mode)
1354 {
1356 _configure_slider_blocks(NULL, self);
1357 }
1358
1360
1361 if(IS_NULL_PTR(w) || w == g->lift_r || w == g->lift_g || w == g->lift_b)
1362 set_HSL_sliders(g->hue_lift, g->sat_lift, p->lift);
1363 if(IS_NULL_PTR(w) || w == g->gamma_r || w == g->gamma_g || w == g->gamma_b)
1364 set_HSL_sliders(g->hue_gamma, g->sat_gamma, p->gamma);
1365 if(IS_NULL_PTR(w) || w == g->gain_r || w == g->gain_g || w == g->gain_b)
1366 set_HSL_sliders(g->hue_gain, g->sat_gain, p->gain);
1367
1369}
1370
1381
1382#ifdef SHOW_COLOR_WHEELS
1383static gboolean dt_iop_area_draw(GtkWidget *widget, cairo_t *cr, dt_iop_module_t *self)
1384{
1385 float flt_bg = 0.5;
1386 if(gtk_widget_get_state_flags(widget) & GTK_STATE_FLAG_SELECTED) flt_bg = 0.6;
1387 float flt_dark = flt_bg / 1.5, flt_light = flt_bg * 1.5;
1388
1389 uint32_t bg = ((255 << 24) | ((int)floor(flt_bg * 255 + 0.5) << 16) | ((int)floor(flt_bg * 255 + 0.5) << 8)
1390 | (int)floor(flt_bg * 255 + 0.5));
1391 // bg = 0xffffffff;
1392 // uint32_t dark = ((255 << 24) |
1393 // ((int)floor(flt_dark * 255 + 0.5) << 16) |
1394 // ((int)floor(flt_dark * 255 + 0.5) << 8) |
1395 // (int)floor(flt_dark * 255 + 0.5));
1396 uint32_t light = ((255 << 24) | ((int)floor(flt_light * 255 + 0.5) << 16)
1397 | ((int)floor(flt_light * 255 + 0.5) << 8) | (int)floor(flt_light * 255 + 0.5));
1398
1399 GtkAllocation allocation;
1400 gtk_widget_get_allocation(widget, &allocation);
1401 int width = allocation.width, height = allocation.height;
1402 if(width % 2 == 0) width--;
1403 if(height % 2 == 0) height--;
1404 double center_x = (float)width / 2.0, center_y = (float)height / 2.0;
1405 double diameter = MIN(width, height) - 4;
1406 double r_outside = diameter / 2.0, r_inside = r_outside * 0.87;
1407 double r_outside_2 = r_outside * r_outside, r_inside_2 = r_inside * r_inside;
1408
1409 // clear the background
1410 cairo_set_source_rgb(cr, flt_bg, flt_bg, flt_bg);
1411 cairo_paint(cr);
1412
1413 /* Create an image initialized with the ring colors */
1414 gint stride = cairo_format_stride_for_width(CAIRO_FORMAT_RGB24, width);
1415 guint32 *buf = (guint32 *)malloc(sizeof(guint32) * height * stride / 4);
1416
1417 for(int y = 0; y < height; y++)
1418 {
1419 guint32 *p = buf + y * width;
1420
1421 double dy = -(y + 0.5 - center_y);
1422
1423 for(int x = 0; x < width; x++)
1424 {
1425 double dx = x + 0.5 - center_x;
1426 double dist = dx * dx + dy * dy;
1427 if(dist < r_inside_2 || dist > r_outside_2)
1428 {
1429 uint32_t col = bg;
1430 if((abs(dx) < 1 && abs(dy) < 3) || (abs(dx) < 3 && abs(dy) < 1)) col = light;
1431 *p++ = col;
1432 continue;
1433 }
1434
1435 double angle = atan2(dy, dx) - M_PI_2;
1436 if(angle < 0.0) angle += 2.0 * M_PI;
1437
1438 double hue = angle / (2.0 * M_PI);
1439
1441 hsl2rgb(rgb, hue, 1.0, 0.5);
1442
1443 *p++ = (((int)floor(rgb[0] * 255 + 0.5) << 16) | ((int)floor(rgb[1] * 255 + 0.5) << 8)
1444 | (int)floor(rgb[2] * 255 + 0.5));
1445 }
1446 }
1447
1448 cairo_surface_t *source
1449 = cairo_image_surface_create_for_data((unsigned char *)buf, CAIRO_FORMAT_RGB24, width, height, stride);
1450
1451 cairo_set_source_surface(cr, source, 0.0, 0.0);
1452 cairo_paint(cr);
1453 dt_free(buf);
1454
1455 // draw border
1456 float line_width = 1;
1457 cairo_set_line_width(cr, line_width);
1458
1459 cairo_set_source_rgb(cr, flt_bg, flt_bg, flt_bg);
1460 cairo_new_path(cr);
1461 cairo_arc(cr, center_x, center_y, r_outside, 0.0, 2.0 * M_PI);
1462 cairo_stroke(cr);
1463 cairo_arc(cr, center_x, center_y, r_inside, 0.0, 2.0 * M_PI);
1464 cairo_stroke(cr);
1465
1466 cairo_set_source_rgb(cr, flt_dark, flt_dark, flt_dark);
1467 cairo_new_path(cr);
1468 cairo_arc(cr, center_x, center_y, r_outside, M_PI, 1.5 * M_PI);
1469 cairo_stroke(cr);
1470 cairo_arc(cr, center_x, center_y, r_inside, 0.0, 0.5 * M_PI);
1471 cairo_stroke(cr);
1472
1473 cairo_set_source_rgb(cr, flt_light, flt_light, flt_light);
1474 cairo_new_path(cr);
1475 cairo_arc(cr, center_x, center_y, r_outside, 0.0, 0.5 * M_PI);
1476 cairo_stroke(cr);
1477 cairo_arc(cr, center_x, center_y, r_inside, M_PI, 1.5 * M_PI);
1478 cairo_stroke(cr);
1479
1480 // draw selector
1481 double r = 255 / 255.0, g = 155 / 255.0, b = 40 / 255.0;
1482 double h, s, v;
1483
1484 gtk_rgb_to_hsv(r, g, b, &h, &s, &v);
1485
1486 cairo_save(cr);
1487 cairo_set_source_rgba(cr, 1.0, 1.0, 1.0, 0.7);
1488
1489 cairo_translate(cr, center_x, center_y);
1490 cairo_rotate(cr, h * 2.0 * M_PI - M_PI_2);
1491
1492 cairo_arc(cr, r_inside * v, 0.0, 3.0, 0, 2.0 * M_PI);
1493 cairo_stroke(cr);
1494
1495 cairo_restore(cr);
1496
1497 cairo_surface_destroy(source);
1498
1499 return TRUE;
1500}
1501#endif
1502
1503static void _configure_slider_blocks(gpointer instance, dt_iop_module_t *self)
1504{
1506
1507 GtkWidget *new_container = NULL;
1508 GtkWidget *old_container = gtk_bin_get_child(GTK_BIN(g->main_box));
1509
1510 for(int i=0; i<3; i++)
1511 {
1512 g_object_ref(G_OBJECT(g->blocks[i]));
1513 if(old_container) gtk_container_remove(GTK_CONTAINER(old_container), g->blocks[i]);
1514 }
1515
1516 if(old_container) gtk_widget_destroy(old_container);
1517
1518 const gchar *long_label[]
1519 = { N_("shadows: lift / offset"),
1520 N_("mid-tones: gamma / power"),
1521 N_("highlights: gain / slope") };
1522
1523 gchar *layout = dt_conf_get_string("plugins/darkroom/colorbalance/layout");
1524 new_container = gtk_box_new(GTK_ORIENTATION_VERTICAL, DT_GUI_BOX_SPACING);
1525
1526 for(int i=0; i<3; i++)
1527 {
1528 if(i == 0)
1529 gtk_label_set_text(GTK_LABEL(g->main_label), _(long_label[0]));
1530 else
1531 {
1532 GtkWidget *label = dt_ui_section_label_new(_(long_label[i]));
1533 gtk_container_add(GTK_CONTAINER(new_container), label);
1534 if(old_container) gtk_widget_show(label);
1535 }
1536
1537 gtk_container_add(GTK_CONTAINER(new_container), g->blocks[i]);
1538 }
1539
1540 dt_free(layout);
1541
1542 for(int i=0; i<3; i++) g_object_unref(G_OBJECT(g->blocks[i]));
1543
1544 gtk_container_add(GTK_CONTAINER(g->main_box), new_container);
1545 if(old_container) gtk_widget_show(new_container);
1546}
1547
1548static void _cycle_layout_callback(GtkWidget *label, GdkEventButton *event, dt_iop_module_t *self)
1549{
1550 _configure_slider_blocks(NULL, self);
1551}
1552
1553#define HSL_CALLBACK(which) \
1554static void which##_callback(GtkWidget *slider, gpointer user_data) \
1555{ \
1556 dt_iop_module_t *self = (dt_iop_module_t *)user_data; \
1557 dt_iop_colorbalance_params_t *p = (dt_iop_colorbalance_params_t *)self->params; \
1558 dt_iop_colorbalance_gui_data_t *g = (dt_iop_colorbalance_gui_data_t *)dt_iop_gui_data(self); \
1559 \
1560 if(dt_gui_widgets_suppressed()) return; \
1561 \
1562 dt_iop_color_picker_reset(self, TRUE); \
1563 \
1564 float hsl[3] = {dt_bauhaus_slider_get(g->hue_##which) / 360.0f, \
1565 dt_bauhaus_slider_get(g->sat_##which) / 100.0f, \
1566 0.5f}; \
1567 \
1568 if(slider == g->hue_##which) \
1569 update_saturation_slider_color(g->sat_##which, hsl[0]); \
1570 set_RGB_sliders(g->which##_r, g->which##_g, g->which##_b, hsl, p->which, p->mode); \
1571 dt_dev_add_history_item(self->dev, self, TRUE, TRUE); \
1572}
1573
1577
1579{
1581
1582 g->mode = NULL;
1583
1584 for (int k=0; k<LEVELS; k++)
1585 {
1586 g->color_patches_flags[k] = INVALID;
1587 g->luma_patches_flags[k] = INVALID;
1588 }
1589
1590 GtkWidget *mode_box = self->gui->widget = gtk_box_new(GTK_ORIENTATION_VERTICAL, DT_GUI_BOX_SPACING);
1591
1592 // mode choice
1593 g->mode = dt_bauhaus_combobox_from_params(self, N_("mode"));
1594 gtk_widget_set_tooltip_text(g->mode, _("color-grading mapping method"));
1595
1596 // control choice
1598 dt_bauhaus_widget_set_label(g->controls, N_("color control sliders"));
1599 dt_bauhaus_combobox_add(g->controls, _("HSL"));
1600 dt_bauhaus_combobox_add(g->controls, _("RGBL"));
1601 dt_bauhaus_combobox_add(g->controls, _("both"));
1602 gtk_box_pack_start(GTK_BOX(self->gui->widget), GTK_WIDGET(g->controls), TRUE, TRUE, 0);
1603 gtk_widget_set_tooltip_text(g->controls, _("color-grading mapping method"));
1604 g_signal_connect(G_OBJECT(g->controls), "value-changed", G_CALLBACK(controls_callback), self);
1605
1606 const char *mode = dt_conf_get_string_const("plugins/darkroom/colorbalance/controls");
1607 dt_bauhaus_combobox_set(g->controls, !g_strcmp0(mode, "RGBL") ? RGBL :
1608 !g_strcmp0(mode, "BOTH") ? BOTH : HSL);
1609
1610 g->master_box = self->gui->widget = gtk_box_new(GTK_ORIENTATION_VERTICAL, DT_GUI_BOX_SPACING);
1611
1612 gtk_box_pack_start(GTK_BOX(g->master_box), dt_ui_section_label_new(_("master")), FALSE, FALSE, 0);
1613
1614 g->saturation = dt_bauhaus_slider_from_params(self, "saturation");
1615 dt_bauhaus_slider_set_soft_range(g->saturation, 0.5f, 1.5f);
1616 dt_bauhaus_slider_set_digits(g->saturation, 4);
1617 dt_bauhaus_slider_set_format(g->saturation, "%");
1618 gtk_widget_set_tooltip_text(g->saturation, _("saturation correction before the color balance"));
1619
1620 g->saturation_out = dt_bauhaus_slider_from_params(self, "saturation_out");
1621 dt_bauhaus_slider_set_soft_range(g->saturation_out, 0.5f, 1.5f);
1622 dt_bauhaus_slider_set_digits(g->saturation_out, 4);
1623 dt_bauhaus_slider_set_format(g->saturation_out, "%");
1624 gtk_widget_set_tooltip_text(g->saturation_out, _("saturation correction after the color balance"));
1625
1627 dt_bauhaus_slider_from_params(self, "grey"));
1628 dt_bauhaus_slider_set_format(g->grey, "%");
1629 gtk_widget_set_tooltip_text(g->grey, _("adjust to match a neutral tone"));
1630
1631 g->contrast = dt_bauhaus_slider_from_params(self, N_("contrast"));
1632 dt_bauhaus_slider_set_soft_range(g->contrast, 0.5f, 1.5f);
1633 dt_bauhaus_slider_set_digits(g->contrast, 4);
1634 dt_bauhaus_slider_set_factor(g->contrast, -100.0f);
1635 dt_bauhaus_slider_set_offset(g->contrast, 100.0f);
1636 dt_bauhaus_slider_set_format(g->contrast, "%");
1637 gtk_widget_set_tooltip_text(g->contrast, _("contrast"));
1638
1639#ifdef SHOW_COLOR_WHEELS
1640 GtkWidget *hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, DT_GUI_BOX_SPACING);
1641 gtk_box_pack_start(GTK_BOX(self->widget), hbox, FALSE, FALSE, 0);
1642
1644 gtk_box_pack_start(GTK_BOX(hbox), area, TRUE, TRUE, 0);
1645
1646 // gtk_widget_add_events(g->area,
1647 // GDK_POINTER_MOTION_MASK |
1648 // GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_LEAVE_NOTIFY_MASK);
1649 g_signal_connect(G_OBJECT(area), "draw", G_CALLBACK(dt_iop_area_draw), self);
1650 // g_signal_connect (G_OBJECT (area), "button-press-event",
1651 // G_CALLBACK (dt_iop_colorbalance_button_press), self);
1652 // g_signal_connect (G_OBJECT (area), "motion-notify-event",
1653 // G_CALLBACK (dt_iop_colorbalance_motion_notify), self);
1654 // g_signal_connect (G_OBJECT (area), "leave-notify-event",
1655 // G_CALLBACK (dt_iop_colorbalance_leave_notify), self);
1656
1658 gtk_box_pack_start(GTK_BOX(hbox), area, TRUE, TRUE, 0);
1659
1660 // gtk_widget_add_events(g->area,
1661 // GDK_POINTER_MOTION_MASK |
1662 // GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_LEAVE_NOTIFY_MASK);
1663 g_signal_connect(G_OBJECT(area), "draw", G_CALLBACK(dt_iop_area_draw), self);
1664 // g_signal_connect (G_OBJECT (area), "button-press-event",
1665 // G_CALLBACK (dt_iop_colorbalance_button_press), self);
1666 // g_signal_connect (G_OBJECT (area), "motion-notify-event",
1667 // G_CALLBACK (dt_iop_colorbalance_motion_notify), self);
1668 // g_signal_connect (G_OBJECT (area), "leave-notify-event",
1669 // G_CALLBACK (dt_iop_colorbalance_leave_notify), self);
1670
1672 gtk_box_pack_start(GTK_BOX(hbox), area, TRUE, TRUE, 0);
1673
1674 // gtk_widget_add_events(g->area,
1675 // GDK_POINTER_MOTION_MASK |
1676 // GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_LEAVE_NOTIFY_MASK);
1677 g_signal_connect(G_OBJECT(area), "draw", G_CALLBACK(dt_iop_area_draw), self);
1678// g_signal_connect (G_OBJECT (area), "button-press-event",
1679// G_CALLBACK (dt_iop_colorbalance_button_press), self);
1680// g_signal_connect (G_OBJECT (area), "motion-notify-event",
1681// G_CALLBACK (dt_iop_colorbalance_motion_notify), self);
1682// g_signal_connect (G_OBJECT (area), "leave-notify-event",
1683// G_CALLBACK (dt_iop_colorbalance_leave_notify), self);
1684#endif
1685
1686 g->main_label = dt_ui_section_label_new(""); // is set in _configure_slider_blocks
1687 gtk_widget_set_tooltip_text(g->main_label, _("click to cycle layout"));
1688 GtkWidget *main_label_box = gtk_event_box_new();
1689 gtk_container_add(GTK_CONTAINER(main_label_box), g->main_label);
1690 g_signal_connect(G_OBJECT(main_label_box), "button-release-event", G_CALLBACK(_cycle_layout_callback), self);
1691
1692 g->main_box = gtk_event_box_new(); // is filled in _configure_slider_blocks
1693
1694 char field_name[10];
1695
1696#define ADD_CHANNEL(which, section, c, n, N, text, span) \
1697 sprintf(field_name, "%s[%d]", #which, CHANNEL_##N); \
1698 g->which##_##c = dt_bauhaus_slider_from_params(self, field_name); \
1699 dt_bauhaus_slider_set_soft_range(g->which##_##c, -span+1.0, span+1.0); \
1700 dt_bauhaus_slider_set_digits(g->which##_##c, 5); \
1701 dt_bauhaus_slider_set_offset(g->which##_##c, -1.0); \
1702 dt_bauhaus_slider_set_feedback(g->which##_##c, 0); \
1703 gtk_widget_set_tooltip_text(g->which##_##c, _(text[CHANNEL_##N])); \
1704 dt_bauhaus_widget_set_label(g->which##_##c, #n); \
1705
1706#define ADD_BLOCK(blk, which, section, text, span, satspan) \
1707 g->blocks[blk] = self->gui->widget = gtk_box_new(GTK_ORIENTATION_VERTICAL, DT_GUI_BOX_SPACING); \
1708 \
1709 sprintf(field_name, "%s[%d]", #which, CHANNEL_FACTOR); \
1710 g->which##_factor = dt_color_picker_new(self, DT_COLOR_PICKER_AREA, \
1711 dt_bauhaus_slider_from_params(self, field_name)); \
1712 dt_bauhaus_slider_set_soft_range(g->which##_factor, -span+1.0, span+1.0); \
1713 dt_bauhaus_slider_set_digits(g->which##_factor, 4); \
1714 dt_bauhaus_slider_set_factor(g->which##_factor, 100.0); \
1715 dt_bauhaus_slider_set_offset(g->which##_factor, - 100.0); \
1716 dt_bauhaus_slider_set_format(g->which##_factor,"%"); \
1717 dt_bauhaus_slider_set_feedback(g->which##_factor, 0); \
1718 dt_bauhaus_slider_set_stop(g->which##_factor, 0.0, 0.0, 0.0, 0.0); \
1719 dt_bauhaus_slider_set_stop(g->which##_factor, 1.0, 1.0, 1.0, 1.0); \
1720 gtk_widget_set_tooltip_text(g->which##_factor, _(text[CHANNEL_FACTOR])); \
1721 dt_bauhaus_widget_set_label(g->which##_factor, N_("factor")); \
1722 \
1723 g->hue_##which = dt_color_picker_new(self, DT_COLOR_PICKER_AREA, \
1724 dt_bauhaus_slider_new_with_range_and_feedback(dt_bauhaus_get_global(), DT_GUI_MODULE(self), \
1725 0.0f, 360.0f, 0, 0.0f, 2, 0)); \
1726 dt_bauhaus_widget_set_label(g->hue_##which, N_("hue")); \
1727 dt_bauhaus_slider_set_format(g->hue_##which, "\302\260"); \
1728 dt_bauhaus_slider_set_stop(g->hue_##which, 0.0f, 1.0f, 0.0f, 0.0f); \
1729 dt_bauhaus_slider_set_stop(g->hue_##which, 0.166f, 1.0f, 1.0f, 0.0f); \
1730 dt_bauhaus_slider_set_stop(g->hue_##which, 0.322f, 0.0f, 1.0f, 0.0f); \
1731 dt_bauhaus_slider_set_stop(g->hue_##which, 0.498f, 0.0f, 1.0f, 1.0f); \
1732 dt_bauhaus_slider_set_stop(g->hue_##which, 0.664f, 0.0f, 0.0f, 1.0f); \
1733 dt_bauhaus_slider_set_stop(g->hue_##which, 0.830f, 1.0f, 0.0f, 1.0f); \
1734 dt_bauhaus_slider_set_stop(g->hue_##which, 1.0f, 1.0f, 0.0f, 0.0f); \
1735 gtk_widget_set_tooltip_text(g->hue_##which, _("select the hue")); \
1736 g_signal_connect(G_OBJECT(g->hue_##which), "value-changed", \
1737 G_CALLBACK(which##_callback), self); \
1738 gtk_box_pack_start(GTK_BOX(self->gui->widget), g->hue_##which, TRUE, TRUE, 0); \
1739 \
1740 g->sat_##which = dt_bauhaus_slider_new_with_range_and_feedback(dt_bauhaus_get_global(), DT_GUI_MODULE(self), \
1741 0.0f, 100.0f, 0, 0.0f, 2, 0); \
1742 dt_bauhaus_slider_set_soft_max(g->sat_##which, satspan); \
1743 dt_bauhaus_widget_set_label(g->sat_##which, N_("saturation")); \
1744 dt_bauhaus_slider_set_format(g->sat_##which, "%"); \
1745 dt_bauhaus_slider_set_stop(g->sat_##which, 0.0f, 0.2f, 0.2f, 0.2f); \
1746 dt_bauhaus_slider_set_stop(g->sat_##which, 1.0f, 1.0f, 1.0f, 1.0f); \
1747 gtk_widget_set_tooltip_text(g->sat_##which, _("select the saturation")); \
1748 g_signal_connect(G_OBJECT(g->sat_##which), "value-changed", \
1749 G_CALLBACK(which##_callback), self); \
1750 gtk_box_pack_start(GTK_BOX(self->gui->widget), g->sat_##which, TRUE, TRUE, 0); \
1751 \
1752 ADD_CHANNEL(which, section, r, red, RED, text, span) \
1753 dt_bauhaus_slider_set_stop(g->which##_r, 0.0, 0.0, 1.0, 1.0); \
1754 dt_bauhaus_slider_set_stop(g->which##_r, 0.5, 1.0, 1.0, 1.0); \
1755 dt_bauhaus_slider_set_stop(g->which##_r, 1.0, 1.0, 0.0, 0.0); \
1756 ADD_CHANNEL(which, section, g, green, GREEN, text, span) \
1757 dt_bauhaus_slider_set_stop(g->which##_g, 0.0, 1.0, 0.0, 1.0); \
1758 dt_bauhaus_slider_set_stop(g->which##_g, 0.5, 1.0, 1.0, 1.0); \
1759 dt_bauhaus_slider_set_stop(g->which##_g, 1.0, 0.0, 1.0, 0.0); \
1760 ADD_CHANNEL(which, section, b, blue, BLUE, text, span) \
1761 dt_bauhaus_slider_set_stop(g->which##_b, 0.0, 1.0, 1.0, 0.0); \
1762 dt_bauhaus_slider_set_stop(g->which##_b, 0.5, 1.0, 1.0, 1.0); \
1763 dt_bauhaus_slider_set_stop(g->which##_b, 1.0, 0.0, 0.0, 1.0); \
1764
1765 static const char *lift_messages[]
1766 = { N_("factor of lift/offset"),
1767 N_("factor of red for lift/offset"),
1768 N_("factor of green for lift/offset"),
1769 N_("factor of blue for lift/offset") };
1770
1771 static const char *gamma_messages[]
1772 = { N_("factor of gamma/power"),
1773 N_("factor of red for gamma/power"),
1774 N_("factor of green for gamma/power"),
1775 N_("factor of blue for gamma/power") };
1776
1777 static const char *gain_messages[]
1778 = { N_("factor of gain/slope"),
1779 N_("factor of red for gain/slope"),
1780 N_("factor of green for gain/slope"),
1781 N_("factor of blue for gain/slope") };
1782
1783 ADD_BLOCK(0, lift, N_("shadows"), lift_messages, 0.05f, 5.0f)
1784 ADD_BLOCK(1, gamma, N_("mid-tones"), gamma_messages, 0.5f, 20.0f)
1785 ADD_BLOCK(2, gain, N_("highlights"), gain_messages, 0.5f, 25.0f)
1786 _configure_slider_blocks(NULL, self);
1787
1788 g->optimizer_box = self->gui->widget = gtk_box_new(GTK_ORIENTATION_VERTICAL, DT_GUI_BOX_SPACING);
1789
1790 gtk_box_pack_start(GTK_BOX(self->gui->widget), dt_ui_section_label_new(_("auto optimizers")), FALSE, FALSE, 0);
1791
1792 g->auto_luma = dt_color_picker_new(self, DT_COLOR_PICKER_AREA,
1794 dt_bauhaus_widget_set_label(g->auto_luma, N_("optimize luma"));
1795 gtk_widget_set_tooltip_text(g->auto_luma, _("fit the whole histogram and center the average luma"));
1796 gtk_box_pack_start(GTK_BOX(self->gui->widget), g->auto_luma, FALSE, FALSE, 0);
1797
1798 g->auto_color = dt_color_picker_new(self, DT_COLOR_PICKER_AREA,
1800 dt_bauhaus_widget_set_label(g->auto_color, N_("neutralize colors"));
1801 gtk_widget_set_tooltip_text(g->auto_color, _("optimize the RGB curves to remove color casts"));
1802 gtk_box_pack_start(GTK_BOX(self->gui->widget), g->auto_color, FALSE, FALSE, 0);
1803
1804 // start building top level widget
1805 self->gui->widget = gtk_box_new(GTK_ORIENTATION_VERTICAL, DT_GUI_BOX_SPACING);
1806
1807 gtk_box_pack_start(GTK_BOX(self->gui->widget), GTK_WIDGET(mode_box), TRUE, TRUE, 0);
1808 gtk_box_pack_start(GTK_BOX(self->gui->widget), GTK_WIDGET(g->master_box), TRUE, TRUE, 0);
1809 gtk_box_pack_start(GTK_BOX(self->gui->widget), GTK_WIDGET(main_label_box), TRUE, TRUE, 0);
1810 gtk_box_pack_start(GTK_BOX(self->gui->widget), GTK_WIDGET(g->main_box), TRUE, TRUE, 0);
1811 gtk_box_pack_start(GTK_BOX(self->gui->widget), GTK_WIDGET(g->optimizer_box), TRUE, TRUE, 0);
1812
1814 G_CALLBACK(_configure_slider_blocks), (gpointer)self);
1815}
1816
1823
1824// clang-format off
1825// modelines: These editor modelines have been set for all relevant files by tools/update_modelines.py
1826// vim: shiftwidth=2 expandtab tabstop=2 cindent
1827// kate: tab-indents: off; indent-width 2; replace-tabs on; indent-mode cstyle; remove-trailing-spaces modified;
1828// clang-format on
static double dist(double x1, double y1, double x2, double y2)
Definition ashift_lsd.c:250
static void error(char *msg)
Definition ashift_lsd.c:202
#define TRUE
Definition ashift_lsd.c:162
#define FALSE
Definition ashift_lsd.c:158
void dt_bauhaus_slider_set_soft_range(GtkWidget *widget, float soft_min, float soft_max)
Definition bauhaus.c:1498
void dt_bauhaus_slider_set_digits(GtkWidget *widget, int val)
Definition bauhaus.c:3343
void dt_bauhaus_slider_set_stop(GtkWidget *widget, float stop, float r, float g, float b)
Definition bauhaus.c:2161
int dt_bauhaus_combobox_get(GtkWidget *widget)
Definition bauhaus.c:2136
void dt_bauhaus_slider_set_offset(GtkWidget *widget, float offset)
Definition bauhaus.c:3430
void dt_bauhaus_slider_set(GtkWidget *widget, float pos)
Definition bauhaus.c:3331
void dt_bauhaus_combobox_set(GtkWidget *widget, const int pos)
Definition bauhaus.c:2090
void dt_bauhaus_widget_set_label(GtkWidget *widget, const char *label)
Definition bauhaus.c:1504
GtkWidget * dt_bauhaus_combobox_new(dt_bauhaus_t *bh, dt_gui_module_t *self)
Definition bauhaus.c:1698
void dt_bauhaus_slider_set_format(GtkWidget *widget, const char *format)
Definition bauhaus.c:3407
void dt_bauhaus_combobox_add(GtkWidget *widget, const char *text)
Definition bauhaus.c:1824
void dt_bauhaus_slider_set_factor(GtkWidget *widget, float factor)
Definition bauhaus.c:3423
int dt_develop_blend_legacy_params_from_so(dt_iop_module_so_t *module_so, const void *const old_params, const int old_version, void *new_params, const int new_version, const int length)
Definition blend.c:2264
int dt_develop_blend_version(void)
Definition blend.c:1714
#define CHANNEL_SIZE
@ IOP_CS_LAB
void dt_iop_color_picker_reset(dt_iop_module_t *module, gboolean keep)
GtkWidget * dt_color_picker_new(dt_iop_module_t *module, dt_iop_color_picker_kind_t kind, GtkWidget *w)
@ DT_COLOR_PICKER_AREA
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)
const char ** description(struct dt_iop_module_t *self)
int default_group()
static void _check_tuner_picker_labels(dt_iop_module_t *self)
__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 ivoid, void *const ovoid)
void gui_reset(dt_iop_module_t *self)
static void apply_autocolor(dt_iop_module_t *self)
static void apply_lift_auto(dt_iop_module_t *self)
static void add_preset(dt_iop_module_so_t *self, const char *name, const char *pi, const int version, const char *bpi, const int blendop_version)
static void apply_gain_neutralize(dt_iop_module_t *self)
void gui_update(dt_iop_module_t *self)
static void apply_autoluma(dt_iop_module_t *self)
static void _configure_slider_blocks(gpointer instance, dt_iop_module_t *self)
static float CDL(float x, float slope, float offset, float power)
static void apply_gamma_auto(dt_iop_module_t *self)
const char * aliases()
static void _cycle_layout_callback(GtkWidget *label, GdkEventButton *event, dt_iop_module_t *self)
dt_iop_colorbalance_mode_t
@ LEGACY
@ SLOPE_OFFSET_POWER
@ LIFT_GAMMA_GAIN
_controls_t
@ BOTH
@ RGBL
@ HSL
const char * name()
static void set_HSL_sliders(GtkWidget *hue, GtkWidget *sat, float RGB[4])
static void apply_gain_auto(dt_iop_module_t *self)
_colorbalance_patch_t
@ AUTO_SELECTED
@ USER_SELECTED
@ INVALID
static void apply_gamma_neutralize(dt_iop_module_t *self)
void gui_init(dt_iop_module_t *self)
static void apply_lift_neutralize(dt_iop_module_t *self)
_colorbalance_channel_t
@ CHANNEL_BLUE
@ CHANNEL_SIZE
@ CHANNEL_GREEN
@ CHANNEL_FACTOR
@ CHANNEL_RED
void gui_changed(dt_iop_module_t *self, GtkWidget *w, void *previous)
#define HSL_CALLBACK(which)
static void set_RGB_sliders(GtkWidget *R, GtkWidget *G, GtkWidget *B, float hsl[3], float *p, int mode)
void cleanup_global(dt_iop_module_so_t *module)
struct dt_iop_colorbalance_global_data_t dt_iop_colorbalance_global_data_t
void cleanup_pipe(dt_iop_module_t *self, dt_dev_pixelpipe_t *pipe, dt_dev_pixelpipe_iop_t *piece)
int default_colorspace(dt_iop_module_t *self, dt_dev_pixelpipe_t *pipe, const dt_dev_pixelpipe_iop_t *piece)
void input_format(dt_iop_module_t *self, dt_dev_pixelpipe_t *pipe, dt_dev_pixelpipe_iop_t *piece, dt_iop_buffer_dsc_t *dsc)
int flags()
#define ADD_BLOCK(blk, which, section, text, span, satspan)
static void update_saturation_slider_color(GtkWidget *slider, float hue)
void gui_cleanup(struct dt_iop_module_t *self)
void init_presets(dt_iop_module_so_t *self)
void init_pipe(dt_iop_module_t *self, dt_dev_pixelpipe_t *pipe, dt_dev_pixelpipe_iop_t *piece)
void set_visible_widgets(dt_iop_colorbalance_gui_data_t *g)
static void apply_autogrey(dt_iop_module_t *self)
void init_global(dt_iop_module_so_t *module)
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)
_colorbalance_levels_t
@ LIFT
@ LEVELS
@ GAMMA
@ GAIN
static void controls_callback(GtkWidget *combo, dt_iop_module_t *self)
void color_picker_apply(dt_iop_module_t *self, GtkWidget *picker, dt_dev_pixelpipe_t *pipe, dt_dev_pixelpipe_iop_t *piece)
int legacy_params(dt_iop_module_t *self, const void *const old_params, const int old_version, void *new_params, const int new_version)
static const float x
const float v
#define B(y, x)
void rgb2hsl(const dt_aligned_pixel_t rgb, float *h, float *s, float *l)
Convert RGB to HSL. Common helper used by iop modules.
void hsl2rgb(dt_aligned_pixel_t rgb, float h, float s, float l)
Convert HSL back to RGB. Common helper used by iop modules.
struct _GtkWidget GtkWidget
GtkWidget, opaque, spelled exactly as GTK spells it.
Definition colorspaces.h:98
dt_prophotorgb_to_XYZ(rgb, XYZ)
static dt_aligned_pixel_t rgb
dt_Lab_to_XYZ(Lab, XYZ)
dt_XYZ_to_sRGB(XYZ, result)
dt_XYZ_to_prophotorgb(XYZ, rgb)
static dt_aligned_pixel_t XYZ
const dt_colormatrix_t dt_aligned_pixel_t out
dt_XYZ_to_Lab(XYZ, Lab)
static dt_aligned_pixel_t RGB
gchar * dt_conf_get_string(const char *name)
Read the stored string for name as a private copy.
void dt_conf_set_string(const char *name, const char *val)
const char * dt_conf_get_string_const(const char *name)
Borrow the stored string for name without copying it.
struct dt_bauhaus_t * dt_bauhaus_get_global(void)
Definition darktable.c:646
#define dt_dev_add_history_item(dev, module, enable, redraw)
void dt_iop_params_t
Definition dev_history.h:43
void default_input_format(dt_iop_module_t *self, dt_dev_pixelpipe_t *pipe, dt_dev_pixelpipe_iop_t *piece, dt_iop_buffer_dsc_t *dsc)
@ DT_DEV_PIXELPIPE_DISPLAY_MASK
Definition develop.h:123
GtkWidget * dtgtk_drawing_area_new_with_aspect_ratio(double aspect)
Definition drawingarea.c:54
unsigned char * dt_exif_xmp_decode(const char *input, const int len, int *output_len)
Definition exif.cc:2207
What a photograph says about itself: the EXIF, IPTC and XMP tags a camera and a cataloguer write,...
@ TYPE_FLOAT
Definition format.h:56
void dt_gui_presets_add_with_blendop(const char *name, dt_dev_operation_t op, const int32_t version, const void *params, const int32_t params_size, const void *blend_params, const int32_t enabled)
#define DT_GUI_MODULE(x)
const char ** dt_iop_set_description(dt_iop_module_t *module, const char *main_text, const char *purpose, const char *input, const char *process, const char *output)
Definition imageop.c:1893
@ IOP_FLAGS_INCLUDE_IN_STYLES
Definition imageop.h:185
@ IOP_FLAGS_DEPRECATED
Definition imageop.h:187
@ IOP_FLAGS_SUPPORTS_BLENDING
Definition imageop.h:186
@ IOP_GROUP_COLOR
Definition imageop.h:158
GtkWidget * dt_bauhaus_slider_from_params(dt_iop_module_t *self, const char *param)
GtkWidget * dt_bauhaus_combobox_from_params(dt_iop_module_t *self, const char *param)
#define IOP_GUI_FREE
Definition imageop_gui.h:96
static dt_iop_gui_data_t * dt_iop_gui_data(const struct dt_iop_module_t *m)
The module's GUI data blob, NULL-safe for headless callers: IOP process() implementations read it for...
Definition imageop_gui.h:81
#define IOP_GUI_ALLOC(module)
Definition imageop_gui.h:93
void *const ovoid
GtkWidget * dt_ui_section_label_new(const gchar *str)
Definition label.c:114
@ DT_DEBUG_OPENCL
Definition logging.h:57
void dt_print(dt_debug_thread_t thread, const char *msg,...) __attribute__((format(printf
Print to stdout when thread is enabled, prefixed with seconds since startup.
float *const restrict const size_t k
float *const restrict const size_t const size_t ch
#define IS_NULL_PTR(p)
C is way too permissive with !=, == and if(var) checks, which can mean too many things depending on w...
Definition macros.h:96
#define R
#define M_PI
Definition math.h:47
#define dt_free_align(ptr)
Release memory from dt_alloc_align() and set ptr to NULL.
Definition mem_alloc.h:214
static void * dt_calloc_align(size_t size)
dt_alloc_align() followed by a zero fill.
Definition mem_alloc.h:225
#define dt_free(ptr)
g_free() ptr and set it to NULL, skipping both if it is already NULL.
Definition mem_alloc.h:171
uint32_t width
Definition mipmap_cache.c:0
uint32_t height
Definition mipmap_cache.c:1
#define DT_MODULE_INTROSPECTION(MODVER, PARAMSTYPE)
DT_MODULE() for a module whose params struct is introspected.
int dt_opencl_enqueue_kernel_2d(const int dev, const int kernel, const size_t *sizes)
Definition opencl.c:2554
int dt_opencl_create_kernel(const int prog, const char *name)
Definition opencl.c:2448
void dt_opencl_free_kernel(const int kernel)
Definition opencl.c:2491
int dt_opencl_set_kernel_arg(const int dev, const int kernel, const int num, const size_t size, const void *arg)
Definition opencl.c:2545
#define ROUNDUPDHT(a, b)
Definition opencl.h:86
#define ROUNDUPDWD(a, b)
Definition opencl.h:85
#define __OMP_PARALLEL_FOR_SIMD__(...)
Definition openmp.h:96
#define DT_DEBUG_CONTROL_SIGNAL_DISCONNECT(ctlsig, cb, user_data)
Definition signal.h:407
struct dt_control_signal_t * dt_control_signal_get_global(void)
Definition darktable.c:616
@ DT_SIGNAL_PREFERENCES_CHANGE
This signal is raised after preferences have been changed no parameters no return.
Definition signal.h:276
#define DT_DEBUG_CONTROL_SIGNAL_CONNECT(ctlsig, signal, cb, user_data)
Definition signal.h:396
DT_ALIGNED_PIXEL float dt_aligned_pixel_t[4]
Definition simd.h:53
const float r
struct dt_iop_module_t *void * data
unsigned int channels
Definition format.h:83
dt_iop_buffer_type_t datatype
Definition format.h:85
float gain[CHANNEL_SIZE]
float lift[CHANNEL_SIZE]
dt_iop_colorbalance_mode_t mode
float gamma[CHANNEL_SIZE]
_colorbalance_patch_t color_patches_flags[LEVELS]
_colorbalance_patch_t luma_patches_flags[LEVELS]
dt_iop_colorbalance_mode_t mode
float lift[CHANNEL_SIZE]
float gain[CHANNEL_SIZE]
float gamma[CHANNEL_SIZE]
dt_dev_operation_t op
Definition imageop.h:235
dt_iop_global_data_t * data
Definition imageop.h:238
dt_iop_params_t * default_params
Definition imageop.h:333
struct dt_develop_t * dev
Definition imageop.h:311
dt_iop_global_data_t * global_data
Definition imageop.h:337
dt_aligned_pixel_t picked_color_min
Definition imageop.h:287
dt_aligned_pixel_t picked_color_max
Definition imageop.h:287
dt_aligned_pixel_t picked_color
Definition imageop.h:287
dt_iop_params_t * params
Definition imageop.h:333
Region of interest passed through the pixelpipe.
Definition format.h:49
int width
Definition format.h:50
int height
Definition format.h:50
#define __DT_CLONE_TARGETS__
#define MIN(a, b)
Definition thinplate.c:32
#define MAX(a, b)
Definition thinplate.c:29
gboolean dt_gui_widgets_suppressed(void)
#define dt_gui_freeze_begin()
#define dt_gui_freeze_end()
#define DT_GUI_BOX_SPACING