Ansel 0.0
A darktable fork - bloat + design vision
Loading...
Searching...
No Matches
rgblevels.c
Go to the documentation of this file.
1/*
2 This file is part of darktable,
3 Copyright (C) 2019-2021 Diederik Ter Rahe.
4 Copyright (C) 2019 Edgardo Hoszowski.
5 Copyright (C) 2019 Jacques Le Clerc.
6 Copyright (C) 2019-2022 Pascal Obry.
7 Copyright (C) 2019 Tobias Ellinghaus.
8 Copyright (C) 2020, 2022 Aldric Renaudin.
9 Copyright (C) 2020-2021 Chris Elston.
10 Copyright (C) 2020-2021 Dan Torop.
11 Copyright (C) 2020 Hubert Kowalski.
12 Copyright (C) 2020 Marco.
13 Copyright (C) 2020-2021 Ralf Brown.
14 Copyright (C) 2021 lhietal.
15 Copyright (C) 2022-2026 Aurélien PIERRE.
16 Copyright (C) 2022 Hanno Schwalm.
17 Copyright (C) 2022 Martin Bařinka.
18 Copyright (C) 2022 Philipp Lutz.
19 Copyright (C) 2022 Victor Forsiuk.
20 Copyright (C) 2023 Alynx Zhou.
21
22 darktable is free software: you can redistribute it and/or modify
23 it under the terms of the GNU General Public License as published by
24 the Free Software Foundation, either version 3 of the License, or
25 (at your option) any later version.
26
27 darktable is distributed in the hope that it will be useful,
28 but WITHOUT ANY WARRANTY; without even the implied warranty of
29 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
30 GNU General Public License for more details.
31
32 You should have received a copy of the GNU General Public License
33 along with darktable. If not, see <http://www.gnu.org/licenses/>.
34*/
35#ifdef HAVE_CONFIG_H
36#include "config.h"
37#endif
38
39#include "common/iop_profile.h"
40#include "bauhaus/bauhaus.h"
42#include "common/rgb_norms.h"
43#include "control/control.h"
44#include "develop/imageop.h"
45#include "develop/imageop_gui.h"
46#include "dtgtk/drawingarea.h"
47
49#include "gui/draw.h"
50#include "gui/gtk.h"
51#include "libs/colorpicker.h"
52
53#define DT_GUI_CURVE_EDITOR_INSET DT_PIXEL_APPLY_DPI(5)
54
56
57#define RGBLEVELS_MIN 0.f
58#define RGBLEVELS_MID 0.5f
59#define RGBLEVELS_MAX 1.f
60
68
70{
71 DT_IOP_RGBLEVELS_LINKED_CHANNELS = 0, // $DESCRIPTION: "RGB, linked channels"
72 DT_IOP_RGBLEVELS_INDEPENDENT_CHANNELS = 1 // $DESCRIPTION: "RGB, independent channels"
74
76{
77 dt_iop_rgblevels_autoscale_t autoscale; // $DEFAULT: DT_IOP_RGBLEVELS_LINKED_CHANNELS $DESCRIPTION: "mode" (DT_IOP_RGBLEVELS_INDEPENDENT_CHANNELS, DT_IOP_RGBLEVELS_LINKED_CHANNELS)
78 dt_iop_rgb_norms_t preserve_colors; // $DEFAULT: DT_RGB_NORM_LUMINANCE $DESCRIPTION: "preserve colors"
81
83{
85
86 GtkWidget *cmb_autoscale; // (DT_IOP_RGBLEVELS_INDEPENDENT_CHANNELS, DT_IOP_RGBLEVELS_LINKED_CHANNELS)
87 GtkDrawingArea *area;
89 GtkNotebook *channel_tabs;
92
93 int call_auto_levels; // should we calculate levels automatically?
94 int draw_selected_region; // are we drawing the selected region?
95 float posx_from, posx_to, posy_from, posy_to; // coordinates of the area
96 dt_boundingbox_t box_cood; // normalized coordinates
97 int button_down; // user pressed the mouse button?
98
106
113
114const char *name()
115{
116 return _("levels");
117}
118
120{
121 return IOP_GROUP_COLOR;
122}
123
128
130{
131 return IOP_CS_RGB;
132}
133
134const char **description(struct dt_iop_module_t *self)
135{
136 return dt_iop_set_description(self, _("adjust black, white and mid-gray points in RGB color space"),
137 _("corrective and creative"),
138 _("linear, RGB, display-referred"),
139 _("non-linear, RGB"),
140 _("non-linear, RGB, display-referred"));
141}
142
144{
146 if(!IS_NULL_PTR(g))
147 {
148 g->button_down = g->draw_selected_region = 0;
149 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(g->bt_select_region), g->draw_selected_region);
150 }
151}
152
158
159static void _develop_ui_pipe_finished_callback(gpointer instance, dt_iop_module_t *self)
160{
163
164 if(IS_NULL_PTR(g)) return;
165
166 // FIXME: this doesn't seems the right place to update params and GUI ...
167 // update auto levels
169 if(g->call_auto_levels == 2)
170 {
171 g->call_auto_levels = -1;
172
174
175 memcpy(p, &g->params, sizeof(dt_iop_rgblevels_params_t));
176
178
180 g->call_auto_levels = 0;
182
184
185 gui_update(self);
186
188 }
189 else
190 {
192 }
193}
194
195static void _compute_lut(const dt_dev_pixelpipe_iop_t *piece)
196{
198
199 // Building the lut for values in the [0,1] range
200 if(d->params.autoscale == DT_IOP_RGBLEVELS_LINKED_CHANNELS)
201 {
202 const int c = 0;
203 const float delta = (d->params.levels[c][2] - d->params.levels[c][0]) / 2.0f;
204 const float mid = d->params.levels[c][0] + delta;
205 const float tmp = (d->params.levels[c][1] - mid) / delta;
206 d->inv_gamma[0] = d->inv_gamma[1] = d->inv_gamma[2] = pow(10, tmp);
207
208 for(unsigned int i = 0; i < 0x10000; i++)
209 {
210 float percentage = (float)i / (float)0x10000ul;
211 d->lut[0][i] = d->lut[1][i] = d->lut[2][i] = pow(percentage, d->inv_gamma[c]);
212 }
213 }
214 else
215 {
216 for(int c = 0; c < 3; c++)
217 {
218 const float delta = (d->params.levels[c][2] - d->params.levels[c][0]) / 2.0f;
219 const float mid = d->params.levels[c][0] + delta;
220 const float tmp = (d->params.levels[c][1] - mid) / delta;
221 d->inv_gamma[c] = pow(10, tmp);
222
223 for(unsigned int i = 0; i < 0x10000; i++)
224 {
225 float percentage = (float)i / (float)0x10000ul;
226 d->lut[c][i] = pow(percentage, d->inv_gamma[c]);
227 }
228 }
229 }
230}
231
233{
234 switch(p->autoscale)
235 {
237 gtk_notebook_set_show_tabs(g->channel_tabs, TRUE);
238 break;
240 gtk_notebook_set_show_tabs(g->channel_tabs, FALSE);
241 break;
242 }
243
244 if(p->autoscale == DT_IOP_RGBLEVELS_LINKED_CHANNELS)
245 gtk_widget_set_visible(g->cmb_preserve_colors, TRUE);
246 else
247 gtk_widget_set_visible(g->cmb_preserve_colors, FALSE);
248}
249
250static gboolean _area_leave_notify_callback(GtkWidget *widget, GdkEventCrossing *event, dt_iop_module_t *self)
251{
253 c->mouse_x = c->mouse_y = -1.0;
254 gtk_widget_queue_draw(widget);
255 return TRUE;
256}
257
258static gboolean _area_draw_callback(GtkWidget *widget, cairo_t *crf, dt_iop_module_t *self)
259{
262
263 const int inset = DT_GUI_CURVE_EDITOR_INSET;
264 GtkAllocation allocation;
265 gtk_widget_get_allocation(GTK_WIDGET(c->area), &allocation);
266 int width = allocation.width, height = allocation.height;
267 cairo_surface_t *cst = dt_cairo_image_surface_create(CAIRO_FORMAT_ARGB32, width, height);
268 cairo_t *cr = cairo_create(cst);
269
270 // clear bg
271 cairo_set_source_rgb(cr, .2, .2, .2);
272 cairo_paint(cr);
273
274 cairo_translate(cr, inset, inset);
275 width -= 2 * inset;
276 height -= 2 * inset;
277
278 cairo_set_line_width(cr, DT_PIXEL_APPLY_DPI(1.0));
279 cairo_set_source_rgb(cr, .1, .1, .1);
280 cairo_rectangle(cr, 0, 0, width, height);
281 cairo_stroke(cr);
282
283 cairo_set_source_rgb(cr, .3, .3, .3);
284 cairo_rectangle(cr, 0, 0, width, height);
285 cairo_fill(cr);
286
287 // draw grid
288 cairo_set_line_width(cr, DT_PIXEL_APPLY_DPI(.4));
289 cairo_set_source_rgb(cr, .1, .1, .1);
290 dt_draw_vertical_lines(cr, 4, 0, 0, width, height);
291
292 // Drawing the vertical line indicators
293 cairo_set_line_width(cr, DT_PIXEL_APPLY_DPI(2.));
294
295 for(int k = 0; k < 3; k++)
296 {
297 if(k == c->handle_move && c->mouse_x > 0)
298 cairo_set_source_rgb(cr, 1, 1, 1);
299 else
300 cairo_set_source_rgb(cr, .7, .7, .7);
301
302 cairo_move_to(cr, width * p->levels[c->channel][k], height);
303 cairo_rel_line_to(cr, 0, -height);
304 cairo_stroke(cr);
305 }
306
307 // draw x positions
308 cairo_set_line_width(cr, DT_PIXEL_APPLY_DPI(1.));
309 const float arrw = DT_PIXEL_APPLY_DPI(7.0f);
310 for(int k = 0; k < 3; k++)
311 {
312 switch(k)
313 {
314 case 0:
315 cairo_set_source_rgb(cr, 0, 0, 0);
316 break;
317
318 case 1:
319 cairo_set_source_rgb(cr, 0.5, 0.5, 0.5);
320 break;
321
322 default:
323 cairo_set_source_rgb(cr, 1, 1, 1);
324 break;
325 }
326
327 cairo_move_to(cr, width * p->levels[c->channel][k], height + inset - 1);
328 cairo_rel_line_to(cr, -arrw * .5f, 0);
329 cairo_rel_line_to(cr, arrw * .5f, -arrw);
330 cairo_rel_line_to(cr, arrw * .5f, arrw);
331 cairo_close_path(cr);
332 if(c->handle_move == k && c->mouse_x > 0)
333 cairo_fill(cr);
334 else
335 cairo_stroke(cr);
336 }
337
338 cairo_translate(cr, 0, height);
339
340 // draw histogram in background
341 // only if the module is enabled
342 if(self->enabled)
343 {
344 const int ch = c->channel;
345 const uint32_t *hist = self->histogram;
346 const gboolean is_linear = FALSE;
347 float hist_max;
348
349 if(p->autoscale == DT_IOP_RGBLEVELS_LINKED_CHANNELS)
351 else
352 hist_max = self->histogram_max[ch];
353
354 if (!is_linear)
355 hist_max = logf(1.0 + hist_max);
356
357 if(!IS_NULL_PTR(hist) && hist_max > 0.0f)
358 {
359 cairo_push_group_with_content(cr, CAIRO_CONTENT_COLOR);
360 cairo_scale(cr, width / 255.0, -(height - DT_PIXEL_APPLY_DPI(5)) / hist_max);
361
362 if(p->autoscale == DT_IOP_RGBLEVELS_LINKED_CHANNELS)
363 {
364 cairo_set_operator(cr, CAIRO_OPERATOR_ADD);
366 {
368 dt_draw_histogram_8(cr, hist, 4, k, is_linear);
369 }
370 }
371 else if(p->autoscale == DT_IOP_RGBLEVELS_INDEPENDENT_CHANNELS)
372 {
374 dt_draw_histogram_8(cr, hist, 4, ch, is_linear);
375 }
376
377 cairo_pop_group_to_source(cr);
378 cairo_paint_with_alpha(cr, 0.2);
379 }
380 }
381
382 // Cleaning up
383 cairo_destroy(cr);
384 cairo_set_source_surface(crf, cst, 0, 0);
385 cairo_paint(crf);
386 cairo_surface_destroy(cst);
387 return TRUE;
388}
389
390static void _rgblevels_move_handle(dt_iop_module_t *self, const int handle_move, const float new_pos, float *levels,
391 const float drag_start_percentage)
392{
394 float min_x = 0.f;
395 float max_x = 1.f;
396
397 if((handle_move < 0) || handle_move > 2) return;
398
399 if(IS_NULL_PTR(levels)) return;
400
401 // Determining the minimum and maximum bounds for the drag handles
402 switch(handle_move)
403 {
404 case 0:
405 max_x = fminf(levels[2] - (0.05 / drag_start_percentage), 1);
406 max_x = fminf((levels[2] * (1 - drag_start_percentage) - 0.05) / (1 - drag_start_percentage), max_x);
407 break;
408
409 case 1:
410 min_x = levels[0] + 0.05;
411 max_x = levels[2] - 0.05;
412 break;
413
414 case 2:
415 min_x = fmaxf((0.05 / drag_start_percentage) + levels[0], 0);
416 min_x = fmaxf((levels[0] * (1 - drag_start_percentage) + 0.05) / (1 - drag_start_percentage), min_x);
417 break;
418 }
419
420 levels[handle_move] = fminf(max_x, fmaxf(min_x, new_pos));
421
422 if(handle_move != 1) levels[1] = levels[0] + (drag_start_percentage * (levels[2] - levels[0]));
423
424 c->last_picked_color = -1;
425
427
428 gtk_widget_queue_draw(GTK_WIDGET(c->area));
429}
430
431static gboolean _area_motion_notify_callback(GtkWidget *widget, GdkEventMotion *event, dt_iop_module_t *self)
432{
435 const int inset = DT_GUI_CURVE_EDITOR_INSET;
436 GtkAllocation allocation;
437 gtk_widget_get_allocation(widget, &allocation);
438 int height = allocation.height - 2 * inset, width = allocation.width - 2 * inset;
439 if(!c->dragging)
440 {
441 c->mouse_x = CLAMP(event->x - inset, 0, width);
442 c->drag_start_percentage = (p->levels[c->channel][1] - p->levels[c->channel][0]) / (p->levels[c->channel][2] - p->levels[c->channel][0]);
443 }
444 c->mouse_y = CLAMP(event->y - inset, 0, height);
445
446 if(c->dragging)
447 {
448 if(c->handle_move >= 0 && c->handle_move < 3)
449 {
450 const float mx = (CLAMP(event->x - inset, 0, width)) / (float)width;
451
452 _rgblevels_move_handle(self, c->handle_move, mx, p->levels[c->channel], c->drag_start_percentage);
453 }
454 }
455 else
456 {
457 c->handle_move = 0;
458 const float mx = CLAMP(event->x - inset, 0, width) / (float)width;
459 float dist = fabsf(p->levels[c->channel][0] - mx);
460 for(int k = 1; k < 3; k++)
461 {
462 float d2 = fabsf(p->levels[c->channel][k] - mx);
463 if(d2 < dist)
464 {
465 c->handle_move = k;
466 dist = d2;
467 }
468 }
469
470 gtk_widget_queue_draw(widget);
471 }
472
473 return TRUE;
474}
475
476static gboolean _area_button_press_callback(GtkWidget *widget, GdkEventButton *event, dt_iop_module_t *self)
477{
478 // set active point
479 if(event->button == 1)
480 {
481 if(self->dev->gui_module != self) dt_iop_request_focus(self);
482
483 if(event->type == GDK_2BUTTON_PRESS)
484 {
486
487 // Reset
491
492 for(int i = 0; i < 3; i++)
493 p->levels[c->channel][i] = default_params->levels[c->channel][i];
494
495 // Needed in case the user scrolls or drags immediately after a reset,
496 // as drag_start_percentage is only updated when the mouse is moved.
497 c->drag_start_percentage = 0.5;
499 gtk_widget_queue_draw(self->widget);
500 }
501 else
502 {
504
506 c->dragging = 1;
507 }
508 return TRUE;
509 }
510 return FALSE;
511}
512
513static gboolean _area_button_release_callback(GtkWidget *widget, GdkEventButton *event, dt_iop_module_t *self)
514{
515 if(event->button == 1)
516 {
518 c->dragging = 0;
519 return TRUE;
520 }
521 return FALSE;
522}
523
524static gboolean _area_scroll_callback(GtkWidget *widget, GdkEventScroll *event, dt_iop_module_t *self)
525{
528
529 int delta_y;
530
532
533 if(c->dragging)
534 {
535 return FALSE;
536 }
537
538 if(self->dev->gui_module != self) dt_iop_request_focus(self);
539
540 const float interval = 0.002; // Distance moved for each scroll event
541 if(dt_gui_get_scroll_unit_deltas(event, NULL, &delta_y))
542 {
543 const float new_position = p->levels[c->channel][c->handle_move] - interval * delta_y;
544 _rgblevels_move_handle(self, c->handle_move, new_position, p->levels[c->channel], c->drag_start_percentage);
545 return TRUE;
546 }
547
548 return TRUE; // Ensure that scrolling the widget cannot move side panel
549}
550
551static void _auto_levels_callback(GtkButton *button, dt_iop_module_t *self)
552{
553 if(dt_gui_widgets_suppressed()) return;
554
556
558 if(self->off)
559 {
560 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(self->off), 1);
562 }
563
565
567 if(g->call_auto_levels == 0)
568 {
569 g->box_cood[0] = g->box_cood[1] = g->box_cood[2] = g->box_cood[3] = 0.f;
570 g->call_auto_levels = 1;
571 }
573
575}
576
577static void _select_region_toggled_callback(GtkToggleButton *togglebutton, dt_iop_module_t *self)
578{
579 if(dt_gui_widgets_suppressed()) return;
580
582
584 if(self->off)
585 {
586 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(self->off), 1);
588 }
589
591
593
594 if(gtk_toggle_button_get_active(togglebutton))
595 {
596 g->draw_selected_region = 1;
597 }
598 else
599 g->draw_selected_region = 0;
600
601 g->posx_from = g->posx_to = g->posy_from = g->posy_to = 0;
602
604}
605
606void gui_changed(dt_iop_module_t *self, GtkWidget *w, void *previous)
607{
610
612
613 if(w == g->cmb_autoscale)
614 {
615 g->channel = DT_IOP_RGBLEVELS_R;
616 gtk_notebook_set_current_page(GTK_NOTEBOOK(g->channel_tabs), g->channel);
617
619 }
620}
621
622static void _tab_switch_callback(GtkNotebook *notebook, GtkWidget *page, guint page_num, dt_iop_module_t *self)
623{
624 if(dt_gui_widgets_suppressed()) return;
626
627 g->channel = (dt_iop_rgblevels_channel_t)page_num;
628
629 gtk_widget_queue_draw(self->widget);
630}
631
633{
635}
636
638{
639 (void)pipe;
640 (void)piece;
643
644 const dt_iop_rgblevels_channel_t channel = c->channel;
645
646 /* we need to save the last picked color to prevent flickering when
647 * changing from one picker to another, as the picked_color value does not
648 * update as rapidly */
649 const float mean_picked_color = *self->picked_color;
650
651 if(mean_picked_color != c->last_picked_color)
652 {
653 dt_aligned_pixel_t previous_color;
654 previous_color[0] = p->levels[channel][0];
655 previous_color[1] = p->levels[channel][1];
656 previous_color[2] = p->levels[channel][2];
657
658 c->last_picked_color = mean_picked_color;
659
660 if(picker == c->blackpick)
661 {
662 if(mean_picked_color > p->levels[channel][1])
663 {
664 p->levels[channel][0] = p->levels[channel][1] - FLT_EPSILON;
665 }
666 else
667 {
668 p->levels[channel][0] = mean_picked_color;
669 }
670 }
671 else if(picker == c->greypick)
672 {
673 if(mean_picked_color < p->levels[channel][0] || mean_picked_color > p->levels[channel][2])
674 {
675 p->levels[channel][1] = p->levels[channel][1];
676 }
677 else
678 {
679 p->levels[channel][1] = mean_picked_color;
680 }
681 }
682 else if(picker == c->whitepick)
683 {
684 if(mean_picked_color < p->levels[channel][1])
685 {
686 p->levels[channel][2] = p->levels[channel][1] + FLT_EPSILON;
687 }
688 else
689 {
690 p->levels[channel][2] = mean_picked_color;
691 }
692 }
693
694 if(previous_color[0] != p->levels[channel][0]
695 || previous_color[1] != p->levels[channel][1]
696 || previous_color[2] != p->levels[channel][2])
697 {
699 }
700 }
701
702}
703
706{
709
710 if(dt_dev_pixelpipe_has_preview_output(self->dev, pipe, NULL))
712 else
713 piece->request_histogram &= ~(DT_REQUEST_ON);
714
715 memcpy(&(d->params), p, sizeof(dt_iop_rgblevels_params_t));
716
717 for(int i = 0; i < DT_IOP_RGBLEVELS_MAX_CHANNELS; i++)
718 {
719 for(int c = 0; c < 3; c++)
720 {
721 if(d->params.autoscale == DT_IOP_RGBLEVELS_LINKED_CHANNELS)
722 d->params.levels[i][c] = p->levels[0][c];
723 else
724 d->params.levels[i][c] = p->levels[i][c];
725 }
726 }
727
728 _compute_lut(piece);
729}
730
736
738{
739 // clean up everything again.
740 dt_free_align(piece->data);
741 piece->data = NULL;
742}
743
745{
748
749 dt_bauhaus_combobox_set(g->cmb_autoscale, p->autoscale);
750 dt_bauhaus_combobox_set(g->cmb_preserve_colors, p->preserve_colors);
751 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(g->bt_select_region), g->draw_selected_region);
753
754 gtk_widget_queue_draw(self->widget);
755}
756
757void gui_focus(struct dt_iop_module_t *self, gboolean in)
758{
759 if(!in) _turn_select_region_off(self);
760}
761
762void gui_reset(struct dt_iop_module_t *self)
763{
765
767
768 g->channel = DT_IOP_RGBLEVELS_R;
769
770 gtk_widget_queue_draw(self->widget);
771}
772
774{
776
778
780
781 for(int c = 0; c < DT_IOP_RGBLEVELS_MAX_CHANNELS; c++)
782 {
783 d->levels[c][0] = RGBLEVELS_MIN;
784 d->levels[c][1] = RGBLEVELS_MID;
785 d->levels[c][2] = RGBLEVELS_MAX;
786 }
787}
788
790{
792
793 g->channel = DT_IOP_RGBLEVELS_R;
794 g->call_auto_levels = 0;
795 g->draw_selected_region = 0;
796 g->posx_from = g->posx_to = g->posy_from = g->posy_to = 0.f;
797 g->box_cood[0] = g->box_cood[1] = g->box_cood[2] = g->box_cood[3] = 0.f;
798 g->button_down = 0;
799}
800
802{
804
805 change_image(self);
806
807 c->mouse_x = c->mouse_y = -1.0;
808 c->dragging = 0;
809 c->last_picked_color = -1;
810
811 c->cmb_autoscale = dt_bauhaus_combobox_from_params(self, "autoscale");
812 gtk_widget_set_tooltip_text(c->cmb_autoscale, _("choose between linked and independent channels."));
813
814 c->channel_tabs = GTK_NOTEBOOK(gtk_notebook_new());
815 dt_ui_notebook_page(c->channel_tabs, N_("R"), _("curve nodes for r channel"));
816 dt_ui_notebook_page(c->channel_tabs, N_("G"), _("curve nodes for g channel"));
817 dt_ui_notebook_page(c->channel_tabs, N_("B"), _("curve nodes for b channel"));
818 g_signal_connect(G_OBJECT(c->channel_tabs), "switch_page", G_CALLBACK(_tab_switch_callback), self);
819 dt_ui_notebook_set_picker_owner(c->channel_tabs, self);
820 gtk_box_pack_start(GTK_BOX(self->widget), GTK_WIDGET(c->channel_tabs), FALSE, FALSE, 0);
821
822 c->area = GTK_DRAWING_AREA(gtk_drawing_area_new());
823 gtk_widget_set_hexpand(GTK_WIDGET(c->area), TRUE);
824
825 gtk_box_pack_start(GTK_BOX(self->widget),
826 dt_ui_resizable_drawing_area(GTK_WIDGET(c->area),
827 "plugins/darkroom/rgblevels/graphheight", 280, 100),
828 FALSE, FALSE, 0);
829
830 g_object_set_data(G_OBJECT(c->area), "iop-instance", self);
831
832 gtk_widget_set_tooltip_text(GTK_WIDGET(c->area),_("drag handles to set black, gray, and white points. "
833 "operates on L channel."));
834
835 gtk_widget_add_events(GTK_WIDGET(c->area), GDK_POINTER_MOTION_MASK
836 | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK
837 | GDK_LEAVE_NOTIFY_MASK | GDK_ENTER_NOTIFY_MASK
839 g_signal_connect(G_OBJECT(c->area), "draw", G_CALLBACK(_area_draw_callback), self);
840 g_signal_connect(G_OBJECT(c->area), "button-press-event", G_CALLBACK(_area_button_press_callback), self);
841 g_signal_connect(G_OBJECT(c->area), "button-release-event", G_CALLBACK(_area_button_release_callback), self);
842 g_signal_connect(G_OBJECT(c->area), "motion-notify-event", G_CALLBACK(_area_motion_notify_callback), self);
843 g_signal_connect(G_OBJECT(c->area), "leave-notify-event", G_CALLBACK(_area_leave_notify_callback), self);
844 g_signal_connect(G_OBJECT(c->area), "scroll-event", G_CALLBACK(_area_scroll_callback), self);
845
846 c->blackpick = dt_color_picker_new(self, DT_COLOR_PICKER_POINT, NULL);
847 gtk_widget_set_tooltip_text(c->blackpick, _("pick black point from image"));
848 gtk_widget_set_name(GTK_WIDGET(c->blackpick), "picker-black");
849 g_signal_connect(G_OBJECT(c->blackpick), "toggled", G_CALLBACK(_color_picker_callback), self);
850
851 c->greypick = dt_color_picker_new(self, DT_COLOR_PICKER_POINT, NULL);
852 gtk_widget_set_tooltip_text(c->greypick, _("pick medium gray point from image"));
853 gtk_widget_set_name(GTK_WIDGET(c->greypick), "picker-grey");
854 g_signal_connect(G_OBJECT(c->greypick), "toggled", G_CALLBACK(_color_picker_callback), self);
855
856 c->whitepick = dt_color_picker_new(self, DT_COLOR_PICKER_POINT, NULL);
857 gtk_widget_set_tooltip_text(c->whitepick, _("pick white point from image"));
858 gtk_widget_set_name(GTK_WIDGET(c->whitepick), "picker-white");
859 g_signal_connect(G_OBJECT(c->whitepick), "toggled", G_CALLBACK(_color_picker_callback), self);
860
861 GtkWidget *pick_hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, DT_GUI_BOX_SPACING);
862 gtk_box_pack_start(GTK_BOX(pick_hbox), GTK_WIDGET(c->blackpick), TRUE, TRUE, 0);
863 gtk_box_pack_start(GTK_BOX(pick_hbox), GTK_WIDGET(c->greypick ), TRUE, TRUE, 0);
864 gtk_box_pack_start(GTK_BOX(pick_hbox), GTK_WIDGET(c->whitepick), TRUE, TRUE, 0);
865
866 gtk_box_pack_start(GTK_BOX(self->widget), pick_hbox, TRUE, TRUE, 0);
867
868 c->bt_auto_levels = gtk_button_new_with_label(_("auto"));
869 gtk_widget_set_tooltip_text(c->bt_auto_levels, _("apply auto levels"));
870
871 c->bt_select_region = dtgtk_togglebutton_new(dtgtk_cairo_paint_colorpicker, 0, NULL);
872
873 gtk_widget_set_tooltip_text(c->bt_select_region,
874 _("apply auto levels based on a region defined by the user\n"
875 "click and drag to draw the area\n"
876 "right click to cancel"));
877
878 GtkWidget *autolevels_box = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, DT_GUI_BOX_SPACING);
879 gtk_box_pack_start(GTK_BOX(autolevels_box), c->bt_auto_levels, TRUE, TRUE, 0);
880 gtk_box_pack_start(GTK_BOX(autolevels_box), c->bt_select_region, TRUE, TRUE, 0);
881
882 gtk_box_pack_start(GTK_BOX(self->widget), autolevels_box, TRUE, TRUE, 0);
883
884 g_signal_connect(G_OBJECT(c->bt_auto_levels), "clicked", G_CALLBACK(_auto_levels_callback), self);
885 g_signal_connect(G_OBJECT(c->bt_select_region), "toggled", G_CALLBACK(_select_region_toggled_callback), self);
886
887 c->cmb_preserve_colors = dt_bauhaus_combobox_from_params(self, "preserve_colors");
888 gtk_widget_set_tooltip_text(c->cmb_preserve_colors, _("method to preserve colors when applying contrast"));
889
890 // add signal handler for preview pipe finish
892 G_CALLBACK(_develop_ui_pipe_finished_callback), self);
893}
894
901
903static void _get_selected_area(struct dt_iop_module_t *self, const dt_dev_pixelpipe_t *pipe,
904 const dt_dev_pixelpipe_iop_t *piece,
905 dt_iop_rgblevels_gui_data_t *g, const dt_iop_roi_t *const roi_in, int *box_out)
906{
907 box_out[0] = box_out[1] = box_out[2] = box_out[3] = 0;
908
909 if(!IS_NULL_PTR(g))
910 {
911 const int width = roi_in->width;
912 const int height = roi_in->height;
913 dt_boundingbox_t box_cood = { g->box_cood[0], g->box_cood[1], g->box_cood[2], g->box_cood[3] };
914
915 box_cood[0] *= pipe->iwidth;
916 box_cood[1] *= pipe->iheight;
917 box_cood[2] *= pipe->iwidth;
918 box_cood[3] *= pipe->iheight;
919
921 box_cood, 2);
922
923 box_cood[0] *= roi_in->scale;
924 box_cood[1] *= roi_in->scale;
925 box_cood[2] *= roi_in->scale;
926 box_cood[3] *= roi_in->scale;
927
928 box_cood[0] -= roi_in->x;
929 box_cood[1] -= roi_in->y;
930 box_cood[2] -= roi_in->x;
931 box_cood[3] -= roi_in->y;
932
933 int DT_ALIGNED_ARRAY box[4];
934
935 // re-order edges of bounding box
936 box[0] = fminf(box_cood[0], box_cood[2]);
937 box[1] = fminf(box_cood[1], box_cood[3]);
938 box[2] = fmaxf(box_cood[0], box_cood[2]);
939 box[3] = fmaxf(box_cood[1], box_cood[3]);
940
941 // do not continue if box is completely outside of roi
942 if(!(box[0] >= width || box[1] >= height || box[2] < 0 || box[3] < 0))
943 {
944 // clamp bounding box to roi
945 for(int k = 0; k < 4; k += 2) box[k] = MIN(width - 1, MAX(0, box[k]));
946 for(int k = 1; k < 4; k += 2) box[k] = MIN(height - 1, MAX(0, box[k]));
947
948 // safety check: area needs to have minimum 1 pixel width and height
949 if(!(box[2] - box[0] < 1 || box[3] - box[1] < 1))
950 {
951 box_out[0] = box[0];
952 box_out[1] = box[1];
953 box_out[2] = box[2];
954 box_out[3] = box[3];
955 }
956 }
957 }
958}
959
961static void _auto_levels(const float *const img, const int width, const int height, int *box_area,
962 dt_iop_rgblevels_params_t *p, const int _channel, const dt_iop_order_iccprofile_info_t *const work_profile)
963{
964 int y_from, y_to, x_from, x_to;
965 const int ch = 4;
966 const int channel = (p->autoscale == DT_IOP_RGBLEVELS_INDEPENDENT_CHANNELS) ? _channel : 0;
967 if(box_area[2] > box_area[0] && box_area[3] > box_area[1])
968 {
969 y_from = box_area[1];
970 y_to = box_area[3];
971 x_from = box_area[0];
972 x_to = box_area[2];
973 }
974 else
975 {
976 y_from = 0;
977 y_to = height - 1;
978 x_from = 0;
979 x_to = width - 1;
980 }
981
982 float max = -INFINITY;
983 float min = INFINITY;
984
985 for(int y = y_from; y <= y_to; y++)
986 {
987 const float *const in = img + ch * width * y;
988 for(int x = x_from; x <= x_to; x++)
989 {
990 const float *const pixel = in + x * ch;
991
992 if(p->autoscale == DT_IOP_RGBLEVELS_INDEPENDENT_CHANNELS || p->preserve_colors == DT_RGB_NORM_NONE)
993 {
995 {
996 if(pixel[channel] >= 0.f)
997 {
998 max = fmaxf(max, pixel[channel]);
999 min = fminf(min, pixel[channel]);
1000 }
1001 }
1002 else
1003 {
1004 for(int c = 0; c < 3; c++)
1005 {
1006 if(pixel[c] >= 0.f)
1007 {
1008 max = fmaxf(max, pixel[c]);
1009 min = fminf(min, pixel[c]);
1010 }
1011 }
1012 }
1013 }
1014 else
1015 {
1016 const float lum = dt_rgb_norm(pixel, p->preserve_colors, work_profile);
1017 if(lum >= 0.f)
1018 {
1019 max = fmaxf(max, lum);
1020 min = fminf(min, lum);
1021 }
1022 }
1023 }
1024 }
1025
1026 p->levels[channel][0] = CLAMP(min, 0.f, 1.f);
1027 p->levels[channel][2] = CLAMP(max, 0.f, 1.f);
1028 p->levels[channel][1] = (p->levels[channel][2] + p->levels[channel][0]) / 2.f;
1029}
1030
1033 const void *const ivoid, void *const ovoid)
1034{
1035 const dt_iop_roi_t *const roi_in = &piece->roi_in;
1036 const dt_iop_roi_t *const roi_out = &piece->roi_out;
1037
1038 const dt_iop_rgblevels_data_t *const d = (dt_iop_rgblevels_data_t *)piece->data;
1042
1043 // process auto levels
1044 if(!IS_NULL_PTR(g) && dt_dev_pixelpipe_has_preview_output(self->dev, pipe, roi_out))
1045 {
1047 if(g->call_auto_levels == 1 && !dt_gui_widgets_suppressed())
1048 {
1049 g->call_auto_levels = -1;
1050
1052
1053 memcpy(&g->params, p, sizeof(dt_iop_rgblevels_params_t));
1054
1055 int box[4] = { 0 };
1056 _get_selected_area(self, pipe, piece, g, roi_in, box);
1057 _auto_levels((const float *const)ivoid, roi_in->width, roi_in->height, box, &(g->params), g->channel, work_profile);
1058
1060 g->call_auto_levels = 2;
1062 }
1063 else
1064 {
1066 }
1067 }
1068
1069 const dt_aligned_pixel_t mult = { 1.f / (d->params.levels[0][2] - d->params.levels[0][0]),
1070 1.f / (d->params.levels[1][2] - d->params.levels[1][0]),
1071 1.f / (d->params.levels[2][2] - d->params.levels[2][0]) };
1072
1073 const size_t npixels = (size_t)roi_out->width * roi_out->height;
1074 const float *const restrict in = (const float*)ivoid;
1075 float *const restrict out = (float*)ovoid;
1076 if (d->params.autoscale == DT_IOP_RGBLEVELS_INDEPENDENT_CHANNELS || d->params.preserve_colors == DT_RGB_NORM_NONE)
1077 {
1079 for(int k = 0; k < 4U*npixels; k += 4)
1080 {
1081 for(int c = 0; c < 3; c++)
1082 {
1083 const float L_in = in[k+c];
1084
1085 if(L_in <= d->params.levels[c][0])
1086 {
1087 // Anything below the lower threshold just clips to zero
1088 out[k+c] = 0.0f;
1089 }
1090 else if(L_in >= d->params.levels[c][2])
1091 {
1092 const float percentage = (L_in - d->params.levels[c][0]) * mult[c];
1093 out[k+c] = powf(percentage, d->inv_gamma[c]);
1094 }
1095 else
1096 {
1097 // Within the expected input range we can use the lookup table
1098 const float percentage = (L_in - d->params.levels[c][0]) * mult[c];
1099 out[k+c] = d->lut[c][CLAMP((int)(percentage * 0x10000ul), 0, 0xffff)];
1100 }
1101 }
1102 out[k+3] = in[k+3];
1103 }
1104 }
1105 else
1106 {
1107 const int ch_levels = 0;
1108 const float mult_ch = mult[ch_levels];
1109 const float *const restrict levels = d->params.levels[ch_levels];
1111 for(int k = 0; k < 4U*npixels; k += 4)
1112 {
1113 const float lum = dt_rgb_norm(in+k, d->params.preserve_colors, work_profile);
1114 if(lum > levels[0])
1115 {
1116 float curve_lum;
1117 const float percentage = (lum - levels[0]) * mult_ch;
1118 if(lum >= levels[2])
1119 {
1120 curve_lum = powf(percentage, d->inv_gamma[ch_levels]);
1121 }
1122 else
1123 {
1124 // Within the expected input range we can use the lookup table
1125 curve_lum = d->lut[ch_levels][CLAMP((int)(percentage * 0x10000ul), 0, 0xffff)];
1126 }
1127
1128 const float ratio = curve_lum / lum;
1129
1130 for_each_channel(c,aligned(in,out:16))
1131 {
1132 out[k+c] = (ratio * in[k+c]);
1133 }
1134 }
1135 else
1136 {
1137 for_each_channel(c,aligned(out:16))
1138 out[k+c] = 0.f;
1139 }
1140 out[k+3] = in[k+3];
1141 }
1142 }
1143
1144 return 0;
1145}
1146
1147
1148// clang-format off
1149// modelines: These editor modelines have been set for all relevant files by tools/update_modelines.py
1150// vim: shiftwidth=2 expandtab tabstop=2 cindent
1151// kate: tab-indents: off; indent-width 2; replace-tabs on; indent-mode cstyle; remove-trailing-spaces modified;
1152// clang-format on
static double dist(double x1, double y1, double x2, double y2)
Definition ashift_lsd.c:250
#define TRUE
Definition ashift_lsd.c:162
#define FALSE
Definition ashift_lsd.c:158
int levels(struct dt_imageio_module_data_t *data)
Definition avif.c:635
void dt_bauhaus_combobox_set(GtkWidget *widget, const int pos)
Definition bauhaus.c:2304
static void set_color(cairo_t *cr, GdkRGBA color)
Definition bauhaus.h:446
int width
Definition bilateral.h:1
int height
Definition bilateral.h:1
static const dt_aligned_pixel_simd_t const dt_adaptation_t const float p
@ 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_POINT
static const float const float const float min
const float max
const dt_colormatrix_t dt_aligned_pixel_t out
const float delta
typedef void((*dt_cache_allocate_t)(void *userdata, dt_cache_entry_t *entry))
darktable_t darktable
Definition darktable.c:183
#define dt_free_align(ptr)
Definition darktable.h:503
static void * dt_calloc_align(size_t size)
Definition darktable.h:510
#define DT_ALIGNED_ARRAY
Definition darktable.h:400
#define dt_gui_freeze_begin()
Definition darktable.h:900
#define dt_gui_freeze_end()
Definition darktable.h:901
#define for_each_channel(_var,...)
Definition darktable.h:684
float dt_boundingbox_t[4]
Definition darktable.h:731
void void void gboolean dt_gui_widgets_suppressed(void)
Definition gtk.c:194
#define DT_MODULE_INTROSPECTION(MODVER, PARAMSTYPE)
Definition darktable.h:151
#define __DT_CLONE_TARGETS__
Definition darktable.h:379
#define __OMP_PARALLEL_FOR__(...)
Definition darktable.h:270
#define IS_NULL_PTR(p)
C is way too permissive with !=, == and if(var) checks, which can mean too many things depending on w...
Definition darktable.h:293
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:42
#define dt_dev_pixelpipe_update_history_all(dev)
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:1621
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:414
@ DT_DEV_TRANSFORM_DIR_BACK_INCL
Definition develop.h:105
static void dt_draw_vertical_lines(cairo_t *cr, const int num, const int left, const int top, const int right, const int bottom)
Definition draw.h:240
static void dt_draw_histogram_8(cairo_t *cr, const uint32_t *hist, int32_t channels, int32_t channel, const gboolean linear)
Definition draw.h:454
void dtgtk_cairo_paint_colorpicker(cairo_t *cr, gint x, gint y, gint w, gint h, gint flags, void *data)
gboolean dt_gui_get_scroll_unit_deltas(const GdkEventScroll *event, int *delta_x, int *delta_y)
Definition gtk.c:300
GtkWidget * dt_ui_resizable_drawing_area(GtkWidget *area, char *config_str, int default_height, int min_height)
Make a self-drawing widget (typically a GtkDrawingArea graph or scope) vertically resizable.
Definition gtk.c:3068
GtkWidget * dt_ui_notebook_page(GtkNotebook *notebook, const char *text, const char *tooltip)
Definition gtk.c:2470
void dt_ui_notebook_set_picker_owner(GtkNotebook *notebook, gpointer owner)
Register an opaque owner for a GtkNotebook's page switches, and relay every "switch_page" as DT_SIGNA...
Definition gtk.c:2500
static cairo_surface_t * dt_cairo_image_surface_create(cairo_format_t format, int width, int height)
Definition gtk.h:322
#define DT_GUI_BOX_SPACING
Definition gtk.h:109
#define DT_PIXEL_APPLY_DPI(value)
Definition gtk.h:90
void dt_iop_default_init(dt_iop_module_t *module)
Definition imageop.c:321
void dt_iop_request_focus(dt_iop_module_t *module)
Definition imageop.c:2221
const char ** dt_iop_set_description(dt_iop_module_t *module, const char *main_text, const char *purpose, const char *input, const char *process, const char *output)
Definition imageop.c:3220
#define IOP_GUI_FREE
Definition imageop.h:641
static void dt_iop_gui_enter_critical_section(dt_iop_module_t *const module) ACQUIRE(&module -> gui_lock)
Definition imageop.h:450
@ IOP_FLAGS_DEPRECATED
Definition imageop.h:198
@ IOP_FLAGS_SUPPORTS_BLENDING
Definition imageop.h:197
static void dt_iop_gui_leave_critical_section(dt_iop_module_t *const module) RELEASE(&module -> gui_lock)
Definition imageop.h:456
@ IOP_GROUP_COLOR
Definition imageop.h:169
#define IOP_GUI_ALLOC(module)
Definition imageop.h:638
GtkWidget * dt_bauhaus_combobox_from_params(dt_iop_module_t *self, const char *param)
void *const ovoid
dt_iop_order_iccprofile_info_t * dt_ioppr_get_pipe_work_profile_info(const struct dt_dev_pixelpipe_t *pipe)
static const float x
float *const restrict const size_t k
float *const restrict const size_t const size_t ch
float dt_aligned_pixel_t[4]
@ DT_REQUEST_ON
Definition pixelpipe.h:48
static gboolean _area_draw_callback(GtkWidget *widget, cairo_t *crf, dt_iop_module_t *self)
Definition rgblevels.c:258
dt_iop_rgblevels_autoscale_t
Definition rgblevels.c:70
@ DT_IOP_RGBLEVELS_LINKED_CHANNELS
Definition rgblevels.c:71
@ DT_IOP_RGBLEVELS_INDEPENDENT_CHANNELS
Definition rgblevels.c:72
const char ** description(struct dt_iop_module_t *self)
Definition rgblevels.c:134
int default_group()
Definition rgblevels.c:119
#define RGBLEVELS_MIN
Definition rgblevels.c:57
static void _auto_levels_callback(GtkButton *button, dt_iop_module_t *self)
Definition rgblevels.c:551
static gboolean _area_button_release_callback(GtkWidget *widget, GdkEventButton *event, dt_iop_module_t *self)
Definition rgblevels.c:513
static __DT_CLONE_TARGETS__ void _auto_levels(const float *const img, const int width, const int height, int *box_area, dt_iop_rgblevels_params_t *p, const int _channel, const dt_iop_order_iccprofile_info_t *const work_profile)
Definition rgblevels.c:961
void gui_update(dt_iop_module_t *self)
Refresh GUI controls from current params and configuration.
Definition rgblevels.c:744
__DT_CLONE_TARGETS__ int process(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 rgblevels.c:1032
#define DT_GUI_CURVE_EDITOR_INSET
Definition rgblevels.c:53
static gboolean _area_leave_notify_callback(GtkWidget *widget, GdkEventCrossing *event, dt_iop_module_t *self)
Definition rgblevels.c:250
void gui_focus(struct dt_iop_module_t *self, gboolean in)
Definition rgblevels.c:757
const char * name()
Definition rgblevels.c:114
static void _compute_lut(const dt_dev_pixelpipe_iop_t *piece)
Definition rgblevels.c:195
void gui_reset(struct dt_iop_module_t *self)
Definition rgblevels.c:762
static void _rgblevels_move_handle(dt_iop_module_t *self, const int handle_move, const float new_pos, float *levels, const float drag_start_percentage)
Definition rgblevels.c:390
void gui_init(dt_iop_module_t *self)
Definition rgblevels.c:801
void change_image(struct dt_iop_module_t *self)
Definition rgblevels.c:789
static void _select_region_toggled_callback(GtkToggleButton *togglebutton, dt_iop_module_t *self)
Definition rgblevels.c:577
static void _develop_ui_pipe_finished_callback(gpointer instance, dt_iop_module_t *self)
Definition rgblevels.c:159
void gui_changed(dt_iop_module_t *self, GtkWidget *w, void *previous)
Definition rgblevels.c:606
static gboolean _area_scroll_callback(GtkWidget *widget, GdkEventScroll *event, dt_iop_module_t *self)
Definition rgblevels.c:524
static void _turn_selregion_picker_off(struct dt_iop_module_t *self)
Definition rgblevels.c:153
void commit_params(dt_iop_module_t *self, dt_iop_params_t *p1, dt_dev_pixelpipe_t *pipe, dt_dev_pixelpipe_iop_t *piece)
Definition rgblevels.c:704
void gui_cleanup(dt_iop_module_t *self)
Definition rgblevels.c:895
static void _tab_switch_callback(GtkNotebook *notebook, GtkWidget *page, guint page_num, dt_iop_module_t *self)
Definition rgblevels.c:622
void cleanup_pipe(dt_iop_module_t *self, dt_dev_pixelpipe_t *pipe, dt_dev_pixelpipe_iop_t *piece)
Definition rgblevels.c:737
#define RGBLEVELS_MID
Definition rgblevels.c:58
int default_colorspace(dt_iop_module_t *self, dt_dev_pixelpipe_t *pipe, const dt_dev_pixelpipe_iop_t *piece)
Definition rgblevels.c:129
int flags()
Definition rgblevels.c:124
static void _turn_select_region_off(struct dt_iop_module_t *self)
Definition rgblevels.c:143
static gboolean _area_button_press_callback(GtkWidget *widget, GdkEventButton *event, dt_iop_module_t *self)
Definition rgblevels.c:476
void init(dt_iop_module_t *self)
Definition rgblevels.c:773
static void _color_picker_callback(GtkWidget *button, dt_iop_module_t *self)
Definition rgblevels.c:632
static void _rgblevels_show_hide_controls(dt_iop_rgblevels_params_t *p, dt_iop_rgblevels_gui_data_t *g)
Definition rgblevels.c:232
void init_pipe(dt_iop_module_t *self, dt_dev_pixelpipe_t *pipe, dt_dev_pixelpipe_iop_t *piece)
Definition rgblevels.c:731
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_rgblevels_gui_data_t *g, const dt_iop_roi_t *const roi_in, int *box_out)
Definition rgblevels.c:903
static gboolean _area_motion_notify_callback(GtkWidget *widget, GdkEventMotion *event, dt_iop_module_t *self)
Definition rgblevels.c:431
#define RGBLEVELS_MAX
Definition rgblevels.c:59
dt_iop_rgblevels_channel_t
Definition rgblevels.c:62
@ DT_IOP_RGBLEVELS_MAX_CHANNELS
Definition rgblevels.c:66
@ DT_IOP_RGBLEVELS_B
Definition rgblevels.c:65
@ DT_IOP_RGBLEVELS_R
Definition rgblevels.c:63
@ DT_IOP_RGBLEVELS_G
Definition rgblevels.c:64
void color_picker_apply(dt_iop_module_t *self, GtkWidget *picker, dt_dev_pixelpipe_t *pipe, dt_dev_pixelpipe_iop_t *piece)
Definition rgblevels.c:637
#define DT_DEBUG_CONTROL_SIGNAL_DISCONNECT(ctlsig, cb, user_data)
Definition signal.h:387
@ 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:174
#define DT_DEBUG_CONTROL_SIGNAL_CONNECT(ctlsig, signal, cb, user_data)
Definition signal.h:376
struct _GtkWidget GtkWidget
Definition splash.h:29
struct dt_gui_gtk_t * gui
Definition darktable.h:803
struct dt_control_signal_t * signals
Definition darktable.h:802
struct dt_bauhaus_t * bauhaus
Definition darktable.h:806
struct dt_develop_t * develop
Definition darktable.h:798
GdkRGBA graph_colors[3]
Definition bauhaus.h:283
dt_dev_request_flags_t request_histogram
struct dt_iop_module_t *void * data
struct dt_iop_module_t * gui_module
Definition develop.h:165
gint scroll_mask
Definition gtk.h:230
GtkDarktableToggleButton * off
Definition imageop.h:376
dt_iop_params_t * default_params
Definition imageop.h:344
GtkWidget * widget
Definition imageop.h:374
struct dt_develop_t * dev
Definition imageop.h:333
dt_iop_gui_data_t * gui_data
Definition imageop.h:348
dt_dev_request_flags_t request_histogram
Definition imageop.h:296
uint32_t histogram_max[4]
Definition imageop.h:317
gboolean enabled
Definition imageop.h:335
uint32_t * histogram
Definition imageop.h:313
dt_aligned_pixel_t picked_color
Definition imageop.h:309
dt_iop_params_t * params
Definition imageop.h:344
float inv_gamma[DT_IOP_RGBLEVELS_MAX_CHANNELS]
Definition rgblevels.c:110
float lut[DT_IOP_RGBLEVELS_MAX_CHANNELS][0x10000]
Definition rgblevels.c:111
dt_iop_rgblevels_params_t params
Definition rgblevels.c:109
GtkWidget * cmb_preserve_colors
Definition rgblevels.c:88
GtkDrawingArea * area
Definition rgblevels.c:87
GtkNotebook * channel_tabs
Definition rgblevels.c:89
dt_iop_rgblevels_params_t params
Definition rgblevels.c:84
dt_iop_rgblevels_channel_t channel
Definition rgblevels.c:102
dt_boundingbox_t box_cood
Definition rgblevels.c:96
dt_iop_rgb_norms_t preserve_colors
Definition rgblevels.c:78
float levels[DT_IOP_RGBLEVELS_MAX_CHANNELS][3]
Definition rgblevels.c:79
dt_iop_rgblevels_autoscale_t autoscale
Definition rgblevels.c:77
Region of interest passed through the pixelpipe.
Definition imageop.h:72
double scale
Definition imageop.h:74
GtkWidget * notebook
#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)