Ansel 0.0
A darktable fork - bloat + design vision
Loading...
Searching...
No Matches
exposure.c
Go to the documentation of this file.
1/*
2 This file is part of darktable,
3 Copyright (C) 2009-2016, 2018 johannes hanika.
4 Copyright (C) 2010-2011 Bruce Guenter.
5 Copyright (C) 2010-2012 Henrik Andersson.
6 Copyright (C) 2010 José Carlos García Sogo.
7 Copyright (C) 2010 Milan Knížek.
8 Copyright (C) 2010 Stuart Henderson.
9 Copyright (C) 2011 Antony Dovgal.
10 Copyright (C) 2011 Christophe Augier.
11 Copyright (C) 2011 Olivier Tribout.
12 Copyright (C) 2011-2012 Pascal de Bruijn.
13 Copyright (C) 2011 Robert Bieber.
14 Copyright (C) 2011 root.
15 Copyright (C) 2011-2019 Tobias Ellinghaus.
16 Copyright (C) 2011-2014, 2017 Ulrich Pegelow.
17 Copyright (C) 2012 Edouard Gomez.
18 Copyright (C) 2012 Richard Wonka.
19 Copyright (C) 2013, 2020, 2022 Aldric Renaudin.
20 Copyright (C) 2013 Dennis Gnad.
21 Copyright (C) 2013-2014, 2018-2022 Pascal Obry.
22 Copyright (C) 2014-2016 Roman Lebedev.
23 Copyright (C) 2015 Jan Kundrát.
24 Copyright (C) 2015 Pedro Côrte-Real.
25 Copyright (C) 2016 Alexander V. Smal.
26 Copyright (C) 2017 Heiko Bauke.
27 Copyright (C) 2018-2023, 2025-2026 Aurélien PIERRE.
28 Copyright (C) 2018-2019 Edgardo Hoszowski.
29 Copyright (C) 2018 Maurizio Paglia.
30 Copyright (C) 2018 rawfiner.
31 Copyright (C) 2019 Andreas Schneider.
32 Copyright (C) 2019-2020, 2022 Diederik Ter Rahe.
33 Copyright (C) 2020-2022 Chris Elston.
34 Copyright (C) 2020 Ralf Brown.
35 Copyright (C) 2021 Alex Esseling.
36 Copyright (C) 2021 Hubert Kowalski.
37 Copyright (C) 2022 Hanno Schwalm.
38 Copyright (C) 2022 Martin Bařinka.
39 Copyright (C) 2022 Philipp Lutz.
40 Copyright (C) 2023 Luca Zulberti.
41 Copyright (C) 2025-2026 Guillaume Stutin.
42
43 darktable is free software: you can redistribute it and/or modify
44 it under the terms of the GNU General Public License as published by
45 the Free Software Foundation, either version 3 of the License, or
46 (at your option) any later version.
47
48 darktable is distributed in the hope that it will be useful,
49 but WITHOUT ANY WARRANTY; without even the implied warranty of
50 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
51 GNU General Public License for more details.
52
53 You should have received a copy of the GNU General Public License
54 along with darktable. If not, see <http://www.gnu.org/licenses/>.
55*/
56#ifdef HAVE_CONFIG_H
57#include "config.h"
58#include "develop/iop_profile.h"
59#include "common/conf.h"
60#endif
61
62#include <assert.h>
63#include <math.h>
64#include <stdint.h>
65#include <stdlib.h>
66#include <string.h>
67
68#include "widgets/bauhaus.h"
69#include "system/macros.h"
70#include "system/openmp.h"
72#include "system/mem_alloc.h"
73#include "system/simd.h"
74#include "common/logging.h"
76#include "common/histogram.h"
77#include "caches/image_cache.h"
78#include "caches/mipmap_cache.h"
79#include "common/opencl.h"
81#include "develop/develop.h"
82#include "develop/imageop.h"
85#include "develop/imageop_gui.h"
86#include "develop/pixelpipe.h"
87
88#include "gui/presets.h"
90#include "iop/iop_api.h"
91#include "widgets/label.h"
92#include "gui/screen_metrics.h"
93
94#define exposure2white(x) exp2f(-(x))
95#define white2exposure(x) -dt_log2f(fmaxf(1e-20f, x))
96
98
100{
101 EXPOSURE_MODE_MANUAL, // $DESCRIPTION: "manual"
102 EXPOSURE_MODE_DEFLICKER // $DESCRIPTION: "automatic"
104
111
112// uint16_t pixel can have any value in range [0, 65535], thus, there is
113// 65536 possible values.
114#define DEFLICKER_BINS_COUNT (UINT16_MAX + 1)
115
116typedef struct dt_iop_exposure_params_t
117{
118 dt_iop_exposure_mode_t mode; // $DEFAULT: EXPOSURE_MODE_MANUAL
119 float black; // $MIN: -1.0 $MAX: 1.0 $DEFAULT: 0.0 $DESCRIPTION: "black level correction"
120 float exposure; // $MIN: -18.0 $MAX: 18.0 $DEFAULT: 0.0
121 float deflicker_percentile; // $MIN: 0.0 $MAX: 100.0 $DEFAULT: 50.0 $DESCRIPTION: "percentile"
122 float deflicker_target_level; // $MIN: -18.0 $MAX: 18.0 $DEFAULT: -4.0 $DESCRIPTION: "target level"
123 gboolean compensate_exposure_bias; // $DEFAULT: FALSE $DESCRIPTION: "compensate exposure bias"
125
150
158
163
164
165const char *name()
166{
167 return _("exp_osure");
168}
169
170const char** description(struct dt_iop_module_t *self)
171{
172 return dt_iop_set_description(self,
173 _("redo the exposure of the shot as if you were still in-camera\n"
174 "using a color-safe brightening similar to increasing ISO setting"),
175 _("corrective and creative"),
176 _("linear, RGB, scene-referred"),
177 _("linear, RGB"),
178 _("linear, RGB, scene-referred"));
179}
180
182{
183 return IOP_GROUP_TONES;
184}
185
190
195
198
204
205static void _paint_hue(dt_iop_module_t *self);
206static void _exposure_set_black(struct dt_iop_module_t *self, const float black);
207
208int legacy_params(dt_iop_module_t *self, const void *const old_params, const int old_version,
209 void *new_params, const int new_version)
210{
211 if(old_version == 2 && new_version == 6)
212 {
213 typedef struct dt_iop_exposure_params_v2_t
214 {
215 float black, exposure, gain;
216 } dt_iop_exposure_params_v2_t;
217
218 dt_iop_exposure_params_v2_t *o = (dt_iop_exposure_params_v2_t *)old_params;
221
222 *n = *d; // start with a fresh copy of default parameters
223
224 n->black = o->black;
225 n->exposure = o->exposure;
226 n->compensate_exposure_bias = FALSE;
227 return 0;
228 }
229 if(old_version == 3 && new_version == 6)
230 {
231 typedef struct dt_iop_exposure_params_v3_t
232 {
233 float black, exposure;
234 gboolean deflicker;
235 float deflicker_percentile, deflicker_target_level;
236 } dt_iop_exposure_params_v3_t;
237
238 dt_iop_exposure_params_v3_t *o = (dt_iop_exposure_params_v3_t *)old_params;
241
242 *n = *d; // start with a fresh copy of default parameters
243
244 n->mode = o->deflicker ? EXPOSURE_MODE_DEFLICKER : EXPOSURE_MODE_MANUAL;
245 n->black = o->black;
246 n->exposure = o->exposure;
247 n->deflicker_percentile = o->deflicker_percentile;
248 n->deflicker_target_level = o->deflicker_target_level;
249 n->compensate_exposure_bias = FALSE;
250 return 0;
251 }
252 if(old_version == 4 && new_version == 6)
253 {
254 typedef enum dt_iop_exposure_deflicker_histogram_source_t {
255 DEFLICKER_HISTOGRAM_SOURCE_THUMBNAIL,
256 DEFLICKER_HISTOGRAM_SOURCE_SOURCEFILE
257 } dt_iop_exposure_deflicker_histogram_source_t;
258
259 typedef struct dt_iop_exposure_params_v4_t
260 {
262 float black;
263 float exposure;
264 float deflicker_percentile, deflicker_target_level;
265 dt_iop_exposure_deflicker_histogram_source_t deflicker_histogram_source;
266 } dt_iop_exposure_params_v4_t;
267
268 dt_iop_exposure_params_v4_t *o = (dt_iop_exposure_params_v4_t *)old_params;
271
272 *n = *d; // start with a fresh copy of default parameters
273
274 n->mode = o->mode;
275 n->black = o->black;
276 n->exposure = o->exposure;
277 n->deflicker_percentile = o->deflicker_percentile;
278 n->deflicker_target_level = o->deflicker_target_level;
279 // deflicker_histogram_source is dropped. this does change output,
280 // but deflicker still was not publicly released at that point
281 n->compensate_exposure_bias = FALSE;
282 return 0;
283 }
284 if(old_version == 5 && new_version == 6)
285 {
286 typedef struct dt_iop_exposure_params_v5_t
287 {
289 float black;
290 float exposure;
291 float deflicker_percentile, deflicker_target_level;
292 } dt_iop_exposure_params_v5_t;
293
294 dt_iop_exposure_params_v5_t *o = (dt_iop_exposure_params_v5_t *)old_params;
297
298 *n = *d; // start with a fresh copy of default parameters
299
300 n->mode = o->mode;
301 n->black = o->black;
302 n->exposure = o->exposure;
303 n->deflicker_percentile = o->deflicker_percentile;
304 n->deflicker_target_level = o->deflicker_target_level;
305 n->compensate_exposure_bias = FALSE;
306 return 0;
307 }
308 return 1;
309}
310
312{
313 dt_gui_presets_add_generic(_("magic lantern defaults"), self->op,
314 self->version(),
315 &(dt_iop_exposure_params_t){.mode = EXPOSURE_MODE_DEFLICKER,
316 .black = 0.0f,
317 .exposure = 0.0f,
318 .deflicker_percentile = 50.0f,
319 .deflicker_target_level = -4.0f,
320 .compensate_exposure_bias = FALSE},
321 sizeof(dt_iop_exposure_params_t), 1);
322
323
324 // For scene-referred workflow, since filmic doesn't brighten as base curve does,
325 // we need an initial exposure boost. This might be too much in some cases but...
326 // (the preset name is used in develop.c)
327 dt_gui_presets_add_generic(_("scene-referred default"), self->op, self->version(),
328 &(dt_iop_exposure_params_t){.mode = EXPOSURE_MODE_MANUAL,
329 .black = -0.000244140625f,
330 .exposure = 0.7f,
331 .deflicker_percentile = 50.0f,
332 .deflicker_target_level = -4.0f,
333 .compensate_exposure_bias = TRUE},
334 sizeof(dt_iop_exposure_params_t), 1);
335
336 dt_gui_presets_update_ldr(_("scene-referred default"), self->op,
337 self->version(), FOR_RAW);
338}
339
340static void _deflicker_prepare_histogram(dt_iop_module_t *self, uint32_t **histogram,
341 dt_dev_histogram_stats_t *histogram_stats)
342{
343 const dt_image_t *img = dt_image_cache_get(self->dev->image_storage.id, 'r');
344 if(IS_NULL_PTR(img)) return;
345 dt_image_t image = *img;
347
348 if(image.dsc.channels != 1 || image.dsc.datatype != TYPE_UINT16) return;
349
352 DT_MIPMAP_BLOCKING, 'r');
353 if(IS_NULL_PTR(buf.buf))
354 {
355 dt_control_log(_("failed to get raw buffer from image `%s'"), image.filename);
357 return;
358 }
359
360 dt_dev_histogram_collection_params_t histogram_params = { 0 };
361
362 dt_histogram_roi_t histogram_roi = {.width = image.width,
363 .height = image.height,
364
365 // FIXME: get those from rawprepare IOP somehow !!!
366 .crop_x = image.crop_x,
367 .crop_y = image.crop_y,
368 .crop_width = image.crop_width,
369 .crop_height = image.crop_height };
370
371 histogram_params.roi = &histogram_roi;
372 histogram_params.bins_count = DEFLICKER_BINS_COUNT;
373
374 dt_histogram_worker(&histogram_params, histogram_stats, buf.buf, histogram,
376 histogram_stats->ch = 1u;
377
379}
380
381/* input: 0 - 65535 (valid range: from black level to white level) */
382/* output: -16 ... 0 */
383static double _raw_to_ev(uint32_t raw, uint32_t black_level, uint32_t white_level)
384{
385 const uint32_t raw_max = white_level - black_level;
386
387 // we are working on data without black clipping,
388 // so we can get values which are lower than the black level !!!
389 const int64_t raw_val = MAX((int64_t)raw - (int64_t)black_level, 1);
390
391 const double raw_ev = -log2(raw_max) + log2(raw_val);
392
393 return raw_ev;
394}
395
398 const uint32_t *const histogram,
399 const dt_dev_histogram_stats_t *const histogram_stats, float *correction)
400{
401 const dt_iop_exposure_params_t *const p = (const dt_iop_exposure_params_t *const)p1;
402
403 *correction = NAN;
404
405 if(IS_NULL_PTR(histogram)) return;
406
407 const size_t total = (size_t)histogram_stats->ch * histogram_stats->pixels;
408
409 const double thr
410 = CLAMP(((double)total * (double)p->deflicker_percentile / (double)100.0), 0.0, (double)total);
411
412 size_t n = 0;
413 uint32_t raw = 0;
414
415 for(uint32_t i = 0; i < histogram_stats->bins_count; i++)
416 {
417 for(uint32_t k = 0; k < histogram_stats->ch; k++)
418 n += histogram[4 * i + k];
419
420 if((double)n >= thr)
421 {
422 raw = i;
423 break;
424 }
425 }
426
427 const double ev
428 = _raw_to_ev(raw, (uint32_t)piece->dsc_in.rawprepare.raw_black_level,
429 piece->dsc_in.rawprepare.raw_white_point);
430
431 *correction = p->deflicker_target_level - ev;
432}
433
436{
439
440 d->black = d->params.black;
441 float exposure = d->params.exposure;
442
443 if(d->deflicker)
444 {
445 if(!IS_NULL_PTR(g))
446 {
447 // histogram is precomputed and cached
448 _compute_correction(self, &d->params, piece, g->deflicker_histogram, &g->deflicker_histogram_stats,
449 &exposure);
450 }
451 else
452 {
453 uint32_t *histogram = NULL;
454 dt_dev_histogram_stats_t histogram_stats;
455 _deflicker_prepare_histogram(self, &histogram, &histogram_stats);
456 _compute_correction(self, &d->params, piece, histogram, &histogram_stats, &exposure);
457 dt_free(histogram);
458 }
459
460 // second, show computed correction in UI.
462 {
464 g->deflicker_computed_exposure = exposure;
466 }
467 }
468
469 const float white = exposure2white(exposure);
470 d->scale = 1.0 / (white - d->black);
471}
472
473#ifdef HAVE_OPENCL
474int 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)
475{
476 const dt_iop_roi_t *const roi_in = &piece->roi_in;
479
480 cl_int err = -999;
481 const int devid = pipe->devid;
482 const int width = roi_in->width;
483 const int height = roi_in->height;
484
485 size_t sizes[] = { ROUNDUPDWD(width, devid), ROUNDUPDHT(height, devid), 1 };
486 dt_opencl_set_kernel_arg(devid, gd->kernel_exposure, 0, sizeof(cl_mem), (void *)&dev_in);
487 dt_opencl_set_kernel_arg(devid, gd->kernel_exposure, 1, sizeof(cl_mem), (void *)&dev_out);
488 dt_opencl_set_kernel_arg(devid, gd->kernel_exposure, 2, sizeof(int), (void *)&width);
489 dt_opencl_set_kernel_arg(devid, gd->kernel_exposure, 3, sizeof(int), (void *)&height);
490 dt_opencl_set_kernel_arg(devid, gd->kernel_exposure, 4, sizeof(float), (void *)&(d->black));
491 dt_opencl_set_kernel_arg(devid, gd->kernel_exposure, 5, sizeof(float), (void *)&(d->scale));
492 err = dt_opencl_enqueue_kernel_2d(devid, gd->kernel_exposure, sizes);
493 if(err != CL_SUCCESS) goto error;
494
495 return TRUE;
496
497error:
498 dt_print(DT_DEBUG_OPENCL, "[opencl_exposure] couldn't enqueue kernel! %d\n", err);
499 return FALSE;
500}
501#endif
502
504int process(struct dt_iop_module_t *self, const dt_dev_pixelpipe_t *pipe, const dt_dev_pixelpipe_iop_t *piece, const void *const i, void *const o)
505{
506 const dt_iop_roi_t *const roi_out = &piece->roi_out;
507 const dt_iop_exposure_data_t *const d = (const dt_iop_exposure_data_t *const)piece->data;
508
509 const int ch = piece->dsc_in.channels;
510
511 const float *const restrict in = (float*)i;
512 float *const restrict out = (float*)o;
513 const float black = d->black;
514 const float scale = d->scale;
515 const size_t npixels = (size_t)roi_out->width * roi_out->height;
516 const gboolean use_stream = (ch == 4);
517
518 if(ch == 4)
519 {
520 const dt_aligned_pixel_simd_t black_v = dt_simd_set1(black);
521 const dt_aligned_pixel_simd_t scale_v = dt_simd_set1(scale);
523 for(size_t k = 0; k < npixels; k++)
524 {
525 const size_t p = 4 * k;
526 const dt_aligned_pixel_simd_t in_v = dt_load_simd_aligned(in + p);
527 dt_store_simd_nontemporal(out + p, (in_v - black_v) * scale_v);
528 }
529 }
530 else
531 {
532 __OMP_PARALLEL_FOR_SIMD__(aligned(in, out : 64))
533 for(size_t k = 0; k < ch * npixels; k++)
534 {
535 out[k] = (in[k] - black) * scale;
536 }
537 }
538
539 if(use_stream) dt_omploop_sfence(); // ensure streamed RGB writes complete before alpha copy touches output
540
542 dt_iop_alpha_copy(i, o, roi_out->width, roi_out->height);
543
544 return 0;
545}
546
547static float _get_exposure_bias(const struct dt_iop_module_t *self)
548{
549 float bias = 0.0f;
550
551 // just check that pointers exist and are initialized
552 if(self->dev && self->dev->image_storage.exif_exposure_bias)
554
555 // sanity checks, don't trust exif tags too much
556 if(!isnan(bias))
557 return CLAMP(bias, -5.0f, 5.0f);
558 else
559 return 0.0f;
560}
561
562void 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)
563{
564 const dt_iop_roi_t *const roi_out = &piece->roi_out;
566 if(piece->dsc_in.channels != 4) return;
567
569 if(IS_NULL_PTR(input_profile)) return;
570
571 const float *const restrict in = (float*)i;
572 float average_luminance = 0.f;
573 const float norm = 1.f / (float)(roi_out->width * roi_out->height);
574
575 __OMP_PARALLEL_FOR__(reduction(+:average_luminance))
576 for(size_t k = 0; k < roi_out->width * roi_out->height * 4; k += 4)
577 {
578 // Convert each input RGB sample to XYZ to average scene luminance in a profile-aware way.
579 dt_aligned_pixel_t XYZ = { 0.f };
580 dt_ioppr_rgb_matrix_to_xyz(in + k, XYZ, input_profile->matrix_in_transposed, input_profile->lut_in,
581 input_profile->unbounded_coeffs_in, input_profile->lutsize,
582 input_profile->nonlinearlut);
583 average_luminance += XYZ[1] * norm;
584 }
585
586 // Solve the exposure module transfer so the average input luminance lands on middle grey at the output.
587 const float target_lightness = dt_conf_get_float("darkroom/modules/exposure/lightness");
588 const dt_aligned_pixel_t Lab = { target_lightness, 0.f, 0.f, 0.f };
589 dt_aligned_pixel_t XYZ = { 0.f };
591 const float target_luminance = XYZ[1];
592 const float white = p->black + fmaxf(average_luminance - p->black, 1e-6f) / target_luminance;
593 const float exposure = white2exposure(white);
594
595 p->exposure = exposure;
596
597 if(p->compensate_exposure_bias)
598 p->exposure += _get_exposure_bias(self);
599}
600
601
604{
607
608 d->params.black = p->black;
609 d->params.exposure = p->exposure;
610 d->params.deflicker_percentile = p->deflicker_percentile;
611 d->params.deflicker_target_level = p->deflicker_target_level;
612
613 // If exposure bias compensation has been required, add it on top of user exposure correction
614 if(p->compensate_exposure_bias)
615 d->params.exposure -= _get_exposure_bias(self);
616
617 d->deflicker = 0;
618
619 if(p->mode == EXPOSURE_MODE_DEFLICKER
621 && self->dev->image_storage.dsc.channels == 1
623 {
624 d->deflicker = 1;
625 }
626 dt_iop_fmt_log(self, "commit: class=%s needs_rawprepare=%d channels=%i datatype=%i mode=%d -> deflicker=%d",
630 p->mode, d->deflicker);
631
633 for(int k = 0; k < 3; k++) piece->dsc_out.processed_maximum[k] = piece->dsc_in.processed_maximum[k] * d->scale;
634}
635
641
643{
644 dt_free_align(piece->data);
645 piece->data = NULL;
646}
647
649{
651}
652
653void gui_update(struct dt_iop_module_t *self)
654{
657
659 || self->dev->image_storage.dsc.channels != 1
661 {
662 gtk_widget_set_sensitive(GTK_WIDGET(g->mode), FALSE);
663 }
664 else
665 {
666 gtk_widget_set_sensitive(GTK_WIDGET(g->mode), TRUE);
667 }
668
670
671 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(g->compensate_exposure_bias), p->compensate_exposure_bias);
672 /* xgettext:no-c-format */
673 gchar *label = g_strdup_printf(_("compensate camera exposure (%+.1f EV)"), _get_exposure_bias(self));
674 gtk_button_set_label(GTK_BUTTON(g->compensate_exposure_bias), label);
675 gtk_label_set_ellipsize(GTK_LABEL(gtk_bin_get_child(GTK_BIN(g->compensate_exposure_bias))), PANGO_ELLIPSIZE_MIDDLE);
676 dt_free(label);
677
678 g->spot_RGB[0] = 0.f;
679 g->spot_RGB[1] = 0.f;
680 g->spot_RGB[2] = 0.f;
681 g->spot_RGB[3] = 0.f;
682
683 // get the saved params
685
686 const float lightness = dt_conf_get_float("darkroom/modules/exposure/lightness");
687 dt_bauhaus_slider_set(g->lightness_spot, lightness);
688
690
691 dt_free(g->deflicker_histogram);
692
693 gtk_label_set_text(g->deflicker_used_EC, "");
695 g->deflicker_computed_exposure = NAN;
697
698 switch(p->mode)
699 {
701 _autoexp_disable(self);
702 gtk_stack_set_visible_child_name(GTK_STACK(g->mode_stack), "deflicker");
703 _deflicker_prepare_histogram(self, &g->deflicker_histogram, &g->deflicker_histogram_stats);
704 break;
706 default:
707 gtk_stack_set_visible_child_name(GTK_STACK(g->mode_stack), "manual");
708 break;
709 }
710
712}
713
715{
716 const int program = 2; // from programs.conf: basic.cl
719 module->data = gd;
720 gd->kernel_exposure = dt_opencl_create_kernel(program, "exposure");
721}
722
729
730static void _exposure_set_white(struct dt_iop_module_t *self, const float white)
731{
733
734 const float exposure = white2exposure(white);
735 if(p->exposure == exposure) return;
736
737 p->exposure = exposure;
738 if(p->black >= white) _exposure_set_black(self, white - 0.01);
739
741
743 dt_bauhaus_slider_set(g->exposure, p->exposure);
745 dt_dev_add_history_item(self->dev, self, TRUE, TRUE);
746}
747
748static void _exposure_set_black(struct dt_iop_module_t *self, const float black)
749{
751
752 if(p->black == black) return;
753
754 p->black = black;
755 if(p->black >= exposure2white(p->exposure))
756 {
757 _exposure_set_white(self, p->black + 0.01);
758 }
759
762 dt_bauhaus_slider_set(g->black, p->black);
764 dt_dev_add_history_item(self->dev, self, TRUE, TRUE);
765}
766
768{
771
772 // capture gui color picked event.
773 if(self->picked_color_max[0] < self->picked_color_min[0])
774 {
775 dt_print(DT_DEBUG_DEV, "[picker/exposure] rejected invalid min/max min=%g max=%g\n",
776 self->picked_color_min[0], self->picked_color_max[0]);
777 return;
778 }
779 const float *RGB = self->picked_color;
780 dt_print(DT_DEBUG_DEV, "[picker/exposure] RGB=(%g,%g,%g) pipe=%p\n",
781 RGB[0], RGB[1], RGB[2], (void *)pipe);
782
783 // Get the profile of whatever colorspace the picked sample actually lives in: exposure's
784 // iop-order is user-configurable, so its input can be raw camera RGB (before colorin) or
785 // already-managed RGB (if reordered after colorin) -- see dt_ioppr_get_pipe_current_profile_info.
787 if(IS_NULL_PTR(input_profile))
788 {
789 dt_print(DT_DEBUG_DEV, "[picker/exposure] missing input profile\n");
790 return;
791 }
792
793 // Convert to XYZ. Some profiles (e.g. camera input profiles with a parametric TRC, or a
794 // non-linear work profile) need their input curve applied before the matrix -- a bare
795 // dot_product against matrix_in would silently skip that curve and produce garbage XYZ
796 // for any such profile. dt_ioppr_rgb_matrix_to_xyz (same helper autoset() above uses) applies
797 // the curve first whenever profile_info->nonlinearlut says it's needed.
800 dt_ioppr_rgb_matrix_to_xyz(RGB, XYZ, input_profile->matrix_in_transposed, input_profile->lut_in,
801 input_profile->unbounded_coeffs_in, input_profile->lutsize,
802 input_profile->nonlinearlut);
804 Lab[1] = Lab[2] = 0.f; // make color grey to get only the equivalent lighness
806 dt_XYZ_to_sRGB(XYZ, g->spot_RGB);
807
808 // Convert to Lch for GUI feedback (input)
810 dt_Lab_2_LCH(Lab, Lch);
811
812 // Write report in GUI
814 gtk_label_set_text(GTK_LABEL(g->Lch_origin),
815 g_strdup_printf(_("L : \t%.1f %%"), Lch[0]));
816 gtk_widget_queue_draw(g->origin_spot);
818
819 const dt_spot_mode_t mode = dt_bauhaus_combobox_get(g->spot_mode);
820
821 if(mode == DT_SPOT_MODE_MEASURE)
822 {
823 // get the exposure setting
824 float expo = p->exposure;
825
826 // If the exposure bias compensation is on, we need to add it to the user param
827 if(p->compensate_exposure_bias)
828 expo -= _get_exposure_bias(self);
829
830 const float white = exposure2white(-expo);
831
832 // apply the exposure compensation
833 dt_aligned_pixel_t XYZ_out;
834 for(int c = 0; c < 3; c++)
835 XYZ_out[c] = XYZ[c] * white;
836
837 // Convert to Lab for GUI feedback
838 dt_aligned_pixel_t Lab_out;
839 dt_XYZ_to_Lab(XYZ_out, Lab_out);
840 Lab_out[1] = Lab_out[2] = 0.f; // make it grey
841
842 // Return the values in sliders
844 dt_bauhaus_slider_set(g->lightness_spot, Lab_out[0]);
845 _paint_hue(self);
847
848 dt_conf_set_float("darkroom/modules/exposure/lightness", Lab_out[0]);
849 }
850 else if(mode == DT_SPOT_MODE_CORRECT)
851 {
852 // Get the target color in XYZ space
853 dt_aligned_pixel_t Lch_target = { 0.f };
855 Lch_target[0] = dt_bauhaus_slider_get(g->lightness_spot);
857
858 dt_aligned_pixel_t Lab_target = { 0.f };
859 dt_LCH_2_Lab(Lch_target, Lab_target);
860
861 dt_aligned_pixel_t XYZ_target = { 0.f };
862 dt_Lab_to_XYZ(Lab_target, XYZ_target);
863
864 // Get the ratio
865 float white = XYZ[1] / XYZ_target[1];
866 float expo = -white2exposure(white);
867
868 // If the exposure bias compensation is on, we need to subtract it from the user param
869 if(p->compensate_exposure_bias)
870 expo -= _get_exposure_bias(self);
871
872 white = exposure2white(-expo);
873 _exposure_set_white(self, white);
874 }
875}
876
877
879{
880 (void)picker;
881 (void)piece;
882 if(dt_gui_widgets_suppressed()) return;
883 dt_print(DT_DEBUG_DEV, "[picker/exposure] apply picker=%p pipe=%p\n", (void *)picker, (void *)pipe);
885}
886
887
888void gui_changed(dt_iop_module_t *self, GtkWidget *w, void *previous)
889{
892
893 if(w == g->mode)
894 {
895 dt_free(g->deflicker_histogram);
896
897 /* The exposure picker lives on the "manual" stack page. Switching mode can hide
898 that page while the picker stays active, sampling/drawing on the image for a
899 control the user can no longer see or turn off from this widget. */
901
902 switch(p->mode)
903 {
905 _autoexp_disable(self);
907 || self->dev->image_storage.dsc.channels != 1
909 {
910 p->mode = EXPOSURE_MODE_MANUAL;
911 dt_bauhaus_combobox_set(g->mode, p->mode);
912 gtk_widget_set_sensitive(GTK_WIDGET(g->mode), FALSE);
913 break;
914 }
915 gtk_stack_set_visible_child_name(GTK_STACK(g->mode_stack), "deflicker");
916 _deflicker_prepare_histogram(self, &g->deflicker_histogram, &g->deflicker_histogram_stats);
917 break;
919 default:
920 gtk_stack_set_visible_child_name(GTK_STACK(g->mode_stack), "manual");
921 break;
922 }
923 }
924 else if(w == g->exposure)
925 {
926 const float white = exposure2white(p->exposure);
927 if(p->black >= white)
928 _exposure_set_black(self, white - 0.01);
929 }
930 else if(w == g->black)
931 {
932 const float white = exposure2white(p->exposure);
933 if(p->black >= white)
934 _exposure_set_white(self, p->black + 0.01);
935 }
936}
937
938
939static gboolean _draw(GtkWidget *widget, cairo_t *cr, dt_iop_module_t *self)
940{
941 if(dt_gui_widgets_suppressed()) return FALSE;
942
944
946 if(!isnan(g->deflicker_computed_exposure))
947 {
948 gchar *str = g_strdup_printf(_("%.2f EV"), g->deflicker_computed_exposure);
949
951 gtk_label_set_text(g->deflicker_used_EC, str);
953
954 dt_free(str);
955 }
957 return FALSE;
958}
959
960
961static gboolean _target_color_draw(GtkWidget *widget, cairo_t *crf, gpointer user_data)
962{
963 dt_iop_module_t *self = (dt_iop_module_t *)user_data;
965
966 // Init
967 GtkAllocation allocation;
968 gtk_widget_get_allocation(widget, &allocation);
969 int width = allocation.width, height = allocation.height;
970 cairo_surface_t *cst = dt_cairo_image_surface_create(CAIRO_FORMAT_ARGB32, width, height);
971 cairo_t *cr = cairo_create(cst);
972
973 // Margins
974 const float margin = 2. * DT_PIXEL_APPLY_DPI(1.5);
976 height -= 2 * margin;
977
978 // Paint target color
979 dt_aligned_pixel_t RGB = { 0 };
980 dt_aligned_pixel_t Lch = { 0 };
981 dt_aligned_pixel_t Lab = { 0 };
982 dt_aligned_pixel_t XYZ = { 0 };
983 Lch[0] = dt_bauhaus_slider_get(g->lightness_spot);
984 Lch[1] = 0.f;
985 Lch[2] = 0.f;
986 dt_LCH_2_Lab(Lch, Lab);
989
990 cairo_set_source_rgb(cr, RGB[0], RGB[1], RGB[2]);
991 cairo_rectangle(cr, INNER_PADDING, margin, width, height);
992 cairo_fill(cr);
993
994 // Clean
995 cairo_stroke(cr);
996 cairo_destroy(cr);
997 cairo_set_source_surface(crf, cst, 0, 0);
998 cairo_paint(crf);
999 cairo_surface_destroy(cst);
1000 return TRUE;
1001}
1002
1003
1004static gboolean _origin_color_draw(GtkWidget *widget, cairo_t *crf, gpointer user_data)
1005{
1006 dt_iop_module_t *self = (dt_iop_module_t *)user_data;
1008
1009 // Init
1010 GtkAllocation allocation;
1011 gtk_widget_get_allocation(widget, &allocation);
1012 int width = allocation.width;
1013 int height = allocation.height;
1014 cairo_surface_t *cst = dt_cairo_image_surface_create(CAIRO_FORMAT_ARGB32, width, height);
1015 cairo_t *cr = cairo_create(cst);
1016
1017 // Margins
1018 const float margin = 2. * DT_PIXEL_APPLY_DPI(1.5);
1020 height -= 2 * margin;
1021
1022 cairo_set_source_rgb(cr, g->spot_RGB[0], g->spot_RGB[1], g->spot_RGB[2]);
1023 cairo_rectangle(cr, INNER_PADDING, margin, width, height);
1024 cairo_fill(cr);
1025
1026 // Clean
1027 cairo_stroke(cr);
1028 cairo_destroy(cr);
1029 cairo_set_source_surface(crf, cst, 0, 0);
1030 cairo_paint(crf);
1031 cairo_surface_destroy(cst);
1032 return TRUE;
1033}
1034
1035static void _paint_hue(dt_iop_module_t *self)
1036{
1037 // update the fill background color of LCh sliders
1039
1040 const float lightness_min = dt_bauhaus_slider_get_hard_min(g->lightness_spot);
1041 const float lightness_max = dt_bauhaus_slider_get_hard_max(g->lightness_spot);
1042
1043 const float lightness_range = lightness_max - lightness_min;
1044
1045 for(int i = 0; i < DT_BAUHAUS_SLIDER_MAX_STOPS; i++)
1046 {
1047 const float stop = ((float)i / (float)(DT_BAUHAUS_SLIDER_MAX_STOPS - 1));
1048 const float x = lightness_min + stop * lightness_range;
1049 dt_aligned_pixel_t RGB = { 0 };
1050 const dt_aligned_pixel_t Lch = { x, 0.f, 0. };
1051 dt_aligned_pixel_t Lab = { 0 };
1052 dt_aligned_pixel_t XYZ = { 0 };
1053
1054 dt_LCH_2_Lab(Lch, Lab);
1057
1058 dt_bauhaus_slider_set_stop(g->lightness_spot, stop, RGB[0], RGB[1], RGB[2]);
1059 }
1060
1061 gtk_widget_queue_draw(g->lightness_spot);
1062 gtk_widget_queue_draw(g->target_spot);
1063}
1064
1065
1067{
1068 if(dt_gui_widgets_suppressed()) return;
1069
1071
1072 dt_aligned_pixel_t Lch_target = { 0.f };
1073
1074 Lch_target[0] = dt_bauhaus_slider_get(g->lightness_spot);
1075
1076 // Save the color on change
1077 dt_conf_set_float("darkroom/modules/exposure/lightness", Lch_target[0]);
1078
1080 _paint_hue(self);
1082
1083 // Re-run auto compute only for the module that currently owns the active picker.
1084 const dt_spot_mode_t mode = dt_bauhaus_combobox_get(g->spot_mode);
1085 const gboolean picker_active = dt_iop_color_picker_is_active_module(self);
1086 if(mode == DT_SPOT_MODE_CORRECT && picker_active)
1087 _auto_set_exposure(self, self->dev->pipe);
1088 // else : just record new values and do nothing
1089}
1090
1091void gui_reset(struct dt_iop_module_t *self)
1092{
1094}
1095
1096void gui_init(struct dt_iop_module_t *self)
1097{
1099
1100 g->deflicker_histogram = NULL;
1101
1102 // Start building top level widget
1103 // Note: stupid dt_bauhaus_toggle/combobox/slider_from_params read self directly
1104 // and are hard-coded to pack_start widgets to self->widget.
1105 // So we need to store the main widget first, stupidly update the ref to self->widget,
1106 // as we add boxes and restore it last to the main widget.
1107 // The guy who thought of that should really burn his computer and do everyone a favour.
1108 GtkWidget *main_widget = self->gui->widget = GTK_WIDGET(gtk_box_new(GTK_ORIENTATION_VERTICAL, DT_GUI_BOX_SPACING));
1109
1110 g->mode = dt_bauhaus_combobox_from_params(self, N_("mode"));
1111
1112 g->mode_stack = GTK_STACK(gtk_stack_new());
1113 gtk_stack_set_homogeneous(GTK_STACK(g->mode_stack),FALSE);
1114 gtk_box_pack_start(GTK_BOX(self->gui->widget), GTK_WIDGET(g->mode_stack), TRUE, TRUE, 0);
1115
1116 GtkWidget *vbox_manual = self->gui->widget = gtk_box_new(GTK_ORIENTATION_VERTICAL, DT_GUI_BOX_SPACING);
1117 gtk_stack_add_named(GTK_STACK(g->mode_stack), vbox_manual, "manual");
1118
1119 g->compensate_exposure_bias = dt_bauhaus_toggle_from_params(self, "compensate_exposure_bias");
1120 gtk_widget_set_tooltip_text(g->compensate_exposure_bias, _("automatically remove the camera exposure bias\n"
1121 "this is useful if you exposed the image to the right."));
1122
1123 g->exposure = dt_color_picker_new(self, DT_COLOR_PICKER_AREA,
1124 dt_bauhaus_slider_from_params(self, N_("exposure")));
1125 gtk_widget_set_tooltip_text(g->exposure, _("adjust the exposure correction"));
1126 dt_bauhaus_slider_set_digits(g->exposure, 3);
1127 dt_bauhaus_slider_set_format(g->exposure, _(" EV"));
1128 dt_bauhaus_slider_set_soft_range(g->exposure, -3.0, 4.0);
1129
1130 g->black = dt_bauhaus_slider_from_params(self, "black");
1131 gtk_widget_set_tooltip_text(g->black, _("adjust the black level to unclip negative RGB values.\n"
1132 "you should never use it to add more density in blacks!\n"
1133 "if poorly set, it will clip near-black colors out of gamut\n"
1134 "by pushing RGB values into negatives."));
1136 dt_bauhaus_slider_set_soft_range(g->black, -0.1, 0.1);
1137
1138 GtkWidget *vbox_deflicker = self->gui->widget = gtk_box_new(GTK_ORIENTATION_VERTICAL, DT_GUI_BOX_SPACING);
1139 gtk_stack_add_named(GTK_STACK(g->mode_stack), vbox_deflicker, "deflicker");
1140
1141 g->deflicker_percentile = dt_bauhaus_slider_from_params(self, "deflicker_percentile");
1142 dt_bauhaus_slider_set_format(g->deflicker_percentile, "%");
1143 gtk_widget_set_tooltip_text(g->deflicker_percentile,
1144 // xgettext:no-c-format
1145 _("where in the histogram to meter for deflicking. E.g. 50% is median"));
1146
1147 g->deflicker_target_level = dt_bauhaus_slider_from_params(self, "deflicker_target_level");
1148 dt_bauhaus_slider_set_format(g->deflicker_target_level, _(" EV"));
1149 gtk_widget_set_tooltip_text(g->deflicker_target_level,
1150 _("where to place the exposure level for processed pics, EV below overexposure."));
1151
1152 GtkBox *hbox1 = GTK_BOX(gtk_box_new(GTK_ORIENTATION_HORIZONTAL, DT_GUI_BOX_SPACING));
1153 gtk_box_pack_start(GTK_BOX(hbox1), GTK_WIDGET(dt_ui_label_new(_("computed EC: "))), FALSE, FALSE, 0);
1154 g->deflicker_used_EC = GTK_LABEL(dt_ui_label_new("")); // This gets filled in by process
1155 gtk_widget_set_tooltip_text(GTK_WIDGET(g->deflicker_used_EC), _("what exposure correction has actually been used"));
1156 gtk_box_pack_start(GTK_BOX(hbox1), GTK_WIDGET(g->deflicker_used_EC), FALSE, FALSE, 0);
1157
1159 g->deflicker_computed_exposure = NAN;
1161
1162 self->gui->widget = main_widget;
1163
1165 (&g->cs,
1166 "plugins/darkroom/exposure/mapping",
1167 _("spot exposure mapping"),
1168 GTK_BOX(self->gui->widget), GTK_PACK_END);
1169
1170 DT_BAUHAUS_COMBOBOX_NEW_FULL(dt_bauhaus_get_global(), g->spot_mode, DT_GUI_MODULE(self), N_("spot mode"),
1171 _("\"correction\" automatically adjust exposure\n"
1172 "such that the input lightness is mapped to the target.\n"
1173 "\"measure\" simply shows how an input color is mapped by the exposure compensation\n"
1174 "and can be used to define a target."),
1175 0, NULL, self,
1176 N_("correction"),
1177 N_("measure"));
1178 gtk_box_pack_start(GTK_BOX(g->cs.container), GTK_WIDGET(g->spot_mode), TRUE, TRUE, 0);
1179 g_signal_connect(G_OBJECT(g->spot_mode), "value-changed", G_CALLBACK(_spot_settings_changed_callback), self);
1180
1181 GtkWidget *hhbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, DT_GUI_BOX_SPACING);
1182 GtkWidget *vvbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, DT_GUI_BOX_SPACING);
1183
1184 gtk_box_pack_start(GTK_BOX(vvbox), dt_ui_section_label_new(_("input")), FALSE, FALSE, 0);
1185
1186 g->origin_spot = GTK_WIDGET(gtk_drawing_area_new());
1187 gtk_widget_set_size_request(g->origin_spot, 2 * DT_PIXEL_APPLY_DPI(dt_bauhaus_get_global()->quad_width),
1189 gtk_widget_set_tooltip_text(GTK_WIDGET(g->origin_spot),
1190 _("the input color that should be mapped to the target"));
1191
1192 g_signal_connect(G_OBJECT(g->origin_spot), "draw", G_CALLBACK(_origin_color_draw), self);
1193 gtk_box_pack_start(GTK_BOX(vvbox), g->origin_spot, TRUE, TRUE, 0);
1194
1195 g->Lch_origin = gtk_label_new(_("L : \tN/A"));
1196 gtk_widget_set_tooltip_text(GTK_WIDGET(g->Lch_origin),
1197 _("these LCh coordinates are computed from CIE Lab 1976 coordinates"));
1198 gtk_box_pack_start(GTK_BOX(vvbox), GTK_WIDGET(g->Lch_origin), FALSE, FALSE, 0);
1199
1200 gtk_box_pack_start(GTK_BOX(hhbox), GTK_WIDGET(vvbox), FALSE, FALSE, DT_BAUHAUS_SPACE);
1201
1202 vvbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, DT_GUI_BOX_SPACING);
1203
1204 gtk_box_pack_start(GTK_BOX(vvbox), dt_ui_section_label_new(_("target")), FALSE, TRUE, 0);
1205
1206 g->target_spot = GTK_WIDGET(gtk_drawing_area_new());
1207 gtk_widget_set_size_request(g->target_spot, 2 * DT_PIXEL_APPLY_DPI(dt_bauhaus_get_global()->quad_width),
1209 gtk_widget_set_tooltip_text(GTK_WIDGET(g->target_spot),
1210 _("the desired target exposure after mapping"));
1211
1212 g_signal_connect(G_OBJECT(g->target_spot), "draw", G_CALLBACK(_target_color_draw), self);
1213 gtk_box_pack_start(GTK_BOX(vvbox), g->target_spot, TRUE, TRUE, 0);
1214
1215 g->lightness_spot = dt_bauhaus_slider_new_with_range(dt_bauhaus_get_global(), DT_GUI_MODULE(self), 0., 100., 0, 0, 1);
1216 dt_bauhaus_widget_set_label(g->lightness_spot, N_("lightness"));
1217 dt_bauhaus_slider_set_format(g->lightness_spot, "%");
1218 dt_bauhaus_slider_set_default(g->lightness_spot, 50.f);
1219 gtk_box_pack_start(GTK_BOX(vvbox), GTK_WIDGET(g->lightness_spot), TRUE, TRUE, 0);
1220 g_signal_connect(G_OBJECT(g->lightness_spot), "value-changed", G_CALLBACK(_spot_settings_changed_callback), self);
1221
1222 gtk_box_pack_start(GTK_BOX(hhbox), GTK_WIDGET(vvbox), TRUE, TRUE, DT_BAUHAUS_SPACE);
1223
1224 gtk_box_pack_start(GTK_BOX(g->cs.container), GTK_WIDGET(hhbox), FALSE, FALSE, 0);
1225
1226 g_signal_connect(G_OBJECT(self->gui->widget), "draw", G_CALLBACK(_draw), self);
1227}
1228
1230{
1232
1233 dt_free(g->deflicker_histogram);
1234
1236}
1237
1238// clang-format off
1239// modelines: These editor modelines have been set for all relevant files by tools/update_modelines.py
1240// vim: shiftwidth=2 expandtab tabstop=2 cindent
1241// kate: tab-indents: off; indent-width 2; replace-tabs on; indent-mode cstyle; remove-trailing-spaces modified;
1242// clang-format on
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:3346
void dt_bauhaus_slider_set_default(GtkWidget *widget, float def)
Definition bauhaus.c:1491
void dt_bauhaus_slider_set_stop(GtkWidget *widget, float stop, float r, float g, float b)
Definition bauhaus.c:2164
float dt_bauhaus_slider_get(GtkWidget *widget)
Definition bauhaus.c:3283
int dt_bauhaus_combobox_get(GtkWidget *widget)
Definition bauhaus.c:2139
float dt_bauhaus_slider_get_hard_min(GtkWidget *widget)
Definition bauhaus.c:1425
void dt_bauhaus_slider_set(GtkWidget *widget, float pos)
Definition bauhaus.c:3334
void dt_bauhaus_combobox_set(GtkWidget *widget, const int pos)
Definition bauhaus.c:2093
void dt_bauhaus_widget_set_label(GtkWidget *widget, const char *label)
Definition bauhaus.c:1504
GtkWidget * dt_bauhaus_slider_new_with_range(dt_bauhaus_t *bh, dt_gui_module_t *self, float min, float max, float step, float defval, int digits)
Definition bauhaus.c:1635
void dt_bauhaus_slider_set_format(GtkWidget *widget, const char *format)
Definition bauhaus.c:3410
float dt_bauhaus_slider_get_hard_max(GtkWidget *widget)
Definition bauhaus.c:1450
#define DT_BAUHAUS_SPACE
Definition bauhaus.h:291
#define INTERNAL_PADDING
Definition bauhaus.h:78
#define DT_BAUHAUS_COMBOBOX_NEW_FULL(bauhaus, widget, action, label, tip, pos, callback, data,...)
Definition bauhaus.h:379
#define DT_BAUHAUS_SLIDER_MAX_STOPS
Definition bauhaus.h:73
#define INNER_PADDING
Definition bauhaus.h:81
typedef void((*dt_cache_allocate_t)(void *userdata, dt_cache_entry_t *entry))
dt_spot_mode_t
return vector dt_simd_set1(valid ?(scaling+NORM_MIN) :NORM_MIN)
void dt_gui_new_collapsible_section(dt_gui_collapsible_section_t *cs, const char *confname, const char *label, GtkBox *parent, GtkPackType pack)
Create a collapsible section and pack it into the parent box.
void dt_gui_update_collapsible_section(dt_gui_collapsible_section_t *cs)
@ IOP_CS_RGB
void dt_iop_color_picker_reset(dt_iop_module_t *module, gboolean keep)
gboolean dt_iop_color_picker_is_active_module(const dt_iop_module_t *module)
Tell whether one module currently owns the active darkroom picker.
GtkWidget * dt_color_picker_new(dt_iop_module_t *module, dt_iop_color_picker_kind_t kind, GtkWidget *w)
@ DT_COLOR_PICKER_AREA
static const float x
struct _GtkWidget GtkWidget
GtkWidget, opaque, spelled exactly as GTK spells it.
Definition colorspaces.h:98
dt_Lab_to_XYZ(Lab, XYZ)
dt_XYZ_to_sRGB(XYZ, result)
static dt_aligned_pixel_t XYZ
static dt_aligned_pixel_t Lab
const dt_colormatrix_t dt_aligned_pixel_t out
dt_XYZ_to_Lab(XYZ, Lab)
static dt_aligned_pixel_t RGB
static dt_aligned_pixel_t Lch
void dt_conf_set_float(const char *name, float val)
float dt_conf_get_float(const char *name)
Float for name, clamped to its declared bounds.
void dt_histogram_worker(dt_dev_histogram_collection_params_t *const histogram_params, dt_dev_histogram_stats_t *histogram_stats, const void *const pixel, uint32_t **histogram, const dt_worker Worker, const dt_iop_order_iccprofile_info_t *const profile_info)
void dt_histogram_helper_cs_RAW_uint16(const dt_dev_histogram_collection_params_t *const histogram_params, const void *pixel, uint32_t *histogram, int j, const dt_iop_order_iccprofile_info_t *const profile_info)
dt_image_pipe_class_t dt_image_pipe_class(const dt_image_t *img)
const char * dt_image_pipe_class_name(const dt_image_pipe_class_t klass)
gboolean dt_image_needs_rawprepare(const dt_image_t *img)
void dt_control_log(const char *msg,...)
Definition control.c:839
struct dt_bauhaus_t * dt_bauhaus_get_global(void)
Definition darktable.c:649
#define dt_dev_add_history_item(dev, module, enable, redraw)
void dt_iop_params_t
Definition dev_history.h:43
void default_output_format(dt_iop_module_t *self, dt_dev_pixelpipe_t *pipe, dt_dev_pixelpipe_iop_t *piece, dt_iop_buffer_dsc_t *dsc)
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)
gboolean dt_dev_pixelpipe_has_preview_output(const dt_develop_t *dev, const dt_dev_pixelpipe_t *pipe, const dt_iop_roi_t *roi)
Definition develop.c:405
@ DT_DEV_PIXELPIPE_DISPLAY_MASK
Definition develop.h:123
static float _get_exposure_bias(const struct dt_iop_module_t *self)
Definition exposure.c:547
void commit_params(struct dt_iop_module_t *self, dt_iop_params_t *p1, dt_dev_pixelpipe_t *pipe, dt_dev_pixelpipe_iop_t *piece)
Definition exposure.c:602
static gboolean _draw(GtkWidget *widget, cairo_t *cr, dt_iop_module_t *self)
Definition exposure.c:939
const char ** description(struct dt_iop_module_t *self)
Definition exposure.c:170
int default_group()
Definition exposure.c:181
static void _spot_settings_changed_callback(GtkWidget *slider, dt_iop_module_t *self)
Definition exposure.c:1066
static gboolean _origin_color_draw(GtkWidget *widget, cairo_t *crf, gpointer user_data)
Definition exposure.c:1004
#define DEFLICKER_BINS_COUNT
Definition exposure.c:114
static double _raw_to_ev(uint32_t raw, uint32_t black_level, uint32_t white_level)
Definition exposure.c:383
#define exposure2white(x)
Definition exposure.c:94
dt_spot_mode_t
Definition exposure.c:106
@ DT_SPOT_MODE_MEASURE
Definition exposure.c:108
@ DT_SPOT_MODE_CORRECT
Definition exposure.c:107
@ DT_SPOT_MODE_LAST
Definition exposure.c:109
static void _auto_set_exposure(dt_iop_module_t *self, dt_dev_pixelpipe_t *pipe)
Definition exposure.c:767
void init_pipe(struct dt_iop_module_t *self, dt_dev_pixelpipe_t *pipe, dt_dev_pixelpipe_iop_t *piece)
Definition exposure.c:636
const char * name()
Definition exposure.c:165
static void _exposure_set_black(struct dt_iop_module_t *self, const float black)
Definition exposure.c:748
void output_format(dt_iop_module_t *self, dt_dev_pixelpipe_t *pipe, dt_dev_pixelpipe_iop_t *piece, dt_iop_buffer_dsc_t *dsc)
Definition exposure.c:199
void gui_reset(struct dt_iop_module_t *self)
Definition exposure.c:1091
void gui_update(struct dt_iop_module_t *self)
Refresh GUI controls from current params and configuration.
Definition exposure.c:653
static void _exposure_set_white(struct dt_iop_module_t *self, const float white)
Definition exposure.c:730
void gui_init(struct dt_iop_module_t *self)
Definition exposure.c:1096
void gui_changed(dt_iop_module_t *self, GtkWidget *w, void *previous)
Definition exposure.c:888
static void _autoexp_disable(dt_iop_module_t *self)
Definition exposure.c:648
#define white2exposure(x)
Definition exposure.c:95
void cleanup_global(dt_iop_module_so_t *module)
Definition exposure.c:723
static void _compute_correction(dt_iop_module_t *self, dt_iop_params_t *p1, const dt_dev_pixelpipe_iop_t *piece, const uint32_t *const histogram, const dt_dev_histogram_stats_t *const histogram_stats, float *correction)
Definition exposure.c:396
int default_colorspace(dt_iop_module_t *self, dt_dev_pixelpipe_t *pipe, const dt_dev_pixelpipe_iop_t *piece)
Definition exposure.c:191
int flags()
Definition exposure.c:186
static void _process_common_setup(dt_iop_module_t *self, const dt_dev_pixelpipe_t *pipe, const dt_dev_pixelpipe_iop_t *piece)
Definition exposure.c:434
void gui_cleanup(struct dt_iop_module_t *self)
Definition exposure.c:1229
void init_presets(dt_iop_module_so_t *self)
Definition exposure.c:311
static void _deflicker_prepare_histogram(dt_iop_module_t *self, uint32_t **histogram, dt_dev_histogram_stats_t *histogram_stats)
Definition exposure.c:340
__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 i, void *const o)
Definition exposure.c:504
static void _paint_hue(dt_iop_module_t *self)
Definition exposure.c:1035
void cleanup_pipe(struct dt_iop_module_t *self, dt_dev_pixelpipe_t *pipe, dt_dev_pixelpipe_iop_t *piece)
Definition exposure.c:642
void init_global(dt_iop_module_so_t *module)
Definition exposure.c:714
dt_iop_exposure_mode_t
Definition exposure.c:100
@ EXPOSURE_MODE_MANUAL
Definition exposure.c:101
@ EXPOSURE_MODE_DEFLICKER
Definition exposure.c:102
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)
Definition exposure.c:562
int process_cl(struct dt_iop_module_t *self, const dt_dev_pixelpipe_t *pipe, const dt_dev_pixelpipe_iop_t *piece, cl_mem dev_in, cl_mem dev_out)
Definition exposure.c:474
static gboolean _target_color_draw(GtkWidget *widget, cairo_t *crf, gpointer user_data)
Definition exposure.c:961
void color_picker_apply(dt_iop_module_t *self, GtkWidget *picker, dt_dev_pixelpipe_t *pipe, dt_dev_pixelpipe_iop_t *piece)
Definition exposure.c:878
int legacy_params(dt_iop_module_t *self, const void *const old_params, const int old_version, void *new_params, const int new_version)
Definition exposure.c:208
@ TYPE_UINT16
Definition format.h:57
void dt_gui_presets_update_ldr(const char *name, dt_dev_operation_t op, const int32_t version, const int ldrflag)
void dt_gui_presets_add_generic(const char *name, dt_dev_operation_t op, const int32_t version, const void *params, const int32_t params_size, const int32_t enabled)
#define DT_GUI_MODULE(x)
@ FOR_RAW
dt_image_t * dt_image_cache_get(const int32_t imgid, char mode)
void dt_image_cache_read_release(const dt_image_t *img)
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:1909
void dt_iop_gui_leave_critical_section(dt_iop_module_t *const module)
Release what dt_iop_gui_enter_critical_section() took. Also a no-op headless.
#define dt_iop_fmt_log(module, fmt,...)
Debug helper to trace a module's input-format-driven decisions on the -d pipe channel (DT_DEBUG_PIPE)...
Definition imageop.h:535
@ IOP_FLAGS_SUPPORTS_BLENDING
Definition imageop.h:186
@ IOP_FLAGS_ALLOW_TILING
Definition imageop.h:188
void dt_iop_gui_enter_critical_section(dt_iop_module_t *const module)
Take the module's GUI lock, serialising access to its dt_iop_gui_data_t.
@ IOP_GROUP_TONES
Definition imageop.h:156
GtkWidget * dt_bauhaus_toggle_from_params(dt_iop_module_t *self, const char *param)
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
GtkWidget * dt_ui_section_label_new(const gchar *str)
Definition label.c:114
GtkWidget * dt_ui_label_new(const gchar *str)
Definition label.c:125
static const dt_dev_pixelpipe_t * pipe
Definition layers.h:49
static const dt_dev_pixelpipe_t const dt_dev_pixelpipe_iop_t * piece
Definition layers.h:50
@ DT_DEBUG_OPENCL
Definition logging.h:57
@ DT_DEBUG_DEV
Definition logging.h:53
void dt_print(dt_debug_thread_t thread, const char *msg,...) __attribute__((format(printf
Print to stdout when thread is enabled, prefixed with seconds since startup.
float *const restrict const size_t k
float *const restrict const size_t const size_t ch
#define IS_NULL_PTR(p)
C is way too permissive with !=, == and if(var) checks, which can mean too many things depending on w...
Definition macros.h:96
#define dt_free_align(ptr)
Release memory from dt_alloc_align() and set ptr to NULL.
Definition mem_alloc.h:214
static void * dt_calloc_align(size_t size)
dt_alloc_align() followed by a zero fill.
Definition mem_alloc.h:225
#define dt_free(ptr)
g_free() ptr and set it to NULL, skipping both if it is already NULL.
Definition mem_alloc.h:171
uint32_t width
Definition mipmap_cache.c:0
uint32_t height
Definition mipmap_cache.c:1
#define o
@ DT_MIPMAP_BLOCKING
@ DT_MIPMAP_FULL
#define dt_mipmap_cache_get(B, C, D, E, F)
#define dt_mipmap_cache_release(B)
#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:2612
int dt_opencl_create_kernel(const int prog, const char *name)
Definition opencl.c:2506
void dt_opencl_free_kernel(const int kernel)
Definition opencl.c:2549
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:2603
#define ROUNDUPDHT(a, b)
Definition opencl.h:86
#define ROUNDUPDWD(a, b)
Definition opencl.h:85
#define dt_omploop_sfence()
Definition openmp.h:164
#define __OMP_PARALLEL_FOR__(...)
Definition openmp.h:95
#define __OMP_PARALLEL_FOR_SIMD__(...)
Definition openmp.h:96
static cairo_surface_t * dt_cairo_image_surface_create(cairo_format_t format, int width, int height)
DT_ALIGNED_PIXEL float dt_aligned_pixel_t[4]
Definition simd.h:53
const struct dt_histogram_roi_t * roi
Definition pixelpipe.h:60
dt_image_t image_storage
Definition develop.h:225
struct dt_dev_pixelpipe_t * pipe
Definition develop.h:214
int32_t height
Definition image.h:406
float exif_exposure_bias
Definition image.h:377
int32_t crop_height
Definition image.h:407
int32_t width
Definition image.h:406
int32_t crop_y
Definition image.h:407
int32_t crop_x
Definition image.h:407
dt_iop_buffer_dsc_t dsc
Definition image.h:428
int32_t crop_width
Definition image.h:407
char filename[DT_MAX_FILENAME_LEN]
Definition image.h:395
int32_t id
Definition image.h:410
unsigned int channels
Definition format.h:83
dt_iop_buffer_type_t datatype
Definition format.h:85
dt_iop_exposure_params_t params
Definition exposure.c:153
GtkWidget * compensate_exposure_bias
Definition exposure.c:137
GtkWidget * deflicker_percentile
Definition exposure.c:132
dt_gui_collapsible_section_t cs
Definition exposure.c:145
dt_dev_histogram_stats_t deflicker_histogram_stats
Definition exposure.c:135
uint32_t * deflicker_histogram
Definition exposure.c:134
dt_aligned_pixel_t spot_RGB
Definition exposure.c:147
GtkWidget * deflicker_target_level
Definition exposure.c:133
dt_iop_exposure_mode_t mode
Definition exposure.c:118
gboolean compensate_exposure_bias
Definition exposure.c:123
GtkWidget * widget
Definition imageop_gui.h:47
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_iop_module_gui_t * gui
Definition imageop.h:346
struct dt_develop_t * dev
Definition imageop.h:311
dt_iop_global_data_t * global_data
Definition imageop.h:337
dt_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
A profile reduced to the arithmetic the pixel loop can run: two matrices and six tone-curve LUTs,...
int nonlinearlut
Non-zero when the profile has tone curves at all; tested as a boolean everywhere, but it is really th...
int lutsize
Entry count of each of the six LUTs. Always 65536 in practice: both callers of dt_ioppr_init_profile_...
float * lut_in[3]
Per-channel encoded -> linear tone curve, lutsize entries each, sampled over [0,1]....
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 MAX(a, b)
Definition thinplate.c:29
Telling the user something happened.
gboolean dt_gui_widgets_suppressed(void)
#define dt_gui_freeze_begin()
#define dt_gui_freeze_end()
#define DT_GUI_BOX_SPACING
#define DT_PIXEL_APPLY_DPI(value)