From 6de9c5ee23ecf75e2bce98e63c215bf29f173704 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aur=C3=A9lien=20PIERRE?= Date: Sat, 3 Apr 2021 13:13:45 +0200 Subject: [PATCH 1/9] colorbalancergb: display luminance masks --- src/iop/colorbalancergb.c | 176 +++++++++++++++++++++++++++++++++----- 1 file changed, 154 insertions(+), 22 deletions(-) diff --git a/src/iop/colorbalancergb.c b/src/iop/colorbalancergb.c index 4779f9156f1c..4bf902c5c2f7 100644 --- a/src/iop/colorbalancergb.c +++ b/src/iop/colorbalancergb.c @@ -66,7 +66,7 @@ typedef struct dt_iop_colorbalancergb_params_t float global_C; // $MIN: 0.0 $MAX: 1.0 $DEFAULT: 0.0 $DESCRIPTION: "chroma" float global_H; // $MIN: 0.0 $MAX: 360.0 $DEFAULT: 0.0 $DESCRIPTION: "hue" float shadows_weight; // $MIN: -1.0 $MAX: 1.0 $DEFAULT: 0.0 $DESCRIPTION: "shadows fall-off" - float midtones_weight; // $MIN: -6.0 $MAX: 6.0 $DEFAULT: 0.0 $DESCRIPTION: "white pivot" + float power_fulcrum; // $MIN: -6.0 $MAX: 6.0 $DEFAULT: 0.0 $DESCRIPTION: "white pivot" float highlights_weight; // $MIN: -1.0 $MAX: 1.0 $DEFAULT: 0.0 $DESCRIPTION: "highlights fall-off" float chroma_shadows; // $MIN: -1.0 $MAX: 1.0 $DEFAULT: 0.0 $DESCRIPTION: "shadows" float chroma_highlights; // $MIN: -1.0 $MAX: 1.0 $DEFAULT: 0.0 $DESCRIPTION: "highlights" @@ -89,19 +89,31 @@ typedef struct dt_iop_colorbalancergb_params_t } dt_iop_colorbalancergb_params_t; +typedef enum dt_iop_colorbalancergb_mask_data_t +{ + MASK_SHADOWS = 0, + MASK_MIDTONES = 1, + MASK_HIGHLIGHTS = 2, + MASK_NONE +} dt_iop_colorbalancergb_mask_data_t; + + typedef struct dt_iop_colorbalancergb_gui_data_t { GtkWidget *shadows_H, *midtones_H, *highlights_H, *global_H; GtkWidget *shadows_C, *midtones_C, *highlights_C, *global_C; GtkWidget *shadows_Y, *midtones_Y, *highlights_Y, *global_Y; - GtkWidget *shadows_weight, *midtones_weight, *highlights_weight; + GtkWidget *shadows_weight, *midtones_weight, *highlights_weight, *power_fulcrum; GtkWidget *chroma_highlights, *chroma_global, *chroma_shadows, *chroma_midtones; GtkWidget *saturation_global, *saturation_highlights, *saturation_midtones, *saturation_shadows; GtkWidget *purity_global, *purity_highlights, *purity_midtones, *purity_shadows; GtkWidget *hue_angle; GtkNotebook *notebook; + gboolean mask_display; + dt_iop_colorbalancergb_mask_data_t mask_type; } dt_iop_colorbalancergb_gui_data_t; + typedef struct dt_iop_colorbalancergb_data_t { float global[4]; @@ -113,7 +125,7 @@ typedef struct dt_iop_colorbalancergb_data_t float saturation_global, saturation[4]; float purity_global, purity[4]; float hue_angle; - float shadows_weight, midtones_weight, highlights_weight; + float shadows_weight, power_fulcrum, highlights_weight; float *gamut_LUT; float max_chroma; gboolean lut_inited; @@ -179,7 +191,7 @@ int legacy_params(dt_iop_module_t *self, const void *const old_params, const int float global_C; // $MIN: 0.0 $MAX: 1.0 $DEFAULT: 0.0 $DESCRIPTION: "chroma" float global_H; // $MIN: 0.0 $MAX: 360.0 $DEFAULT: 0.0 $DESCRIPTION: "hue" float shadows_weight; // $MIN: -1.0 $MAX: 1.0 $DEFAULT: 0.0 $DESCRIPTION: "tonal weight" - float midtones_weight; // $MIN: -6.0 $MAX: 6.0 $DEFAULT: 0.0 $DESCRIPTION: "fulcrum" + float power_fulcrum; // $MIN: -6.0 $MAX: 6.0 $DEFAULT: 0.0 $DESCRIPTION: "fulcrum" float highlights_weight; // $MIN: -1.0 $MAX: 1.0 $DEFAULT: 0.0 $DESCRIPTION: "tonal weight" float chroma_shadows; // $MIN: -1.0 $MAX: 1.0 $DEFAULT: 0.0 $DESCRIPTION: "shadows" float chroma_highlights; // $MIN: -1.0 $MAX: 1.0 $DEFAULT: 0.0 $DESCRIPTION: "highlights" @@ -246,7 +258,9 @@ void process(struct dt_iop_module_t *self, dt_dev_pixelpipe_iop_t *piece, const void *const ovoid, const dt_iop_roi_t *const roi_in, const dt_iop_roi_t *const roi_out) { dt_iop_colorbalancergb_data_t *d = (dt_iop_colorbalancergb_data_t *)piece->data; - const struct dt_iop_order_iccprofile_info_t *const work_profile = dt_ioppr_get_pipe_current_profile_info(self, piece->pipe); + dt_iop_colorbalancergb_gui_data_t *g = (dt_iop_colorbalancergb_gui_data_t *)self->gui_data; + const struct dt_iop_order_iccprofile_info_t *const work_profile + = dt_ioppr_get_pipe_current_profile_info(self, piece->pipe); if(work_profile == NULL) return; // no point float DT_ALIGNED_ARRAY RGB_to_XYZ[3][4]; @@ -294,13 +308,25 @@ void process(struct dt_iop_module_t *self, dt_dev_pixelpipe_iop_t *piece, const const float *const restrict saturation = __builtin_assume_aligned((const float *const restrict)d->saturation, 16); const float *const restrict purity = __builtin_assume_aligned((const float *const restrict)d->purity, 16); + const gint mask_display + = (self->dev->gui_attached && (piece->pipe->type & DT_DEV_PIXELPIPE_FULL) == DT_DEV_PIXELPIPE_FULL + && g->mask_display); + + // pixel size of the checker background + const size_t checker_1 = (mask_display) ? DT_PIXEL_APPLY_DPI(8) : 0; + const size_t checker_2 = 2 * checker_1; + #ifdef _OPENMP -#pragma omp parallel for simd default(none) aligned(in, out, gamut_LUT: 64) aligned(global, highlights, shadows, midtones, chroma, saturation, purity:16)\ - dt_omp_firstprivate(in, out, roi_in, roi_out, d, input_matrix, output_matrix, gamut_LUT, white_grading_RGB, \ - global, highlights, shadows, midtones, chroma, saturation, purity) schedule(static) +#pragma omp parallel for simd default(none) aligned(in, out, gamut_LUT: 64) \ + aligned(global, highlights, shadows, midtones, chroma, saturation, purity:16)\ + dt_omp_firstprivate(in, out, roi_in, roi_out, d, g, mask_display, input_matrix, output_matrix, gamut_LUT, white_grading_RGB, \ + global, highlights, shadows, midtones, chroma, saturation, purity, checker_1, checker_2) \ + schedule(static) collapse(2) #endif - for(size_t k = 0; k < (size_t)4 * roi_in->width * roi_out->height; k += 4) + for(size_t i = 0; i < roi_out->height; i++) + for(size_t j = 0; j < roi_out->width; j++) { + const size_t k = ((i * roi_out->width) + j) * 4; const float *const restrict pix_in = __builtin_assume_aligned(in + k, 16); float *const restrict pix_out = __builtin_assume_aligned(out + k, 16); @@ -355,12 +381,12 @@ void process(struct dt_iop_module_t *self, dt_dev_pixelpipe_iop_t *piece, const // midtones : power with sign preservation const float sign = (RGB[c] < 0.f) ? -1.f : 1.f; - RGB[c] = sign * powf(fabsf(RGB[c]) / d->midtones_weight, midtones[c]) * d->midtones_weight; + RGB[c] = sign * powf(fabsf(RGB[c]) / d->power_fulcrum, midtones[c]) * d->power_fulcrum; } // for the Y midtones power (gamma), we need to go in Ych again because RGB doesn't preserve color gradingRGB_to_Ych(RGB, Ych, white_grading_RGB); - Y = Ych[0] = powf(fmaxf(Ych[0] / d->midtones_weight, 0.f), d->midtones_Y) * d->midtones_weight; + Y = Ych[0] = powf(fmaxf(Ych[0] / d->power_fulcrum, 0.f), d->midtones_Y) * d->power_fulcrum; Ych_to_gradingRGB(Ych, RGB, white_grading_RGB); /* Perceptual color adjustments */ @@ -423,7 +449,48 @@ void process(struct dt_iop_module_t *self, dt_dev_pixelpipe_iop_t *piece, const Ych_to_gradingRGB(Ych, RGB, white_grading_RGB); dot_product(RGB, output_matrix, pix_out); - for(size_t c = 0; c < 4; ++c) pix_out[c] = fmaxf(pix_out[c], 0.f); + + if(mask_display) + { + // draw checkerboard + float color; + if(i % checker_1 < i % checker_2) + { + if(j % checker_1 < j % checker_2) color = 1.0f; + else color = 0.5f; + } + else + { + if(j % checker_1 < j % checker_2) color = 0.5f; + else color = 1.0f; + } + + float opacity; + switch(g->mask_type) + { + case MASK_SHADOWS: + opacity = alpha; + break; + case MASK_MIDTONES: + opacity = gamma; + break; + case MASK_HIGHLIGHTS: + opacity = beta; + break; + case MASK_NONE: + default: + opacity = 0.f; + break; + } + const float opacity_comp = 1.0f - opacity; + + for(size_t c = 0; c < 4; ++c) pix_out[c] = opacity_comp * color + opacity * fmaxf(pix_out[c], 0.f); + pix_out[3] = 1.f; + } + else + { + for(size_t c = 0; c < 4; ++c) pix_out[c] = fmaxf(pix_out[c], 0.f); + } } } @@ -488,7 +555,7 @@ void commit_params(struct dt_iop_module_t *self, dt_iop_params_t *p1, dt_dev_pix Ych_to_gradingRGB(Ych, d->midtones, NULL); for(size_t c = 0; c < 4; c++) d->midtones[c] = 1.f / (1.f + (d->midtones[c] - RGB_norm[c])); d->midtones_Y = 1.f / (1.f + p->midtones_Y); - d->midtones_weight = exp2f(p->midtones_weight); + d->power_fulcrum = exp2f(p->power_fulcrum); } // Check if the RGB working profile has changed in pipe @@ -668,6 +735,44 @@ static void paint_chroma_slider(GtkWidget *w, const float hue) } +static void mask_callback(GtkWidget *togglebutton, dt_iop_module_t *self) +{ + if(darktable.gui->reset) return; + dt_iop_request_focus(self); + + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(self->off), TRUE); + + dt_iop_colorbalancergb_gui_data_t *g = (dt_iop_colorbalancergb_gui_data_t *)self->gui_data; + + // if blend module is displaying mask do not display it here + if(self->request_mask_display) + { + dt_control_log(_("cannot display masks when the blending mask is displayed")); + g->mask_display = 0; + } + else + { + g->mask_display = dt_bauhaus_widget_get_quad_active(GTK_WIDGET(togglebutton)); + } + + if(g->mask_display) + { + if(togglebutton == g->shadows_weight) g->mask_type = MASK_SHADOWS; + if(togglebutton == g->midtones_weight) g->mask_type = MASK_MIDTONES; + if(togglebutton == g->highlights_weight) g->mask_type = MASK_HIGHLIGHTS; + } + else + { + g->mask_type = MASK_NONE; + } + + dt_bauhaus_widget_set_quad_active(GTK_WIDGET(g->shadows_weight), g->mask_type == MASK_SHADOWS); + dt_bauhaus_widget_set_quad_active(GTK_WIDGET(g->midtones_weight), g->mask_type == MASK_MIDTONES); + dt_bauhaus_widget_set_quad_active(GTK_WIDGET(g->highlights_weight), g->mask_type == MASK_HIGHLIGHTS); + + dt_iop_refresh_center(self); +} + void gui_changed(dt_iop_module_t *self, GtkWidget *w, void *previous) { dt_iop_colorbalancergb_gui_data_t *g = (dt_iop_colorbalancergb_gui_data_t *)self->gui_data; @@ -708,7 +813,6 @@ void gui_update(dt_iop_module_t *self) dt_bauhaus_slider_set_soft(g->saturation_midtones, p->saturation_midtones); dt_bauhaus_slider_set_soft(g->saturation_shadows, p->saturation_shadows); - dt_bauhaus_slider_set_soft(g->purity_global, p->purity_global); dt_bauhaus_slider_set_soft(g->purity_highlights, p->purity_highlights); dt_bauhaus_slider_set_soft(g->purity_midtones, p->purity_midtones); @@ -726,7 +830,7 @@ void gui_update(dt_iop_module_t *self) dt_bauhaus_slider_set_soft(g->midtones_C, p->midtones_C); dt_bauhaus_slider_set_soft(g->midtones_H, p->midtones_H); dt_bauhaus_slider_set_soft(g->midtones_Y, p->midtones_Y); - dt_bauhaus_slider_set_soft(g->midtones_weight, p->midtones_weight); + dt_bauhaus_slider_set_soft(g->power_fulcrum, p->power_fulcrum); dt_bauhaus_slider_set_soft(g->highlights_C, p->highlights_C); dt_bauhaus_slider_set_soft(g->highlights_H, p->highlights_H); @@ -735,6 +839,12 @@ void gui_update(dt_iop_module_t *self) gui_changed(self, NULL, NULL); dt_iop_color_picker_reset(self, TRUE); + g->mask_display = FALSE; + g->mask_type = MASK_NONE; + + dt_bauhaus_widget_set_quad_active(GTK_WIDGET(g->shadows_weight), FALSE); + dt_bauhaus_widget_set_quad_active(GTK_WIDGET(g->midtones_weight), FALSE); + dt_bauhaus_widget_set_quad_active(GTK_WIDGET(g->highlights_weight), FALSE); } @@ -945,19 +1055,28 @@ void gui_init(dt_iop_module_t *self) // Page masks self->widget = dt_ui_notebook_page(g->notebook, _("masks"), _("isolate luminances")); + gtk_box_pack_start(GTK_BOX(self->widget), dt_ui_section_label_new(_("luminance ranges")), FALSE, FALSE, 0); + g->shadows_weight = dt_bauhaus_slider_from_params(self, "shadows_weight"); dt_bauhaus_slider_set_digits(g->shadows_weight, 4); dt_bauhaus_slider_set_step(g->shadows_weight, 0.1); dt_bauhaus_slider_set_format(g->shadows_weight, "%.2f %%"); dt_bauhaus_slider_set_factor(g->shadows_weight, 100.0f); gtk_widget_set_tooltip_text(g->shadows_weight, _("weight of the shadows over the whole tonal range")); - - g->midtones_weight = dt_bauhaus_slider_from_params(self, "midtones_weight"); - dt_bauhaus_slider_set_soft_range(g->midtones_weight, -2., +2.); - dt_bauhaus_slider_set_step(g->midtones_weight, 0.1); - dt_bauhaus_slider_set_digits(g->midtones_weight, 4); - dt_bauhaus_slider_set_format(g->midtones_weight, "%.2f EV"); - gtk_widget_set_tooltip_text(g->midtones_weight, _("peak white luminance value used to normalize the power function")); + dt_bauhaus_widget_set_quad_paint(g->shadows_weight, dtgtk_cairo_paint_showmask, + CPF_STYLE_FLAT | CPF_DO_NOT_USE_BORDER, NULL); + dt_bauhaus_widget_set_quad_toggle(g->shadows_weight, TRUE); + g_signal_connect(G_OBJECT(g->shadows_weight), "quad-pressed", G_CALLBACK(mask_callback), self); + + g->midtones_weight = dt_bauhaus_combobox_new(self); + dt_bauhaus_widget_set_label(g->midtones_weight, NULL, N_("midtones roll-off")); + gtk_widget_set_tooltip_text(g->midtones_weight, _("weights of midtones over the whole tonal range." + "this is the remainder of highlights and shadows masks")); + dt_bauhaus_widget_set_quad_paint(g->midtones_weight, dtgtk_cairo_paint_showmask, + CPF_STYLE_FLAT | CPF_DO_NOT_USE_BORDER, NULL); + dt_bauhaus_widget_set_quad_toggle(g->midtones_weight, TRUE); + g_signal_connect(G_OBJECT(g->midtones_weight), "quad-pressed", G_CALLBACK(mask_callback), self); + gtk_box_pack_start(GTK_BOX(self->widget), g->midtones_weight, FALSE, FALSE, 0); g->highlights_weight = dt_bauhaus_slider_from_params(self, "highlights_weight"); dt_bauhaus_slider_set_step(g->highlights_weight, 0.1); @@ -965,6 +1084,19 @@ void gui_init(dt_iop_module_t *self) dt_bauhaus_slider_set_format(g->highlights_weight, "%.2f %%"); dt_bauhaus_slider_set_factor(g->highlights_weight, 100.0f); gtk_widget_set_tooltip_text(g->highlights_weight, _("weights of highlights over the whole tonal range")); + dt_bauhaus_widget_set_quad_paint(g->highlights_weight, dtgtk_cairo_paint_showmask, + CPF_STYLE_FLAT | CPF_DO_NOT_USE_BORDER, NULL); + dt_bauhaus_widget_set_quad_toggle(g->highlights_weight, TRUE); + g_signal_connect(G_OBJECT(g->highlights_weight), "quad-pressed", G_CALLBACK(mask_callback), self); + + gtk_box_pack_start(GTK_BOX(self->widget), dt_ui_section_label_new(_("threshold")), FALSE, FALSE, 0); + + g->power_fulcrum = dt_bauhaus_slider_from_params(self, "power_fulcrum"); + dt_bauhaus_slider_set_soft_range(g->power_fulcrum, -2., +2.); + dt_bauhaus_slider_set_step(g->power_fulcrum, 0.1); + dt_bauhaus_slider_set_digits(g->power_fulcrum, 4); + dt_bauhaus_slider_set_format(g->power_fulcrum, "%.2f EV"); + gtk_widget_set_tooltip_text(g->power_fulcrum, _("peak white luminance value used to normalize the power function")); // paint backgrounds From c14f446963648ab05411f5f3bd373d14bafba3c8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aur=C3=A9lien=20PIERRE?= Date: Sat, 3 Apr 2021 14:39:55 +0200 Subject: [PATCH 2/9] colorbalancergb: add mask roll-off graph --- src/iop/colorbalancergb.c | 100 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 100 insertions(+) diff --git a/src/iop/colorbalancergb.c b/src/iop/colorbalancergb.c index 4bf902c5c2f7..1ed3bb3fa9c1 100644 --- a/src/iop/colorbalancergb.c +++ b/src/iop/colorbalancergb.c @@ -28,6 +28,7 @@ #include "develop/imageop.h" #include "develop/imageop_math.h" #include "develop/imageop_gui.h" +#include "dtgtk/drawingarea.h" #include "dtgtk/gradientslider.h" #include "gui/accelerators.h" #include "gui/gtk.h" @@ -108,6 +109,7 @@ typedef struct dt_iop_colorbalancergb_gui_data_t GtkWidget *saturation_global, *saturation_highlights, *saturation_midtones, *saturation_shadows; GtkWidget *purity_global, *purity_highlights, *purity_midtones, *purity_shadows; GtkWidget *hue_angle; + GtkDrawingArea *area; GtkNotebook *notebook; gboolean mask_display; dt_iop_colorbalancergb_mask_data_t mask_type; @@ -773,6 +775,97 @@ static void mask_callback(GtkWidget *togglebutton, dt_iop_module_t *self) dt_iop_refresh_center(self); } +static gboolean dt_iop_tonecurve_draw(GtkWidget *widget, cairo_t *crf, gpointer user_data) +{ + dt_iop_module_t *self = (dt_iop_module_t *)user_data; + dt_iop_colorbalancergb_params_t *p = (dt_iop_colorbalancergb_params_t *)self->params; + const float shadows_weight = 2.f + p->shadows_weight * 2.f; + const float highlights_weight = 2.f + p->highlights_weight * 2.f; + + // Cache the graph objects to avoid recomputing all the view at each redraw + GtkAllocation allocation; + gtk_widget_get_allocation(widget, &allocation); + GtkStyleContext *context = gtk_widget_get_style_context(widget); + + cairo_surface_t *cst = + dt_cairo_image_surface_create(CAIRO_FORMAT_ARGB32, allocation.width, allocation.height); + PangoFontDescription *desc = + pango_font_description_copy_static(darktable.bauhaus->pango_font_desc); + cairo_t *cr = cairo_create(cst); + PangoLayout *layout = pango_cairo_create_layout(cr); + + const gint font_size = pango_font_description_get_size(desc); + pango_font_description_set_size(desc, 0.95 * font_size); + pango_layout_set_font_description(layout, desc); + pango_cairo_context_set_resolution(pango_layout_get_context(layout), darktable.gui->dpi); + + //const float inset = DT_PIXEL_APPLY_DPI(4); + const float margin_top = 0; + const float margin_bottom = 0; + const float margin_left = 0; + const float margin_right = 0; + + const float graph_width = allocation.width - margin_right - margin_left; // align the right border on sliders + const float graph_height = allocation.height - margin_bottom - margin_top; // give room to nodes + + gtk_render_background(context, cr, 0, 0, allocation.width, allocation.height); + + // set the graph as the origin of the coordinates + cairo_translate(cr, margin_left, margin_top); + cairo_set_line_cap(cr, CAIRO_LINE_CAP_ROUND); + + set_color(cr, darktable.bauhaus->graph_bg); + cairo_rectangle(cr, 0, 0, graph_width, graph_height); + cairo_fill(cr); + + float *LUT[3]; + for(size_t c = 0; c < 3; c++) LUT[c] = dt_alloc_align_float(LUT_ELEM); + + for(size_t k = 0 ; k < LUT_ELEM; k++) + { + const float Y = k / (float)(LUT_ELEM - 1); + const float x_offset = (Y - 0.1845f) / 0.1845f; + const float alpha = 1.f / (1.f + expf(x_offset * shadows_weight)); // opacity of shadows + const float gamma = expf(-0.1845f * x_offset * x_offset / (shadows_weight * highlights_weight)); + const float beta = 1.f / (1.f + expf(-x_offset * highlights_weight)); // opacity of highlights + + LUT[0][k] = alpha; + LUT[1][k] = gamma; + LUT[2][k] = beta; + } + + set_color(cr, darktable.bauhaus->graph_fg); + cairo_set_line_width(cr, DT_PIXEL_APPLY_DPI(2.)); + + for(size_t c = 0; c < 3; c++) + { + cairo_move_to(cr, 0, (1.f - LUT[c][0]) * graph_height); + for(size_t k = 0; k < LUT_ELEM; k++) + { + const float x = (float)k / (float)(LUT_ELEM - 1) * graph_width; + const float y = (1.f - LUT[c][k]) * graph_height; + cairo_line_to(cr, x, y); + } + cairo_stroke(cr); + } + + for(size_t c = 0; c < 3; c++) dt_free_align(LUT[c]); + + cairo_restore(cr); + + // restore font size + pango_font_description_set_size(desc, font_size); + pango_layout_set_font_description(layout, desc); + + cairo_destroy(cr); + cairo_set_source_surface(crf, cst, 0, 0); + cairo_paint(crf); + cairo_surface_destroy(cst); + g_object_unref(layout); + pango_font_description_free(desc); + return TRUE; +} + void gui_changed(dt_iop_module_t *self, GtkWidget *w, void *previous) { dt_iop_colorbalancergb_gui_data_t *g = (dt_iop_colorbalancergb_gui_data_t *)self->gui_data; @@ -792,6 +885,9 @@ void gui_changed(dt_iop_module_t *self, GtkWidget *w, void *previous) if(!w || w == g->highlights_H) paint_chroma_slider(g->highlights_C, p->highlights_H); + if(!w || w == g->shadows_weight || w == g->highlights_weight) + gtk_widget_queue_draw(GTK_WIDGET(g->area)); + --darktable.gui->reset; } @@ -1057,6 +1153,10 @@ void gui_init(dt_iop_module_t *self) gtk_box_pack_start(GTK_BOX(self->widget), dt_ui_section_label_new(_("luminance ranges")), FALSE, FALSE, 0); + g->area = GTK_DRAWING_AREA(dtgtk_drawing_area_new_with_aspect_ratio(0.75)); + g_signal_connect(G_OBJECT(g->area), "draw", G_CALLBACK(dt_iop_tonecurve_draw), self); + gtk_box_pack_start(GTK_BOX(self->widget), GTK_WIDGET(g->area), FALSE, FALSE, 0); + g->shadows_weight = dt_bauhaus_slider_from_params(self, "shadows_weight"); dt_bauhaus_slider_set_digits(g->shadows_weight, 4); dt_bauhaus_slider_set_step(g->shadows_weight, 0.1); From d5aa7cda7de32596b731f22a8b438341f1215647 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aur=C3=A9lien=20PIERRE?= Date: Sun, 4 Apr 2021 13:12:43 +0200 Subject: [PATCH 3/9] colorbalancergb: change the midtones mask computation --- src/iop/colorbalancergb.c | 92 +++++++++++++++++++++++---------------- 1 file changed, 54 insertions(+), 38 deletions(-) diff --git a/src/iop/colorbalancergb.c b/src/iop/colorbalancergb.c index 1ed3bb3fa9c1..50587e52f802 100644 --- a/src/iop/colorbalancergb.c +++ b/src/iop/colorbalancergb.c @@ -27,6 +27,7 @@ #include "develop/blend.h" #include "develop/imageop.h" #include "develop/imageop_math.h" +#include "develop/openmp_maths.h" #include "develop/imageop_gui.h" #include "dtgtk/drawingarea.h" #include "dtgtk/gradientslider.h" @@ -66,9 +67,9 @@ typedef struct dt_iop_colorbalancergb_params_t float global_Y; // $MIN: -1.0 $MAX: 1.0 $DEFAULT: 0.0 $DESCRIPTION: "luminance" float global_C; // $MIN: 0.0 $MAX: 1.0 $DEFAULT: 0.0 $DESCRIPTION: "chroma" float global_H; // $MIN: 0.0 $MAX: 360.0 $DEFAULT: 0.0 $DESCRIPTION: "hue" - float shadows_weight; // $MIN: -1.0 $MAX: 1.0 $DEFAULT: 0.0 $DESCRIPTION: "shadows fall-off" - float power_fulcrum; // $MIN: -6.0 $MAX: 6.0 $DEFAULT: 0.0 $DESCRIPTION: "white pivot" - float highlights_weight; // $MIN: -1.0 $MAX: 1.0 $DEFAULT: 0.0 $DESCRIPTION: "highlights fall-off" + float shadows_weight; // $MIN: 0.0 $MAX: 3.0 $DEFAULT: 1.0 $DESCRIPTION: "shadows fall-off" + float power_fulcrum; // $MIN: -6.0 $MAX: 6.0 $DEFAULT: 0.0 $DESCRIPTION: "white pivot" + float highlights_weight; // $MIN: 0.0 $MAX: 3.0 $DEFAULT: 1.0 $DESCRIPTION: "highlights fall-off" float chroma_shadows; // $MIN: -1.0 $MAX: 1.0 $DEFAULT: 0.0 $DESCRIPTION: "shadows" float chroma_highlights; // $MIN: -1.0 $MAX: 1.0 $DEFAULT: 0.0 $DESCRIPTION: "highlights" float chroma_global; // $MIN: -1.0 $MAX: 1.0 $DEFAULT: 0.0 $DESCRIPTION: "global" @@ -127,7 +128,7 @@ typedef struct dt_iop_colorbalancergb_data_t float saturation_global, saturation[4]; float purity_global, purity[4]; float hue_angle; - float shadows_weight, power_fulcrum, highlights_weight; + float shadows_weight, power_fulcrum, highlights_weight, midtones_weight; float *gamut_LUT; float max_chroma; gboolean lut_inited; @@ -256,6 +257,39 @@ static void mat3mul4(float *dst, const float *const m1, const float *const m2) } +#ifdef _OPENMP +#pragma omp declare simd aligned(output, output_comp: 16) uniform(shadows_weight, midtones_weight, highlights_weight) +#endif +static inline void opacity_masks(const float x, + const float shadows_weight, const float highlights_weight, const float midtones_weight, + float output[4], float output_comp[4]) +{ + const float grey = 0.5f; + const float x_offset = (x - grey); + const float x_offset_norm = x_offset / grey; + const float alpha = 1.f / (1.f + expf(x_offset_norm * shadows_weight)); // opacity of shadows + const float beta = 1.f / (1.f + expf(-x_offset_norm * highlights_weight)); // opacity of highlights + const float alpha_comp = 1.f - alpha; + const float beta_comp = 1.f - beta; + const float gamma = expf(-sqf(x_offset) * midtones_weight / 4.f) * sqf(alpha_comp) * sqf(beta_comp) * 8.f; // opacity of midtones + const float gamma_comp = 1.f - gamma; + + output[0] = alpha; + output[1] = gamma; + output[2] = beta; + output[3] = 0.f; + + if(output_comp) + { + output_comp[0] = alpha_comp; + output_comp[1] = gamma_comp; + output_comp[2] = beta_comp; + output_comp[3] = 0.f; + } +} + + + void process(struct dt_iop_module_t *self, dt_dev_pixelpipe_iop_t *piece, const void *const ivoid, void *const ovoid, const dt_iop_roi_t *const roi_in, const dt_iop_roi_t *const roi_out) { @@ -344,14 +378,10 @@ void process(struct dt_iop_module_t *self, dt_dev_pixelpipe_iop_t *piece, const const int is_black = (Y == 0.f); // Opacities for luma masks - const float x_offset = (Y - 0.1845f) / 0.1845f; - - const float alpha = 1.f / (1.f + expf(x_offset * d->shadows_weight)); // opacity of shadows - const float gamma = expf(-0.1845f * x_offset * x_offset / (d->shadows_weight * d->highlights_weight)); - const float beta = 1.f / (1.f + expf(- x_offset * d->highlights_weight)); // opacity of highlights - const float DT_ALIGNED_PIXEL opacities[4] = { alpha, gamma, beta, 0.f }; - const float alpha_comp = 1.f - alpha; - const float beta_comp = 1.f - beta; + float DT_ALIGNED_PIXEL opacities[4]; + float DT_ALIGNED_PIXEL opacities_comp[4]; + opacity_masks(powf(Y, 0.4101205819200422f), // center middle grey in 50 % + d->shadows_weight, d->highlights_weight, d->midtones_weight, opacities, opacities_comp); // Hue shift - do it now because we need the gamut limit at output hue right after Ych[2] += d->hue_angle; @@ -378,7 +408,7 @@ void process(struct dt_iop_module_t *self, dt_dev_pixelpipe_iop_t *piece, const RGB[c] = RGB[c] + global[c]; // highlights, shadows : 2 slopes with masking - RGB[c] *= beta_comp * (alpha_comp + alpha * shadows[c]) + beta * highlights[c]; + RGB[c] *= opacities_comp[2] * (opacities_comp[0] + opacities[0] * shadows[c]) + opacities[2] * highlights[c]; // factorization of : (RGB[c] * (1.f - alpha) + RGB[c] * d->shadows[c] * alpha) * (1.f - beta) + RGB[c] * d->highlights[c] * beta; // midtones : power with sign preservation @@ -467,23 +497,7 @@ void process(struct dt_iop_module_t *self, dt_dev_pixelpipe_iop_t *piece, const else color = 1.0f; } - float opacity; - switch(g->mask_type) - { - case MASK_SHADOWS: - opacity = alpha; - break; - case MASK_MIDTONES: - opacity = gamma; - break; - case MASK_HIGHLIGHTS: - opacity = beta; - break; - case MASK_NONE: - default: - opacity = 0.f; - break; - } + float opacity = opacities[g->mask_type]; const float opacity_comp = 1.0f - opacity; for(size_t c = 0; c < 4; ++c) pix_out[c] = opacity_comp * color + opacity * fmaxf(pix_out[c], 0.f); @@ -558,6 +572,8 @@ void commit_params(struct dt_iop_module_t *self, dt_iop_params_t *p1, dt_dev_pix for(size_t c = 0; c < 4; c++) d->midtones[c] = 1.f / (1.f + (d->midtones[c] - RGB_norm[c])); d->midtones_Y = 1.f / (1.f + p->midtones_Y); d->power_fulcrum = exp2f(p->power_fulcrum); + d->midtones_weight = sqf(d->shadows_weight) * sqf(d->highlights_weight) / + (sqf(d->shadows_weight) + sqf(d->highlights_weight)); } // Check if the RGB working profile has changed in pipe @@ -775,6 +791,7 @@ static void mask_callback(GtkWidget *togglebutton, dt_iop_module_t *self) dt_iop_refresh_center(self); } + static gboolean dt_iop_tonecurve_draw(GtkWidget *widget, cairo_t *crf, gpointer user_data) { dt_iop_module_t *self = (dt_iop_module_t *)user_data; @@ -818,20 +835,19 @@ static gboolean dt_iop_tonecurve_draw(GtkWidget *widget, cairo_t *crf, gpointer cairo_rectangle(cr, 0, 0, graph_width, graph_height); cairo_fill(cr); + // from https://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.583.3007&rep=rep1&type=pdf + const float midtones_weight + = sqf(shadows_weight) * sqf(highlights_weight) / (sqf(shadows_weight) + sqf(highlights_weight)); + float *LUT[3]; for(size_t c = 0; c < 3; c++) LUT[c] = dt_alloc_align_float(LUT_ELEM); for(size_t k = 0 ; k < LUT_ELEM; k++) { const float Y = k / (float)(LUT_ELEM - 1); - const float x_offset = (Y - 0.1845f) / 0.1845f; - const float alpha = 1.f / (1.f + expf(x_offset * shadows_weight)); // opacity of shadows - const float gamma = expf(-0.1845f * x_offset * x_offset / (shadows_weight * highlights_weight)); - const float beta = 1.f / (1.f + expf(-x_offset * highlights_weight)); // opacity of highlights - - LUT[0][k] = alpha; - LUT[1][k] = gamma; - LUT[2][k] = beta; + float output[4]; + opacity_masks(Y, shadows_weight, highlights_weight, midtones_weight, output, NULL); + for(size_t c = 0; c < 3; c++) LUT[c][k] = output[c]; } set_color(cr, darktable.bauhaus->graph_fg); From c0f1a40ecf1625c3c72dff3fbebf058d11f20a7b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aur=C3=A9lien=20PIERRE?= Date: Sun, 4 Apr 2021 13:35:34 +0200 Subject: [PATCH 4/9] colorbalancergb: add grey fulcrum parameter --- src/iop/colorbalancergb.c | 105 ++++++++++++++++++++++++++++++-------- 1 file changed, 83 insertions(+), 22 deletions(-) diff --git a/src/iop/colorbalancergb.c b/src/iop/colorbalancergb.c index 50587e52f802..2ada3e5e5d1a 100644 --- a/src/iop/colorbalancergb.c +++ b/src/iop/colorbalancergb.c @@ -49,7 +49,7 @@ #define DEG_TO_RAD(x) ((x + ANGLE_SHIFT) * M_PI / 180.f) #define RAD_TO_DEG(x) (x * 180.f / M_PI - ANGLE_SHIFT) -DT_MODULE_INTROSPECTION(2, dt_iop_colorbalancergb_params_t) +DT_MODULE_INTROSPECTION(3, dt_iop_colorbalancergb_params_t) typedef struct dt_iop_colorbalancergb_params_t @@ -86,8 +86,12 @@ typedef struct dt_iop_colorbalancergb_params_t float purity_midtones; // $MIN: -1.0 $MAX: 1.0 $DEFAULT: 0.0 $DESCRIPTION: "midtones" float purity_shadows; // $MIN: -1.0 $MAX: 1.0 $DEFAULT: 0.0 $DESCRIPTION: "shadows" + /* params of v3 */ + float grey_fulcrum; // $MIN: 0.0 $MAX: 1.0 $DEFAULT: 0.1845 $DESCRIPTION: "middle-grey fulcrum" + /* add future params after this so the legacy params import can use a blind memcpy */ + } dt_iop_colorbalancergb_params_t; @@ -105,7 +109,7 @@ typedef struct dt_iop_colorbalancergb_gui_data_t GtkWidget *shadows_H, *midtones_H, *highlights_H, *global_H; GtkWidget *shadows_C, *midtones_C, *highlights_C, *global_C; GtkWidget *shadows_Y, *midtones_Y, *highlights_Y, *global_Y; - GtkWidget *shadows_weight, *midtones_weight, *highlights_weight, *power_fulcrum; + GtkWidget *shadows_weight, *grey_fulcrum, *highlights_weight, *power_fulcrum; GtkWidget *chroma_highlights, *chroma_global, *chroma_shadows, *chroma_midtones; GtkWidget *saturation_global, *saturation_highlights, *saturation_midtones, *saturation_shadows; GtkWidget *purity_global, *purity_highlights, *purity_midtones, *purity_shadows; @@ -128,7 +132,7 @@ typedef struct dt_iop_colorbalancergb_data_t float saturation_global, saturation[4]; float purity_global, purity[4]; float hue_angle; - float shadows_weight, power_fulcrum, highlights_weight, midtones_weight; + float shadows_weight, power_fulcrum, highlights_weight, midtones_weight, grey_fulcrum; float *gamut_LUT; float max_chroma; gboolean lut_inited; @@ -177,7 +181,7 @@ int default_colorspace(dt_iop_module_t *self, dt_dev_pixelpipe_t *pipe, dt_dev_p int legacy_params(dt_iop_module_t *self, const void *const old_params, const int old_version, void *new_params, const int new_version) { - if(old_version == 1 && new_version == 2) + if(old_version == 1 && new_version == 3) { typedef struct dt_iop_colorbalancergb_params_v1_t { @@ -215,6 +219,57 @@ int legacy_params(dt_iop_module_t *self, const void *const old_params, const int dt_iop_colorbalancergb_params_t *n = (dt_iop_colorbalancergb_params_t *)new_params; n->saturation_global /= 180.f / M_PI; + n->grey_fulcrum = 0.1845f; + + return 0; + } + + if(old_version == 2 && new_version == 3) + { + typedef struct dt_iop_colorbalancergb_params_v2_t + { + /* params of v1 */ + float shadows_Y; // $MIN: -1.0 $MAX: 1.0 $DEFAULT: 0.0 $DESCRIPTION: "luminance" + float shadows_C; // $MIN: 0.0 $MAX: 1.0 $DEFAULT: 0.0 $DESCRIPTION: "chroma" + float shadows_H; // $MIN: 0.0 $MAX: 360.0 $DEFAULT: 0.0 $DESCRIPTION: "hue" + float midtones_Y; // $MIN: -1.0 $MAX: 1.0 $DEFAULT: 0.0 $DESCRIPTION: "luminance" + float midtones_C; // $MIN: -1.0 $MAX: 1.0 $DEFAULT: 0.0 $DESCRIPTION: "chroma" + float midtones_H; // $MIN: 0.0 $MAX: 360.0 $DEFAULT: 0.0 $DESCRIPTION: "hue" + float highlights_Y; // $MIN: -1.0 $MAX: 1.0 $DEFAULT: 0.0 $DESCRIPTION: "luminance" + float highlights_C; // $MIN: 0.0 $MAX: 1.0 $DEFAULT: 0.0 $DESCRIPTION: "chroma" + float highlights_H; // $MIN: 0.0 $MAX: 360.0 $DEFAULT: 0.0 $DESCRIPTION: "hue" + float global_Y; // $MIN: -1.0 $MAX: 1.0 $DEFAULT: 0.0 $DESCRIPTION: "luminance" + float global_C; // $MIN: 0.0 $MAX: 1.0 $DEFAULT: 0.0 $DESCRIPTION: "chroma" + float global_H; // $MIN: 0.0 $MAX: 360.0 $DEFAULT: 0.0 $DESCRIPTION: "hue" + float shadows_weight; // $MIN: 0.0 $MAX: 3.0 $DEFAULT: 1.0 $DESCRIPTION: "shadows fall-off" + float power_fulcrum; // $MIN: -6.0 $MAX: 6.0 $DEFAULT: 0.0 $DESCRIPTION: "white pivot" + float highlights_weight; // $MIN: 0.0 $MAX: 3.0 $DEFAULT: 1.0 $DESCRIPTION: "highlights fall-off" + float chroma_shadows; // $MIN: -1.0 $MAX: 1.0 $DEFAULT: 0.0 $DESCRIPTION: "shadows" + float chroma_highlights; // $MIN: -1.0 $MAX: 1.0 $DEFAULT: 0.0 $DESCRIPTION: "highlights" + float chroma_global; // $MIN: -1.0 $MAX: 1.0 $DEFAULT: 0.0 $DESCRIPTION: "global" + float chroma_midtones; // $MIN: -1.0 $MAX: 1.0 $DEFAULT: 0.0 $DESCRIPTION: "midtones" + float saturation_global; // $MIN: -1.0 $MAX: 1.0 $DEFAULT: 0.0 $DESCRIPTION: "global" + float saturation_highlights; // $MIN: -1.0 $MAX: 1.0 $DEFAULT: 0.0 $DESCRIPTION: "highlights" + float saturation_midtones; // $MIN: -1.0 $MAX: 1.0 $DEFAULT: 0.0 $DESCRIPTION: "midtones" + float saturation_shadows; // $MIN: -1.0 $MAX: 1.0 $DEFAULT: 0.0 $DESCRIPTION: "shadows" + float hue_angle; // $MIN: -180. $MAX: 180. $DEFAULT: 0.0 $DESCRIPTION: "hue shift" + + /* params of v2 */ + float purity_global; // $MIN: -1.0 $MAX: 1.0 $DEFAULT: 0.0 $DESCRIPTION: "global" + float purity_highlights; // $MIN: -1.0 $MAX: 1.0 $DEFAULT: 0.0 $DESCRIPTION: "highlights" + float purity_midtones; // $MIN: -1.0 $MAX: 1.0 $DEFAULT: 0.0 $DESCRIPTION: "midtones" + float purity_shadows; // $MIN: -1.0 $MAX: 1.0 $DEFAULT: 0.0 $DESCRIPTION: "shadows" + + } dt_iop_colorbalancergb_params_v2_t; + + // Init params with defaults + memcpy(new_params, self->default_params, sizeof(dt_iop_colorbalancergb_params_t)); + + // Copy the common part of the params struct + memcpy(new_params, old_params, sizeof(dt_iop_colorbalancergb_params_v2_t)); + + dt_iop_colorbalancergb_params_t *n = (dt_iop_colorbalancergb_params_t *)new_params; + n->grey_fulcrum = 0.1845f; return 0; } @@ -261,12 +316,12 @@ static void mat3mul4(float *dst, const float *const m1, const float *const m2) #pragma omp declare simd aligned(output, output_comp: 16) uniform(shadows_weight, midtones_weight, highlights_weight) #endif static inline void opacity_masks(const float x, - const float shadows_weight, const float highlights_weight, const float midtones_weight, + const float shadows_weight, const float highlights_weight, + const float midtones_weight, const float grey_fulcrum, float output[4], float output_comp[4]) { - const float grey = 0.5f; - const float x_offset = (x - grey); - const float x_offset_norm = x_offset / grey; + const float x_offset = (x - grey_fulcrum); + const float x_offset_norm = x_offset / grey_fulcrum; const float alpha = 1.f / (1.f + expf(x_offset_norm * shadows_weight)); // opacity of shadows const float beta = 1.f / (1.f + expf(-x_offset_norm * highlights_weight)); // opacity of highlights const float alpha_comp = 1.f - alpha; @@ -381,7 +436,7 @@ void process(struct dt_iop_module_t *self, dt_dev_pixelpipe_iop_t *piece, const float DT_ALIGNED_PIXEL opacities[4]; float DT_ALIGNED_PIXEL opacities_comp[4]; opacity_masks(powf(Y, 0.4101205819200422f), // center middle grey in 50 % - d->shadows_weight, d->highlights_weight, d->midtones_weight, opacities, opacities_comp); + d->shadows_weight, d->highlights_weight, d->midtones_weight, d->grey_fulcrum, opacities, opacities_comp); // Hue shift - do it now because we need the gamut limit at output hue right after Ych[2] += d->hue_angle; @@ -574,6 +629,7 @@ void commit_params(struct dt_iop_module_t *self, dt_iop_params_t *p1, dt_dev_pix d->power_fulcrum = exp2f(p->power_fulcrum); d->midtones_weight = sqf(d->shadows_weight) * sqf(d->highlights_weight) / (sqf(d->shadows_weight) + sqf(d->highlights_weight)); + d->grey_fulcrum = powf(p->grey_fulcrum, 0.4101205819200422f); } // Check if the RGB working profile has changed in pipe @@ -776,7 +832,7 @@ static void mask_callback(GtkWidget *togglebutton, dt_iop_module_t *self) if(g->mask_display) { if(togglebutton == g->shadows_weight) g->mask_type = MASK_SHADOWS; - if(togglebutton == g->midtones_weight) g->mask_type = MASK_MIDTONES; + if(togglebutton == g->grey_fulcrum) g->mask_type = MASK_MIDTONES; if(togglebutton == g->highlights_weight) g->mask_type = MASK_HIGHLIGHTS; } else @@ -785,7 +841,7 @@ static void mask_callback(GtkWidget *togglebutton, dt_iop_module_t *self) } dt_bauhaus_widget_set_quad_active(GTK_WIDGET(g->shadows_weight), g->mask_type == MASK_SHADOWS); - dt_bauhaus_widget_set_quad_active(GTK_WIDGET(g->midtones_weight), g->mask_type == MASK_MIDTONES); + dt_bauhaus_widget_set_quad_active(GTK_WIDGET(g->grey_fulcrum), g->mask_type == MASK_MIDTONES); dt_bauhaus_widget_set_quad_active(GTK_WIDGET(g->highlights_weight), g->mask_type == MASK_HIGHLIGHTS); dt_iop_refresh_center(self); @@ -838,6 +894,7 @@ static gboolean dt_iop_tonecurve_draw(GtkWidget *widget, cairo_t *crf, gpointer // from https://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.583.3007&rep=rep1&type=pdf const float midtones_weight = sqf(shadows_weight) * sqf(highlights_weight) / (sqf(shadows_weight) + sqf(highlights_weight)); + const float grey_fulcrum = powf(p->grey_fulcrum, 0.4101205819200422f); float *LUT[3]; for(size_t c = 0; c < 3; c++) LUT[c] = dt_alloc_align_float(LUT_ELEM); @@ -846,7 +903,7 @@ static gboolean dt_iop_tonecurve_draw(GtkWidget *widget, cairo_t *crf, gpointer { const float Y = k / (float)(LUT_ELEM - 1); float output[4]; - opacity_masks(Y, shadows_weight, highlights_weight, midtones_weight, output, NULL); + opacity_masks(Y, shadows_weight, highlights_weight, midtones_weight, grey_fulcrum, output, NULL); for(size_t c = 0; c < 3; c++) LUT[c][k] = output[c]; } @@ -901,7 +958,7 @@ void gui_changed(dt_iop_module_t *self, GtkWidget *w, void *previous) if(!w || w == g->highlights_H) paint_chroma_slider(g->highlights_C, p->highlights_H); - if(!w || w == g->shadows_weight || w == g->highlights_weight) + if(!w || w == g->shadows_weight || w == g->highlights_weight || w == g->grey_fulcrum) gtk_widget_queue_draw(GTK_WIDGET(g->area)); --darktable.gui->reset; @@ -949,13 +1006,15 @@ void gui_update(dt_iop_module_t *self) dt_bauhaus_slider_set_soft(g->highlights_Y, p->highlights_Y); dt_bauhaus_slider_set_soft(g->highlights_weight, p->highlights_weight); + dt_bauhaus_slider_set_soft(g->grey_fulcrum, p->grey_fulcrum); + gui_changed(self, NULL, NULL); dt_iop_color_picker_reset(self, TRUE); g->mask_display = FALSE; g->mask_type = MASK_NONE; dt_bauhaus_widget_set_quad_active(GTK_WIDGET(g->shadows_weight), FALSE); - dt_bauhaus_widget_set_quad_active(GTK_WIDGET(g->midtones_weight), FALSE); + dt_bauhaus_widget_set_quad_active(GTK_WIDGET(g->grey_fulcrum), FALSE); dt_bauhaus_widget_set_quad_active(GTK_WIDGET(g->highlights_weight), FALSE); } @@ -1184,15 +1243,17 @@ void gui_init(dt_iop_module_t *self) dt_bauhaus_widget_set_quad_toggle(g->shadows_weight, TRUE); g_signal_connect(G_OBJECT(g->shadows_weight), "quad-pressed", G_CALLBACK(mask_callback), self); - g->midtones_weight = dt_bauhaus_combobox_new(self); - dt_bauhaus_widget_set_label(g->midtones_weight, NULL, N_("midtones roll-off")); - gtk_widget_set_tooltip_text(g->midtones_weight, _("weights of midtones over the whole tonal range." - "this is the remainder of highlights and shadows masks")); - dt_bauhaus_widget_set_quad_paint(g->midtones_weight, dtgtk_cairo_paint_showmask, + g->grey_fulcrum = dt_bauhaus_slider_from_params(self, "grey_fulcrum"); + dt_bauhaus_slider_set_digits(g->grey_fulcrum, 4); + dt_bauhaus_slider_set_step(g->grey_fulcrum, 0.01); + dt_bauhaus_slider_set_format(g->grey_fulcrum, "%.2f %%"); + dt_bauhaus_slider_set_factor(g->grey_fulcrum, 100.0f); + gtk_widget_set_tooltip_text(g->grey_fulcrum, _("position of the middle-grey reference for masking.")); + dt_bauhaus_widget_set_quad_paint(g->grey_fulcrum, dtgtk_cairo_paint_showmask, CPF_STYLE_FLAT | CPF_DO_NOT_USE_BORDER, NULL); - dt_bauhaus_widget_set_quad_toggle(g->midtones_weight, TRUE); - g_signal_connect(G_OBJECT(g->midtones_weight), "quad-pressed", G_CALLBACK(mask_callback), self); - gtk_box_pack_start(GTK_BOX(self->widget), g->midtones_weight, FALSE, FALSE, 0); + dt_bauhaus_widget_set_quad_toggle(g->grey_fulcrum, TRUE); + g_signal_connect(G_OBJECT(g->grey_fulcrum), "quad-pressed", G_CALLBACK(mask_callback), self); + gtk_box_pack_start(GTK_BOX(self->widget), g->grey_fulcrum, FALSE, FALSE, 0); g->highlights_weight = dt_bauhaus_slider_from_params(self, "highlights_weight"); dt_bauhaus_slider_set_step(g->highlights_weight, 0.1); From ced8deaa67e5dee84d933b0a8357b1399902bf5f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aur=C3=A9lien=20PIERRE?= Date: Sun, 4 Apr 2021 13:55:26 +0200 Subject: [PATCH 5/9] colorbalancergb: refactor GUI --- src/iop/colorbalancergb.c | 184 ++++++++++++++++++++------------------ 1 file changed, 95 insertions(+), 89 deletions(-) diff --git a/src/iop/colorbalancergb.c b/src/iop/colorbalancergb.c index 2ada3e5e5d1a..1088314dbd33 100644 --- a/src/iop/colorbalancergb.c +++ b/src/iop/colorbalancergb.c @@ -68,7 +68,7 @@ typedef struct dt_iop_colorbalancergb_params_t float global_C; // $MIN: 0.0 $MAX: 1.0 $DEFAULT: 0.0 $DESCRIPTION: "chroma" float global_H; // $MIN: 0.0 $MAX: 360.0 $DEFAULT: 0.0 $DESCRIPTION: "hue" float shadows_weight; // $MIN: 0.0 $MAX: 3.0 $DEFAULT: 1.0 $DESCRIPTION: "shadows fall-off" - float power_fulcrum; // $MIN: -6.0 $MAX: 6.0 $DEFAULT: 0.0 $DESCRIPTION: "white pivot" + float white_fulcrum; // $MIN: -16.0 $MAX: 16.0 $DEFAULT: 0.0 $DESCRIPTION: "white fulcrum" float highlights_weight; // $MIN: 0.0 $MAX: 3.0 $DEFAULT: 1.0 $DESCRIPTION: "highlights fall-off" float chroma_shadows; // $MIN: -1.0 $MAX: 1.0 $DEFAULT: 0.0 $DESCRIPTION: "shadows" float chroma_highlights; // $MIN: -1.0 $MAX: 1.0 $DEFAULT: 0.0 $DESCRIPTION: "highlights" @@ -81,10 +81,10 @@ typedef struct dt_iop_colorbalancergb_params_t float hue_angle; // $MIN: -180. $MAX: 180. $DEFAULT: 0.0 $DESCRIPTION: "hue shift" /* params of v2 */ - float purity_global; // $MIN: -1.0 $MAX: 1.0 $DEFAULT: 0.0 $DESCRIPTION: "global" - float purity_highlights; // $MIN: -1.0 $MAX: 1.0 $DEFAULT: 0.0 $DESCRIPTION: "highlights" - float purity_midtones; // $MIN: -1.0 $MAX: 1.0 $DEFAULT: 0.0 $DESCRIPTION: "midtones" - float purity_shadows; // $MIN: -1.0 $MAX: 1.0 $DEFAULT: 0.0 $DESCRIPTION: "shadows" + float brilliance_global; // $MIN: -1.0 $MAX: 1.0 $DEFAULT: 0.0 $DESCRIPTION: "global" + float brilliance_highlights; // $MIN: -1.0 $MAX: 1.0 $DEFAULT: 0.0 $DESCRIPTION: "highlights" + float brilliance_midtones; // $MIN: -1.0 $MAX: 1.0 $DEFAULT: 0.0 $DESCRIPTION: "midtones" + float brilliance_shadows; // $MIN: -1.0 $MAX: 1.0 $DEFAULT: 0.0 $DESCRIPTION: "shadows" /* params of v3 */ float grey_fulcrum; // $MIN: 0.0 $MAX: 1.0 $DEFAULT: 0.1845 $DESCRIPTION: "middle-grey fulcrum" @@ -109,10 +109,10 @@ typedef struct dt_iop_colorbalancergb_gui_data_t GtkWidget *shadows_H, *midtones_H, *highlights_H, *global_H; GtkWidget *shadows_C, *midtones_C, *highlights_C, *global_C; GtkWidget *shadows_Y, *midtones_Y, *highlights_Y, *global_Y; - GtkWidget *shadows_weight, *grey_fulcrum, *highlights_weight, *power_fulcrum; + GtkWidget *shadows_weight, *grey_fulcrum, *highlights_weight, *white_fulcrum; GtkWidget *chroma_highlights, *chroma_global, *chroma_shadows, *chroma_midtones; GtkWidget *saturation_global, *saturation_highlights, *saturation_midtones, *saturation_shadows; - GtkWidget *purity_global, *purity_highlights, *purity_midtones, *purity_shadows; + GtkWidget *brilliance_global, *brilliance_highlights, *brilliance_midtones, *brilliance_shadows; GtkWidget *hue_angle; GtkDrawingArea *area; GtkNotebook *notebook; @@ -130,9 +130,9 @@ typedef struct dt_iop_colorbalancergb_data_t float midtones_Y; float chroma_global, chroma[4]; float saturation_global, saturation[4]; - float purity_global, purity[4]; + float brilliance_global, brilliance[4]; float hue_angle; - float shadows_weight, power_fulcrum, highlights_weight, midtones_weight, grey_fulcrum; + float shadows_weight, white_fulcrum, highlights_weight, midtones_weight, grey_fulcrum; float *gamut_LUT; float max_chroma; gboolean lut_inited; @@ -198,7 +198,7 @@ int legacy_params(dt_iop_module_t *self, const void *const old_params, const int float global_C; // $MIN: 0.0 $MAX: 1.0 $DEFAULT: 0.0 $DESCRIPTION: "chroma" float global_H; // $MIN: 0.0 $MAX: 360.0 $DEFAULT: 0.0 $DESCRIPTION: "hue" float shadows_weight; // $MIN: -1.0 $MAX: 1.0 $DEFAULT: 0.0 $DESCRIPTION: "tonal weight" - float power_fulcrum; // $MIN: -6.0 $MAX: 6.0 $DEFAULT: 0.0 $DESCRIPTION: "fulcrum" + float white_fulcrum; // $MIN: -6.0 $MAX: 6.0 $DEFAULT: 0.0 $DESCRIPTION: "fulcrum" float highlights_weight; // $MIN: -1.0 $MAX: 1.0 $DEFAULT: 0.0 $DESCRIPTION: "tonal weight" float chroma_shadows; // $MIN: -1.0 $MAX: 1.0 $DEFAULT: 0.0 $DESCRIPTION: "shadows" float chroma_highlights; // $MIN: -1.0 $MAX: 1.0 $DEFAULT: 0.0 $DESCRIPTION: "highlights" @@ -242,7 +242,7 @@ int legacy_params(dt_iop_module_t *self, const void *const old_params, const int float global_C; // $MIN: 0.0 $MAX: 1.0 $DEFAULT: 0.0 $DESCRIPTION: "chroma" float global_H; // $MIN: 0.0 $MAX: 360.0 $DEFAULT: 0.0 $DESCRIPTION: "hue" float shadows_weight; // $MIN: 0.0 $MAX: 3.0 $DEFAULT: 1.0 $DESCRIPTION: "shadows fall-off" - float power_fulcrum; // $MIN: -6.0 $MAX: 6.0 $DEFAULT: 0.0 $DESCRIPTION: "white pivot" + float white_fulcrum; // $MIN: -6.0 $MAX: 6.0 $DEFAULT: 0.0 $DESCRIPTION: "white pivot" float highlights_weight; // $MIN: 0.0 $MAX: 3.0 $DEFAULT: 1.0 $DESCRIPTION: "highlights fall-off" float chroma_shadows; // $MIN: -1.0 $MAX: 1.0 $DEFAULT: 0.0 $DESCRIPTION: "shadows" float chroma_highlights; // $MIN: -1.0 $MAX: 1.0 $DEFAULT: 0.0 $DESCRIPTION: "highlights" @@ -255,10 +255,10 @@ int legacy_params(dt_iop_module_t *self, const void *const old_params, const int float hue_angle; // $MIN: -180. $MAX: 180. $DEFAULT: 0.0 $DESCRIPTION: "hue shift" /* params of v2 */ - float purity_global; // $MIN: -1.0 $MAX: 1.0 $DEFAULT: 0.0 $DESCRIPTION: "global" - float purity_highlights; // $MIN: -1.0 $MAX: 1.0 $DEFAULT: 0.0 $DESCRIPTION: "highlights" - float purity_midtones; // $MIN: -1.0 $MAX: 1.0 $DEFAULT: 0.0 $DESCRIPTION: "midtones" - float purity_shadows; // $MIN: -1.0 $MAX: 1.0 $DEFAULT: 0.0 $DESCRIPTION: "shadows" + float brilliance_global; // $MIN: -1.0 $MAX: 1.0 $DEFAULT: 0.0 $DESCRIPTION: "global" + float brilliance_highlights; // $MIN: -1.0 $MAX: 1.0 $DEFAULT: 0.0 $DESCRIPTION: "highlights" + float brilliance_midtones; // $MIN: -1.0 $MAX: 1.0 $DEFAULT: 0.0 $DESCRIPTION: "midtones" + float brilliance_shadows; // $MIN: -1.0 $MAX: 1.0 $DEFAULT: 0.0 $DESCRIPTION: "shadows" } dt_iop_colorbalancergb_params_v2_t; @@ -397,7 +397,7 @@ void process(struct dt_iop_module_t *self, dt_dev_pixelpipe_iop_t *piece, const const float *const restrict chroma = __builtin_assume_aligned((const float *const restrict)d->chroma, 16); const float *const restrict saturation = __builtin_assume_aligned((const float *const restrict)d->saturation, 16); - const float *const restrict purity = __builtin_assume_aligned((const float *const restrict)d->purity, 16); + const float *const restrict brilliance = __builtin_assume_aligned((const float *const restrict)d->brilliance, 16); const gint mask_display = (self->dev->gui_attached && (piece->pipe->type & DT_DEV_PIXELPIPE_FULL) == DT_DEV_PIXELPIPE_FULL @@ -409,9 +409,9 @@ void process(struct dt_iop_module_t *self, dt_dev_pixelpipe_iop_t *piece, const #ifdef _OPENMP #pragma omp parallel for simd default(none) aligned(in, out, gamut_LUT: 64) \ - aligned(global, highlights, shadows, midtones, chroma, saturation, purity:16)\ + aligned(global, highlights, shadows, midtones, chroma, saturation, brilliance:16)\ dt_omp_firstprivate(in, out, roi_in, roi_out, d, g, mask_display, input_matrix, output_matrix, gamut_LUT, white_grading_RGB, \ - global, highlights, shadows, midtones, chroma, saturation, purity, checker_1, checker_2) \ + global, highlights, shadows, midtones, chroma, saturation, brilliance, checker_1, checker_2) \ schedule(static) collapse(2) #endif for(size_t i = 0; i < roi_out->height; i++) @@ -468,12 +468,12 @@ void process(struct dt_iop_module_t *self, dt_dev_pixelpipe_iop_t *piece, const // midtones : power with sign preservation const float sign = (RGB[c] < 0.f) ? -1.f : 1.f; - RGB[c] = sign * powf(fabsf(RGB[c]) / d->power_fulcrum, midtones[c]) * d->power_fulcrum; + RGB[c] = sign * powf(fabsf(RGB[c]) / d->white_fulcrum, midtones[c]) * d->white_fulcrum; } // for the Y midtones power (gamma), we need to go in Ych again because RGB doesn't preserve color gradingRGB_to_Ych(RGB, Ych, white_grading_RGB); - Y = Ych[0] = powf(fmaxf(Ych[0] / d->power_fulcrum, 0.f), d->midtones_Y) * d->power_fulcrum; + Y = Ych[0] = powf(fmaxf(Ych[0] / d->white_fulcrum, 0.f), d->midtones_Y) * d->white_fulcrum; Ych_to_gradingRGB(Ych, RGB, white_grading_RGB); /* Perceptual color adjustments */ @@ -510,8 +510,8 @@ void process(struct dt_iop_module_t *self, dt_dev_pixelpipe_iop_t *piece, const { sin_T, cos_T } }; float SO[2]; - // Purity & Saturation : mix of chroma and luminance - const float boosts[2] = { 1.f + d->purity_global + scalar_product(opacities, purity), // move in S direction + // brilliance & Saturation : mix of chroma and luminance + const float boosts[2] = { 1.f + d->brilliance_global + scalar_product(opacities, brilliance), // move in S direction d->saturation_global + scalar_product(opacities, saturation) }; // move in O direction SO[0] = fmaxf(JC[0] * M_rot_dir[0][0] + JC[1] * M_rot_dir[0][1] * boosts[0], 0.f); @@ -584,11 +584,11 @@ void commit_params(struct dt_iop_module_t *self, dt_iop_params_t *p1, dt_dev_pix d->saturation[2] = p->saturation_highlights; d->saturation[3] = 0.f; - d->purity_global = p->purity_global; - d->purity[0] = p->purity_shadows; - d->purity[1] = p->purity_midtones; - d->purity[2] = p->purity_highlights; - d->purity[3] = 0.f; + d->brilliance_global = p->brilliance_global; + d->brilliance[0] = p->brilliance_shadows; + d->brilliance[1] = p->brilliance_midtones; + d->brilliance[2] = p->brilliance_highlights; + d->brilliance[3] = 0.f; d->hue_angle = M_PI * p->hue_angle / 180.f; @@ -626,7 +626,7 @@ void commit_params(struct dt_iop_module_t *self, dt_iop_params_t *p1, dt_dev_pix Ych_to_gradingRGB(Ych, d->midtones, NULL); for(size_t c = 0; c < 4; c++) d->midtones[c] = 1.f / (1.f + (d->midtones[c] - RGB_norm[c])); d->midtones_Y = 1.f / (1.f + p->midtones_Y); - d->power_fulcrum = exp2f(p->power_fulcrum); + d->white_fulcrum = exp2f(p->white_fulcrum); d->midtones_weight = sqf(d->shadows_weight) * sqf(d->highlights_weight) / (sqf(d->shadows_weight) + sqf(d->highlights_weight)); d->grey_fulcrum = powf(p->grey_fulcrum, 0.4101205819200422f); @@ -739,7 +739,9 @@ void color_picker_apply(dt_iop_module_t *self, GtkWidget *picker, dt_dev_pixelpi dt_iop_colorbalancergb_params_t *p = (dt_iop_colorbalancergb_params_t *)self->params; float Ych[4] = { 0.f }; + float max_Ych[4] = { 0.f }; pipe_RGB_to_Ych(self, piece, (const float *)self->picked_color, Ych); + pipe_RGB_to_Ych(self, piece, (const float *)self->picked_color_max, max_Ych); float hue = RAD_TO_DEG(Ych[2]) + 180.f; // take the opponent color hue = (hue > 360.f) ? hue - 360.f : hue; // normalize in [0 ; 360]° @@ -772,6 +774,11 @@ void color_picker_apply(dt_iop_module_t *self, GtkWidget *picker, dt_dev_pixelpi dt_bauhaus_slider_set_soft(g->highlights_H, p->highlights_H); dt_bauhaus_slider_set_soft(g->highlights_C, p->highlights_C); } + else if(picker == g->white_fulcrum) + { + p->white_fulcrum = log2f(max_Ych[0]); + dt_bauhaus_slider_set_soft(g->white_fulcrum, p->white_fulcrum); + } else fprintf(stderr, "[colorbalancergb] unknown color picker\n"); --darktable.gui->reset; @@ -982,10 +989,10 @@ void gui_update(dt_iop_module_t *self) dt_bauhaus_slider_set_soft(g->saturation_midtones, p->saturation_midtones); dt_bauhaus_slider_set_soft(g->saturation_shadows, p->saturation_shadows); - dt_bauhaus_slider_set_soft(g->purity_global, p->purity_global); - dt_bauhaus_slider_set_soft(g->purity_highlights, p->purity_highlights); - dt_bauhaus_slider_set_soft(g->purity_midtones, p->purity_midtones); - dt_bauhaus_slider_set_soft(g->purity_shadows, p->purity_shadows); + dt_bauhaus_slider_set_soft(g->brilliance_global, p->brilliance_global); + dt_bauhaus_slider_set_soft(g->brilliance_highlights, p->brilliance_highlights); + dt_bauhaus_slider_set_soft(g->brilliance_midtones, p->brilliance_midtones); + dt_bauhaus_slider_set_soft(g->brilliance_shadows, p->brilliance_shadows); dt_bauhaus_slider_set_soft(g->global_C, p->global_C); dt_bauhaus_slider_set_soft(g->global_H, p->global_H); @@ -999,7 +1006,7 @@ void gui_update(dt_iop_module_t *self) dt_bauhaus_slider_set_soft(g->midtones_C, p->midtones_C); dt_bauhaus_slider_set_soft(g->midtones_H, p->midtones_H); dt_bauhaus_slider_set_soft(g->midtones_Y, p->midtones_Y); - dt_bauhaus_slider_set_soft(g->power_fulcrum, p->power_fulcrum); + dt_bauhaus_slider_set_soft(g->white_fulcrum, p->white_fulcrum); dt_bauhaus_slider_set_soft(g->highlights_C, p->highlights_C); dt_bauhaus_slider_set_soft(g->highlights_H, p->highlights_H); @@ -1097,38 +1104,38 @@ void gui_init(dt_iop_module_t *self) gtk_widget_set_tooltip_text(g->saturation_highlights, _("increase or decrease saturation proportionnaly to the original pixel saturation")); - gtk_box_pack_start(GTK_BOX(self->widget), dt_ui_section_label_new(_("perceptual purity grading")), FALSE, FALSE, 0); + gtk_box_pack_start(GTK_BOX(self->widget), dt_ui_section_label_new(_("perceptual brilliance grading")), FALSE, FALSE, 0); - g->purity_global = dt_bauhaus_slider_from_params(self, "purity_global"); - dt_bauhaus_slider_set_soft_range(g->purity_global, -0.5, 0.5); - dt_bauhaus_slider_set_digits(g->purity_global, 4); - dt_bauhaus_slider_set_factor(g->purity_global, 100.0f); - dt_bauhaus_slider_set_format(g->purity_global, "%.2f %%"); - gtk_widget_set_tooltip_text(g->purity_global, _("add or remove purity by an absolute amount")); + g->brilliance_global = dt_bauhaus_slider_from_params(self, "brilliance_global"); + dt_bauhaus_slider_set_soft_range(g->brilliance_global, -0.5, 0.5); + dt_bauhaus_slider_set_digits(g->brilliance_global, 4); + dt_bauhaus_slider_set_factor(g->brilliance_global, 100.0f); + dt_bauhaus_slider_set_format(g->brilliance_global, "%.2f %%"); + gtk_widget_set_tooltip_text(g->brilliance_global, _("add or remove brilliance by an absolute amount")); - g->purity_shadows = dt_bauhaus_slider_from_params(self, "purity_shadows"); - dt_bauhaus_slider_set_digits(g->purity_shadows, 4); - dt_bauhaus_slider_set_factor(g->purity_shadows, 100.0f); - dt_bauhaus_slider_set_format(g->purity_shadows, "%.2f %%"); - gtk_widget_set_tooltip_text(g->purity_shadows, _("increase or decrease purity proportionnaly to the original pixel purity")); + g->brilliance_shadows = dt_bauhaus_slider_from_params(self, "brilliance_shadows"); + dt_bauhaus_slider_set_digits(g->brilliance_shadows, 4); + dt_bauhaus_slider_set_factor(g->brilliance_shadows, 100.0f); + dt_bauhaus_slider_set_format(g->brilliance_shadows, "%.2f %%"); + gtk_widget_set_tooltip_text(g->brilliance_shadows, _("increase or decrease brilliance proportionnaly to the original pixel brilliance")); - g->purity_midtones= dt_bauhaus_slider_from_params(self, "purity_midtones"); - dt_bauhaus_slider_set_digits(g->purity_midtones, 4); - dt_bauhaus_slider_set_factor(g->purity_midtones, 100.0f); - dt_bauhaus_slider_set_format(g->purity_midtones, "%.2f %%"); - gtk_widget_set_tooltip_text(g->purity_midtones, _("increase or decrease purity proportionnaly to the original pixel purity")); + g->brilliance_midtones= dt_bauhaus_slider_from_params(self, "brilliance_midtones"); + dt_bauhaus_slider_set_digits(g->brilliance_midtones, 4); + dt_bauhaus_slider_set_factor(g->brilliance_midtones, 100.0f); + dt_bauhaus_slider_set_format(g->brilliance_midtones, "%.2f %%"); + gtk_widget_set_tooltip_text(g->brilliance_midtones, _("increase or decrease brilliance proportionnaly to the original pixel brilliance")); - g->purity_highlights = dt_bauhaus_slider_from_params(self, "purity_highlights"); - dt_bauhaus_slider_set_digits(g->purity_highlights, 4); - dt_bauhaus_slider_set_factor(g->purity_highlights, 100.0f); - dt_bauhaus_slider_set_format(g->purity_highlights, "%.2f %%"); - gtk_widget_set_tooltip_text(g->purity_highlights, _("increase or decrease purity proportionnaly to the original pixel purity")); + g->brilliance_highlights = dt_bauhaus_slider_from_params(self, "brilliance_highlights"); + dt_bauhaus_slider_set_digits(g->brilliance_highlights, 4); + dt_bauhaus_slider_set_factor(g->brilliance_highlights, 100.0f); + dt_bauhaus_slider_set_format(g->brilliance_highlights, "%.2f %%"); + gtk_widget_set_tooltip_text(g->brilliance_highlights, _("increase or decrease brilliance proportionnaly to the original pixel brilliance")); // Page 4-ways self->widget = dt_ui_notebook_page(g->notebook, _("4 ways"), _("selective color grading")); - gtk_box_pack_start(GTK_BOX(self->widget), dt_ui_section_label_new(_("global")), FALSE, FALSE, 0); + gtk_box_pack_start(GTK_BOX(self->widget), dt_ui_section_label_new(_("global offset")), FALSE, FALSE, 0); g->global_Y = dt_bauhaus_slider_from_params(self, "global_Y"); dt_bauhaus_slider_set_soft_range(g->global_Y, -0.05, 0.05); @@ -1151,7 +1158,7 @@ void gui_init(dt_iop_module_t *self) dt_bauhaus_slider_set_format(g->global_C, "%.2f %%"); gtk_widget_set_tooltip_text(g->global_C, _("chroma of the global color offset")); - gtk_box_pack_start(GTK_BOX(self->widget), dt_ui_section_label_new(_("shadows")), FALSE, FALSE, 0); + gtk_box_pack_start(GTK_BOX(self->widget), dt_ui_section_label_new(_("shadows lift")), FALSE, FALSE, 0); g->shadows_Y = dt_bauhaus_slider_from_params(self, "shadows_Y"); dt_bauhaus_slider_set_soft_range(g->shadows_Y, -0.5, 0.5); @@ -1175,31 +1182,7 @@ void gui_init(dt_iop_module_t *self) dt_bauhaus_slider_set_format(g->shadows_C, "%.2f %%"); gtk_widget_set_tooltip_text(g->shadows_C, _("chroma of the color gain in shadows")); - gtk_box_pack_start(GTK_BOX(self->widget), dt_ui_section_label_new(_("midtones")), FALSE, FALSE, 0); - - g->midtones_Y = dt_bauhaus_slider_from_params(self, "midtones_Y"); - dt_bauhaus_slider_set_soft_range(g->midtones_Y, -0.25, 0.25); - dt_bauhaus_slider_set_factor(g->midtones_Y, 100.0f); - dt_bauhaus_slider_set_digits(g->midtones_Y, 4); - dt_bauhaus_slider_set_format(g->midtones_Y, "%.2f %%"); - gtk_widget_set_tooltip_text(g->midtones_Y, _("luminance exponent in midtones")); - - g->midtones_H = dt_color_picker_new(self, DT_COLOR_PICKER_AREA, dt_bauhaus_slider_from_params(self, "midtones_H")); - dt_bauhaus_slider_set_feedback(g->midtones_H, 0); - dt_bauhaus_slider_set_step(g->midtones_H, 10.); - dt_bauhaus_slider_set_digits(g->midtones_H, 4); - dt_bauhaus_slider_set_format(g->midtones_H, "%.2f °"); - gtk_widget_set_tooltip_text(g->midtones_H, _("hue of the color exponent in midtones")); - - g->midtones_C = dt_bauhaus_slider_from_params(self, "midtones_C"); - dt_bauhaus_slider_set_soft_range(g->midtones_C, 0., 0.02); - dt_bauhaus_slider_set_step(g->midtones_C, 0.005); - dt_bauhaus_slider_set_digits(g->midtones_C, 4); - dt_bauhaus_slider_set_factor(g->midtones_C, 100.0f); - dt_bauhaus_slider_set_format(g->midtones_C, "%.2f %%"); - gtk_widget_set_tooltip_text(g->midtones_C, _("chroma of the color exponent in midtones")); - - gtk_box_pack_start(GTK_BOX(self->widget), dt_ui_section_label_new(_("highlights")), FALSE, FALSE, 0); + gtk_box_pack_start(GTK_BOX(self->widget), dt_ui_section_label_new(_("highlights gain")), FALSE, FALSE, 0); g->highlights_Y = dt_bauhaus_slider_from_params(self, "highlights_Y"); dt_bauhaus_slider_set_soft_range(g->highlights_Y, -0.5, 0.5); @@ -1223,6 +1206,30 @@ void gui_init(dt_iop_module_t *self) dt_bauhaus_slider_set_format(g->highlights_C, "%.2f %%"); gtk_widget_set_tooltip_text(g->highlights_C, _("chroma of the color gain in highlights")); + gtk_box_pack_start(GTK_BOX(self->widget), dt_ui_section_label_new(_("power")), FALSE, FALSE, 0); + + g->midtones_Y = dt_bauhaus_slider_from_params(self, "midtones_Y"); + dt_bauhaus_slider_set_soft_range(g->midtones_Y, -0.25, 0.25); + dt_bauhaus_slider_set_factor(g->midtones_Y, 100.0f); + dt_bauhaus_slider_set_digits(g->midtones_Y, 4); + dt_bauhaus_slider_set_format(g->midtones_Y, "%.2f %%"); + gtk_widget_set_tooltip_text(g->midtones_Y, _("luminance exponent in midtones")); + + g->midtones_H = dt_color_picker_new(self, DT_COLOR_PICKER_AREA, dt_bauhaus_slider_from_params(self, "midtones_H")); + dt_bauhaus_slider_set_feedback(g->midtones_H, 0); + dt_bauhaus_slider_set_step(g->midtones_H, 10.); + dt_bauhaus_slider_set_digits(g->midtones_H, 4); + dt_bauhaus_slider_set_format(g->midtones_H, "%.2f °"); + gtk_widget_set_tooltip_text(g->midtones_H, _("hue of the color exponent in midtones")); + + g->midtones_C = dt_bauhaus_slider_from_params(self, "midtones_C"); + dt_bauhaus_slider_set_soft_range(g->midtones_C, 0., 0.02); + dt_bauhaus_slider_set_step(g->midtones_C, 0.005); + dt_bauhaus_slider_set_digits(g->midtones_C, 4); + dt_bauhaus_slider_set_factor(g->midtones_C, 100.0f); + dt_bauhaus_slider_set_format(g->midtones_C, "%.2f %%"); + gtk_widget_set_tooltip_text(g->midtones_C, _("chroma of the color exponent in midtones")); + // Page masks self->widget = dt_ui_notebook_page(g->notebook, _("masks"), _("isolate luminances")); @@ -1268,13 +1275,12 @@ void gui_init(dt_iop_module_t *self) gtk_box_pack_start(GTK_BOX(self->widget), dt_ui_section_label_new(_("threshold")), FALSE, FALSE, 0); - g->power_fulcrum = dt_bauhaus_slider_from_params(self, "power_fulcrum"); - dt_bauhaus_slider_set_soft_range(g->power_fulcrum, -2., +2.); - dt_bauhaus_slider_set_step(g->power_fulcrum, 0.1); - dt_bauhaus_slider_set_digits(g->power_fulcrum, 4); - dt_bauhaus_slider_set_format(g->power_fulcrum, "%.2f EV"); - gtk_widget_set_tooltip_text(g->power_fulcrum, _("peak white luminance value used to normalize the power function")); - + g->white_fulcrum = dt_color_picker_new(self, DT_COLOR_PICKER_AREA, dt_bauhaus_slider_from_params(self, "white_fulcrum")); + dt_bauhaus_slider_set_soft_range(g->white_fulcrum, -2., +2.); + dt_bauhaus_slider_set_step(g->white_fulcrum, 0.1); + dt_bauhaus_slider_set_digits(g->white_fulcrum, 4); + dt_bauhaus_slider_set_format(g->white_fulcrum, "%.2f EV"); + gtk_widget_set_tooltip_text(g->white_fulcrum, _("peak white luminance value used to normalize the power function")); // paint backgrounds for(int i = 0; i < DT_BAUHAUS_SLIDER_MAX_STOPS; i++) From a403753850dd9c149dcb4653ce75455ed7baa953 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aur=C3=A9lien=20PIERRE?= Date: Mon, 5 Apr 2021 21:32:16 +0200 Subject: [PATCH 6/9] colorbalancergb: improve graph --- src/iop/colorbalancergb.c | 83 ++++++++++++++++++++++++++++++++++++--- 1 file changed, 77 insertions(+), 6 deletions(-) diff --git a/src/iop/colorbalancergb.c b/src/iop/colorbalancergb.c index 1088314dbd33..e63d11207aa3 100644 --- a/src/iop/colorbalancergb.c +++ b/src/iop/colorbalancergb.c @@ -32,6 +32,7 @@ #include "dtgtk/drawingarea.h" #include "dtgtk/gradientslider.h" #include "gui/accelerators.h" +#include "gui/draw.h" #include "gui/gtk.h" #include "gui/presets.h" #include "gui/color_picker_proxy.h" @@ -879,10 +880,19 @@ static gboolean dt_iop_tonecurve_draw(GtkWidget *widget, cairo_t *crf, gpointer pango_layout_set_font_description(layout, desc); pango_cairo_context_set_resolution(pango_layout_get_context(layout), darktable.gui->dpi); - //const float inset = DT_PIXEL_APPLY_DPI(4); - const float margin_top = 0; - const float margin_bottom = 0; - const float margin_left = 0; + char text[256]; + + // Get the text line height for spacing + PangoRectangle ink; + snprintf(text, sizeof(text), "X"); + pango_layout_set_text(layout, text, -1); + pango_layout_get_pixel_extents(layout, &ink, NULL); + const float line_height = ink.height; + + const float inset = DT_PIXEL_APPLY_DPI(4); + const float margin_top = inset; + const float margin_bottom = line_height + 2 * inset; + const float margin_left = line_height + inset; const float margin_right = 0; const float graph_width = allocation.width - margin_right - margin_left; // align the right border on sliders @@ -890,13 +900,63 @@ static gboolean dt_iop_tonecurve_draw(GtkWidget *widget, cairo_t *crf, gpointer gtk_render_background(context, cr, 0, 0, allocation.width, allocation.height); + // draw x gradient as axis legend + cairo_pattern_t *grad; + grad = cairo_pattern_create_linear(margin_left, 0.0, graph_width, 0.0); + dt_cairo_perceptual_gradient(grad, 1.0); + cairo_set_line_width(cr, 0.0); + cairo_rectangle(cr, margin_left, graph_height + 2 * inset, graph_width, line_height); + cairo_set_source(cr, grad); + cairo_fill(cr); + cairo_pattern_destroy(grad); + + // draw y gradient as axis legend + const int stride = cairo_format_stride_for_width(CAIRO_FORMAT_ARGB32, line_height); + unsigned char *data = malloc(stride * graph_height); + cairo_surface_t *surface = cairo_image_surface_create_for_data(data, CAIRO_FORMAT_ARGB32, (size_t)line_height, (size_t)graph_height, stride); + + const size_t checker_1 = DT_PIXEL_APPLY_DPI(6); + const size_t checker_2 = 2 * checker_1; + +#ifdef _OPENMP +#pragma omp parallel for simd default(none) \ + dt_omp_firstprivate(data, graph_height, line_height, checker_1, checker_2) \ + schedule(static) collapse(2) +#endif + for(size_t i = 0; i < (size_t)graph_height; i++) + for(size_t j = 0; j < (size_t)line_height; j++) + { + const size_t k = ((i * (size_t)line_height) + j) * 4; + unsigned char color; + const float alpha = (float)i / graph_height; + if(i % checker_1 < i % checker_2) + { + if(j % checker_1 < j % checker_2) color = 150; + else color = 100; + } + else + { + if(j % checker_1 < j % checker_2) color = 100; + else color = 150; + } + + for(size_t c = 0; c < 4; ++c) data[k + c] = color * alpha; + data[k+3] = alpha * 255; + } + + cairo_set_source_surface(cr, surface, 0, margin_top); + cairo_paint(cr); + free(data); + cairo_surface_destroy(surface); + // set the graph as the origin of the coordinates cairo_translate(cr, margin_left, margin_top); cairo_set_line_cap(cr, CAIRO_LINE_CAP_ROUND); set_color(cr, darktable.bauhaus->graph_bg); cairo_rectangle(cr, 0, 0, graph_width, graph_height); - cairo_fill(cr); + cairo_fill_preserve(cr); + cairo_clip(cr); // from https://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.583.3007&rep=rep1&type=pdf const float midtones_weight @@ -906,6 +966,11 @@ static gboolean dt_iop_tonecurve_draw(GtkWidget *widget, cairo_t *crf, gpointer float *LUT[3]; for(size_t c = 0; c < 3; c++) LUT[c] = dt_alloc_align_float(LUT_ELEM); +#ifdef _OPENMP +#pragma omp parallel for simd default(none) \ + dt_omp_firstprivate(LUT, shadows_weight, midtones_weight, highlights_weight, grey_fulcrum) \ + schedule(static) +#endif for(size_t k = 0 ; k < LUT_ELEM; k++) { const float Y = k / (float)(LUT_ELEM - 1); @@ -914,11 +979,17 @@ static gboolean dt_iop_tonecurve_draw(GtkWidget *widget, cairo_t *crf, gpointer for(size_t c = 0; c < 3; c++) LUT[c][k] = output[c]; } - set_color(cr, darktable.bauhaus->graph_fg); + GdkRGBA fg_color = darktable.bauhaus->graph_fg; cairo_set_line_width(cr, DT_PIXEL_APPLY_DPI(2.)); for(size_t c = 0; c < 3; c++) { + GdkRGBA line_color = { fg_color.red * (1. - (2 - c) / 4.), + fg_color.green * (1. - (2 - c) / 4.), + fg_color.blue * (1. - (2 - c) / 4.), + fg_color.alpha }; + set_color(cr, line_color); + cairo_move_to(cr, 0, (1.f - LUT[c][0]) * graph_height); for(size_t k = 0; k < LUT_ELEM; k++) { From f1b49ff55b8abca8752225fc8c8a22a3416a58f7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aur=C3=A9lien=20PIERRE?= Date: Wed, 7 Apr 2021 13:24:13 +0200 Subject: [PATCH 7/9] =?UTF-8?q?colorbalancergb:=C2=A0minor=20code=20refact?= =?UTF-8?q?oring?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/iop/colorbalancergb.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/iop/colorbalancergb.c b/src/iop/colorbalancergb.c index e63d11207aa3..f491f65b0d8c 100644 --- a/src/iop/colorbalancergb.c +++ b/src/iop/colorbalancergb.c @@ -556,12 +556,13 @@ void process(struct dt_iop_module_t *self, dt_dev_pixelpipe_iop_t *piece, const float opacity = opacities[g->mask_type]; const float opacity_comp = 1.0f - opacity; - for(size_t c = 0; c < 4; ++c) pix_out[c] = opacity_comp * color + opacity * fmaxf(pix_out[c], 0.f); + for_four_channels(c) pix_out[c] = opacity_comp * color + opacity * fmaxf(pix_out[c], 0.f); pix_out[3] = 1.f; } else { - for(size_t c = 0; c < 4; ++c) pix_out[c] = fmaxf(pix_out[c], 0.f); + for_four_channels(c) pix_out[c] = fmaxf(pix_out[c], 0.f); + pix_out[3] = pix_in[3]; // copy alpha } } } @@ -901,8 +902,7 @@ static gboolean dt_iop_tonecurve_draw(GtkWidget *widget, cairo_t *crf, gpointer gtk_render_background(context, cr, 0, 0, allocation.width, allocation.height); // draw x gradient as axis legend - cairo_pattern_t *grad; - grad = cairo_pattern_create_linear(margin_left, 0.0, graph_width, 0.0); + cairo_pattern_t *grad = cairo_pattern_create_linear(margin_left, 0.0, graph_width, 0.0); dt_cairo_perceptual_gradient(grad, 1.0); cairo_set_line_width(cr, 0.0); cairo_rectangle(cr, margin_left, graph_height + 2 * inset, graph_width, line_height); @@ -940,8 +940,8 @@ static gboolean dt_iop_tonecurve_draw(GtkWidget *widget, cairo_t *crf, gpointer else color = 150; } - for(size_t c = 0; c < 4; ++c) data[k + c] = color * alpha; - data[k+3] = alpha * 255; + for_four_channels(c) data[k + c] = color * alpha; + data[k + 3] = alpha * 255; } cairo_set_source_surface(cr, surface, 0, margin_top); From 4a6be4537b87ef6cda817ffc5440be313dc97ac9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aur=C3=A9lien=20PIERRE?= Date: Wed, 7 Apr 2021 14:09:35 +0200 Subject: [PATCH 8/9] =?UTF-8?q?Revert=20"colorbalancergb:=C2=A0minor=20cod?= =?UTF-8?q?e=20refactoring"?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reverts commit f1b49ff55b8abca8752225fc8c8a22a3416a58f7. --- src/iop/colorbalancergb.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/iop/colorbalancergb.c b/src/iop/colorbalancergb.c index f491f65b0d8c..e63d11207aa3 100644 --- a/src/iop/colorbalancergb.c +++ b/src/iop/colorbalancergb.c @@ -556,13 +556,12 @@ void process(struct dt_iop_module_t *self, dt_dev_pixelpipe_iop_t *piece, const float opacity = opacities[g->mask_type]; const float opacity_comp = 1.0f - opacity; - for_four_channels(c) pix_out[c] = opacity_comp * color + opacity * fmaxf(pix_out[c], 0.f); + for(size_t c = 0; c < 4; ++c) pix_out[c] = opacity_comp * color + opacity * fmaxf(pix_out[c], 0.f); pix_out[3] = 1.f; } else { - for_four_channels(c) pix_out[c] = fmaxf(pix_out[c], 0.f); - pix_out[3] = pix_in[3]; // copy alpha + for(size_t c = 0; c < 4; ++c) pix_out[c] = fmaxf(pix_out[c], 0.f); } } } @@ -902,7 +901,8 @@ static gboolean dt_iop_tonecurve_draw(GtkWidget *widget, cairo_t *crf, gpointer gtk_render_background(context, cr, 0, 0, allocation.width, allocation.height); // draw x gradient as axis legend - cairo_pattern_t *grad = cairo_pattern_create_linear(margin_left, 0.0, graph_width, 0.0); + cairo_pattern_t *grad; + grad = cairo_pattern_create_linear(margin_left, 0.0, graph_width, 0.0); dt_cairo_perceptual_gradient(grad, 1.0); cairo_set_line_width(cr, 0.0); cairo_rectangle(cr, margin_left, graph_height + 2 * inset, graph_width, line_height); @@ -940,8 +940,8 @@ static gboolean dt_iop_tonecurve_draw(GtkWidget *widget, cairo_t *crf, gpointer else color = 150; } - for_four_channels(c) data[k + c] = color * alpha; - data[k + 3] = alpha * 255; + for(size_t c = 0; c < 4; ++c) data[k + c] = color * alpha; + data[k+3] = alpha * 255; } cairo_set_source_surface(cr, surface, 0, margin_top); From 9a8bf639c71c8a2b4da4ee097ecadb275b1e9c41 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aur=C3=A9lien=20PIERRE?= Date: Thu, 8 Apr 2021 15:02:27 +0200 Subject: [PATCH 9/9] colorbalancergb: remove a left-over --- src/iop/colorbalancergb.c | 1 - 1 file changed, 1 deletion(-) diff --git a/src/iop/colorbalancergb.c b/src/iop/colorbalancergb.c index e63d11207aa3..455f534100d4 100644 --- a/src/iop/colorbalancergb.c +++ b/src/iop/colorbalancergb.c @@ -1331,7 +1331,6 @@ void gui_init(dt_iop_module_t *self) CPF_STYLE_FLAT | CPF_DO_NOT_USE_BORDER, NULL); dt_bauhaus_widget_set_quad_toggle(g->grey_fulcrum, TRUE); g_signal_connect(G_OBJECT(g->grey_fulcrum), "quad-pressed", G_CALLBACK(mask_callback), self); - gtk_box_pack_start(GTK_BOX(self->widget), g->grey_fulcrum, FALSE, FALSE, 0); g->highlights_weight = dt_bauhaus_slider_from_params(self, "highlights_weight"); dt_bauhaus_slider_set_step(g->highlights_weight, 0.1);