Ansel 0.0
A darktable fork - bloat + design vision
Loading...
Searching...
No Matches
basicadj.c
Go to the documentation of this file.
1/*
2 This file is part of darktable,
3 Copyright (C) 2019 Andreas Schneider.
4 Copyright (C) 2019-2020, 2023, 2025-2026 Aurélien PIERRE.
5 Copyright (C) 2019 Barna Keresztes.
6 Copyright (C) 2019-2020, 2022 Diederik Ter Rahe.
7 Copyright (C) 2019 Diederik ter Rahe.
8 Copyright (C) 2019 Edgardo Hoszowski.
9 Copyright (C) 2019 Heiko Bauke.
10 Copyright (C) 2019 luzpaz.
11 Copyright (C) 2019-2022 Pascal Obry.
12 Copyright (C) 2019 Philippe Weyland.
13 Copyright (C) 2019 Tobias Ellinghaus.
14 Copyright (C) 2020, 2022 Aldric Renaudin.
15 Copyright (C) 2020-2021 Chris Elston.
16 Copyright (C) 2020 coolcom200.
17 Copyright (C) 2020 Harold le Clément de Saint-Marcq.
18 Copyright (C) 2020 Hubert Kowalski.
19 Copyright (C) 2020-2021 Ralf Brown.
20 Copyright (C) 2021 Roman Lebedev.
21 Copyright (C) 2021 Victor Forsiuk.
22 Copyright (C) 2022 Hanno Schwalm.
23 Copyright (C) 2022 Martin Bařinka.
24 Copyright (C) 2022 Philipp Lutz.
25 Copyright (C) 2023-2024 Alynx Zhou.
26 Copyright (C) 2025-2026 Guillaume Stutin.
27
28 darktable is free software: you can redistribute it and/or modify
29 it under the terms of the GNU General Public License as published by
30 the Free Software Foundation, either version 3 of the License, or
31 (at your option) any later version.
32
33 darktable is distributed in the hope that it will be useful,
34 but WITHOUT ANY WARRANTY; without even the implied warranty of
35 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
36 GNU General Public License for more details.
37
38 You should have received a copy of the GNU General Public License
39 along with darktable. If not, see <http://www.gnu.org/licenses/>.
40*/
41
42/*
43 auto exposure is based on RawTherapee's Auto Levels
44*/
45
46#ifdef HAVE_CONFIG_H
47#include "config.h"
48#include "develop/iop_profile.h"
50#endif
51
52#include "widgets/bauhaus.h"
53#include "system/macros.h"
54#include "system/openmp.h"
56#include "system/mem_alloc.h"
58#include "control/control.h"
60#include "math/math.h"
61#include "pixel/rgb_norms.h"
62#include "develop/imageop.h"
63#include "develop/imageop_gui.h"
64
66#include "develop/tiling.h"
68#include "control/signal.h"
69
71
72#define exposure2white(x) exp2f(-(x))
73
75{
76 float black_point; /* $MIN: -1.0 $MAX: 1.0 $DEFAULT: 0.0
77 $DESCRIPTION:"black level correction" */
78 float exposure; // $MIN: -18.0 $MAX: 18.0 $DEFAULT: 0.0
79 float hlcompr; /* $MIN: 0 $MAX: 500.0 $DEFAULT: 0.0
80 $DESCRIPTION:"highlight compression" */
82 float contrast; // $MIN: -1.0 $MAX: 5.0 $DEFAULT: 0.0
83 dt_iop_rgb_norms_t preserve_colors; /* $DEFAULT: DT_RGB_NORM_LUMINANCE
84 $DESCRIPTION:"preserve colors" */
85 float middle_grey; // $MIN: 0.05 $MAX: 100 $DEFAULT: 18.42 $DESCRIPTION: "middle gray"
86 float brightness; // $MIN: -4.0 $MAX: 4.0 $DEFAULT: 0.0
87 float saturation; // $MIN: -1.0 $MAX: 1.0 $DEFAULT: 0.0
88 float vibrance; // $MIN: -1.0 $MAX: 1.0 $DEFAULT: 0.0
89 float clip; // $MIN: -1.0 $MAX: 1.0 $DEFAULT: 0.0
91
116
123
124int legacy_params(dt_iop_module_t *self, const void *const old_params, const int old_version, void *new_params,
125 const int new_version)
126{
127 if(old_version == 1 && new_version == 2)
128 {
129 typedef struct dt_iop_basicadj_params_v1_t
130 {
131 float black_point;
132 float exposure;
133 float hlcompr;
134 float hlcomprthresh;
135 float contrast;
136 int preserve_colors;
137 float middle_grey;
138 float brightness;
139 float saturation;
140 float clip;
141 } dt_iop_basicadj_params_v1_t;
142
143 const dt_iop_basicadj_params_v1_t *old = old_params;
144 dt_iop_basicadj_params_t *new = new_params;
145
146 new->black_point = old->black_point;
147 new->exposure = old->exposure;
148 new->hlcompr = old->hlcompr;
149 new->hlcomprthresh = old->hlcomprthresh;
150 new->contrast = old->contrast;
151 new->preserve_colors = old->preserve_colors;
152 new->middle_grey = old->middle_grey;
153 new->brightness = old->brightness;
154 new->saturation = old->saturation;
155 new->clip = old->clip;
156 new->vibrance = 0;
157 return 0;
158 }
159 return 1;
160}
161
162const char *deprecated_msg()
163{
164 return _("this module is deprecated. please use the quick access panel instead.");
165}
166
167const char *name()
168{
169 return _("basic adjustments");
170}
171
172const char **description(struct dt_iop_module_t *self)
173{
174 return dt_iop_set_description(self, _("apply usual image adjustments"),
175 _("creative"),
176 _("linear, RGB, scene-referred"),
177 _("non-linear, RGB"),
178 _("non-linear, RGB, scene-referred"));
179}
180
182{
183 return IOP_GROUP_EFFECTS;
184}
185
190
192{
193 return IOP_CS_RGB;
194}
195
197{
199 if(!IS_NULL_PTR(g))
200 {
201 g->button_down = g->draw_selected_region = 0;
202 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(g->bt_select_region), g->draw_selected_region);
203 }
204}
205
211
212void gui_changed(dt_iop_module_t *self, GtkWidget *w, void *previous)
213{
215}
216
218{
220}
221
222static void _auto_levels_callback(GtkButton *button, dt_iop_module_t *self)
223{
224 if(dt_gui_widgets_suppressed()) return;
225
227
229 if(self->gui->off)
230 {
231 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(self->gui->off), 1);
232 dt_dev_add_history_item(self->dev, self, TRUE, TRUE);
233 }
234
236
238 if(g->call_auto_exposure == 0)
239 {
240 g->box_cood[0] = g->box_cood[1] = g->box_cood[2] = g->box_cood[3] = 0.f;
241 g->call_auto_exposure = 1;
242 }
244
246}
247
248static void _select_region_toggled_callback(GtkToggleButton *togglebutton, dt_iop_module_t *self)
249{
250 if(dt_gui_widgets_suppressed()) return;
251
253
255 if(self->gui->off)
256 {
257 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(self->gui->off), 1);
258 dt_dev_add_history_item(self->dev, self, TRUE, TRUE);
259 }
260
262
264 if(gtk_toggle_button_get_active(togglebutton))
265 {
266 g->draw_selected_region = 1;
267 }
268 else
269 g->draw_selected_region = 0;
270
271 g->posx_from = g->posx_to = g->posy_from = g->posy_to = 0;
273}
274
275static void _develop_ui_pipe_finished_callback(gpointer instance, gpointer user_data)
276{
277 dt_iop_module_t *self = (dt_iop_module_t *)user_data;
280
281 if(IS_NULL_PTR(g)) return;
282
283 // FIXME: this doesn't seems the right place to update params and GUI ...
284 // update auto levels
286 if(g->call_auto_exposure == 2)
287 {
288 g->call_auto_exposure = -1;
290
291 memcpy(p, &g->params, sizeof(dt_iop_basicadj_params_t));
292
293 dt_dev_add_history_item(self->dev, self, TRUE, TRUE);
294
296 g->call_auto_exposure = 0;
298
300
301 gui_update(self);
302
304 }
305 else
306 {
308 }
309}
310
311static void _signal_profile_user_changed(gpointer instance, uint8_t profile_type, gpointer user_data)
312{
313 if(profile_type == DT_COLORSPACES_PROFILE_TYPE_WORK)
314 {
315 dt_iop_module_t *self = (dt_iop_module_t *)user_data;
316 if(!self->enabled) return;
317
320
321 const dt_iop_order_iccprofile_info_t *const work_profile
323 const float def_middle_grey
324 = (work_profile) ? (dt_ioppr_get_profile_info_middle_grey(work_profile) * 100.f) : 18.42f;
325
326 if(def->middle_grey != def_middle_grey)
327 {
328 def->middle_grey = def_middle_grey;
329
330 if(!IS_NULL_PTR(g))
331 {
333
334 dt_bauhaus_slider_set_default(g->sl_middle_grey, def_middle_grey);
335
337 }
338 }
339 }
340}
341
342int mouse_moved(struct dt_iop_module_t *self, double x, double y, double pressure, int which)
343{
344 int handled = 0;
346 if(!IS_NULL_PTR(g) && g->draw_selected_region && g->button_down && self->enabled)
347 {
348 float point[2] = { (float)x, (float)y };
351
352 g->posx_to = point[0];
353 g->posy_to = point[1];
354
356
357 handled = 1;
358 }
359
360 return handled;
361}
362
363int button_released(struct dt_iop_module_t *self, double x, double y, int which, uint32_t state)
364{
365 int handled = 0;
367 if(!IS_NULL_PTR(g) && g->draw_selected_region && self->enabled)
368 {
369 if(fabsf(g->posx_from - g->posx_to) > 1 && fabsf(g->posy_from - g->posy_to) > 1)
370 {
371 g->box_cood[0] = g->posx_from;
372 g->box_cood[1] = g->posy_from;
373 g->box_cood[2] = g->posx_to;
374 g->box_cood[3] = g->posy_to;
376
377 g->button_down = 0;
378 g->call_auto_exposure = 1;
379
381 }
382 else
383 g->button_down = 0;
384
385 handled = 1;
386 }
387
388 return handled;
389}
390
391int button_pressed(struct dt_iop_module_t *self, double x, double y, double pressure, int which, int type,
392 uint32_t state)
393{
394 int handled = 0;
396 if(!IS_NULL_PTR(g) && g->draw_selected_region && self->enabled)
397 {
398 if((which == 3) || (which == 1 && type == GDK_2BUTTON_PRESS))
399 {
401
402 handled = 1;
403 }
404 else if(which == 1)
405 {
406 float point[2] = { (float)x, (float)y };
409
410 g->posx_from = g->posx_to = point[0];
411 g->posy_from = g->posy_to = point[1];
412
413 g->button_down = 1;
414
415 handled = 1;
416 }
417 }
418
419 return handled;
420}
421
422void gui_post_expose(struct dt_iop_module_t *self, cairo_t *cr, int32_t width, int32_t height, int32_t pointerx,
423 int32_t pointery)
424{
426 if(IS_NULL_PTR(g) || !self->enabled) return;
427 if(!g->draw_selected_region || !g->button_down) return;
428 if(g->posx_from == g->posx_to && g->posy_from == g->posy_to) return;
429
430 dt_develop_t *dev = self->dev;
431 //const float wd = dt_dev_roi_request_preview_width(dev);
432 //const float ht = dt_dev_roi_request_preview_height(dev);
433 const float zoom_scale = dt_dev_get_overlay_scale(dev);
434
435 const float posx_from = fmin(g->posx_from, g->posx_to);
436 const float posx_to = fmax(g->posx_from, g->posx_to);
437 const float posy_from = fmin(g->posy_from, g->posy_to);
438 const float posy_to = fmax(g->posy_from, g->posy_to);
439
440 cairo_save(cr);
441 cairo_set_line_width(cr, 1.0 / zoom_scale);
442 cairo_set_source_rgb(cr, .2, .2, .2);
443
444 //cairo_translate(cr, width / 2.0, height / 2.0f);
445 //cairo_scale(cr, zoom_scale, zoom_scale);
446 //cairo_translate(cr, -.5f * wd - dt_dev_viewport_center_x(dev) * wd, -.5f * ht - dt_dev_viewport_center_y(dev) * ht);
448
449 cairo_set_line_cap(cr, CAIRO_LINE_CAP_ROUND);
450
451 cairo_rectangle(cr, posx_from, posy_from, (posx_to - posx_from), (posy_to - posy_from));
452 cairo_stroke(cr);
453 cairo_translate(cr, 1.0 / zoom_scale, 1.0 / zoom_scale);
454 cairo_set_source_rgb(cr, .8, .8, .8);
455 cairo_rectangle(cr, posx_from + 1.0 / zoom_scale, posy_from, (posx_to - posx_from) - 3. / zoom_scale,
456 (posy_to - posy_from) - 2. / zoom_scale);
457 cairo_stroke(cr);
458
459 cairo_restore(cr);
460}
461
463{
464 (void)picker;
465 (void)piece;
466 if(dt_gui_widgets_suppressed()) return;
469
470 const dt_iop_order_iccprofile_info_t *const work_profile = dt_ioppr_get_pipe_current_profile_info(self, pipe);
471 p->middle_grey = (work_profile) ? (dt_ioppr_get_rgb_matrix_luminance(self->picked_color,
472 work_profile->matrix_in,
473 work_profile->lut_in,
474 work_profile->unbounded_coeffs_in,
475 work_profile->lutsize,
476 work_profile->nonlinearlut) * 100.f)
478
480 dt_bauhaus_slider_set(g->sl_middle_grey, p->middle_grey);
482
483 dt_dev_add_history_item(self->dev, self, TRUE, TRUE);
484}
485
486static inline float get_gamma(const float x, const float gamma)
487{
488 return powf(x, gamma);
489}
490
491static inline float get_lut_gamma(const float x, const float gamma, const float *const lut)
492{
493 return (x > 1.f) ? get_gamma(x, gamma) : lut[CLAMP((int)(x * 0x10000ul), 0, 0xffff)];
494}
495
496static inline float get_contrast(const float x, const float contrast, const float middle_grey,
497 const float inv_middle_grey)
498{
499 return powf(x * inv_middle_grey, contrast) * middle_grey;
500}
501
502static inline float get_lut_contrast(const float x, const float contrast, const float middle_grey,
503 const float inv_middle_grey, const float *const lut)
504{
505 return (x > 1.f) ? get_contrast(x, contrast, middle_grey, inv_middle_grey)
506 : lut[CLAMP((int)(x * 0x10000ul), 0, 0xffff)];
507}
508
509void tiling_callback(struct dt_iop_module_t *self, const struct dt_dev_pixelpipe_t *pipe, const struct dt_dev_pixelpipe_iop_t *piece, struct dt_develop_tiling_t *tiling)
510{
511 (void)self;
512 (void)pipe;
513 (void)piece;
514 tiling->factor = 2.0f;
515 tiling->factor_cl = 3.0f;
516 tiling->maxbuf = 1.0f;
517 tiling->maxbuf_cl = 1.0f;
518 tiling->overhead = 0;
519 tiling->overlap = 0;
520 tiling->xalign = 1;
521 tiling->yalign = 1;
522}
525{
528
529 memcpy(&d->params, params, sizeof(dt_iop_basicadj_params_t));
530
531 const float brightness = p->brightness * 2.f;
532 const float gamma = (brightness >= 0.0f) ? 1.0f / (1.0f + brightness) : (1.0f - brightness);
533 const float contrast = p->contrast + 1.0f;
534 const float middle_grey = (p->middle_grey > 0.f) ? (p->middle_grey / 100.f) : 0.1842f;
535 const float inv_middle_grey = 1.f / middle_grey;
536
537 const int process_gamma = (p->brightness != 0.f);
538 const int plain_contrast = (!p->preserve_colors && p->contrast != 0.f);
539
540 // Building the lut for values in the [0,1] range
541 if(process_gamma || plain_contrast)
542 {
543 for(unsigned int i = 0; i < 0x10000; i++)
544 {
545 const float percentage = (float)i / (float)0x10000ul;
546 if(process_gamma) d->lut_gamma[i] = get_gamma(percentage, gamma);
547 if(plain_contrast) d->lut_contrast[i] = get_contrast(percentage, contrast, middle_grey, inv_middle_grey);
548 }
549 }
550}
551
553{
555 piece->data_size = sizeof(dt_iop_basicadj_data_t);
556}
557
559{
560 dt_free_align(piece->data);
561 piece->data = NULL;
562}
563
564void gui_update(struct dt_iop_module_t *self)
565{
567
568 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(g->bt_select_region), g->draw_selected_region);
569}
570
571void gui_focus(struct dt_iop_module_t *self, gboolean in)
572{
573 if(!in) _turn_select_region_off(self);
574}
575
577{
579
580 g->call_auto_exposure = 0;
581 g->draw_selected_region = 0;
582 g->posx_from = g->posx_to = g->posy_from = g->posy_to = 0.f;
583 g->box_cood[0] = g->box_cood[1] = g->box_cood[2] = g->box_cood[3] = 0.f;
584 g->button_down = 0;
585}
586
587void gui_init(struct dt_iop_module_t *self)
588{
590
591 change_image(self);
592
593 self->gui->widget = GTK_WIDGET(gtk_box_new(GTK_ORIENTATION_VERTICAL, DT_GUI_BOX_SPACING));
594
595 g->sl_black_point = dt_bauhaus_slider_from_params(self, "black_point");
596 dt_bauhaus_slider_set_soft_range(g->sl_black_point, -0.1, 0.1);
597 dt_bauhaus_slider_set_digits(g->sl_black_point, 4);
598 gtk_widget_set_tooltip_text(g->sl_black_point, _("adjust the black level to unclip negative RGB values.\n"
599 "you should never use it to add more density in blacks!\n"
600 "if poorly set, it will clip near-black colors out of gamut\n"
601 "by pushing RGB values into negatives"));
602
603 g->sl_exposure = dt_bauhaus_slider_from_params(self, N_("exposure"));
604 dt_bauhaus_slider_set_soft_range(g->sl_exposure, -4.0, 4.0);
605 dt_bauhaus_slider_set_format(g->sl_exposure, _(" EV"));
606 gtk_widget_set_tooltip_text(g->sl_exposure, _("adjust the exposure correction"));
607
608 g->sl_hlcompr = dt_bauhaus_slider_from_params(self, "hlcompr");
609 dt_bauhaus_slider_set_soft_max(g->sl_hlcompr, 100.0);
610 gtk_widget_set_tooltip_text(g->sl_hlcompr, _("highlight compression adjustment"));
611
612 g->sl_contrast = dt_bauhaus_slider_from_params(self, N_("contrast"));
613 dt_bauhaus_slider_set_soft_range(g->sl_contrast, -1.0, 1.0);
614 gtk_widget_set_tooltip_text(g->sl_contrast, _("contrast adjustment"));
615
616 g->cmb_preserve_colors = dt_bauhaus_combobox_from_params(self, "preserve_colors") ;
617 gtk_widget_set_tooltip_text(g->cmb_preserve_colors, _("method to preserve colors when applying contrast"));
618
619 g->sl_middle_grey = dt_color_picker_new(self, DT_COLOR_PICKER_AREA,
620 dt_bauhaus_slider_from_params(self, "middle_grey"));
621 dt_bauhaus_slider_set_format(g->sl_middle_grey, "%");
622 gtk_widget_set_tooltip_text(g->sl_middle_grey, _("middle gray adjustment"));
623 g_signal_connect(G_OBJECT(g->sl_middle_grey), "quad-pressed", G_CALLBACK(_color_picker_callback), self);
624
625 g->sl_brightness = dt_bauhaus_slider_from_params(self, N_("brightness"));
626 dt_bauhaus_slider_set_soft_range(g->sl_brightness, -1.0, 1.0);
627 gtk_widget_set_tooltip_text(g->sl_brightness,_("brightness adjustment"));
628
629 g->sl_saturation = dt_bauhaus_slider_from_params(self, N_("saturation"));
630 gtk_widget_set_tooltip_text(g->sl_saturation,_("saturation adjustment"));
631
632 g->sl_vibrance = dt_bauhaus_slider_from_params(self, N_("vibrance"));
633 gtk_widget_set_tooltip_text(g->sl_vibrance, _("vibrance adjustment"));
634
635 GtkWidget *autolevels_box = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, DT_GUI_BOX_SPACING);
636
637 g->bt_auto_levels = dt_action_button_new(NULL, N_("auto"), _auto_levels_callback, self, _("apply auto exposure based on the entire image"), 0, 0);
638 gtk_widget_set_size_request(g->bt_auto_levels, -1, DT_PIXEL_APPLY_DPI(24));
639 gtk_box_pack_start(GTK_BOX(autolevels_box), g->bt_auto_levels, TRUE, TRUE, 0);
640
641 g->bt_select_region = dtgtk_togglebutton_new(dtgtk_cairo_paint_colorpicker, 0, NULL);
642
643 gtk_widget_set_tooltip_text(g->bt_select_region,
644 _("apply auto exposure based on a region defined by the user\n"
645 "click and drag to draw the area\n"
646 "right click to cancel"));
647 g_signal_connect(G_OBJECT(g->bt_select_region), "toggled", G_CALLBACK(_select_region_toggled_callback), self);
648 gtk_box_pack_start(GTK_BOX(autolevels_box), g->bt_select_region, TRUE, TRUE, 0);
649
650 gtk_box_pack_start(GTK_BOX(self->gui->widget), autolevels_box, TRUE, TRUE, 0);
651
652 g->sl_clip = dt_bauhaus_slider_from_params(self, N_("clip"));
653 dt_bauhaus_slider_set_digits(g->sl_clip, 3);
654 gtk_widget_set_tooltip_text(g->sl_clip, _("adjusts clipping value for auto exposure calculation"));
655
656 // add signal handler for preview pipe finish
658 G_CALLBACK(_develop_ui_pipe_finished_callback), self);
659 // and profile change
661 G_CALLBACK(_signal_profile_user_changed), self);
662}
663
671
672static inline int64_t doubleToRawLongBits(double d)
673{
674 union {
675 double f;
676 int64_t i;
677 } tmp;
678 tmp.f = d;
679 return tmp.i;
680}
681
682static inline double longBitsToDouble(int64_t i)
683{
684 union {
685 double f;
686 int64_t i;
687 } tmp;
688 tmp.i = i;
689 return tmp.f;
690}
691
692static inline int ilogbp1(double d)
693{
694 const int m = d < 4.9090934652977266E-91;
695 d = m ? 2.037035976334486E90 * d : d;
696 int q = (doubleToRawLongBits(d) >> 52) & 0x7ff;
697 q = m ? q - (300 + 0x03fe) : q - 0x03fe;
698 return q;
699}
700
701// calculate x * 2^q
702static inline double ldexpk(double x, int32_t q)
703{
704 int32_t m = q < 0 ? -1 : 0;
705 m = (((m + q) >> 9) - m) << 7;
706 q = q - (m << 2);
707 double u = longBitsToDouble(((int64_t)(m + 0x3ff)) << 52);
708 double u2 = u * u;
709 u2 = u2 * u2;
710 x = x * u2;
711 u = longBitsToDouble(((int64_t)(q + 0x3ff)) << 52);
712 return x * u;
713}
714
715static inline double xlog(double d)
716{
717 const int e = ilogbp1(d * 0.7071);
718 const double m = ldexpk(d, -e);
719
720 double x = (m - 1) / (m + 1);
721 const double x2 = x * x;
722
723 double t = 0.148197055177935105296783;
724 t = fma(t, x2, 0.153108178020442575739679);
725 t = fma(t, x2, 0.181837339521549679055568);
726 t = fma(t, x2, 0.22222194152736701733275);
727 t = fma(t, x2, 0.285714288030134544449368);
728 t = fma(t, x2, 0.399999999989941956712869);
729 t = fma(t, x2, 0.666666666666685503450651);
730 t = fma(t, x2, 2);
731
732 x = x * t + 0.693147180559945286226764 * e;
733
734 if(!isfinite(d)) x = INFINITY;
735 if(d < 0) x = NAN;
736 if(d == 0) x = -INFINITY;
737
738 return x;
739}
740
741static inline double gamma2(double x)
742{
743 const double sRGBGammaCurve = 2.4;
744 return (x <= 0.00304) ? (x * 12.92) : (1.055 * exp(log(x) / sRGBGammaCurve) - 0.055);
745}
746
747static inline double igamma2(double x)
748{
749 const double sRGBGammaCurve = 2.4;
750 return (x <= 0.03928) ? (x / 12.92) : (exp(log((x + 0.055) / 1.055) * sRGBGammaCurve));
751}
752
754static int _get_auto_exp_histogram(const float *const img, const int width, const int height, int *box_area,
755 uint32_t **_histogram, unsigned int *_hist_size, int *_histcompr)
756{
757 int err = 0;
758 const int ch = 4;
759 const int histcompr = 3;
760 const unsigned int hist_size = 65536 >> histcompr;
761 uint32_t *histogram = NULL;
762 const float mul = hist_size;
763
765 sizeof(uint32_t) * hist_size,
766 0);
767 if(IS_NULL_PTR(histogram))
768 {
769 err = 1;
770 goto cleanup;
771 }
772
773 memset(histogram, 0, sizeof(uint32_t) * hist_size);
774
775 if(box_area[2] > box_area[0] && box_area[3] > box_area[1])
776 {
777 for(int y = box_area[1]; y <= box_area[3]; y++)
778 {
779 const float *const in = img + (size_t)ch * width * y;
780 for(int x = box_area[0]; x <= box_area[2]; x++)
781 {
782 const float *const pixel = in + x * ch;
783
784 for(int c = 0; c < 3; c++)
785 {
786 if(pixel[c] <= 0.f)
787 {
788 histogram[0]++;
789 }
790 else if(pixel[c] >= 1.f)
791 {
792 histogram[hist_size - 1]++;
793 }
794 else
795 {
796 const uint32_t R = (uint32_t)(pixel[c] * mul);
797 histogram[R]++;
798 }
799 }
800 }
801 }
802 }
803 else
804 {
805 for(int i = 0; i < width * height * ch; i += ch)
806 {
807 const float *const pixel = img + i;
808
809 for(int c = 0; c < 3; c++)
810 {
811 if(pixel[c] <= 0.f)
812 {
813 histogram[0]++;
814 }
815 else if(pixel[c] >= 1.f)
816 {
817 histogram[hist_size - 1]++;
818 }
819 else
820 {
821 const uint32_t R = (uint32_t)(pixel[c] * mul);
822 histogram[R]++;
823 }
824 }
825 }
826 }
827
828cleanup:
829 *_histogram = histogram;
830 *_hist_size = hist_size;
831 *_histcompr = histcompr;
832 return err;
833}
834
836static void _get_sum_and_average(const uint32_t *const histogram, const int hist_size, float *_sum, float *_avg)
837{
838 float sum = 0.f;
839 float avg = 0.f;
840
841 for(int i = 0; i < hist_size; i++)
842 {
843 float val = histogram[i];
844 sum += val;
845 avg += i * val;
846 }
847
848 avg /= sum;
849
850 *_sum = sum;
851 *_avg = avg;
852}
853
854static inline float hlcurve(const float level, const float hlcomp, const float hlrange)
855{
856 if(hlcomp > 0.0f)
857 {
858 float val = level + (hlrange - 1.f);
859
860 // to avoid division by zero
861 if(val == 0.0f)
862 {
863 val = 0.000001f;
864 }
865
866 float Y = val / hlrange;
867 Y *= hlcomp;
868
869 // to avoid log(<=0)
870 if(Y <= -1.0f)
871 {
872 Y = -.999999f;
873 }
874
875 float R = hlrange / (val * hlcomp);
876 return log1pf(Y) * R;
877 }
878 else
879 {
880 return 1.f;
881 }
882}
883
885static void _get_auto_exp(const uint32_t *const histogram, const unsigned int hist_size, const int histcompr,
886 const float defgain, const float clip, const float midgray, float *_expcomp,
887 float *_bright, float *_contr, float *_black, float *_hlcompr, float *_hlcomprthresh)
888{
889 float expcomp = 0.f;
890 float black = 0.f;
891 float bright = 0.f;
892 float contr = 0.f;
893 float hlcompr = 0.f;
894 float hlcomprthresh = 0.f;
895
896 float scale = 65536.0f;
897
898 const int imax = 65536 >> histcompr;
899 int overex = 0;
900 float sum = 0.f, hisum = 0.f, losum = 0.f;
901 float ave = 0.f;
902
903 // find average luminance
904 _get_sum_and_average(histogram, hist_size, &sum, &ave);
905
906 // find median of luminance
907 int median = 0, count = histogram[0];
908
909 while(count < sum / 2)
910 {
911 median++;
912 count += histogram[median];
913 }
914
915 if(median == 0 || ave < 1.f) // probably the image is a blackframe
916 {
917 expcomp = 0.f;
918 black = 0.f;
919 bright = 0.f;
920 contr = 0.f;
921 hlcompr = 0.f;
922 hlcomprthresh = 0.f;
923 goto cleanup;
924 }
925
926 // compute std dev on the high and low side of median
927 // and octiles of histogram
928 float octile[8] = { 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f }, ospread = 0.f;
929 count = 0;
930
931 int i = 0;
932
933 for(; i < MIN((int)ave, imax); i++)
934 {
935 if(count < 8)
936 {
937 octile[count] += histogram[i];
938
939 if(octile[count] > sum / 8.f || (count == 7 && octile[count] > sum / 16.f))
940 {
941 octile[count] = xlog(1. + (float)i) / log(2.f);
942 count++;
943 }
944 }
945
946 losum += histogram[i];
947 }
948
949 for(; i < imax; i++)
950 {
951 if(count < 8)
952 {
953 octile[count] += histogram[i];
954
955 if(octile[count] > sum / 8.f || (count == 7 && octile[count] > sum / 16.f))
956 {
957 octile[count] = xlog(1.f + (float)i) / log(2.f);
958 count++;
959 }
960 }
961
962 hisum += histogram[i];
963 }
964
965 // probably the image is a blackframe
966 if(losum == 0.f || hisum == 0.f)
967 {
968 expcomp = 0.f;
969 black = 0.f;
970 bright = 0.f;
971 contr = 0.f;
972 hlcompr = 0.f;
973 hlcomprthresh = 0.f;
974 goto cleanup;
975 }
976
977 // if very overxposed image
978 if(octile[6] > log1pf((float)imax) / log2(2.f)) //*** Is this correct? log2(2) == 1
979 {
980 octile[6] = 1.5f * octile[5] - 0.5f * octile[4];
981 overex = 2;
982 }
983
984 // if overexposed
985 if(octile[7] > log1pf((float)imax) / log2(2.f)) //*** Is this correct? log2(2) == 1
986 {
987 octile[7] = 1.5f * octile[6] - 0.5f * octile[5];
988 overex = 1;
989 }
990
991 // store values of octile[6] and octile[7] for calculation of exposure compensation
992 // if we don't do this and the pixture is underexposed, calculation of exposure compensation assumes
993 // that it's overexposed and calculates the wrong direction
994 float oct6, oct7;
995 oct6 = octile[6];
996 oct7 = octile[7];
997
998 for(int ii = 1; ii < 8; ii++)
999 {
1000 if(octile[ii] == 0.0f)
1001 {
1002 octile[ii] = octile[ii - 1];
1003 }
1004 }
1005
1006 // compute weighted average separation of octiles
1007 // for future use in contrast setting
1008 for(int ii = 1; ii < 6; ii++)
1009 {
1010 ospread += (octile[ii + 1] - octile[ii])
1011 / MAX(0.5f, (ii > 2 ? (octile[ii + 1] - octile[3]) : (octile[3] - octile[ii])));
1012 }
1013
1014 ospread /= 5.f;
1015
1016 // probably the image is a blackframe
1017 if(ospread <= 0.f)
1018 {
1019 expcomp = 0.f;
1020 black = 0.f;
1021 bright = 0.f;
1022 contr = 0.f;
1023 hlcompr = 0.f;
1024 hlcomprthresh = 0.f;
1025 goto cleanup;
1026 }
1027
1028 // compute clipping points based on the original histograms (linear, without exp comp.)
1029 unsigned int clipped = 0;
1030 int rawmax = (imax)-1;
1031
1032 while(histogram[rawmax] + clipped <= 0 && rawmax > 1)
1033 {
1034 clipped += histogram[rawmax];
1035 rawmax--;
1036 }
1037
1038 // compute clipped white point
1039 unsigned int clippable = (int)(sum * clip);
1040 clipped = 0;
1041 int whiteclip = (imax)-1;
1042
1043 while(whiteclip > 1 && (histogram[whiteclip] + clipped) <= clippable)
1044 {
1045 clipped += histogram[whiteclip];
1046 whiteclip--;
1047 }
1048
1049 // compute clipped black point
1050 clipped = 0;
1051 int shc = 0;
1052
1053 while(shc < whiteclip - 1 && histogram[shc] + clipped <= clippable)
1054 {
1055 clipped += histogram[shc];
1056 shc++;
1057 }
1058
1059 // rescale to 65535 max
1060 rawmax <<= histcompr;
1061 whiteclip <<= histcompr;
1062 ave = ave * (1 << histcompr);
1063 median <<= histcompr;
1064 shc <<= histcompr;
1065
1066 // compute exposure compensation as geometric mean of the amount that
1067 // sets the mean or median at middle gray, and the amount that sets the estimated top
1068 // of the histogram at or near clipping.
1069 const float expcomp1 = (logf(midgray * scale / (ave - shc + midgray * shc))) / DT_M_LN2f;
1070 float expcomp2;
1071
1072 if(overex == 0) // image is not overexposed
1073 {
1074 expcomp2 = 0.5f * ((15.5f - histcompr - (2.f * oct7 - oct6)) + logf(scale / rawmax) / DT_M_LN2f);
1075 }
1076 else
1077 {
1078 expcomp2 = 0.5f * ((15.5f - histcompr - (2.f * octile[7] - octile[6])) + logf(scale / rawmax) / DT_M_LN2f);
1079 }
1080
1081 if(fabsf(expcomp1) - fabsf(expcomp2) > 1.f) // for great expcomp
1082 {
1083 expcomp = (expcomp1 * fabsf(expcomp2) + expcomp2 * fabsf(expcomp1)) / (fabsf(expcomp1) + fabsf(expcomp2));
1084 }
1085 else
1086 {
1087 expcomp = 0.5 * (double)expcomp1 + 0.5 * (double)expcomp2; // for small expcomp
1088 }
1089
1090 const float gain = expf(expcomp * DT_M_LN2f);
1091
1092 const float corr = sqrtf(gain * scale / rawmax);
1093 black = shc * corr;
1094
1095 // now tune hlcompr to bring back rawmax to 65535
1096 hlcomprthresh = 0.f;
1097 // this is a series approximation of the actual formula for comp,
1098 // which is a transcendental equation
1099 const float comp = (gain * ((float)whiteclip) / scale - 1.f) * 2.3f; // 2.3 instead of 2 to increase slightly comp
1100 hlcompr = (comp / (fmaxf(0.0f, expcomp) + 1.0f));
1101 hlcompr = fmaxf(0.f, fminf(100.f, hlcompr));
1102
1103 // now find brightness if gain didn't bring ave to midgray using
1104 // the envelope of the actual 'control cage' brightness curve for simplicity
1105 const float midtmp = gain * sqrtf(median * ave) / scale;
1106
1107 if(midtmp < 0.1f)
1108 {
1109 bright = (midgray - midtmp) * 15.0f / (midtmp);
1110 }
1111 else
1112 {
1113 bright = (midgray - midtmp) * 15.0f / (0.10833 - 0.0833f * midtmp);
1114 }
1115
1116 bright = 0.25f * MAX(0.f, bright);
1117
1118 // compute contrast that spreads the average spacing of octiles
1119 contr = (midgray * 100.f) * (1.1f - ospread);
1120 contr = MAX(0.f, MIN(100.f, contr));
1121 // take gamma into account
1122 double whiteclipg = gamma2(whiteclip * corr);
1123
1124 float gavg = 0.f;
1125
1126 float val = 0.f;
1127 const float increment = corr * (1 << histcompr);
1128
1129 for(int ii = 0; ii<65536>> histcompr; ii++)
1130 {
1131 // gavg += histogram[ii] * _get_LUTf(gamma2curve, gamma2curve_size, val);
1132 gavg += histogram[ii] * gamma2(val);
1133 val += increment;
1134 }
1135
1136 gavg /= sum;
1137
1138 if(black < gavg)
1139 {
1140 const int maxwhiteclip = (gavg - black) * 4 / 3
1141 + black; // don't let whiteclip be so large that the histogram average goes above 3/4
1142
1143 if(whiteclipg < maxwhiteclip)
1144 {
1145 whiteclipg = maxwhiteclip;
1146 }
1147 }
1148
1149 whiteclipg
1150 = igamma2(whiteclipg); // need to inverse gamma transform to get correct exposure compensation parameter
1151
1152 // correction with gamma
1153 black = (black / whiteclipg);
1154
1155 expcomp = CLAMP(expcomp, -5.0f, 12.0f);
1156
1157 bright = MAX(-100.f, MIN(bright, 100.f));
1158
1159cleanup:
1160 black /= 100.f;
1161 bright /= 100.f;
1162 contr /= 100.f;
1163
1164 if(isnan(expcomp))
1165 {
1166 expcomp = 0.f;
1167 fprintf(stderr, "[_get_auto_exp] expcomp is NaN!!!\n");
1168 }
1169 if(isnan(black))
1170 {
1171 black = 0.f;
1172 fprintf(stderr, "[_get_auto_exp] black is NaN!!!\n");
1173 }
1174 if(isnan(bright))
1175 {
1176 bright = 0.f;
1177 fprintf(stderr, "[_get_auto_exp] bright is NaN!!!\n");
1178 }
1179 if(isnan(contr))
1180 {
1181 contr = 0.f;
1182 fprintf(stderr, "[_get_auto_exp] contr is NaN!!!\n");
1183 }
1184 if(isnan(hlcompr))
1185 {
1186 hlcompr = 0.f;
1187 fprintf(stderr, "[_get_auto_exp] hlcompr is NaN!!!\n");
1188 }
1189 if(isnan(hlcomprthresh))
1190 {
1191 hlcomprthresh = 0.f;
1192 fprintf(stderr, "[_get_auto_exp] hlcomprthresh is NaN!!!\n");
1193 }
1194
1195 *_expcomp = expcomp;
1196 *_black = black;
1197 *_bright = bright;
1198 *_contr = contr;
1199 *_hlcompr = hlcompr;
1200 *_hlcomprthresh = hlcomprthresh;
1201}
1202
1203static inline __attribute__((always_inline)) int _auto_exposure(const float *const img, const int width, const int height, int *box_area,
1204 const float clip, const float midgray, float *_expcomp, float *_bright, float *_contr,
1205 float *_black, float *_hlcompr, float *_hlcomprthresh)
1206{
1207 uint32_t *histogram = NULL;
1208 unsigned int hist_size = 0;
1209 int histcompr = 0;
1210
1211 const float defGain = 0.0f;
1212
1213 if(_get_auto_exp_histogram(img, width, height, box_area, &histogram, &hist_size, &histcompr))
1214 {
1216 return 1;
1217 }
1218 _get_auto_exp(histogram, hist_size, histcompr, defGain, clip, midgray, _expcomp, _bright, _contr, _black,
1219 _hlcompr, _hlcomprthresh);
1220
1222 return 0;
1223}
1224
1226static void _get_selected_area(struct dt_iop_module_t *self, const dt_dev_pixelpipe_t *pipe,
1227 const dt_dev_pixelpipe_iop_t *piece,
1228 dt_iop_basicadj_gui_data_t *g, const dt_iop_roi_t *const roi_in, int *box_out)
1229{
1230 box_out[0] = box_out[1] = box_out[2] = box_out[3] = 0;
1231
1232 if(!IS_NULL_PTR(g))
1233 {
1234 const int width = roi_in->width;
1235 const int height = roi_in->height;
1236 dt_boundingbox_t box_cood = { g->box_cood[0], g->box_cood[1], g->box_cood[2], g->box_cood[3] };
1237
1238 box_cood[0] *= pipe->iwidth;
1239 box_cood[1] *= pipe->iheight;
1240 box_cood[2] *= pipe->iwidth;
1241 box_cood[3] *= pipe->iheight;
1242
1244 box_cood, 2);
1245
1246 box_cood[0] *= roi_in->scale;
1247 box_cood[1] *= roi_in->scale;
1248 box_cood[2] *= roi_in->scale;
1249 box_cood[3] *= roi_in->scale;
1250
1251 box_cood[0] -= roi_in->x;
1252 box_cood[1] -= roi_in->y;
1253 box_cood[2] -= roi_in->x;
1254 box_cood[3] -= roi_in->y;
1255
1256 int box[4];
1257
1258 // re-order edges of bounding box
1259 box[0] = fminf(box_cood[0], box_cood[2]);
1260 box[1] = fminf(box_cood[1], box_cood[3]);
1261 box[2] = fmaxf(box_cood[0], box_cood[2]);
1262 box[3] = fmaxf(box_cood[1], box_cood[3]);
1263
1264 // do not continue if box is completely outside of roi
1265 if(!(box[0] >= width || box[1] >= height || box[2] < 0 || box[3] < 0))
1266 {
1267 // clamp bounding box to roi
1268 for(int k = 0; k < 4; k += 2) box[k] = MIN(width - 1, MAX(0, box[k]));
1269 for(int k = 1; k < 4; k += 2) box[k] = MIN(height - 1, MAX(0, box[k]));
1270
1271 // safety check: area needs to have minimum 1 pixel width and height
1272 if(!(box[2] - box[0] < 1 || box[3] - box[1] < 1))
1273 {
1274 box_out[0] = box[0];
1275 box_out[1] = box[1];
1276 box_out[2] = box[2];
1277 box_out[3] = box[3];
1278 }
1279 }
1280 }
1281}
1282
1284int process(struct dt_iop_module_t *self, const dt_dev_pixelpipe_t *pipe, const dt_dev_pixelpipe_iop_t *piece, const void *const ivoid,
1285 void *const ovoid)
1286{
1287 const dt_iop_roi_t *const roi_in = &piece->roi_in;
1288 const dt_iop_roi_t *const roi_out = &piece->roi_out;
1289 const dt_iop_order_iccprofile_info_t *const work_profile = dt_ioppr_get_iop_work_profile_info(self, self->dev->iop);
1290
1291 const int ch = piece->dsc_in.channels;
1295
1296 // process auto levels
1297 if(!IS_NULL_PTR(g) && dt_dev_pixelpipe_has_preview_output(self->dev, pipe, roi_out))
1298 {
1300 if(g->call_auto_exposure == 1 && !dt_gui_widgets_suppressed())
1301 {
1302 g->call_auto_exposure = -1;
1304
1305 memcpy(&g->params, p, sizeof(dt_iop_basicadj_params_t));
1306
1307 int box[4] = { 0 };
1308 _get_selected_area(self, pipe, piece, g, roi_in, box);
1309 if(_auto_exposure((const float *const)ivoid, roi_in->width, roi_in->height, box, g->params.clip,
1310 g->params.middle_grey / 100.f, &g->params.exposure, &g->params.brightness,
1311 &g->params.contrast, &g->params.black_point, &g->params.hlcompr, &g->params.hlcomprthresh))
1312 {
1313 return 1;
1314 }
1315
1317 g->call_auto_exposure = 2;
1319 }
1320 else
1321 {
1323 }
1324 }
1325
1326 const float black_point = p->black_point;
1327 const float hlcompr = p->hlcompr;
1328 const float hlcomprthresh = p->hlcomprthresh;
1329 const float saturation = p->saturation + 1.0f;
1330 const float vibrance = p->vibrance / 1.4f;
1331 const float contrast = p->contrast + 1.0f;
1332 const float white = exposure2white(p->exposure);
1333 const float scale = 1.0f / (white - p->black_point);
1334 const float middle_grey = (p->middle_grey > 0.f) ? (p->middle_grey / 100.f) : 0.1842f;
1335 const float inv_middle_grey = 1.f / middle_grey;
1336 const float brightness = p->brightness * 2.f;
1337 const float gamma = (brightness >= 0.0f) ? 1.0f / (1.0f + brightness) : (1.0f - brightness);
1338
1339 const float hlcomp = hlcompr / 100.0f;
1340 const float shoulder = ((hlcomprthresh / 100.f) / 8.0f) + 0.1f;
1341 const float hlrange = 1.0f - shoulder;
1342
1343 const int plain_contrast = (!p->preserve_colors && p->contrast != 0.f);
1344 const int preserve_colors = (p->contrast != 0.f) ? p->preserve_colors : 0;
1345 const int process_gamma = (p->brightness != 0.f);
1346 const int process_saturation_vibrance = (p->saturation != 0.f)||(p->vibrance != 0.f);
1347 const int process_hlcompr = (p->hlcompr > 0.f);
1348
1349 const float *const in = (const float *const)ivoid;
1350 float *const out = (float *const)ovoid;
1351 const size_t stride = (size_t)roi_out->height * roi_out->width * ch;
1353 for(size_t k = 0; k < stride; k += ch)
1354 {
1355 for(size_t c = 0; c < 3; c++)
1356 {
1357 // exposure
1358 out[k + c] = (in[k + c] - black_point) * scale;
1359 }
1360
1361 // highlight compression
1362 if(process_hlcompr)
1363 {
1364 const float lum = (work_profile) ? dt_ioppr_get_rgb_matrix_luminance(out + k,
1365 work_profile->matrix_in,
1366 work_profile->lut_in,
1367 work_profile->unbounded_coeffs_in,
1368 work_profile->lutsize,
1369 work_profile->nonlinearlut)
1371 if(lum > 0.f)
1372 {
1373 const float ratio = hlcurve(lum, hlcomp, hlrange);
1374
1375 for(size_t c = 0; c < 3; c++)
1376 {
1377 out[k + c] = (ratio * out[k + c]);
1378 }
1379 }
1380 }
1381
1382 for(size_t c = 0; c < 3; c++)
1383 {
1384 // gamma
1385 if(process_gamma && out[k + c] > 0.f) out[k + c] = get_lut_gamma(out[k + c], gamma, d->lut_gamma);
1386
1387 // contrast
1388 if(plain_contrast && out[k + c] > 0.f)
1389 out[k + c] = get_lut_contrast(out[k + c], contrast, middle_grey, inv_middle_grey, d->lut_contrast);
1390 }
1391
1392 // contrast (with preserve colors)
1393 if(preserve_colors != DT_RGB_NORM_NONE)
1394 {
1395 float ratio = 1.f;
1396 const float lum = dt_rgb_norm(out + k, preserve_colors, work_profile);
1397 if(lum > 0.f)
1398 {
1399 const float contrast_lum = powf(lum * inv_middle_grey, contrast) * middle_grey;
1400 ratio = contrast_lum / lum;
1401 }
1402
1403 for(size_t c = 0; c < 3; c++)
1404 {
1405 out[k + c] = (ratio * out[k + c]);
1406 }
1407 }
1408
1409 // saturation
1410 if(process_saturation_vibrance)
1411 {
1412 const float average = (out[k] + out[k+1] + out[k+2]) / 3;
1413 const float delta = sqrtf( (average-out[k])*(average-out[k])+(average-out[k+1])*(average-out[k+1])+(average-out[k+2])*(average-out[k+2]));
1414 const float P = vibrance * (1 - powf(delta, fabsf(vibrance)));
1415
1416 for(size_t c = 0; c < 3; c++)
1417 {
1418 out[k + c] = average + (saturation + P) * (out[k+c] - average);
1419 }
1420 }
1421
1422 out[k + 3] = in[k + 3];
1423 }
1424 return 0;
1425}
1426
1427#undef exposure2white
1428
1429// clang-format off
1430// modelines: These editor modelines have been set for all relevant files by tools/update_modelines.py
1431// vim: shiftwidth=2 expandtab tabstop=2 cindent
1432// kate: tab-indents: off; indent-width 2; replace-tabs on; indent-mode cstyle; remove-trailing-spaces modified;
1433// clang-format on
#define TRUE
Definition ashift_lsd.c:162
void cleanup(dt_imageio_module_format_t *self)
Definition avif.c:170
#define m
Definition basecurve.c:283
static float get_lut_gamma(const float x, const float gamma, const float *const lut)
Definition basicadj.c:491
const char ** description(struct dt_iop_module_t *self)
Definition basicadj.c:172
int default_group()
Definition basicadj.c:181
__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)
Definition basicadj.c:1284
static __DT_CLONE_TARGETS__ void _get_sum_and_average(const uint32_t *const histogram, const int hist_size, float *_sum, float *_avg)
Definition basicadj.c:836
static __DT_CLONE_TARGETS__ void _get_auto_exp(const uint32_t *const histogram, const unsigned int hist_size, const int histcompr, const float defgain, const float clip, const float midgray, float *_expcomp, float *_bright, float *_contr, float *_black, float *_hlcompr, float *_hlcomprthresh)
Definition basicadj.c:885
static void _auto_levels_callback(GtkButton *button, dt_iop_module_t *self)
Definition basicadj.c:222
static double longBitsToDouble(int64_t i)
Definition basicadj.c:682
static float get_gamma(const float x, const float gamma)
Definition basicadj.c:486
static int ilogbp1(double d)
Definition basicadj.c:692
void commit_params(struct dt_iop_module_t *self, dt_iop_params_t *params, dt_dev_pixelpipe_t *pipe, dt_dev_pixelpipe_iop_t *piece)
Definition basicadj.c:523
#define exposure2white(x)
Definition basicadj.c:72
void gui_focus(struct dt_iop_module_t *self, gboolean in)
Definition basicadj.c:571
static float get_contrast(const float x, const float contrast, const float middle_grey, const float inv_middle_grey)
Definition basicadj.c:496
void init_pipe(struct dt_iop_module_t *self, dt_dev_pixelpipe_t *pipe, dt_dev_pixelpipe_iop_t *piece)
Definition basicadj.c:552
static void _signal_profile_user_changed(gpointer instance, uint8_t profile_type, gpointer user_data)
Definition basicadj.c:311
const char * name()
Definition basicadj.c:167
static double xlog(double d)
Definition basicadj.c:715
void gui_update(struct dt_iop_module_t *self)
Definition basicadj.c:564
static float get_lut_contrast(const float x, const float contrast, const float middle_grey, const float inv_middle_grey, const float *const lut)
Definition basicadj.c:502
void change_image(struct dt_iop_module_t *self)
Definition basicadj.c:576
static void _select_region_toggled_callback(GtkToggleButton *togglebutton, dt_iop_module_t *self)
Definition basicadj.c:248
void gui_init(struct dt_iop_module_t *self)
Definition basicadj.c:587
int button_pressed(struct dt_iop_module_t *self, double x, double y, double pressure, int which, int type, uint32_t state)
Definition basicadj.c:391
void gui_changed(dt_iop_module_t *self, GtkWidget *w, void *previous)
Definition basicadj.c:212
static __DT_CLONE_TARGETS__ int _get_auto_exp_histogram(const float *const img, const int width, const int height, int *box_area, uint32_t **_histogram, unsigned int *_hist_size, int *_histcompr)
Definition basicadj.c:754
static double igamma2(double x)
Definition basicadj.c:747
int button_released(struct dt_iop_module_t *self, double x, double y, int which, uint32_t state)
Definition basicadj.c:363
static void _turn_selregion_picker_off(struct dt_iop_module_t *self)
Definition basicadj.c:206
void tiling_callback(struct dt_iop_module_t *self, const struct dt_dev_pixelpipe_t *pipe, const struct dt_dev_pixelpipe_iop_t *piece, struct dt_develop_tiling_t *tiling)
Definition basicadj.c:509
static __DT_CLONE_TARGETS__ void _get_selected_area(struct dt_iop_module_t *self, const dt_dev_pixelpipe_t *pipe, const dt_dev_pixelpipe_iop_t *piece, dt_iop_basicadj_gui_data_t *g, const dt_iop_roi_t *const roi_in, int *box_out)
Definition basicadj.c:1226
int default_colorspace(dt_iop_module_t *self, dt_dev_pixelpipe_t *pipe, const dt_dev_pixelpipe_iop_t *piece)
Definition basicadj.c:191
static int64_t doubleToRawLongBits(double d)
Definition basicadj.c:672
static double gamma2(double x)
Definition basicadj.c:741
int flags()
Definition basicadj.c:186
void gui_post_expose(struct dt_iop_module_t *self, cairo_t *cr, int32_t width, int32_t height, int32_t pointerx, int32_t pointery)
Definition basicadj.c:422
static void _turn_select_region_off(struct dt_iop_module_t *self)
Definition basicadj.c:196
void gui_cleanup(struct dt_iop_module_t *self)
Definition basicadj.c:664
static void _develop_ui_pipe_finished_callback(gpointer instance, gpointer user_data)
Definition basicadj.c:275
static void _color_picker_callback(GtkWidget *button, dt_iop_module_t *self)
Definition basicadj.c:217
void cleanup_pipe(struct dt_iop_module_t *self, dt_dev_pixelpipe_t *pipe, dt_dev_pixelpipe_iop_t *piece)
Definition basicadj.c:558
const char * deprecated_msg()
Definition basicadj.c:162
int mouse_moved(struct dt_iop_module_t *self, double x, double y, double pressure, int which)
Definition basicadj.c:342
static double ldexpk(double x, int32_t q)
Definition basicadj.c:702
void color_picker_apply(dt_iop_module_t *self, GtkWidget *picker, dt_dev_pixelpipe_t *pipe, dt_dev_pixelpipe_iop_t *piece)
Definition basicadj.c:462
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 basicadj.c:124
static float hlcurve(const float level, const float hlcomp, const float hlrange)
Definition basicadj.c:854
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_default(GtkWidget *widget, float def)
Definition bauhaus.c:1491
void dt_bauhaus_slider_set_soft_max(GtkWidget *widget, float val)
Definition bauhaus.c:1474
void dt_bauhaus_slider_set(GtkWidget *widget, float pos)
Definition bauhaus.c:3331
void dt_bauhaus_slider_set_format(GtkWidget *widget, const char *format)
Definition bauhaus.c:3407
typedef void((*dt_cache_allocate_t)(void *userdata, dt_cache_entry_t *entry))
static float dt_camera_rgb_luminance(const float4 rgb)
@ IOP_CS_RGB
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
static const float x
const float f
const float *const lut
const int t
struct _GtkWidget GtkWidget
GtkWidget, opaque, spelled exactly as GTK spells it.
Definition colorspaces.h:98
const dt_colormatrix_t dt_aligned_pixel_t out
const float delta
#define P(V, params)
void dt_control_queue_redraw_center()
Request a redraw of the centre view.
Definition control.c:924
static float dt_rgb_norm(const float4 in, const int norm, const int work_profile, constant dt_colorspaces_iccprofile_info_cl_t *profile_info, read_only image2d_t lut)
@ DT_RGB_NORM_NONE
#define dt_dev_add_history_item(dev, module, enable, redraw)
void dt_iop_params_t
Definition dev_history.h:43
#define dt_dev_pixelpipe_update_history_all(dev)
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)
__DT_CLONE_TARGETS__ dt_iop_order_iccprofile_info_t * dt_ioppr_get_iop_work_profile_info(struct dt_iop_module_t *module, GList *iop_list)
int dt_dev_distort_transform_plus(const dt_dev_pixelpipe_t *pipe, const double iop_order, const int transf_direction, float *points, size_t points_count)
Definition develop.c:1797
float dt_dev_get_overlay_scale(dt_develop_t *dev)
Get the overlay scale factor in GUI logical coordinates.
Definition develop.c:1959
void dt_dev_coordinates_image_norm_to_preview_abs(dt_develop_t *dev, float *points, size_t num_points)
Definition develop.c:1291
gboolean dt_dev_rescale_roi(dt_develop_t *dev, cairo_t *cr, int32_t width, int32_t height)
Scale the ROI to fit within given width/height, centered.
Definition develop.c:2071
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:402
void dt_dev_coordinates_widget_to_image_norm(dt_develop_t *dev, float *points, size_t num_points)
Coordinate conversion helpers between widget, normalized image, and absolute image spaces.
Definition develop.c:1144
void dt_dev_coordinates_image_abs_to_raw_norm(dt_develop_t *dev, float *points, size_t num_points)
Definition develop.c:1285
@ DT_DEV_TRANSFORM_DIR_BACK_INCL
Definition develop.h:110
float dt_boundingbox_t[4]
Definition image.h:83
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
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.
void dt_iop_request_focus(dt_iop_module_t *module)
Move darkroom focus to module, or clear it with NULL.
@ IOP_FLAGS_DEPRECATED
Definition imageop.h:187
@ 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_EFFECTS
Definition imageop.h:161
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_action_button_new(dt_lib_module_t *self, const gchar *label, gpointer callback, gpointer data, const gchar *tooltip, guint accel_key, GdkModifierType mods)
Definition lib.c:1336
_lib_location_type_t type
Definition location.c:1
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 DT_M_LN2f
Definition math.h:57
#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
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.
#define median(a, n)
#define __OMP_PARALLEL_FOR__(...)
Definition openmp.h:95
#define dt_pixelpipe_cache_alloc_align_cache(size, id)
#define dt_pixelpipe_cache_free_align(mem)
@ DT_COLORSPACES_PROFILE_TYPE_WORK
#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_CONTROL_PROFILE_USER_CHANGED
This signal is raised when a profile is changed by the user 1 uint32_t : the profile type that has ch...
Definition signal.h:245
@ DT_SIGNAL_DEVELOP_PREVIEW_PIPE_FINISHED
This signal is raised when develop preview pipe process is finished no param, no returned value.
Definition signal.h:177
#define DT_DEBUG_CONTROL_SIGNAL_CONNECT(ctlsig, signal, cb, user_data)
Definition signal.h:396
float dt_aligned_pixel_simd_t __attribute__((vector_size(16), aligned(16)))
Apply one channel's tone curve to each of the three colour channels, or pass the channel through unto...
Definition simd.h:55
const float uint32_t state[4]
const float u2
dt_iop_buffer_dsc_t dsc_in
struct dt_iop_module_t *void * data
GList * iop
Definition develop.h:269
float lut_contrast[0x10000]
Definition basicadj.c:121
dt_iop_basicadj_params_t params
Definition basicadj.c:119
float lut_gamma[0x10000]
Definition basicadj.c:120
GtkWidget * cmb_preserve_colors
Definition basicadj.c:109
dt_boundingbox_t box_cood
Definition basicadj.c:99
dt_iop_basicadj_params_t params
Definition basicadj.c:94
GtkWidget * bt_select_region
Definition basicadj.c:103
dt_iop_rgb_norms_t preserve_colors
Definition basicadj.c:83
unsigned int channels
Definition format.h:83
GtkWidget * widget
Definition imageop_gui.h:47
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
gboolean enabled
Definition imageop.h:313
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]....
dt_colormatrix_t matrix_in
RGB -> XYZ (D50), row-major. matrix_in[1][*] is the luminance row. NaN in [0][0] marks the whole prof...
Region of interest passed through the pixelpipe.
Definition format.h:49
double scale
Definition format.h:51
int width
Definition format.h:50
int height
Definition format.h:50
GQueue * log
Definition supervisor.c:121
#define __DT_CLONE_TARGETS__
typedef double((*spd)(unsigned long int wavelength, double TempK))
#define MIN(a, b)
Definition thinplate.c:32
#define MAX(a, b)
Definition thinplate.c:29
GtkWidget * dtgtk_togglebutton_new(DTGTKCairoPaintIconFunc paint, gint paintflags, void *paintdata)
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)
void dtgtk_cairo_paint_colorpicker(cairo_t *cr, gint x, gint y, gint w, gint h, gint flags, void *data)