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"
38#endif
39
40#include "system/macros.h"
41#include "system/openmp.h"
43#include "system/mem_alloc.h"
44#include "system/simd.h"
46#include "develop/iop_profile.h"
47#include "widgets/bauhaus.h"
48#include "pixel/rgb_norms.h"
49#include "develop/imageop.h"
50#include "develop/imageop_gui.h"
51
53#include "widgets/draw.h"
54#include "widgets/notebook.h"
55#include "widgets/scroll_wrap.h"
56#include "gui/screen_metrics.h"
58#include "control/signal.h"
59
60#define DT_GUI_CURVE_EDITOR_INSET DT_PIXEL_APPLY_DPI(5)
61
63
64#define RGBLEVELS_MIN 0.f
65#define RGBLEVELS_MID 0.5f
66#define RGBLEVELS_MAX 1.f
67
75
77{
78 DT_IOP_RGBLEVELS_LINKED_CHANNELS = 0, // $DESCRIPTION: "RGB, linked channels"
79 DT_IOP_RGBLEVELS_INDEPENDENT_CHANNELS = 1 // $DESCRIPTION: "RGB, independent channels"
81
83{
84 dt_iop_rgblevels_autoscale_t autoscale; // $DEFAULT: DT_IOP_RGBLEVELS_LINKED_CHANNELS $DESCRIPTION: "mode" (DT_IOP_RGBLEVELS_INDEPENDENT_CHANNELS, DT_IOP_RGBLEVELS_LINKED_CHANNELS)
85 dt_iop_rgb_norms_t preserve_colors; // $DEFAULT: DT_RGB_NORM_LUMINANCE $DESCRIPTION: "preserve colors"
88
90{
92
93 GtkWidget *cmb_autoscale; // (DT_IOP_RGBLEVELS_INDEPENDENT_CHANNELS, DT_IOP_RGBLEVELS_LINKED_CHANNELS)
94 GtkDrawingArea *area;
96 GtkNotebook *channel_tabs;
99
100 int call_auto_levels; // should we calculate levels automatically?
101 int draw_selected_region; // are we drawing the selected region?
102 float posx_from, posx_to, posy_from, posy_to; // coordinates of the area
103 dt_boundingbox_t box_cood; // normalized coordinates
104 int button_down; // user pressed the mouse button?
105
113
120
121const char *name()
122{
123 return _("levels");
124}
125
127{
128 return IOP_GROUP_COLOR;
129}
130
135
137{
138 return IOP_CS_RGB;
139}
140
141const char **description(struct dt_iop_module_t *self)
142{
143 return dt_iop_set_description(self, _("adjust black, white and mid-gray points in RGB color space"),
144 _("corrective and creative"),
145 _("linear, RGB, display-referred"),
146 _("non-linear, RGB"),
147 _("non-linear, RGB, display-referred"));
148}
149
151{
153 if(!IS_NULL_PTR(g))
154 {
155 g->button_down = g->draw_selected_region = 0;
156 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(g->bt_select_region), g->draw_selected_region);
157 }
158}
159
165
166static void _develop_ui_pipe_finished_callback(gpointer instance, dt_iop_module_t *self)
167{
170
171 if(IS_NULL_PTR(g)) return;
172
173 // FIXME: this doesn't seems the right place to update params and GUI ...
174 // update auto levels
176 if(g->call_auto_levels == 2)
177 {
178 g->call_auto_levels = -1;
179
181
182 memcpy(p, &g->params, sizeof(dt_iop_rgblevels_params_t));
183
184 dt_dev_add_history_item(self->dev, self, TRUE, TRUE);
185
187 g->call_auto_levels = 0;
189
191
192 gui_update(self);
193
195 }
196 else
197 {
199 }
200}
201
202static void _compute_lut(const dt_dev_pixelpipe_iop_t *piece)
203{
205
206 // Building the lut for values in the [0,1] range
207 if(d->params.autoscale == DT_IOP_RGBLEVELS_LINKED_CHANNELS)
208 {
209 const int c = 0;
210 const float delta = (d->params.levels[c][2] - d->params.levels[c][0]) / 2.0f;
211 const float mid = d->params.levels[c][0] + delta;
212 const float tmp = (d->params.levels[c][1] - mid) / delta;
213 d->inv_gamma[0] = d->inv_gamma[1] = d->inv_gamma[2] = pow(10, tmp);
214
215 for(unsigned int i = 0; i < 0x10000; i++)
216 {
217 float percentage = (float)i / (float)0x10000ul;
218 d->lut[0][i] = d->lut[1][i] = d->lut[2][i] = pow(percentage, d->inv_gamma[c]);
219 }
220 }
221 else
222 {
223 for(int c = 0; c < 3; c++)
224 {
225 const float delta = (d->params.levels[c][2] - d->params.levels[c][0]) / 2.0f;
226 const float mid = d->params.levels[c][0] + delta;
227 const float tmp = (d->params.levels[c][1] - mid) / delta;
228 d->inv_gamma[c] = pow(10, tmp);
229
230 for(unsigned int i = 0; i < 0x10000; i++)
231 {
232 float percentage = (float)i / (float)0x10000ul;
233 d->lut[c][i] = pow(percentage, d->inv_gamma[c]);
234 }
235 }
236 }
237}
238
240{
241 switch(p->autoscale)
242 {
244 gtk_notebook_set_show_tabs(g->channel_tabs, TRUE);
245 break;
247 gtk_notebook_set_show_tabs(g->channel_tabs, FALSE);
248 break;
249 }
250
251 if(p->autoscale == DT_IOP_RGBLEVELS_LINKED_CHANNELS)
252 gtk_widget_set_visible(g->cmb_preserve_colors, TRUE);
253 else
254 gtk_widget_set_visible(g->cmb_preserve_colors, FALSE);
255}
256
257static gboolean _area_leave_notify_callback(GtkWidget *widget, GdkEventCrossing *event, dt_iop_module_t *self)
258{
260 c->mouse_x = c->mouse_y = -1.0;
261 gtk_widget_queue_draw(widget);
262 return TRUE;
263}
264
265static gboolean _area_draw_callback(GtkWidget *widget, cairo_t *crf, dt_iop_module_t *self)
266{
269
270 const int inset = DT_GUI_CURVE_EDITOR_INSET;
271 GtkAllocation allocation;
272 gtk_widget_get_allocation(GTK_WIDGET(c->area), &allocation);
273 int width = allocation.width, height = allocation.height;
274 cairo_surface_t *cst = dt_cairo_image_surface_create(CAIRO_FORMAT_ARGB32, width, height);
275 cairo_t *cr = cairo_create(cst);
276
277 // clear bg
278 cairo_set_source_rgb(cr, .2, .2, .2);
279 cairo_paint(cr);
280
281 cairo_translate(cr, inset, inset);
282 width -= 2 * inset;
283 height -= 2 * inset;
284
285 cairo_set_line_width(cr, DT_PIXEL_APPLY_DPI(1.0));
286 cairo_set_source_rgb(cr, .1, .1, .1);
287 cairo_rectangle(cr, 0, 0, width, height);
288 cairo_stroke(cr);
289
290 cairo_set_source_rgb(cr, .3, .3, .3);
291 cairo_rectangle(cr, 0, 0, width, height);
292 cairo_fill(cr);
293
294 // draw grid
295 cairo_set_line_width(cr, DT_PIXEL_APPLY_DPI(.4));
296 cairo_set_source_rgb(cr, .1, .1, .1);
297 dt_draw_vertical_lines(cr, 4, 0, 0, width, height);
298
299 // Drawing the vertical line indicators
300 cairo_set_line_width(cr, DT_PIXEL_APPLY_DPI(2.));
301
302 for(int k = 0; k < 3; k++)
303 {
304 if(k == c->handle_move && c->mouse_x > 0)
305 cairo_set_source_rgb(cr, 1, 1, 1);
306 else
307 cairo_set_source_rgb(cr, .7, .7, .7);
308
309 cairo_move_to(cr, width * p->levels[c->channel][k], height);
310 cairo_rel_line_to(cr, 0, -height);
311 cairo_stroke(cr);
312 }
313
314 // draw x positions
315 cairo_set_line_width(cr, DT_PIXEL_APPLY_DPI(1.));
316 const float arrw = DT_PIXEL_APPLY_DPI(7.0f);
317 for(int k = 0; k < 3; k++)
318 {
319 switch(k)
320 {
321 case 0:
322 cairo_set_source_rgb(cr, 0, 0, 0);
323 break;
324
325 case 1:
326 cairo_set_source_rgb(cr, 0.5, 0.5, 0.5);
327 break;
328
329 default:
330 cairo_set_source_rgb(cr, 1, 1, 1);
331 break;
332 }
333
334 cairo_move_to(cr, width * p->levels[c->channel][k], height + inset - 1);
335 cairo_rel_line_to(cr, -arrw * .5f, 0);
336 cairo_rel_line_to(cr, arrw * .5f, -arrw);
337 cairo_rel_line_to(cr, arrw * .5f, arrw);
338 cairo_close_path(cr);
339 if(c->handle_move == k && c->mouse_x > 0)
340 cairo_fill(cr);
341 else
342 cairo_stroke(cr);
343 }
344
345 cairo_translate(cr, 0, height);
346
347 // draw histogram in background
348 // only if the module is enabled
349 if(self->enabled)
350 {
351 const int ch = c->channel;
352 const uint32_t *hist = self->histogram;
353 const gboolean is_linear = FALSE;
354 float hist_max;
355
356 if(p->autoscale == DT_IOP_RGBLEVELS_LINKED_CHANNELS)
358 else
359 hist_max = self->histogram_max[ch];
360
361 if (!is_linear)
362 hist_max = logf(1.0 + hist_max);
363
364 if(!IS_NULL_PTR(hist) && hist_max > 0.0f)
365 {
366 cairo_push_group_with_content(cr, CAIRO_CONTENT_COLOR);
367 cairo_scale(cr, width / 255.0, -(height - DT_PIXEL_APPLY_DPI(5)) / hist_max);
368
369 if(p->autoscale == DT_IOP_RGBLEVELS_LINKED_CHANNELS)
370 {
371 cairo_set_operator(cr, CAIRO_OPERATOR_ADD);
373 {
374 set_color(cr, dt_bauhaus_get_global()->graph_colors[k]);
375 dt_draw_histogram_8(cr, hist, 4, k, is_linear);
376 }
377 }
378 else if(p->autoscale == DT_IOP_RGBLEVELS_INDEPENDENT_CHANNELS)
379 {
380 set_color(cr, dt_bauhaus_get_global()->graph_colors[ch]);
381 dt_draw_histogram_8(cr, hist, 4, ch, is_linear);
382 }
383
384 cairo_pop_group_to_source(cr);
385 cairo_paint_with_alpha(cr, 0.2);
386 }
387 }
388
389 // Cleaning up
390 cairo_destroy(cr);
391 cairo_set_source_surface(crf, cst, 0, 0);
392 cairo_paint(crf);
393 cairo_surface_destroy(cst);
394 return TRUE;
395}
396
397static void _rgblevels_move_handle(dt_iop_module_t *self, const int handle_move, const float new_pos, float *levels,
398 const float drag_start_percentage)
399{
401 float min_x = 0.f;
402 float max_x = 1.f;
403
404 if((handle_move < 0) || handle_move > 2) return;
405
406 if(IS_NULL_PTR(levels)) return;
407
408 // Determining the minimum and maximum bounds for the drag handles
409 switch(handle_move)
410 {
411 case 0:
412 max_x = fminf(levels[2] - (0.05 / drag_start_percentage), 1);
413 max_x = fminf((levels[2] * (1 - drag_start_percentage) - 0.05) / (1 - drag_start_percentage), max_x);
414 break;
415
416 case 1:
417 min_x = levels[0] + 0.05;
418 max_x = levels[2] - 0.05;
419 break;
420
421 case 2:
422 min_x = fmaxf((0.05 / drag_start_percentage) + levels[0], 0);
423 min_x = fmaxf((levels[0] * (1 - drag_start_percentage) + 0.05) / (1 - drag_start_percentage), min_x);
424 break;
425 }
426
427 levels[handle_move] = fminf(max_x, fmaxf(min_x, new_pos));
428
429 if(handle_move != 1) levels[1] = levels[0] + (drag_start_percentage * (levels[2] - levels[0]));
430
431 c->last_picked_color = -1;
432
433 dt_dev_add_history_item(self->dev, self, TRUE, TRUE);
434
435 gtk_widget_queue_draw(GTK_WIDGET(c->area));
436}
437
438static gboolean _area_motion_notify_callback(GtkWidget *widget, GdkEventMotion *event, dt_iop_module_t *self)
439{
442 const int inset = DT_GUI_CURVE_EDITOR_INSET;
443 GtkAllocation allocation;
444 gtk_widget_get_allocation(widget, &allocation);
445 int height = allocation.height - 2 * inset, width = allocation.width - 2 * inset;
446 if(!c->dragging)
447 {
448 c->mouse_x = CLAMP(event->x - inset, 0, width);
449 c->drag_start_percentage = (p->levels[c->channel][1] - p->levels[c->channel][0]) / (p->levels[c->channel][2] - p->levels[c->channel][0]);
450 }
451 c->mouse_y = CLAMP(event->y - inset, 0, height);
452
453 if(c->dragging)
454 {
455 if(c->handle_move >= 0 && c->handle_move < 3)
456 {
457 const float mx = (CLAMP(event->x - inset, 0, width)) / (float)width;
458
459 _rgblevels_move_handle(self, c->handle_move, mx, p->levels[c->channel], c->drag_start_percentage);
460 }
461 }
462 else
463 {
464 c->handle_move = 0;
465 const float mx = CLAMP(event->x - inset, 0, width) / (float)width;
466 float dist = fabsf(p->levels[c->channel][0] - mx);
467 for(int k = 1; k < 3; k++)
468 {
469 float d2 = fabsf(p->levels[c->channel][k] - mx);
470 if(d2 < dist)
471 {
472 c->handle_move = k;
473 dist = d2;
474 }
475 }
476
477 gtk_widget_queue_draw(widget);
478 }
479
480 return TRUE;
481}
482
483static gboolean _area_button_press_callback(GtkWidget *widget, GdkEventButton *event, dt_iop_module_t *self)
484{
485 // set active point
486 if(event->button == 1)
487 {
488 if(self->dev->gui_module != self) dt_iop_request_focus(self);
489
490 if(event->type == GDK_2BUTTON_PRESS)
491 {
493
494 // Reset
498
499 for(int i = 0; i < 3; i++)
500 p->levels[c->channel][i] = default_params->levels[c->channel][i];
501
502 // Needed in case the user scrolls or drags immediately after a reset,
503 // as drag_start_percentage is only updated when the mouse is moved.
504 c->drag_start_percentage = 0.5;
505 dt_dev_add_history_item(self->dev, self, TRUE, TRUE);
506 gtk_widget_queue_draw(self->gui->widget);
507 }
508 else
509 {
511
513 c->dragging = 1;
514 }
515 return TRUE;
516 }
517 return FALSE;
518}
519
520static gboolean _area_button_release_callback(GtkWidget *widget, GdkEventButton *event, dt_iop_module_t *self)
521{
522 if(event->button == 1)
523 {
525 c->dragging = 0;
526 return TRUE;
527 }
528 return FALSE;
529}
530
531static gboolean _area_scroll_callback(GtkWidget *widget, GdkEventScroll *event, dt_iop_module_t *self)
532{
535
536 int delta_y;
537
539
540 if(c->dragging)
541 {
542 return FALSE;
543 }
544
545 if(self->dev->gui_module != self) dt_iop_request_focus(self);
546
547 const float interval = 0.002; // Distance moved for each scroll event
548 if(dt_gui_get_scroll_unit_deltas(event, NULL, &delta_y))
549 {
550 const float new_position = p->levels[c->channel][c->handle_move] - interval * delta_y;
551 _rgblevels_move_handle(self, c->handle_move, new_position, p->levels[c->channel], c->drag_start_percentage);
552 return TRUE;
553 }
554
555 return TRUE; // Ensure that scrolling the widget cannot move side panel
556}
557
558static void _auto_levels_callback(GtkButton *button, dt_iop_module_t *self)
559{
560 if(dt_gui_widgets_suppressed()) return;
561
563
565 if(self->gui->off)
566 {
567 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(self->gui->off), 1);
568 dt_dev_add_history_item(self->dev, self, TRUE, TRUE);
569 }
570
572
574 if(g->call_auto_levels == 0)
575 {
576 g->box_cood[0] = g->box_cood[1] = g->box_cood[2] = g->box_cood[3] = 0.f;
577 g->call_auto_levels = 1;
578 }
580
582}
583
584static void _select_region_toggled_callback(GtkToggleButton *togglebutton, dt_iop_module_t *self)
585{
586 if(dt_gui_widgets_suppressed()) return;
587
589
591 if(self->gui->off)
592 {
593 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(self->gui->off), 1);
594 dt_dev_add_history_item(self->dev, self, TRUE, TRUE);
595 }
596
598
600
601 if(gtk_toggle_button_get_active(togglebutton))
602 {
603 g->draw_selected_region = 1;
604 }
605 else
606 g->draw_selected_region = 0;
607
608 g->posx_from = g->posx_to = g->posy_from = g->posy_to = 0;
609
611}
612
613void gui_changed(dt_iop_module_t *self, GtkWidget *w, void *previous)
614{
617
619
620 if(w == g->cmb_autoscale)
621 {
622 g->channel = DT_IOP_RGBLEVELS_R;
623 gtk_notebook_set_current_page(GTK_NOTEBOOK(g->channel_tabs), g->channel);
624
626 }
627}
628
629static void _tab_switch_callback(GtkNotebook *notebook, GtkWidget *page, guint page_num, dt_iop_module_t *self)
630{
631 if(dt_gui_widgets_suppressed()) return;
633
634 g->channel = (dt_iop_rgblevels_channel_t)page_num;
635
636 gtk_widget_queue_draw(self->gui->widget);
637}
638
640{
642}
643
645{
646 (void)pipe;
647 (void)piece;
650
651 const dt_iop_rgblevels_channel_t channel = c->channel;
652
653 /* we need to save the last picked color to prevent flickering when
654 * changing from one picker to another, as the picked_color value does not
655 * update as rapidly */
656 const float mean_picked_color = *self->picked_color;
657
658 if(mean_picked_color != c->last_picked_color)
659 {
660 dt_aligned_pixel_t previous_color;
661 previous_color[0] = p->levels[channel][0];
662 previous_color[1] = p->levels[channel][1];
663 previous_color[2] = p->levels[channel][2];
664
665 c->last_picked_color = mean_picked_color;
666
667 if(picker == c->blackpick)
668 {
669 if(mean_picked_color > p->levels[channel][1])
670 {
671 p->levels[channel][0] = p->levels[channel][1] - FLT_EPSILON;
672 }
673 else
674 {
675 p->levels[channel][0] = mean_picked_color;
676 }
677 }
678 else if(picker == c->greypick)
679 {
680 if(mean_picked_color < p->levels[channel][0] || mean_picked_color > p->levels[channel][2])
681 {
682 p->levels[channel][1] = p->levels[channel][1];
683 }
684 else
685 {
686 p->levels[channel][1] = mean_picked_color;
687 }
688 }
689 else if(picker == c->whitepick)
690 {
691 if(mean_picked_color < p->levels[channel][1])
692 {
693 p->levels[channel][2] = p->levels[channel][1] + FLT_EPSILON;
694 }
695 else
696 {
697 p->levels[channel][2] = mean_picked_color;
698 }
699 }
700
701 if(previous_color[0] != p->levels[channel][0]
702 || previous_color[1] != p->levels[channel][1]
703 || previous_color[2] != p->levels[channel][2])
704 {
705 dt_dev_add_history_item(self->dev, self, TRUE, TRUE);
706 }
707 }
708
709}
710
713{
716
717 if(dt_dev_pixelpipe_has_preview_output(self->dev, pipe, NULL))
719 else
720 piece->request_histogram &= ~(DT_REQUEST_ON);
721
722 memcpy(&(d->params), p, sizeof(dt_iop_rgblevels_params_t));
723
724 for(int i = 0; i < DT_IOP_RGBLEVELS_MAX_CHANNELS; i++)
725 {
726 for(int c = 0; c < 3; c++)
727 {
728 if(d->params.autoscale == DT_IOP_RGBLEVELS_LINKED_CHANNELS)
729 d->params.levels[i][c] = p->levels[0][c];
730 else
731 d->params.levels[i][c] = p->levels[i][c];
732 }
733 }
734
735 _compute_lut(piece);
736}
737
743
745{
746 // clean up everything again.
747 dt_free_align(piece->data);
748 piece->data = NULL;
749}
750
752{
755
756 dt_bauhaus_combobox_set(g->cmb_autoscale, p->autoscale);
757 dt_bauhaus_combobox_set(g->cmb_preserve_colors, p->preserve_colors);
758 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(g->bt_select_region), g->draw_selected_region);
760
761 gtk_widget_queue_draw(self->gui->widget);
762}
763
764void gui_focus(struct dt_iop_module_t *self, gboolean in)
765{
766 if(!in) _turn_select_region_off(self);
767}
768
769void gui_reset(struct dt_iop_module_t *self)
770{
772
774
775 g->channel = DT_IOP_RGBLEVELS_R;
776
777 gtk_widget_queue_draw(self->gui->widget);
778}
779
781{
783
785
787
788 for(int c = 0; c < DT_IOP_RGBLEVELS_MAX_CHANNELS; c++)
789 {
790 d->levels[c][0] = RGBLEVELS_MIN;
791 d->levels[c][1] = RGBLEVELS_MID;
792 d->levels[c][2] = RGBLEVELS_MAX;
793 }
794}
795
797{
799
800 g->channel = DT_IOP_RGBLEVELS_R;
801 g->call_auto_levels = 0;
802 g->draw_selected_region = 0;
803 g->posx_from = g->posx_to = g->posy_from = g->posy_to = 0.f;
804 g->box_cood[0] = g->box_cood[1] = g->box_cood[2] = g->box_cood[3] = 0.f;
805 g->button_down = 0;
806}
807
809{
811
812 change_image(self);
813
814 c->mouse_x = c->mouse_y = -1.0;
815 c->dragging = 0;
816 c->last_picked_color = -1;
817
818 c->cmb_autoscale = dt_bauhaus_combobox_from_params(self, "autoscale");
819 gtk_widget_set_tooltip_text(c->cmb_autoscale, _("choose between linked and independent channels."));
820
821 c->channel_tabs = GTK_NOTEBOOK(gtk_notebook_new());
822 dt_ui_notebook_page(c->channel_tabs, N_("R"), _("curve nodes for r channel"));
823 dt_ui_notebook_page(c->channel_tabs, N_("G"), _("curve nodes for g channel"));
824 dt_ui_notebook_page(c->channel_tabs, N_("B"), _("curve nodes for b channel"));
825 g_signal_connect(G_OBJECT(c->channel_tabs), "switch_page", G_CALLBACK(_tab_switch_callback), self);
826 dt_ui_notebook_set_picker_owner(c->channel_tabs, self);
827 gtk_box_pack_start(GTK_BOX(self->gui->widget), GTK_WIDGET(c->channel_tabs), FALSE, FALSE, 0);
828
829 c->area = GTK_DRAWING_AREA(gtk_drawing_area_new());
830 gtk_widget_set_hexpand(GTK_WIDGET(c->area), TRUE);
831
832 gtk_box_pack_start(GTK_BOX(self->gui->widget),
833 dt_ui_resizable_drawing_area(GTK_WIDGET(c->area),
834 "plugins/darkroom/rgblevels/graphheight", 280, 100),
835 FALSE, FALSE, 0);
836
837 g_object_set_data(G_OBJECT(c->area), "iop-instance", self);
838
839 gtk_widget_set_tooltip_text(GTK_WIDGET(c->area),_("drag handles to set black, gray, and white points. "
840 "operates on L channel."));
841
842 gtk_widget_add_events(GTK_WIDGET(c->area), GDK_POINTER_MOTION_MASK
843 | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK
844 | GDK_LEAVE_NOTIFY_MASK | GDK_ENTER_NOTIFY_MASK
846 g_signal_connect(G_OBJECT(c->area), "draw", G_CALLBACK(_area_draw_callback), self);
847 g_signal_connect(G_OBJECT(c->area), "button-press-event", G_CALLBACK(_area_button_press_callback), self);
848 g_signal_connect(G_OBJECT(c->area), "button-release-event", G_CALLBACK(_area_button_release_callback), self);
849 g_signal_connect(G_OBJECT(c->area), "motion-notify-event", G_CALLBACK(_area_motion_notify_callback), self);
850 g_signal_connect(G_OBJECT(c->area), "leave-notify-event", G_CALLBACK(_area_leave_notify_callback), self);
851 g_signal_connect(G_OBJECT(c->area), "scroll-event", G_CALLBACK(_area_scroll_callback), self);
852
853 c->blackpick = dt_color_picker_new(self, DT_COLOR_PICKER_POINT, NULL);
854 gtk_widget_set_tooltip_text(c->blackpick, _("pick black point from image"));
855 gtk_widget_set_name(GTK_WIDGET(c->blackpick), "picker-black");
856 g_signal_connect(G_OBJECT(c->blackpick), "toggled", G_CALLBACK(_color_picker_callback), self);
857
858 c->greypick = dt_color_picker_new(self, DT_COLOR_PICKER_POINT, NULL);
859 gtk_widget_set_tooltip_text(c->greypick, _("pick medium gray point from image"));
860 gtk_widget_set_name(GTK_WIDGET(c->greypick), "picker-grey");
861 g_signal_connect(G_OBJECT(c->greypick), "toggled", G_CALLBACK(_color_picker_callback), self);
862
863 c->whitepick = dt_color_picker_new(self, DT_COLOR_PICKER_POINT, NULL);
864 gtk_widget_set_tooltip_text(c->whitepick, _("pick white point from image"));
865 gtk_widget_set_name(GTK_WIDGET(c->whitepick), "picker-white");
866 g_signal_connect(G_OBJECT(c->whitepick), "toggled", G_CALLBACK(_color_picker_callback), self);
867
868 GtkWidget *pick_hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, DT_GUI_BOX_SPACING);
869 gtk_box_pack_start(GTK_BOX(pick_hbox), GTK_WIDGET(c->blackpick), TRUE, TRUE, 0);
870 gtk_box_pack_start(GTK_BOX(pick_hbox), GTK_WIDGET(c->greypick ), TRUE, TRUE, 0);
871 gtk_box_pack_start(GTK_BOX(pick_hbox), GTK_WIDGET(c->whitepick), TRUE, TRUE, 0);
872
873 gtk_box_pack_start(GTK_BOX(self->gui->widget), pick_hbox, TRUE, TRUE, 0);
874
875 c->bt_auto_levels = gtk_button_new_with_label(_("auto"));
876 gtk_widget_set_tooltip_text(c->bt_auto_levels, _("apply auto levels"));
877
878 c->bt_select_region = dtgtk_togglebutton_new(dtgtk_cairo_paint_colorpicker, 0, NULL);
879
880 gtk_widget_set_tooltip_text(c->bt_select_region,
881 _("apply auto levels based on a region defined by the user\n"
882 "click and drag to draw the area\n"
883 "right click to cancel"));
884
885 GtkWidget *autolevels_box = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, DT_GUI_BOX_SPACING);
886 gtk_box_pack_start(GTK_BOX(autolevels_box), c->bt_auto_levels, TRUE, TRUE, 0);
887 gtk_box_pack_start(GTK_BOX(autolevels_box), c->bt_select_region, TRUE, TRUE, 0);
888
889 gtk_box_pack_start(GTK_BOX(self->gui->widget), autolevels_box, TRUE, TRUE, 0);
890
891 g_signal_connect(G_OBJECT(c->bt_auto_levels), "clicked", G_CALLBACK(_auto_levels_callback), self);
892 g_signal_connect(G_OBJECT(c->bt_select_region), "toggled", G_CALLBACK(_select_region_toggled_callback), self);
893
894 c->cmb_preserve_colors = dt_bauhaus_combobox_from_params(self, "preserve_colors");
895 gtk_widget_set_tooltip_text(c->cmb_preserve_colors, _("method to preserve colors when applying contrast"));
896
897 // add signal handler for preview pipe finish
899 G_CALLBACK(_develop_ui_pipe_finished_callback), self);
900}
901
908
910static void _get_selected_area(struct dt_iop_module_t *self, const dt_dev_pixelpipe_t *pipe,
911 const dt_dev_pixelpipe_iop_t *piece,
912 dt_iop_rgblevels_gui_data_t *g, const dt_iop_roi_t *const roi_in, int *box_out)
913{
914 box_out[0] = box_out[1] = box_out[2] = box_out[3] = 0;
915
916 if(!IS_NULL_PTR(g))
917 {
918 const int width = roi_in->width;
919 const int height = roi_in->height;
920 dt_boundingbox_t box_cood = { g->box_cood[0], g->box_cood[1], g->box_cood[2], g->box_cood[3] };
921
922 box_cood[0] *= pipe->iwidth;
923 box_cood[1] *= pipe->iheight;
924 box_cood[2] *= pipe->iwidth;
925 box_cood[3] *= pipe->iheight;
926
928 box_cood, 2);
929
930 box_cood[0] *= roi_in->scale;
931 box_cood[1] *= roi_in->scale;
932 box_cood[2] *= roi_in->scale;
933 box_cood[3] *= roi_in->scale;
934
935 box_cood[0] -= roi_in->x;
936 box_cood[1] -= roi_in->y;
937 box_cood[2] -= roi_in->x;
938 box_cood[3] -= roi_in->y;
939
940 int DT_ALIGNED_ARRAY box[4];
941
942 // re-order edges of bounding box
943 box[0] = fminf(box_cood[0], box_cood[2]);
944 box[1] = fminf(box_cood[1], box_cood[3]);
945 box[2] = fmaxf(box_cood[0], box_cood[2]);
946 box[3] = fmaxf(box_cood[1], box_cood[3]);
947
948 // do not continue if box is completely outside of roi
949 if(!(box[0] >= width || box[1] >= height || box[2] < 0 || box[3] < 0))
950 {
951 // clamp bounding box to roi
952 for(int k = 0; k < 4; k += 2) box[k] = MIN(width - 1, MAX(0, box[k]));
953 for(int k = 1; k < 4; k += 2) box[k] = MIN(height - 1, MAX(0, box[k]));
954
955 // safety check: area needs to have minimum 1 pixel width and height
956 if(!(box[2] - box[0] < 1 || box[3] - box[1] < 1))
957 {
958 box_out[0] = box[0];
959 box_out[1] = box[1];
960 box_out[2] = box[2];
961 box_out[3] = box[3];
962 }
963 }
964 }
965}
966
968static void _auto_levels(const float *const img, const int width, const int height, int *box_area,
969 dt_iop_rgblevels_params_t *p, const int _channel, const dt_iop_order_iccprofile_info_t *const work_profile)
970{
971 int y_from, y_to, x_from, x_to;
972 const int ch = 4;
973 const int channel = (p->autoscale == DT_IOP_RGBLEVELS_INDEPENDENT_CHANNELS) ? _channel : 0;
974 if(box_area[2] > box_area[0] && box_area[3] > box_area[1])
975 {
976 y_from = box_area[1];
977 y_to = box_area[3];
978 x_from = box_area[0];
979 x_to = box_area[2];
980 }
981 else
982 {
983 y_from = 0;
984 y_to = height - 1;
985 x_from = 0;
986 x_to = width - 1;
987 }
988
989 float max = -INFINITY;
990 float min = INFINITY;
991
992 for(int y = y_from; y <= y_to; y++)
993 {
994 const float *const in = img + ch * width * y;
995 for(int x = x_from; x <= x_to; x++)
996 {
997 const float *const pixel = in + x * ch;
998
999 if(p->autoscale == DT_IOP_RGBLEVELS_INDEPENDENT_CHANNELS || p->preserve_colors == DT_RGB_NORM_NONE)
1000 {
1002 {
1003 if(pixel[channel] >= 0.f)
1004 {
1005 max = fmaxf(max, pixel[channel]);
1006 min = fminf(min, pixel[channel]);
1007 }
1008 }
1009 else
1010 {
1011 for(int c = 0; c < 3; c++)
1012 {
1013 if(pixel[c] >= 0.f)
1014 {
1015 max = fmaxf(max, pixel[c]);
1016 min = fminf(min, pixel[c]);
1017 }
1018 }
1019 }
1020 }
1021 else
1022 {
1023 const float lum = dt_rgb_norm(pixel, p->preserve_colors, work_profile);
1024 if(lum >= 0.f)
1025 {
1026 max = fmaxf(max, lum);
1027 min = fminf(min, lum);
1028 }
1029 }
1030 }
1031 }
1032
1033 p->levels[channel][0] = CLAMP(min, 0.f, 1.f);
1034 p->levels[channel][2] = CLAMP(max, 0.f, 1.f);
1035 p->levels[channel][1] = (p->levels[channel][2] + p->levels[channel][0]) / 2.f;
1036}
1037
1040 const void *const ivoid, void *const ovoid)
1041{
1042 const dt_iop_roi_t *const roi_in = &piece->roi_in;
1043 const dt_iop_roi_t *const roi_out = &piece->roi_out;
1044
1045 const dt_iop_rgblevels_data_t *const d = (dt_iop_rgblevels_data_t *)piece->data;
1049
1050 // process auto levels
1051 if(!IS_NULL_PTR(g) && dt_dev_pixelpipe_has_preview_output(self->dev, pipe, roi_out))
1052 {
1054 if(g->call_auto_levels == 1 && !dt_gui_widgets_suppressed())
1055 {
1056 g->call_auto_levels = -1;
1057
1059
1060 memcpy(&g->params, p, sizeof(dt_iop_rgblevels_params_t));
1061
1062 int box[4] = { 0 };
1063 _get_selected_area(self, pipe, piece, g, roi_in, box);
1064 _auto_levels((const float *const)ivoid, roi_in->width, roi_in->height, box, &(g->params), g->channel, work_profile);
1065
1067 g->call_auto_levels = 2;
1069 }
1070 else
1071 {
1073 }
1074 }
1075
1076 const dt_aligned_pixel_t mult = { 1.f / (d->params.levels[0][2] - d->params.levels[0][0]),
1077 1.f / (d->params.levels[1][2] - d->params.levels[1][0]),
1078 1.f / (d->params.levels[2][2] - d->params.levels[2][0]) };
1079
1080 const size_t npixels = (size_t)roi_out->width * roi_out->height;
1081 const float *const restrict in = (const float*)ivoid;
1082 float *const restrict out = (float*)ovoid;
1083 if (d->params.autoscale == DT_IOP_RGBLEVELS_INDEPENDENT_CHANNELS || d->params.preserve_colors == DT_RGB_NORM_NONE)
1084 {
1086 for(int k = 0; k < 4U*npixels; k += 4)
1087 {
1088 for(int c = 0; c < 3; c++)
1089 {
1090 const float L_in = in[k+c];
1091
1092 if(L_in <= d->params.levels[c][0])
1093 {
1094 // Anything below the lower threshold just clips to zero
1095 out[k+c] = 0.0f;
1096 }
1097 else if(L_in >= d->params.levels[c][2])
1098 {
1099 const float percentage = (L_in - d->params.levels[c][0]) * mult[c];
1100 out[k+c] = powf(percentage, d->inv_gamma[c]);
1101 }
1102 else
1103 {
1104 // Within the expected input range we can use the lookup table
1105 const float percentage = (L_in - d->params.levels[c][0]) * mult[c];
1106 out[k+c] = d->lut[c][CLAMP((int)(percentage * 0x10000ul), 0, 0xffff)];
1107 }
1108 }
1109 out[k+3] = in[k+3];
1110 }
1111 }
1112 else
1113 {
1114 const int ch_levels = 0;
1115 const float mult_ch = mult[ch_levels];
1116 const float *const restrict levels = d->params.levels[ch_levels];
1118 for(int k = 0; k < 4U*npixels; k += 4)
1119 {
1120 const float lum = dt_rgb_norm(in+k, d->params.preserve_colors, work_profile);
1121 if(lum > levels[0])
1122 {
1123 float curve_lum;
1124 const float percentage = (lum - levels[0]) * mult_ch;
1125 if(lum >= levels[2])
1126 {
1127 curve_lum = powf(percentage, d->inv_gamma[ch_levels]);
1128 }
1129 else
1130 {
1131 // Within the expected input range we can use the lookup table
1132 curve_lum = d->lut[ch_levels][CLAMP((int)(percentage * 0x10000ul), 0, 0xffff)];
1133 }
1134
1135 const float ratio = curve_lum / lum;
1136
1137 for_each_channel(c,aligned(in,out:16))
1138 {
1139 out[k+c] = (ratio * in[k+c]);
1140 }
1141 }
1142 else
1143 {
1144 for_each_channel(c,aligned(out:16))
1145 out[k+c] = 0.f;
1146 }
1147 out[k+3] = in[k+3];
1148 }
1149 }
1150
1151 return 0;
1152}
1153
1154
1155// clang-format off
1156// modelines: These editor modelines have been set for all relevant files by tools/update_modelines.py
1157// vim: shiftwidth=2 expandtab tabstop=2 cindent
1158// kate: tab-indents: off; indent-width 2; replace-tabs on; indent-mode cstyle; remove-trailing-spaces modified;
1159// 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:641
void dt_bauhaus_combobox_set(GtkWidget *widget, const int pos)
Definition bauhaus.c:2090
typedef void((*dt_cache_allocate_t)(void *userdata, dt_cache_entry_t *entry))
@ 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 x
struct _GtkWidget GtkWidget
GtkWidget, opaque, spelled exactly as GTK spells it.
Definition colorspaces.h:98
static const float const float const float min
const float max
const dt_colormatrix_t dt_aligned_pixel_t out
const float delta
struct dt_bauhaus_t * dt_bauhaus_get_global(void)
Definition darktable.c:646
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_work_profile_info(const struct dt_dev_pixelpipe_t *pipe)
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
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
@ DT_DEV_TRANSFORM_DIR_BACK_INCL
Definition develop.h:110
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:255
static void set_color(cairo_t *cr, GdkRGBA color)
Definition draw.h:125
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:469
float dt_boundingbox_t[4]
Definition image.h:83
void dt_iop_default_init(dt_iop_module_t *module)
Definition imageop.c:308
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
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_COLOR
Definition imageop.h:158
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
float *const restrict const size_t k
float *const restrict const size_t const size_t ch
#define IS_NULL_PTR(p)
C is way too permissive with !=, == and if(var) checks, which can mean too many things depending on w...
Definition macros.h:96
#define dt_free_align(ptr)
Release memory from dt_alloc_align() and set ptr to NULL.
Definition mem_alloc.h:214
static void * dt_calloc_align(size_t size)
dt_alloc_align() followed by a zero fill.
Definition mem_alloc.h:225
#define DT_ALIGNED_ARRAY
Align an object on a cacheline boundary, so AVX2 can load it whole.
Definition mem_alloc.h:80
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.
GtkWidget * dt_ui_notebook_page(GtkNotebook *notebook, const char *text, const char *tooltip)
Definition notebook.c:88
void dt_ui_notebook_set_picker_owner(GtkNotebook *notebook, gpointer owner)
Register an opaque owner for a GtkNotebook's page switches, and report every "switch_page" to the hos...
Definition notebook.c:118
#define __OMP_PARALLEL_FOR__(...)
Definition openmp.h:95
@ DT_REQUEST_ON
Definition pixelpipe.h:52
static gboolean _area_draw_callback(GtkWidget *widget, cairo_t *crf, dt_iop_module_t *self)
Definition rgblevels.c:265
dt_iop_rgblevels_autoscale_t
Definition rgblevels.c:77
@ DT_IOP_RGBLEVELS_LINKED_CHANNELS
Definition rgblevels.c:78
@ DT_IOP_RGBLEVELS_INDEPENDENT_CHANNELS
Definition rgblevels.c:79
const char ** description(struct dt_iop_module_t *self)
Definition rgblevels.c:141
int default_group()
Definition rgblevels.c:126
#define RGBLEVELS_MIN
Definition rgblevels.c:64
static void _auto_levels_callback(GtkButton *button, dt_iop_module_t *self)
Definition rgblevels.c:558
static gboolean _area_button_release_callback(GtkWidget *widget, GdkEventButton *event, dt_iop_module_t *self)
Definition rgblevels.c:520
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:968
void gui_update(dt_iop_module_t *self)
Refresh GUI controls from current params and configuration.
Definition rgblevels.c:751
__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:1039
#define DT_GUI_CURVE_EDITOR_INSET
Definition rgblevels.c:60
static gboolean _area_leave_notify_callback(GtkWidget *widget, GdkEventCrossing *event, dt_iop_module_t *self)
Definition rgblevels.c:257
void gui_focus(struct dt_iop_module_t *self, gboolean in)
Definition rgblevels.c:764
const char * name()
Definition rgblevels.c:121
static void _compute_lut(const dt_dev_pixelpipe_iop_t *piece)
Definition rgblevels.c:202
void gui_reset(struct dt_iop_module_t *self)
Definition rgblevels.c:769
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:397
void gui_init(dt_iop_module_t *self)
Definition rgblevels.c:808
void change_image(struct dt_iop_module_t *self)
Definition rgblevels.c:796
static void _select_region_toggled_callback(GtkToggleButton *togglebutton, dt_iop_module_t *self)
Definition rgblevels.c:584
static void _develop_ui_pipe_finished_callback(gpointer instance, dt_iop_module_t *self)
Definition rgblevels.c:166
void gui_changed(dt_iop_module_t *self, GtkWidget *w, void *previous)
Definition rgblevels.c:613
static gboolean _area_scroll_callback(GtkWidget *widget, GdkEventScroll *event, dt_iop_module_t *self)
Definition rgblevels.c:531
static void _turn_selregion_picker_off(struct dt_iop_module_t *self)
Definition rgblevels.c:160
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:711
void gui_cleanup(dt_iop_module_t *self)
Definition rgblevels.c:902
static void _tab_switch_callback(GtkNotebook *notebook, GtkWidget *page, guint page_num, dt_iop_module_t *self)
Definition rgblevels.c:629
void cleanup_pipe(dt_iop_module_t *self, dt_dev_pixelpipe_t *pipe, dt_dev_pixelpipe_iop_t *piece)
Definition rgblevels.c:744
#define RGBLEVELS_MID
Definition rgblevels.c:65
int default_colorspace(dt_iop_module_t *self, dt_dev_pixelpipe_t *pipe, const dt_dev_pixelpipe_iop_t *piece)
Definition rgblevels.c:136
int flags()
Definition rgblevels.c:131
static void _turn_select_region_off(struct dt_iop_module_t *self)
Definition rgblevels.c:150
static gboolean _area_button_press_callback(GtkWidget *widget, GdkEventButton *event, dt_iop_module_t *self)
Definition rgblevels.c:483
void init(dt_iop_module_t *self)
Definition rgblevels.c:780
static void _color_picker_callback(GtkWidget *button, dt_iop_module_t *self)
Definition rgblevels.c:639
static void _rgblevels_show_hide_controls(dt_iop_rgblevels_params_t *p, dt_iop_rgblevels_gui_data_t *g)
Definition rgblevels.c:239
void init_pipe(dt_iop_module_t *self, dt_dev_pixelpipe_t *pipe, dt_dev_pixelpipe_iop_t *piece)
Definition rgblevels.c:738
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:910
static gboolean _area_motion_notify_callback(GtkWidget *widget, GdkEventMotion *event, dt_iop_module_t *self)
Definition rgblevels.c:438
#define RGBLEVELS_MAX
Definition rgblevels.c:66
dt_iop_rgblevels_channel_t
Definition rgblevels.c:69
@ DT_IOP_RGBLEVELS_MAX_CHANNELS
Definition rgblevels.c:73
@ DT_IOP_RGBLEVELS_B
Definition rgblevels.c:72
@ DT_IOP_RGBLEVELS_R
Definition rgblevels.c:70
@ DT_IOP_RGBLEVELS_G
Definition rgblevels.c:71
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:644
static cairo_surface_t * dt_cairo_image_surface_create(cairo_format_t format, int width, int height)
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.
#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_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
DT_ALIGNED_PIXEL float dt_aligned_pixel_t[4]
Definition simd.h:53
#define for_each_channel(_var,...)
Definition simd.h:87
dt_dev_request_flags_t request_histogram
struct dt_iop_module_t *void * data
struct dt_iop_module_t * gui_module
Definition develop.h:170
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
dt_dev_request_flags_t request_histogram
Definition imageop.h:274
uint32_t histogram_max[4]
Definition imageop.h:295
gboolean enabled
Definition imageop.h:313
uint32_t * histogram
Definition imageop.h:291
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,...
float inv_gamma[DT_IOP_RGBLEVELS_MAX_CHANNELS]
Definition rgblevels.c:117
float lut[DT_IOP_RGBLEVELS_MAX_CHANNELS][0x10000]
Definition rgblevels.c:118
dt_iop_rgblevels_params_t params
Definition rgblevels.c:116
GtkWidget * cmb_preserve_colors
Definition rgblevels.c:95
GtkDrawingArea * area
Definition rgblevels.c:94
GtkNotebook * channel_tabs
Definition rgblevels.c:96
dt_iop_rgblevels_params_t params
Definition rgblevels.c:91
dt_iop_rgblevels_channel_t channel
Definition rgblevels.c:109
dt_boundingbox_t box_cood
Definition rgblevels.c:103
dt_iop_rgb_norms_t preserve_colors
Definition rgblevels.c:85
float levels[DT_IOP_RGBLEVELS_MAX_CHANNELS][3]
Definition rgblevels.c:86
dt_iop_rgblevels_autoscale_t autoscale
Definition rgblevels.c:84
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
GtkWidget * notebook
#define __DT_CLONE_TARGETS__
#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_get_scroll_unit_deltas(const GdkEventScroll *event, int *delta_x, int *delta_y)
GdkEventMask dt_widget_scroll_mask(void)
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)