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(darktable.gui->reset) 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(darktable.gui->reset) 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(darktable.gui->reset) 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 gtk_box_pack_start(GTK_BOX(self->widget), GTK_WIDGET(c->channel_tabs), FALSE, FALSE, 0);
820
821 c->area = GTK_DRAWING_AREA(gtk_drawing_area_new());
822 gtk_widget_set_hexpand(GTK_WIDGET(c->area), TRUE);
823
824 gtk_box_pack_start(GTK_BOX(self->widget),
825 dt_ui_resizable_drawing_area(GTK_WIDGET(c->area),
826 "plugins/darkroom/rgblevels/graphheight", 280, 100),
827 FALSE, FALSE, 0);
828
829 g_object_set_data(G_OBJECT(c->area), "iop-instance", self);
830
831 gtk_widget_set_tooltip_text(GTK_WIDGET(c->area),_("drag handles to set black, gray, and white points. "
832 "operates on L channel."));
833
834 gtk_widget_add_events(GTK_WIDGET(c->area), GDK_POINTER_MOTION_MASK
835 | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK
836 | GDK_LEAVE_NOTIFY_MASK | GDK_ENTER_NOTIFY_MASK
838 g_signal_connect(G_OBJECT(c->area), "draw", G_CALLBACK(_area_draw_callback), self);
839 g_signal_connect(G_OBJECT(c->area), "button-press-event", G_CALLBACK(_area_button_press_callback), self);
840 g_signal_connect(G_OBJECT(c->area), "button-release-event", G_CALLBACK(_area_button_release_callback), self);
841 g_signal_connect(G_OBJECT(c->area), "motion-notify-event", G_CALLBACK(_area_motion_notify_callback), self);
842 g_signal_connect(G_OBJECT(c->area), "leave-notify-event", G_CALLBACK(_area_leave_notify_callback), self);
843 g_signal_connect(G_OBJECT(c->area), "scroll-event", G_CALLBACK(_area_scroll_callback), self);
844
845 c->blackpick = dt_color_picker_new(self, DT_COLOR_PICKER_POINT, NULL);
846 gtk_widget_set_tooltip_text(c->blackpick, _("pick black point from image"));
847 gtk_widget_set_name(GTK_WIDGET(c->blackpick), "picker-black");
848 g_signal_connect(G_OBJECT(c->blackpick), "toggled", G_CALLBACK(_color_picker_callback), self);
849
850 c->greypick = dt_color_picker_new(self, DT_COLOR_PICKER_POINT, NULL);
851 gtk_widget_set_tooltip_text(c->greypick, _("pick medium gray point from image"));
852 gtk_widget_set_name(GTK_WIDGET(c->greypick), "picker-grey");
853 g_signal_connect(G_OBJECT(c->greypick), "toggled", G_CALLBACK(_color_picker_callback), self);
854
855 c->whitepick = dt_color_picker_new(self, DT_COLOR_PICKER_POINT, NULL);
856 gtk_widget_set_tooltip_text(c->whitepick, _("pick white point from image"));
857 gtk_widget_set_name(GTK_WIDGET(c->whitepick), "picker-white");
858 g_signal_connect(G_OBJECT(c->whitepick), "toggled", G_CALLBACK(_color_picker_callback), self);
859
860 GtkWidget *pick_hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, DT_GUI_BOX_SPACING);
861 gtk_box_pack_start(GTK_BOX(pick_hbox), GTK_WIDGET(c->blackpick), TRUE, TRUE, 0);
862 gtk_box_pack_start(GTK_BOX(pick_hbox), GTK_WIDGET(c->greypick ), TRUE, TRUE, 0);
863 gtk_box_pack_start(GTK_BOX(pick_hbox), GTK_WIDGET(c->whitepick), TRUE, TRUE, 0);
864
865 gtk_box_pack_start(GTK_BOX(self->widget), pick_hbox, TRUE, TRUE, 0);
866
867 c->bt_auto_levels = gtk_button_new_with_label(_("auto"));
868 gtk_widget_set_tooltip_text(c->bt_auto_levels, _("apply auto levels"));
869
870 c->bt_select_region = dtgtk_togglebutton_new(dtgtk_cairo_paint_colorpicker, 0, NULL);
871
872 gtk_widget_set_tooltip_text(c->bt_select_region,
873 _("apply auto levels based on a region defined by the user\n"
874 "click and drag to draw the area\n"
875 "right click to cancel"));
876
877 GtkWidget *autolevels_box = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, DT_GUI_BOX_SPACING);
878 gtk_box_pack_start(GTK_BOX(autolevels_box), c->bt_auto_levels, TRUE, TRUE, 0);
879 gtk_box_pack_start(GTK_BOX(autolevels_box), c->bt_select_region, TRUE, TRUE, 0);
880
881 gtk_box_pack_start(GTK_BOX(self->widget), autolevels_box, TRUE, TRUE, 0);
882
883 g_signal_connect(G_OBJECT(c->bt_auto_levels), "clicked", G_CALLBACK(_auto_levels_callback), self);
884 g_signal_connect(G_OBJECT(c->bt_select_region), "toggled", G_CALLBACK(_select_region_toggled_callback), self);
885
886 c->cmb_preserve_colors = dt_bauhaus_combobox_from_params(self, "preserve_colors");
887 gtk_widget_set_tooltip_text(c->cmb_preserve_colors, _("method to preserve colors when applying contrast"));
888
889 // add signal handler for preview pipe finish
891 G_CALLBACK(_develop_ui_pipe_finished_callback), self);
892}
893
900
902static void _get_selected_area(struct dt_iop_module_t *self, const dt_dev_pixelpipe_t *pipe,
903 const dt_dev_pixelpipe_iop_t *piece,
904 dt_iop_rgblevels_gui_data_t *g, const dt_iop_roi_t *const roi_in, int *box_out)
905{
906 box_out[0] = box_out[1] = box_out[2] = box_out[3] = 0;
907
908 if(!IS_NULL_PTR(g))
909 {
910 const int width = roi_in->width;
911 const int height = roi_in->height;
912 dt_boundingbox_t box_cood = { g->box_cood[0], g->box_cood[1], g->box_cood[2], g->box_cood[3] };
913
914 box_cood[0] *= pipe->iwidth;
915 box_cood[1] *= pipe->iheight;
916 box_cood[2] *= pipe->iwidth;
917 box_cood[3] *= pipe->iheight;
918
920 box_cood, 2);
921
922 box_cood[0] *= roi_in->scale;
923 box_cood[1] *= roi_in->scale;
924 box_cood[2] *= roi_in->scale;
925 box_cood[3] *= roi_in->scale;
926
927 box_cood[0] -= roi_in->x;
928 box_cood[1] -= roi_in->y;
929 box_cood[2] -= roi_in->x;
930 box_cood[3] -= roi_in->y;
931
932 int DT_ALIGNED_ARRAY box[4];
933
934 // re-order edges of bounding box
935 box[0] = fminf(box_cood[0], box_cood[2]);
936 box[1] = fminf(box_cood[1], box_cood[3]);
937 box[2] = fmaxf(box_cood[0], box_cood[2]);
938 box[3] = fmaxf(box_cood[1], box_cood[3]);
939
940 // do not continue if box is completely outside of roi
941 if(!(box[0] >= width || box[1] >= height || box[2] < 0 || box[3] < 0))
942 {
943 // clamp bounding box to roi
944 for(int k = 0; k < 4; k += 2) box[k] = MIN(width - 1, MAX(0, box[k]));
945 for(int k = 1; k < 4; k += 2) box[k] = MIN(height - 1, MAX(0, box[k]));
946
947 // safety check: area needs to have minimum 1 pixel width and height
948 if(!(box[2] - box[0] < 1 || box[3] - box[1] < 1))
949 {
950 box_out[0] = box[0];
951 box_out[1] = box[1];
952 box_out[2] = box[2];
953 box_out[3] = box[3];
954 }
955 }
956 }
957}
958
960static void _auto_levels(const float *const img, const int width, const int height, int *box_area,
961 dt_iop_rgblevels_params_t *p, const int _channel, const dt_iop_order_iccprofile_info_t *const work_profile)
962{
963 int y_from, y_to, x_from, x_to;
964 const int ch = 4;
965 const int channel = (p->autoscale == DT_IOP_RGBLEVELS_INDEPENDENT_CHANNELS) ? _channel : 0;
966 if(box_area[2] > box_area[0] && box_area[3] > box_area[1])
967 {
968 y_from = box_area[1];
969 y_to = box_area[3];
970 x_from = box_area[0];
971 x_to = box_area[2];
972 }
973 else
974 {
975 y_from = 0;
976 y_to = height - 1;
977 x_from = 0;
978 x_to = width - 1;
979 }
980
981 float max = -INFINITY;
982 float min = INFINITY;
983
984 for(int y = y_from; y <= y_to; y++)
985 {
986 const float *const in = img + ch * width * y;
987 for(int x = x_from; x <= x_to; x++)
988 {
989 const float *const pixel = in + x * ch;
990
991 if(p->autoscale == DT_IOP_RGBLEVELS_INDEPENDENT_CHANNELS || p->preserve_colors == DT_RGB_NORM_NONE)
992 {
994 {
995 if(pixel[channel] >= 0.f)
996 {
997 max = fmaxf(max, pixel[channel]);
998 min = fminf(min, pixel[channel]);
999 }
1000 }
1001 else
1002 {
1003 for(int c = 0; c < 3; c++)
1004 {
1005 if(pixel[c] >= 0.f)
1006 {
1007 max = fmaxf(max, pixel[c]);
1008 min = fminf(min, pixel[c]);
1009 }
1010 }
1011 }
1012 }
1013 else
1014 {
1015 const float lum = dt_rgb_norm(pixel, p->preserve_colors, work_profile);
1016 if(lum >= 0.f)
1017 {
1018 max = fmaxf(max, lum);
1019 min = fminf(min, lum);
1020 }
1021 }
1022 }
1023 }
1024
1025 p->levels[channel][0] = CLAMP(min, 0.f, 1.f);
1026 p->levels[channel][2] = CLAMP(max, 0.f, 1.f);
1027 p->levels[channel][1] = (p->levels[channel][2] + p->levels[channel][0]) / 2.f;
1028}
1029
1032 const void *const ivoid, void *const ovoid)
1033{
1034 const dt_iop_roi_t *const roi_in = &piece->roi_in;
1035 const dt_iop_roi_t *const roi_out = &piece->roi_out;
1036
1037 const dt_iop_rgblevels_data_t *const d = (dt_iop_rgblevels_data_t *)piece->data;
1041
1042 // process auto levels
1043 if(!IS_NULL_PTR(g) && dt_dev_pixelpipe_has_preview_output(self->dev, pipe, roi_out))
1044 {
1046 if(g->call_auto_levels == 1 && !darktable.gui->reset)
1047 {
1048 g->call_auto_levels = -1;
1049
1051
1052 memcpy(&g->params, p, sizeof(dt_iop_rgblevels_params_t));
1053
1054 int box[4] = { 0 };
1055 _get_selected_area(self, pipe, piece, g, roi_in, box);
1056 _auto_levels((const float *const)ivoid, roi_in->width, roi_in->height, box, &(g->params), g->channel, work_profile);
1057
1059 g->call_auto_levels = 2;
1061 }
1062 else
1063 {
1065 }
1066 }
1067
1068 const dt_aligned_pixel_t mult = { 1.f / (d->params.levels[0][2] - d->params.levels[0][0]),
1069 1.f / (d->params.levels[1][2] - d->params.levels[1][0]),
1070 1.f / (d->params.levels[2][2] - d->params.levels[2][0]) };
1071
1072 const size_t npixels = (size_t)roi_out->width * roi_out->height;
1073 const float *const restrict in = (const float*)ivoid;
1074 float *const restrict out = (float*)ovoid;
1075 if (d->params.autoscale == DT_IOP_RGBLEVELS_INDEPENDENT_CHANNELS || d->params.preserve_colors == DT_RGB_NORM_NONE)
1076 {
1078 for(int k = 0; k < 4U*npixels; k += 4)
1079 {
1080 for(int c = 0; c < 3; c++)
1081 {
1082 const float L_in = in[k+c];
1083
1084 if(L_in <= d->params.levels[c][0])
1085 {
1086 // Anything below the lower threshold just clips to zero
1087 out[k+c] = 0.0f;
1088 }
1089 else if(L_in >= d->params.levels[c][2])
1090 {
1091 const float percentage = (L_in - d->params.levels[c][0]) * mult[c];
1092 out[k+c] = powf(percentage, d->inv_gamma[c]);
1093 }
1094 else
1095 {
1096 // Within the expected input range we can use the lookup table
1097 const float percentage = (L_in - d->params.levels[c][0]) * mult[c];
1098 out[k+c] = d->lut[c][CLAMP((int)(percentage * 0x10000ul), 0, 0xffff)];
1099 }
1100 }
1101 out[k+3] = in[k+3];
1102 }
1103 }
1104 else
1105 {
1106 const int ch_levels = 0;
1107 const float mult_ch = mult[ch_levels];
1108 const float *const restrict levels = d->params.levels[ch_levels];
1110 for(int k = 0; k < 4U*npixels; k += 4)
1111 {
1112 const float lum = dt_rgb_norm(in+k, d->params.preserve_colors, work_profile);
1113 if(lum > levels[0])
1114 {
1115 float curve_lum;
1116 const float percentage = (lum - levels[0]) * mult_ch;
1117 if(lum >= levels[2])
1118 {
1119 curve_lum = powf(percentage, d->inv_gamma[ch_levels]);
1120 }
1121 else
1122 {
1123 // Within the expected input range we can use the lookup table
1124 curve_lum = d->lut[ch_levels][CLAMP((int)(percentage * 0x10000ul), 0, 0xffff)];
1125 }
1126
1127 const float ratio = curve_lum / lum;
1128
1129 for_each_channel(c,aligned(in,out:16))
1130 {
1131 out[k+c] = (ratio * in[k+c]);
1132 }
1133 }
1134 else
1135 {
1136 for_each_channel(c,aligned(out:16))
1137 out[k+c] = 0.f;
1138 }
1139 out[k+3] = in[k+3];
1140 }
1141 }
1142
1143 return 0;
1144}
1145
1146
1147// clang-format off
1148// modelines: These editor modelines have been set for all relevant files by tools/update_modelines.py
1149// vim: shiftwidth=2 expandtab tabstop=2 cindent
1150// kate: tab-indents: off; indent-width 2; replace-tabs on; indent-mode cstyle; remove-trailing-spaces modified;
1151// 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:2301
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:181
#define dt_free_align(ptr)
Definition darktable.h:481
static void * dt_calloc_align(size_t size)
Definition darktable.h:488
#define DT_ALIGNED_ARRAY
Definition darktable.h:388
#define for_each_channel(_var,...)
Definition darktable.h:662
float dt_boundingbox_t[4]
Definition darktable.h:709
#define DT_MODULE_INTROSPECTION(MODVER, PARAMSTYPE)
Definition darktable.h:151
#define __DT_CLONE_TARGETS__
Definition darktable.h:367
#define __OMP_PARALLEL_FOR__(...)
Definition darktable.h:258
#define IS_NULL_PTR(p)
C is way too permissive with !=, == and if(var) checks, which can mean too many things depending on w...
Definition darktable.h:281
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:41
#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:1557
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:367
@ 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:219
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:2836
GtkWidget * dt_ui_notebook_page(GtkNotebook *notebook, const char *text, const char *tooltip)
Definition gtk.c:2259
static cairo_surface_t * dt_cairo_image_surface_create(cairo_format_t format, int width, int height)
Definition gtk.h:316
#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:316
void dt_iop_request_focus(dt_iop_module_t *module)
Definition imageop.c:2169
const char ** dt_iop_set_description(dt_iop_module_t *module, const char *main_text, const char *purpose, const char *input, const char *process, const char *output)
Definition imageop.c:3141
#define IOP_GUI_FREE
Definition imageop.h:602
static void dt_iop_gui_enter_critical_section(dt_iop_module_t *const module) ACQUIRE(&module -> gui_lock)
Definition imageop.h:413
@ IOP_FLAGS_DEPRECATED
Definition imageop.h:168
@ IOP_FLAGS_SUPPORTS_BLENDING
Definition imageop.h:167
static void dt_iop_gui_leave_critical_section(dt_iop_module_t *const module) RELEASE(&module -> gui_lock)
Definition imageop.h:419
@ IOP_GROUP_COLOR
Definition imageop.h:139
#define IOP_GUI_ALLOC(module)
Definition imageop.h:599
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:960
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:1031
#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:894
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:902
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:368
@ 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:357
struct _GtkWidget GtkWidget
Definition splash.h:29
struct dt_gui_gtk_t * gui
Definition darktable.h:775
struct dt_control_signal_t * signals
Definition darktable.h:774
struct dt_bauhaus_t * bauhaus
Definition darktable.h:778
struct dt_develop_t * develop
Definition darktable.h:770
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:224
int32_t reset
Definition gtk.h:172
GtkDarktableToggleButton * off
Definition imageop.h:339
dt_iop_params_t * default_params
Definition imageop.h:307
GtkWidget * widget
Definition imageop.h:337
struct dt_develop_t * dev
Definition imageop.h:296
dt_iop_gui_data_t * gui_data
Definition imageop.h:311
dt_dev_request_flags_t request_histogram
Definition imageop.h:266
uint32_t histogram_max[4]
Definition imageop.h:280
gboolean enabled
Definition imageop.h:298
uint32_t * histogram
Definition imageop.h:276
dt_aligned_pixel_t picked_color
Definition imageop.h:272
dt_iop_params_t * params
Definition imageop.h:307
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
#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)