1 /*
2     This file is part of darktable,
3     Copyright (C) 2017-2021 darktable developers.
4 
5     darktable is free software: you can redistribute it and/or modify
6     it under the terms of the GNU General Public License as published by
7     the Free Software Foundation, either version 3 of the License, or
8     (at your option) any later version.
9 
10     darktable is distributed in the hope that it will be useful,
11     but WITHOUT ANY WARRANTY; without even the implied warranty of
12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13     GNU General Public License for more details.
14 
15     You should have received a copy of the GNU General Public License
16     along with darktable.  If not, see <http://www.gnu.org/licenses/>.
17 */
18 
19 #ifdef HAVE_CONFIG_H
20 #include "config.h"
21 #endif
22 #include "bauhaus/bauhaus.h"
23 #include "common/bilateral.h"
24 #include "common/bilateralcl.h"
25 #include "common/colorspaces_inline_conversions.h"
26 #include "common/dwt.h"
27 #include "common/gaussian.h"
28 #include "common/heal.h"
29 #include "common/imagebuf.h"
30 #include "common/opencl.h"
31 #include "develop/blend.h"
32 #include "develop/imageop_math.h"
33 #include "develop/imageop_gui.h"
34 #include "develop/masks.h"
35 #include "iop/iop_api.h"
36 #include "dtgtk/drawingarea.h"
37 #include "gui/accelerators.h"
38 #include "gui/color_picker_proxy.h"
39 #include <stdlib.h>
40 
41 // this is the version of the modules parameters,
42 // and includes version information about compile-time dt
43 DT_MODULE_INTROSPECTION(2, dt_iop_retouch_params_t)
44 
45 #define RETOUCH_NO_FORMS 300
46 #define RETOUCH_MAX_SCALES 15
47 #define RETOUCH_NO_SCALES (RETOUCH_MAX_SCALES + 2)
48 
49 #define RETOUCH_PREVIEW_LVL_MIN -3.0f
50 #define RETOUCH_PREVIEW_LVL_MAX 3.0f
51 
52 typedef enum dt_iop_retouch_drag_types_t {
53   DT_IOP_RETOUCH_WDBAR_DRAG_TOP = 1,
54   DT_IOP_RETOUCH_WDBAR_DRAG_BOTTOM = 2,
55 } dt_iop_retouch_drag_types_t;
56 
57 typedef enum dt_iop_retouch_fill_modes_t {
58   DT_IOP_RETOUCH_FILL_ERASE = 0, // $DESCRIPTION: "erase"
59   DT_IOP_RETOUCH_FILL_COLOR = 1  // $DESCRIPTION: "color"
60 } dt_iop_retouch_fill_modes_t;
61 
62 typedef enum dt_iop_retouch_blur_types_t {
63   DT_IOP_RETOUCH_BLUR_GAUSSIAN = 0, // $DESCRIPTION: "gaussian"
64   DT_IOP_RETOUCH_BLUR_BILATERAL = 1 // $DESCRIPTION: "bilateral"
65 } dt_iop_retouch_blur_types_t;
66 
67 typedef enum dt_iop_retouch_algo_type_t {
68   DT_IOP_RETOUCH_NONE = 0,  // $DESCRIPTION: "unused"
69   DT_IOP_RETOUCH_CLONE = 1, // $DESCRIPTION: "clone"
70   DT_IOP_RETOUCH_HEAL = 2,  // $DESCRIPTION: "heal"
71   DT_IOP_RETOUCH_BLUR = 3,  // $DESCRIPTION: "blur"
72   DT_IOP_RETOUCH_FILL = 4   // $DESCRIPTION: "fill"
73 } dt_iop_retouch_algo_type_t;
74 
75 typedef struct dt_iop_retouch_form_data_t
76 {
77   int formid; // from masks, form->formid
78   int scale;  // 0==original image; 1..RETOUCH_MAX_SCALES==scale; RETOUCH_MAX_SCALES+1==residual
79   dt_iop_retouch_algo_type_t algorithm;  // clone, heal, blur, fill
80 
81   dt_iop_retouch_blur_types_t blur_type; // gaussian, bilateral
82   float blur_radius;                     // radius for blur algorithm
83 
84   dt_iop_retouch_fill_modes_t fill_mode; // mode for fill algorithm, erase or fill with color
85   float fill_color[3];                   // color for fill algorithm
86   float fill_brightness;                 // value to be added to the color
87   int distort_mode; // module v1 => 1, otherwise 2. mode 1 as issues if there's distortion before this module
88 } dt_iop_retouch_form_data_t;
89 
90 typedef struct retouch_user_data_t
91 {
92   dt_iop_module_t *self;
93   dt_dev_pixelpipe_iop_t *piece;
94   dt_iop_roi_t roi;
95   int display_scale;
96   int mask_display;
97   int suppress_mask;
98 } retouch_user_data_t;
99 
100 typedef struct dt_iop_retouch_params_t
101 {
102   dt_iop_retouch_form_data_t rt_forms[RETOUCH_NO_FORMS]; // array of masks index and additional data
103 
104   dt_iop_retouch_algo_type_t algorithm; // $DEFAULT: DT_IOP_RETOUCH_HEAL clone, heal, blur, fill
105 
106   int num_scales;       // $DEFAULT: 0 number of wavelets scales
107   int curr_scale;       // $DEFAULT: 0 current wavelet scale
108   int merge_from_scale; // $DEFAULT: 0
109 
110   float preview_levels[3];
111 
112   dt_iop_retouch_blur_types_t blur_type; // $DEFAULT: DT_IOP_RETOUCH_BLUR_GAUSSIAN $DESCRIPTION: "blur type" gaussian, bilateral
113   float blur_radius; // $MIN: 0.1 $MAX: 200.0 $DEFAULT: 10.0 $DESCRIPTION: "blur radius" radius for blur algorithm
114 
115   dt_iop_retouch_fill_modes_t fill_mode; // $DEFAULT: DT_IOP_RETOUCH_FILL_ERASE $DESCRIPTION: "fill mode" mode for fill algorithm, erase or fill with color
116   float fill_color[3];   // $DEFAULT: 0.0 color for fill algorithm
117   float fill_brightness; // $MIN: -1.0 $MAX: 1.0 $DESCRIPTION: "brightness" value to be added to the color
118 } dt_iop_retouch_params_t;
119 
120 typedef struct dt_iop_retouch_gui_data_t
121 {
122   int copied_scale; // scale to be copied to another scale
123   int mask_display; // should we expose masks?
124   int suppress_mask;         // do not process masks
125   int display_wavelet_scale; // display current wavelet scale
126   int displayed_wavelet_scale; // was display wavelet scale already used?
127   int preview_auto_levels;   // should we calculate levels automatically?
128   float preview_levels[3];   // values for the levels
129   int first_scale_visible;   // 1st scale visible at current zoom level
130 
131   GtkLabel *label_form;                                                   // display number of forms
132   GtkLabel *label_form_selected;                                          // display number of forms selected
133   GtkWidget *bt_edit_masks, *bt_path, *bt_circle, *bt_ellipse, *bt_brush; // shapes
134   GtkWidget *bt_clone, *bt_heal, *bt_blur, *bt_fill;                      // algorithms
135   GtkWidget *bt_showmask, *bt_suppress;                                   // suppress & show masks
136 
137   GtkWidget *wd_bar; // wavelet decompose bar
138   GtkLabel *lbl_num_scales;
139   GtkLabel *lbl_curr_scale;
140   GtkLabel *lbl_merge_from_scale;
141   float wdbar_mouse_x, wdbar_mouse_y;
142   int curr_scale; // scale box under mouse
143   gboolean is_dragging;
144   gboolean upper_cursor; // mouse on merge from scale cursor
145   gboolean lower_cursor; // mouse on num scales cursor
146   gboolean upper_margin; // mouse on the upper band
147   gboolean lower_margin; // mouse on the lower band
148 
149   GtkWidget *bt_display_wavelet_scale; // show decomposed scale
150 
151   GtkWidget *bt_copy_scale; // copy all shapes from one scale to another
152   GtkWidget *bt_paste_scale;
153 
154   GtkWidget *vbox_preview_scale;
155 
156   GtkDarktableGradientSlider *preview_levels_gslider;
157 
158   GtkWidget *bt_auto_levels;
159 
160   GtkWidget *vbox_blur;
161   GtkWidget *cmb_blur_type;
162   GtkWidget *sl_blur_radius;
163 
164   GtkWidget *vbox_fill;
165   GtkWidget *hbox_color_pick;
166   GtkWidget *colorpick;   // select a specific color
167   GtkWidget *colorpicker; // pick a color from the picture
168 
169   GtkWidget *cmb_fill_mode;
170   GtkWidget *sl_fill_brightness;
171 
172   GtkWidget *sl_mask_opacity; // draw mask opacity
173 } dt_iop_retouch_gui_data_t;
174 
175 typedef struct dt_iop_retouch_params_t dt_iop_retouch_data_t;
176 
177 typedef struct dt_iop_retouch_global_data_t
178 {
179   int kernel_retouch_clear_alpha;
180   int kernel_retouch_copy_alpha;
181   int kernel_retouch_copy_buffer_to_buffer;
182   int kernel_retouch_copy_buffer_to_image;
183   int kernel_retouch_fill;
184   int kernel_retouch_copy_image_to_buffer_masked;
185   int kernel_retouch_copy_buffer_to_buffer_masked;
186   int kernel_retouch_image_rgb2lab;
187   int kernel_retouch_image_lab2rgb;
188   int kernel_retouch_copy_mask_to_alpha;
189 } dt_iop_retouch_global_data_t;
190 
191 
192 // this returns a translatable name
name()193 const char *name()
194 {
195   return _("retouch");
196 }
197 
aliases()198 const char *aliases()
199 {
200   return _("split-frequency|healing|cloning|stamp");
201 }
202 
203 
description(struct dt_iop_module_t * self)204 const char *description(struct dt_iop_module_t *self)
205 {
206   return dt_iop_set_description(self, _("remove and clone spots, perform split-frequency skin editing"),
207                                       _("corrective"),
208                                       _("linear, RGB, scene-referred"),
209                                       _("geometric and frequential, RGB"),
210                                       _("linear, RGB, scene-referred"));
211 }
212 
default_group()213 int default_group()
214 {
215   return IOP_GROUP_CORRECT | IOP_GROUP_EFFECTS;
216 }
217 
flags()218 int flags()
219 {
220   return IOP_FLAGS_SUPPORTS_BLENDING | IOP_FLAGS_NO_MASKS;
221 }
222 
default_colorspace(dt_iop_module_t * self,dt_dev_pixelpipe_t * pipe,dt_dev_pixelpipe_iop_t * piece)223 int default_colorspace(dt_iop_module_t *self, dt_dev_pixelpipe_t *pipe, dt_dev_pixelpipe_iop_t *piece)
224 {
225   return iop_cs_rgb;
226 }
227 
legacy_params(dt_iop_module_t * self,const void * const old_params,const int old_version,void * new_params,const int new_version)228 int legacy_params(dt_iop_module_t *self, const void *const old_params, const int old_version, void *new_params,
229                   const int new_version)
230 {
231   if(old_version == 1 && new_version == 2)
232   {
233     typedef struct dt_iop_retouch_form_data_v1_t
234     {
235       int formid; // from masks, form->formid
236       int scale;  // 0==original image; 1..RETOUCH_MAX_SCALES==scale; RETOUCH_MAX_SCALES+1==residual
237       dt_iop_retouch_algo_type_t algorithm; // clone, heal, blur, fill
238 
239       dt_iop_retouch_blur_types_t blur_type; // gaussian, bilateral
240       float blur_radius;                     // radius for blur algorithm
241 
242       dt_iop_retouch_fill_modes_t fill_mode; // mode for fill algorithm, erase or fill with color
243       float fill_color[3];                   // color for fill algorithm
244       float fill_brightness;                 // value to be added to the color
245     } dt_iop_retouch_form_data_v1_t;
246     typedef struct dt_iop_retouch_params_v1_t
247     {
248       dt_iop_retouch_form_data_v1_t rt_forms[RETOUCH_NO_FORMS]; // array of masks index and additional data
249 
250       dt_iop_retouch_algo_type_t algorithm; // $DEFAULT: DT_IOP_RETOUCH_HEAL clone, heal, blur, fill
251 
252       int num_scales;       // $DEFAULT: 0 number of wavelets scales
253       int curr_scale;       // $DEFAULT: 0 current wavelet scale
254       int merge_from_scale; // $DEFAULT: 0
255 
256       float preview_levels[3];
257 
258       dt_iop_retouch_blur_types_t blur_type; // $DEFAULT: DT_IOP_RETOUCH_BLUR_GAUSSIAN $DESCRIPTION: "blur type"
259                                              // gaussian, bilateral
260       float blur_radius; // $MIN: 0.1 $MAX: 200.0 $DEFAULT: 10.0 $DESCRIPTION: "blur radius" radius for blur
261                          // algorithm
262 
263       dt_iop_retouch_fill_modes_t fill_mode; // $DEFAULT: DT_IOP_RETOUCH_FILL_ERASE $DESCRIPTION: "fill mode" mode
264                                              // for fill algorithm, erase or fill with color
265       float fill_color[3];                   // $DEFAULT: 0.0 color for fill algorithm
266       float fill_brightness; // $MIN: -1.0 $MAX: 1.0 $DESCRIPTION: "brightness" value to be added to the color
267     } dt_iop_retouch_params_v1_t;
268 
269     dt_iop_retouch_params_v1_t *o = (dt_iop_retouch_params_v1_t *)old_params;
270     dt_iop_retouch_params_t *n = (dt_iop_retouch_params_t *)new_params;
271     dt_iop_retouch_params_t *d = (dt_iop_retouch_params_t *)self->default_params;
272 
273     *n = *d; // start with a fresh copy of default parameters
274     for(int i = 0; i < RETOUCH_NO_FORMS; i++)
275     {
276       dt_iop_retouch_form_data_v1_t of = o->rt_forms[i];
277       n->rt_forms[i].algorithm = of.algorithm;
278       n->rt_forms[i].blur_radius = of.blur_radius;
279       n->rt_forms[i].blur_type = of.blur_type;
280       n->rt_forms[i].distort_mode = 1;
281       n->rt_forms[i].fill_brightness = of.fill_brightness;
282       n->rt_forms[i].fill_color[0] = of.fill_color[0];
283       n->rt_forms[i].fill_color[1] = of.fill_color[1];
284       n->rt_forms[i].fill_color[2] = of.fill_color[2];
285       n->rt_forms[i].fill_mode = of.fill_mode;
286       n->rt_forms[i].formid = of.formid;
287       n->rt_forms[i].scale = of.scale;
288     }
289     n->algorithm = o->algorithm;
290     n->blur_radius = o->blur_radius;
291     n->blur_type = o->blur_type;
292     n->curr_scale = o->curr_scale;
293     n->fill_brightness = o->fill_brightness;
294     n->fill_color[0] = o->fill_color[0];
295     n->fill_color[1] = o->fill_color[1];
296     n->fill_color[2] = o->fill_color[2];
297     n->fill_mode = o->fill_mode;
298     n->merge_from_scale = o->merge_from_scale;
299     n->num_scales = o->num_scales;
300     n->preview_levels[0] = o->preview_levels[0];
301     n->preview_levels[1] = o->preview_levels[1];
302     n->preview_levels[2] = o->preview_levels[2];
303 
304     return 0;
305   }
306   return 1;
307 }
308 
rt_get_index_from_formid(dt_iop_retouch_params_t * p,const int formid)309 static int rt_get_index_from_formid(dt_iop_retouch_params_t *p, const int formid)
310 {
311   int index = -1;
312   if(formid > 0)
313   {
314     int i = 0;
315 
316     while(index == -1 && i < RETOUCH_NO_FORMS)
317     {
318       if(p->rt_forms[i].formid == formid) index = i;
319       i++;
320     }
321   }
322   return index;
323 }
324 
rt_get_selected_shape_id()325 static int rt_get_selected_shape_id()
326 {
327   return darktable.develop->mask_form_selected_id;
328 }
329 
rt_get_mask_point_group(dt_iop_module_t * self,int formid)330 static dt_masks_point_group_t *rt_get_mask_point_group(dt_iop_module_t *self, int formid)
331 {
332   dt_masks_point_group_t *form_point_group = NULL;
333 
334   const dt_develop_blend_params_t *bp = self->blend_params;
335   if(!bp) return form_point_group;
336 
337   const dt_masks_form_t *grp = dt_masks_get_from_id(self->dev, bp->mask_id);
338   if(grp && (grp->type & DT_MASKS_GROUP))
339   {
340     for(const GList *forms = grp->points; forms; forms = g_list_next(forms))
341     {
342       dt_masks_point_group_t *grpt = (dt_masks_point_group_t *)forms->data;
343       if(grpt->formid == formid)
344       {
345         form_point_group = grpt;
346         break;
347       }
348     }
349   }
350 
351   return form_point_group;
352 }
353 
rt_get_shape_opacity(dt_iop_module_t * self,const int formid)354 static float rt_get_shape_opacity(dt_iop_module_t *self, const int formid)
355 {
356   float opacity = 0.f;
357 
358   dt_masks_point_group_t *grpt = rt_get_mask_point_group(self, formid);
359   if(grpt) opacity = grpt->opacity;
360 
361   return opacity;
362 }
363 
rt_display_selected_fill_color(dt_iop_retouch_gui_data_t * g,dt_iop_retouch_params_t * p)364 static void rt_display_selected_fill_color(dt_iop_retouch_gui_data_t *g, dt_iop_retouch_params_t *p)
365 {
366   GdkRGBA c
367       = (GdkRGBA){.red = p->fill_color[0], .green = p->fill_color[1], .blue = p->fill_color[2], .alpha = 1.0 };
368   gtk_color_chooser_set_rgba(GTK_COLOR_CHOOSER(g->colorpick), &c);
369 }
370 
rt_show_hide_controls(const dt_iop_module_t * self)371 static void rt_show_hide_controls(const dt_iop_module_t *self)
372 {
373   dt_iop_retouch_gui_data_t *g = (dt_iop_retouch_gui_data_t *)self->gui_data;
374   dt_iop_retouch_params_t *p = (dt_iop_retouch_params_t *)self->params;
375 
376   const int creation_continuous = (darktable.develop->form_gui && darktable.develop->form_gui->creation_continuous
377                                    && darktable.develop->form_gui->creation_continuous_module == self);
378 
379   switch(p->algorithm)
380   {
381     case DT_IOP_RETOUCH_HEAL:
382       gtk_widget_hide(GTK_WIDGET(g->vbox_blur));
383       gtk_widget_hide(GTK_WIDGET(g->vbox_fill));
384       break;
385     case DT_IOP_RETOUCH_BLUR:
386       gtk_widget_show(GTK_WIDGET(g->vbox_blur));
387       gtk_widget_hide(GTK_WIDGET(g->vbox_fill));
388       break;
389     case DT_IOP_RETOUCH_FILL:
390       gtk_widget_hide(GTK_WIDGET(g->vbox_blur));
391       gtk_widget_show(GTK_WIDGET(g->vbox_fill));
392       if(p->fill_mode == DT_IOP_RETOUCH_FILL_COLOR)
393         gtk_widget_show(GTK_WIDGET(g->hbox_color_pick));
394       else
395         gtk_widget_hide(GTK_WIDGET(g->hbox_color_pick));
396       break;
397     case DT_IOP_RETOUCH_CLONE:
398     default:
399       gtk_widget_hide(GTK_WIDGET(g->vbox_blur));
400       gtk_widget_hide(GTK_WIDGET(g->vbox_fill));
401       break;
402   }
403 
404   if(g->display_wavelet_scale)
405     gtk_widget_show(GTK_WIDGET(g->vbox_preview_scale));
406   else
407     gtk_widget_hide(GTK_WIDGET(g->vbox_preview_scale));
408 
409   const dt_masks_form_t *form = dt_masks_get_from_id(darktable.develop, rt_get_selected_shape_id());
410   if(form && !creation_continuous)
411     gtk_widget_show(GTK_WIDGET(g->sl_mask_opacity));
412   else
413     gtk_widget_hide(GTK_WIDGET(g->sl_mask_opacity));
414 }
415 
rt_display_selected_shapes_lbl(dt_iop_retouch_gui_data_t * g)416 static void rt_display_selected_shapes_lbl(dt_iop_retouch_gui_data_t *g)
417 {
418   const dt_masks_form_t *form = dt_masks_get_from_id(darktable.develop, rt_get_selected_shape_id());
419   if(form)
420     gtk_label_set_text(g->label_form_selected, form->name);
421   else
422     gtk_label_set_text(g->label_form_selected, _("none"));
423 }
424 
rt_get_selected_shape_index(dt_iop_retouch_params_t * p)425 static int rt_get_selected_shape_index(dt_iop_retouch_params_t *p)
426 {
427   return rt_get_index_from_formid(p, rt_get_selected_shape_id());
428 }
429 
rt_shape_selection_changed(dt_iop_module_t * self)430 static void rt_shape_selection_changed(dt_iop_module_t *self)
431 {
432   dt_iop_retouch_params_t *p = (dt_iop_retouch_params_t *)self->params;
433   dt_iop_retouch_gui_data_t *g = (dt_iop_retouch_gui_data_t *)self->gui_data;
434 
435   ++darktable.gui->reset;
436 
437   int selection_changed = 0;
438 
439   const int index = rt_get_selected_shape_index(p);
440   if(index >= 0)
441   {
442     dt_bauhaus_slider_set(g->sl_mask_opacity, rt_get_shape_opacity(self, p->rt_forms[index].formid));
443 
444     if(p->rt_forms[index].algorithm == DT_IOP_RETOUCH_BLUR)
445     {
446       p->blur_type = p->rt_forms[index].blur_type;
447       p->blur_radius = p->rt_forms[index].blur_radius;
448 
449       dt_bauhaus_combobox_set(g->cmb_blur_type, p->blur_type);
450       dt_bauhaus_slider_set(g->sl_blur_radius, p->blur_radius);
451 
452       selection_changed = 1;
453     }
454     else if(p->rt_forms[index].algorithm == DT_IOP_RETOUCH_FILL)
455     {
456       p->fill_mode = p->rt_forms[index].fill_mode;
457       p->fill_brightness = p->rt_forms[index].fill_brightness;
458       p->fill_color[0] = p->rt_forms[index].fill_color[0];
459       p->fill_color[1] = p->rt_forms[index].fill_color[1];
460       p->fill_color[2] = p->rt_forms[index].fill_color[2];
461 
462       dt_bauhaus_slider_set(g->sl_fill_brightness, p->fill_brightness);
463       dt_bauhaus_combobox_set(g->cmb_fill_mode, p->fill_mode);
464       rt_display_selected_fill_color(g, p);
465 
466       selection_changed = 1;
467     }
468 
469     if(p->algorithm != p->rt_forms[index].algorithm)
470     {
471       p->algorithm = p->rt_forms[index].algorithm;
472 
473       gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(g->bt_clone), (p->algorithm == DT_IOP_RETOUCH_CLONE));
474       gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(g->bt_heal), (p->algorithm == DT_IOP_RETOUCH_HEAL));
475       gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(g->bt_blur), (p->algorithm == DT_IOP_RETOUCH_BLUR));
476       gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(g->bt_fill), (p->algorithm == DT_IOP_RETOUCH_FILL));
477 
478       selection_changed = 1;
479     }
480 
481     if(selection_changed) rt_show_hide_controls(self);
482   }
483 
484   rt_display_selected_shapes_lbl(g);
485 
486   const int creation_continuous = (darktable.develop->form_gui && darktable.develop->form_gui->creation_continuous
487                                    && darktable.develop->form_gui->creation_continuous_module == self);
488 
489   if(index >= 0 && !creation_continuous)
490     gtk_widget_show(GTK_WIDGET(g->sl_mask_opacity));
491   else
492     gtk_widget_hide(GTK_WIDGET(g->sl_mask_opacity));
493 
494   --darktable.gui->reset;
495 
496   if(selection_changed) dt_dev_add_history_item(darktable.develop, self, TRUE);
497 }
498 
499 //---------------------------------------------------------------------------------
500 // helpers
501 //---------------------------------------------------------------------------------
502 
rt_masks_form_change_opacity(dt_iop_module_t * self,int formid,float opacity)503 static void rt_masks_form_change_opacity(dt_iop_module_t *self, int formid, float opacity)
504 {
505   dt_masks_point_group_t *grpt = rt_get_mask_point_group(self, formid);
506   if(grpt)
507   {
508     grpt->opacity = CLAMP(opacity, 0.05f, 1.0f);
509     dt_conf_set_float("plugins/darkroom/masks/opacity", grpt->opacity);
510 
511     dt_dev_add_masks_history_item(darktable.develop, self, TRUE);
512   }
513 }
514 
rt_masks_form_get_opacity(dt_iop_module_t * self,int formid)515 static float rt_masks_form_get_opacity(dt_iop_module_t *self, int formid)
516 {
517   dt_masks_point_group_t *grpt = rt_get_mask_point_group(self, formid);
518   if(grpt)
519     return grpt->opacity;
520   else
521     return 1.0f;
522 }
523 
rt_paste_forms_from_scale(dt_iop_retouch_params_t * p,const int source_scale,const int dest_scale)524 static void rt_paste_forms_from_scale(dt_iop_retouch_params_t *p, const int source_scale, const int dest_scale)
525 {
526   if(source_scale != dest_scale && source_scale >= 0 && dest_scale >= 0)
527   {
528     for(int i = 0; i < RETOUCH_NO_FORMS; i++)
529     {
530       if(p->rt_forms[i].scale == source_scale) p->rt_forms[i].scale = dest_scale;
531     }
532   }
533 }
534 
rt_allow_create_form(dt_iop_module_t * self)535 static int rt_allow_create_form(dt_iop_module_t *self)
536 {
537   int allow = 1;
538 
539   dt_iop_retouch_params_t *p = (dt_iop_retouch_params_t *)self->params;
540   if(p)
541   {
542     allow = (p->rt_forms[RETOUCH_NO_FORMS - 1].formid == 0);
543   }
544   return allow;
545 }
546 
rt_reset_form_creation(GtkWidget * widget,dt_iop_module_t * self)547 static void rt_reset_form_creation(GtkWidget *widget, dt_iop_module_t *self)
548 {
549   const dt_iop_retouch_gui_data_t *g = (dt_iop_retouch_gui_data_t *)self->gui_data;
550 
551   if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(g->bt_path))
552      || gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(g->bt_circle))
553      || gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(g->bt_ellipse))
554      || gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(g->bt_brush)))
555   {
556     // we unset the creation mode
557     dt_masks_change_form_gui(NULL);
558     darktable.develop->form_gui->creation_continuous = FALSE;
559     darktable.develop->form_gui->creation_continuous_module = NULL;
560   }
561 
562   if(widget != g->bt_path) gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(g->bt_path), FALSE);
563   if(widget != g->bt_circle) gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(g->bt_circle), FALSE);
564   if(widget != g->bt_ellipse) gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(g->bt_ellipse), FALSE);
565   if(widget != g->bt_brush) gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(g->bt_brush), FALSE);
566 
567   gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(g->bt_edit_masks), FALSE);
568   gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(g->bt_showmask), FALSE);
569   gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(g->bt_suppress), FALSE);
570   gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(g->colorpicker), FALSE);
571 }
572 
rt_show_forms_for_current_scale(dt_iop_module_t * self)573 static void rt_show_forms_for_current_scale(dt_iop_module_t *self)
574 {
575   if(!self->enabled || darktable.develop->gui_module != self || darktable.develop->form_gui->creation
576      || darktable.develop->form_gui->creation_continuous)
577     return;
578 
579   dt_iop_retouch_params_t *p = (dt_iop_retouch_params_t *)self->params;
580   dt_iop_gui_blend_data_t *bd = (dt_iop_gui_blend_data_t *)self->blend_data;
581   dt_iop_retouch_gui_data_t *g = (dt_iop_retouch_gui_data_t *)self->gui_data;
582   if(bd == NULL) return;
583 
584   const int scale = p->curr_scale;
585   int count = 0;
586 
587   // check if there is a shape on this scale
588   for(int i = 0; i < RETOUCH_NO_FORMS && count == 0; i++)
589   {
590     if(p->rt_forms[i].formid != 0 && p->rt_forms[i].scale == scale) count++;
591   }
592 
593   // if there are shapes on this scale, make the cut shapes button sensitive
594   gtk_widget_set_sensitive(g->bt_copy_scale, count > 0);
595 
596   // if no shapes on this scale, we hide all
597   if(bd->masks_shown == DT_MASKS_EDIT_OFF || count == 0)
598   {
599     dt_masks_change_form_gui(NULL);
600 
601     if(g)
602       gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(g->bt_edit_masks),
603                                    (bd->masks_shown != DT_MASKS_EDIT_OFF)
604                                        && (darktable.develop->gui_module == self));
605 
606     dt_control_queue_redraw_center();
607     return;
608   }
609 
610   // else, we create a new from group with the shapes and display it
611   dt_masks_form_t *grp = dt_masks_create_ext(DT_MASKS_GROUP);
612   for(int i = 0; i < RETOUCH_NO_FORMS; i++)
613   {
614     if(p->rt_forms[i].scale == scale)
615     {
616       const int grid = self->blend_params->mask_id;
617       const int formid = p->rt_forms[i].formid;
618       dt_masks_form_t *form = dt_masks_get_from_id(darktable.develop, formid);
619       if(form)
620       {
621         dt_masks_point_group_t *fpt = (dt_masks_point_group_t *)malloc(sizeof(dt_masks_point_group_t));
622         fpt->formid = formid;
623         fpt->parentid = grid;
624         fpt->state = DT_MASKS_STATE_USE;
625         fpt->opacity = 1.0f;
626         grp->points = g_list_append(grp->points, fpt);
627       }
628     }
629   }
630 
631   dt_masks_form_t *grp2 = dt_masks_create_ext(DT_MASKS_GROUP);
632   grp2->formid = 0;
633   dt_masks_group_ungroup(grp2, grp);
634   dt_masks_change_form_gui(grp2);
635   darktable.develop->form_gui->edit_mode = bd->masks_shown;
636 
637   if(g)
638     gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(g->bt_edit_masks),
639                                  (bd->masks_shown != DT_MASKS_EDIT_OFF) && (darktable.develop->gui_module == self));
640 
641   dt_control_queue_redraw_center();
642 }
643 
644 // called if a shape is added or deleted
rt_resynch_params(struct dt_iop_module_t * self)645 static void rt_resynch_params(struct dt_iop_module_t *self)
646 {
647   dt_iop_retouch_params_t *p = (dt_iop_retouch_params_t *)self->params;
648   dt_develop_blend_params_t *bp = self->blend_params;
649 
650   dt_iop_retouch_form_data_t forms_d[RETOUCH_NO_FORMS];
651   memset(forms_d, 0, sizeof(dt_iop_retouch_form_data_t) * RETOUCH_NO_FORMS);
652 
653   // we go through all forms in blend params
654   dt_masks_form_t *grp = dt_masks_get_from_id(darktable.develop, bp->mask_id);
655   if(grp && (grp->type & DT_MASKS_GROUP))
656   {
657     int new_form_index = 0;
658     for(GList *forms = grp->points; (new_form_index < RETOUCH_NO_FORMS) && forms; forms = g_list_next(forms))
659     {
660       dt_masks_point_group_t *grpt = (dt_masks_point_group_t *)forms->data;
661       if(grpt)
662       {
663         const int formid = grpt->formid;
664 
665         // search for the form on the shapes array
666         const int form_index = rt_get_index_from_formid(p, formid);
667 
668         // if it exists copy it to the new array
669         if(form_index >= 0)
670         {
671           forms_d[new_form_index] = p->rt_forms[form_index];
672 
673           new_form_index++;
674         }
675         else
676         {
677           // if it does not exists add it to the new array
678           const dt_masks_form_t *parent_form = dt_masks_get_from_id(darktable.develop, formid);
679           if(parent_form)
680           {
681             forms_d[new_form_index].formid = formid;
682             forms_d[new_form_index].scale = p->curr_scale;
683             forms_d[new_form_index].algorithm = p->algorithm;
684             forms_d[new_form_index].distort_mode = 2;
685 
686             switch(forms_d[new_form_index].algorithm)
687             {
688               case DT_IOP_RETOUCH_BLUR:
689                 forms_d[new_form_index].blur_type = p->blur_type;
690                 forms_d[new_form_index].blur_radius = p->blur_radius;
691                 break;
692               case DT_IOP_RETOUCH_FILL:
693                 forms_d[new_form_index].fill_mode = p->fill_mode;
694                 forms_d[new_form_index].fill_color[0] = p->fill_color[0];
695                 forms_d[new_form_index].fill_color[1] = p->fill_color[1];
696                 forms_d[new_form_index].fill_color[2] = p->fill_color[2];
697                 forms_d[new_form_index].fill_brightness = p->fill_brightness;
698                 break;
699               default:
700                 break;
701             }
702 
703             new_form_index++;
704           }
705         }
706       }
707     }
708   }
709 
710   // we reaffect params
711   for(int i = 0; i < RETOUCH_NO_FORMS; i++)
712   {
713     p->rt_forms[i] = forms_d[i];
714   }
715 }
716 
rt_masks_form_is_in_roi(dt_iop_module_t * self,dt_dev_pixelpipe_iop_t * piece,dt_masks_form_t * form,const dt_iop_roi_t * roi_in,const dt_iop_roi_t * roi_out)717 static gboolean rt_masks_form_is_in_roi(dt_iop_module_t *self, dt_dev_pixelpipe_iop_t *piece,
718                                         dt_masks_form_t *form, const dt_iop_roi_t *roi_in,
719                                         const dt_iop_roi_t *roi_out)
720 {
721   // we get the area for the form
722   int fl, ft, fw, fh;
723 
724   if(!dt_masks_get_area(self, piece, form, &fw, &fh, &fl, &ft)) return FALSE;
725 
726   // is the form outside of the roi?
727   fw *= roi_in->scale, fh *= roi_in->scale, fl *= roi_in->scale, ft *= roi_in->scale;
728   if(ft >= roi_out->y + roi_out->height || ft + fh <= roi_out->y || fl >= roi_out->x + roi_out->width
729      || fl + fw <= roi_out->x)
730     return FALSE;
731 
732   return TRUE;
733 }
734 
rt_masks_point_denormalize(dt_dev_pixelpipe_iop_t * piece,const dt_iop_roi_t * roi,const float * points,size_t points_count,float * new)735 static void rt_masks_point_denormalize(dt_dev_pixelpipe_iop_t *piece, const dt_iop_roi_t *roi, const float *points,
736                                        size_t points_count, float *new)
737 {
738   const float scalex = piece->pipe->iwidth * roi->scale, scaley = piece->pipe->iheight * roi->scale;
739 
740   for(size_t i = 0; i < points_count * 2; i += 2)
741   {
742     new[i] = points[i] * scalex;
743     new[i + 1] = points[i + 1] * scaley;
744   }
745 }
746 
rt_masks_point_calc_delta(dt_iop_module_t * self,dt_dev_pixelpipe_iop_t * piece,const dt_iop_roi_t * roi,const float * target,const float * source,int * dx,int * dy,const int distort_mode)747 static int rt_masks_point_calc_delta(dt_iop_module_t *self, dt_dev_pixelpipe_iop_t *piece, const dt_iop_roi_t *roi,
748                                      const float *target, const float *source, int *dx, int *dy,
749                                      const int distort_mode)
750 {
751   // if distort_mode==1 we don't scale at the right place, hence false positions if there's distortion before this
752   // module. we keep it for backward compatibility only. all new forms have distort_mode==2
753   float points[4];
754   if(distort_mode == 1)
755   {
756     rt_masks_point_denormalize(piece, roi, target, 1, points);
757     rt_masks_point_denormalize(piece, roi, source, 1, points + 2);
758   }
759   else
760   {
761     points[0] = target[0] * piece->pipe->iwidth;
762     points[1] = target[1] * piece->pipe->iheight;
763     points[2] = source[0] * piece->pipe->iwidth;
764     points[3] = source[1] * piece->pipe->iheight;
765   }
766 
767   int res = dt_dev_distort_transform_plus(self->dev, piece->pipe, self->iop_order, DT_DEV_TRANSFORM_DIR_BACK_INCL, points, 2);
768   if(!res) return res;
769 
770   if(distort_mode == 1)
771   {
772     *dx = points[0] - points[2];
773     *dy = points[1] - points[3];
774   }
775   else
776   {
777     *dx = (points[0] - points[2]) * roi->scale;
778     *dy = (points[1] - points[3]) * roi->scale;
779   }
780 
781   return res;
782 }
783 
784 /* returns (dx dy) to get from the source to the destination */
rt_masks_get_delta_to_destination(dt_iop_module_t * self,dt_dev_pixelpipe_iop_t * piece,const dt_iop_roi_t * roi,dt_masks_form_t * form,int * dx,int * dy,const int distort_mode)785 static int rt_masks_get_delta_to_destination(dt_iop_module_t *self, dt_dev_pixelpipe_iop_t *piece,
786                                              const dt_iop_roi_t *roi, dt_masks_form_t *form, int *dx, int *dy,
787                                              const int distort_mode)
788 {
789   int res = 0;
790 
791   if(form->type & DT_MASKS_PATH)
792   {
793     const dt_masks_point_path_t *pt = (dt_masks_point_path_t *)form->points->data;
794 
795     res = rt_masks_point_calc_delta(self, piece, roi, pt->corner, form->source, dx, dy, distort_mode);
796   }
797   else if(form->type & DT_MASKS_CIRCLE)
798   {
799     const dt_masks_point_circle_t *pt = (dt_masks_point_circle_t *)form->points->data;
800 
801     res = rt_masks_point_calc_delta(self, piece, roi, pt->center, form->source, dx, dy, distort_mode);
802   }
803   else if(form->type & DT_MASKS_ELLIPSE)
804   {
805     const dt_masks_point_ellipse_t *pt = (dt_masks_point_ellipse_t *)form->points->data;
806 
807     res = rt_masks_point_calc_delta(self, piece, roi, pt->center, form->source, dx, dy, distort_mode);
808   }
809   else if(form->type & DT_MASKS_BRUSH)
810   {
811     const dt_masks_point_brush_t *pt = (dt_masks_point_brush_t *)form->points->data;
812 
813     res = rt_masks_point_calc_delta(self, piece, roi, pt->corner, form->source, dx, dy, distort_mode);
814   }
815 
816   return res;
817 }
818 
rt_clamp_minmax(float levels_old[3],float levels_new[3])819 static void rt_clamp_minmax(float levels_old[3], float levels_new[3])
820 {
821   // left or right has changed
822   if((levels_old[0] != levels_new[0] || levels_old[2] != levels_new[2]) && levels_old[1] == levels_new[1])
823   {
824     // if old left and right are the same just use the new values
825     if(levels_old[2] != levels_old[0])
826     {
827       // set the new value but keep the middle proportional
828       const float left = MAX(levels_new[0], RETOUCH_PREVIEW_LVL_MIN);
829       const float right = MIN(levels_new[2], RETOUCH_PREVIEW_LVL_MAX);
830 
831       const float percentage = (levels_old[1] - levels_old[0]) / (levels_old[2] - levels_old[0]);
832       levels_new[1] = left + (right - left) * percentage;
833       levels_new[0] = left;
834       levels_new[2] = right;
835     }
836   }
837 
838   // if all zero make it gray
839   if(levels_new[0] == 0.f && levels_new[1] == 0.f && levels_new[2] == 0.f)
840   {
841     levels_new[0] = -1.5f;
842     levels_new[1] = 0.f;
843     levels_new[2] = 1.5f;
844   }
845 
846   // check the range
847   if(levels_new[2] < levels_new[0] + 0.05f * 2.f) levels_new[2] = levels_new[0] + 0.05f * 2.f;
848   if(levels_new[1] < levels_new[0] + 0.05f) levels_new[1] = levels_new[0] + 0.05f;
849   if(levels_new[1] > levels_new[2] - 0.05f) levels_new[1] = levels_new[2] - 0.05f;
850 
851   {
852     // set the new value but keep the middle proportional
853     const float left = MAX(levels_new[0], RETOUCH_PREVIEW_LVL_MIN);
854     const float right = MIN(levels_new[2], RETOUCH_PREVIEW_LVL_MAX);
855 
856     const float percentage = (levels_new[1] - levels_new[0]) / (levels_new[2] - levels_new[0]);
857     levels_new[1] = left + (right - left) * percentage;
858     levels_new[0] = left;
859     levels_new[2] = right;
860   }
861 }
862 
rt_shape_is_being_added(dt_iop_module_t * self,const int shape_type)863 static int rt_shape_is_being_added(dt_iop_module_t *self, const int shape_type)
864 {
865   int being_added = 0;
866 
867   if(self->dev->form_gui && self->dev->form_visible
868      && ((self->dev->form_gui->creation && self->dev->form_gui->creation_module == self)
869          || (self->dev->form_gui->creation_continuous && self->dev->form_gui->creation_continuous_module == self)))
870   {
871     if(self->dev->form_visible->type & DT_MASKS_GROUP)
872     {
873       GList *forms = self->dev->form_visible->points;
874       if(forms)
875       {
876         dt_masks_point_group_t *grpt = (dt_masks_point_group_t *)forms->data;
877         if(grpt)
878         {
879           const dt_masks_form_t *form = dt_masks_get_from_id(darktable.develop, grpt->formid);
880           if(form) being_added = (form->type & shape_type);
881         }
882       }
883     }
884     else
885       being_added = (self->dev->form_visible->type & shape_type);
886   }
887   return being_added;
888 }
889 
rt_add_shape(GtkWidget * widget,const int creation_continuous,dt_iop_module_t * self)890 static gboolean rt_add_shape(GtkWidget *widget, const int creation_continuous, dt_iop_module_t *self)
891 {
892   //turn module on (else shape creation won't work)
893   gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(self->off), TRUE);
894 
895   //switch mask edit mode off
896   dt_iop_gui_blend_data_t *bd = (dt_iop_gui_blend_data_t *)self->blend_data;
897   if(bd) bd->masks_shown = DT_MASKS_EDIT_OFF;
898 
899   const int allow = rt_allow_create_form(self);
900   if(allow)
901   {
902     rt_reset_form_creation(widget, self);
903 
904     if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)))
905     {
906       rt_show_forms_for_current_scale(self);
907 
908       return FALSE;
909     }
910 
911     dt_iop_retouch_params_t *p = (dt_iop_retouch_params_t *)self->params;
912     dt_iop_retouch_gui_data_t *g = (dt_iop_retouch_gui_data_t *)self->gui_data;
913 
914     // we want to be sure that the iop has focus
915     dt_iop_request_focus(self);
916 
917     dt_masks_type_t type = DT_MASKS_CIRCLE;
918     if(widget == g->bt_path)
919       type = DT_MASKS_PATH;
920     else if(widget == g->bt_circle)
921       type = DT_MASKS_CIRCLE;
922     else if(widget == g->bt_ellipse)
923       type = DT_MASKS_ELLIPSE;
924     else if(widget == g->bt_brush)
925       type = DT_MASKS_BRUSH;
926 
927     // we create the new form
928     dt_masks_form_t *spot = NULL;
929     if(p->algorithm == DT_IOP_RETOUCH_CLONE || p->algorithm == DT_IOP_RETOUCH_HEAL)
930       spot = dt_masks_create(type | DT_MASKS_CLONE);
931     else
932       spot = dt_masks_create(type | DT_MASKS_NON_CLONE);
933 
934     dt_masks_change_form_gui(spot);
935     darktable.develop->form_gui->creation = TRUE;
936     darktable.develop->form_gui->creation_module = self;
937 
938     if(creation_continuous)
939     {
940       darktable.develop->form_gui->creation_continuous = TRUE;
941       darktable.develop->form_gui->creation_continuous_module = self;
942     }
943     else
944     {
945       darktable.develop->form_gui->creation_continuous = FALSE;
946       darktable.develop->form_gui->creation_continuous_module = NULL;
947     }
948 
949     dt_control_queue_redraw_center();
950   }
951   else
952     gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(widget), FALSE);
953 
954   return !allow;
955 }
956 
957 //---------------------------------------------------------------------------------
958 // GUI callbacks
959 //---------------------------------------------------------------------------------
960 
rt_colorpick_color_set_callback(GtkColorButton * widget,dt_iop_module_t * self)961 static void rt_colorpick_color_set_callback(GtkColorButton *widget, dt_iop_module_t *self)
962 {
963   if(darktable.gui->reset) return;
964   dt_iop_retouch_params_t *p = (dt_iop_retouch_params_t *)self->params;
965 
966   // turn off the other color picker
967   dt_iop_color_picker_reset(self, TRUE);
968 
969   GdkRGBA c
970       = (GdkRGBA){.red = p->fill_color[0], .green = p->fill_color[1], .blue = p->fill_color[2], .alpha = 1.0 };
971   gtk_color_chooser_get_rgba(GTK_COLOR_CHOOSER(widget), &c);
972   p->fill_color[0] = c.red;
973   p->fill_color[1] = c.green;
974   p->fill_color[2] = c.blue;
975 
976   const int index = rt_get_selected_shape_index(p);
977   if(index >= 0)
978   {
979     if(p->rt_forms[index].algorithm == DT_IOP_RETOUCH_FILL)
980     {
981       p->rt_forms[index].fill_color[0] = p->fill_color[0];
982       p->rt_forms[index].fill_color[1] = p->fill_color[1];
983       p->rt_forms[index].fill_color[2] = p->fill_color[2];
984     }
985   }
986 
987   dt_dev_add_history_item(darktable.develop, self, TRUE);
988 }
989 
990 // wavelet decompose bar
991 #define RT_WDBAR_INSET 0.2f
992 #define lw DT_PIXEL_APPLY_DPI(1.0f)
993 
rt_update_wd_bar_labels(dt_iop_retouch_params_t * p,dt_iop_retouch_gui_data_t * g)994 static void rt_update_wd_bar_labels(dt_iop_retouch_params_t *p, dt_iop_retouch_gui_data_t *g)
995 {
996   char text[256];
997 
998   snprintf(text, sizeof(text), "%i", p->curr_scale);
999   gtk_label_set_text(g->lbl_curr_scale, text);
1000 
1001   snprintf(text, sizeof(text), "%i", p->num_scales);
1002   gtk_label_set_text(g->lbl_num_scales, text);
1003 
1004   snprintf(text, sizeof(text), "%i", p->merge_from_scale);
1005   gtk_label_set_text(g->lbl_merge_from_scale, text);
1006 }
1007 
rt_num_scales_update(const int _num_scales,dt_iop_module_t * self)1008 static void rt_num_scales_update(const int _num_scales, dt_iop_module_t *self)
1009 {
1010   if(darktable.gui->reset) return;
1011 
1012   dt_iop_retouch_params_t *p = (dt_iop_retouch_params_t *)self->params;
1013   dt_iop_retouch_gui_data_t *g = (dt_iop_retouch_gui_data_t *)self->gui_data;
1014 
1015   const int num_scales = CLAMP(_num_scales, 0, RETOUCH_MAX_SCALES);
1016   if(p->num_scales == num_scales) return;
1017 
1018   p->num_scales = num_scales;
1019 
1020   if(p->num_scales < p->merge_from_scale) p->merge_from_scale = p->num_scales;
1021 
1022   rt_update_wd_bar_labels(p, g);
1023 
1024   dt_dev_add_history_item(darktable.develop, self, TRUE);
1025 }
1026 
rt_curr_scale_update(const int _curr_scale,dt_iop_module_t * self)1027 static void rt_curr_scale_update(const int _curr_scale, dt_iop_module_t *self)
1028 {
1029   if(darktable.gui->reset) return;
1030 
1031   dt_iop_retouch_params_t *p = (dt_iop_retouch_params_t *)self->params;
1032   dt_iop_retouch_gui_data_t *g = (dt_iop_retouch_gui_data_t *)self->gui_data;
1033 
1034   const int curr_scale = CLAMP(_curr_scale, 0, RETOUCH_MAX_SCALES + 1);
1035   if(p->curr_scale == curr_scale) return;
1036 
1037   p->curr_scale = curr_scale;
1038 
1039   rt_show_forms_for_current_scale(self);
1040 
1041   // compute auto levels only the first time display wavelet scale is used,
1042   // only if levels values are the default
1043   // and a detail scale is displayed
1044   dt_iop_gui_enter_critical_section(self);
1045   if(g->displayed_wavelet_scale == 0 && p->preview_levels[0] == RETOUCH_PREVIEW_LVL_MIN
1046      && p->preview_levels[1] == 0.f && p->preview_levels[2] == RETOUCH_PREVIEW_LVL_MAX
1047      && g->preview_auto_levels == 0 && p->curr_scale > 0 && p->curr_scale <= p->num_scales)
1048   {
1049     g->preview_auto_levels = 1;
1050     g->displayed_wavelet_scale = 1;
1051   }
1052   dt_iop_gui_leave_critical_section(self);
1053 
1054   rt_update_wd_bar_labels(p, g);
1055 
1056   dt_dev_add_history_item(darktable.develop, self, TRUE);
1057 }
1058 
rt_merge_from_scale_update(const int _merge_from_scale,dt_iop_module_t * self)1059 static void rt_merge_from_scale_update(const int _merge_from_scale, dt_iop_module_t *self)
1060 {
1061   if(darktable.gui->reset) return;
1062 
1063   dt_iop_retouch_params_t *p = (dt_iop_retouch_params_t *)self->params;
1064   dt_iop_retouch_gui_data_t *g = (dt_iop_retouch_gui_data_t *)self->gui_data;
1065 
1066   const int merge_from_scale = CLAMP(_merge_from_scale, 0, p->num_scales);
1067   if(p->merge_from_scale == merge_from_scale) return;
1068 
1069   p->merge_from_scale = merge_from_scale;
1070 
1071   rt_update_wd_bar_labels(p, g);
1072 
1073   dt_dev_add_history_item(darktable.develop, self, TRUE);
1074 }
1075 
rt_wdbar_leave_notify(GtkWidget * widget,GdkEventCrossing * event,dt_iop_module_t * self)1076 static gboolean rt_wdbar_leave_notify(GtkWidget *widget, GdkEventCrossing *event, dt_iop_module_t *self)
1077 {
1078   dt_iop_retouch_gui_data_t *g = (dt_iop_retouch_gui_data_t *)self->gui_data;
1079 
1080   g->wdbar_mouse_x = g->wdbar_mouse_y = -1;
1081   g->curr_scale = -1;
1082   g->lower_cursor = g->upper_cursor = FALSE;
1083   g->lower_margin = g->upper_margin = FALSE;
1084 
1085   gtk_widget_queue_draw(g->wd_bar);
1086   return TRUE;
1087 }
1088 
rt_wdbar_button_press(GtkWidget * widget,GdkEventButton * event,dt_iop_module_t * self)1089 static gboolean rt_wdbar_button_press(GtkWidget *widget, GdkEventButton *event, dt_iop_module_t *self)
1090 {
1091   if(darktable.gui->reset) return TRUE;
1092 
1093   dt_iop_request_focus(self);
1094 
1095   dt_iop_retouch_gui_data_t *g = (dt_iop_retouch_gui_data_t *)self->gui_data;
1096   GtkAllocation allocation;
1097   gtk_widget_get_allocation(widget, &allocation);
1098   const int inset = round(RT_WDBAR_INSET * allocation.height);
1099   const float box_w = (allocation.width - 2.0f * inset) / (float)RETOUCH_NO_SCALES;
1100 
1101   if(event->button == 1)
1102   {
1103     if(g->lower_margin) // bottom slider
1104     {
1105       if(g->lower_cursor) // is over the arrow?
1106         g->is_dragging = DT_IOP_RETOUCH_WDBAR_DRAG_BOTTOM;
1107       else
1108         rt_num_scales_update(g->wdbar_mouse_x / box_w, self);
1109     }
1110     else if(g->upper_margin) // top slider
1111     {
1112       if(g->upper_cursor) // is over the arrow?
1113         g->is_dragging = DT_IOP_RETOUCH_WDBAR_DRAG_TOP;
1114       else
1115         rt_merge_from_scale_update(g->wdbar_mouse_x / box_w, self);
1116     }
1117     else if (g->curr_scale >= 0)
1118       rt_curr_scale_update(g->curr_scale, self);
1119   }
1120 
1121   gtk_widget_queue_draw(g->wd_bar);
1122   return TRUE;
1123 }
1124 
rt_wdbar_button_release(GtkWidget * widget,GdkEventButton * event,dt_iop_module_t * self)1125 static gboolean rt_wdbar_button_release(GtkWidget *widget, GdkEventButton *event, dt_iop_module_t *self)
1126 {
1127   dt_iop_retouch_gui_data_t *g = (dt_iop_retouch_gui_data_t *)self->gui_data;
1128 
1129   if(event->button == 1) g->is_dragging = 0;
1130 
1131   gtk_widget_queue_draw(g->wd_bar);
1132   return TRUE;
1133 }
1134 
rt_wdbar_scrolled(GtkWidget * widget,GdkEventScroll * event,dt_iop_module_t * self)1135 static gboolean rt_wdbar_scrolled(GtkWidget *widget, GdkEventScroll *event, dt_iop_module_t *self)
1136 {
1137   if(dt_gui_ignore_scroll(event)) return FALSE;
1138 
1139   if(darktable.gui->reset) return TRUE;
1140 
1141   dt_iop_retouch_params_t *p = (dt_iop_retouch_params_t *)self->params;
1142   dt_iop_retouch_gui_data_t *g = (dt_iop_retouch_gui_data_t *)self->gui_data;
1143 
1144   dt_iop_request_focus(self);
1145 
1146   int delta_y;
1147   if(dt_gui_get_scroll_unit_deltas(event, NULL, &delta_y))
1148   {
1149     if(g->lower_margin) // bottom slider
1150       rt_num_scales_update(p->num_scales - delta_y, self);
1151     else if(g->upper_margin) // top slider
1152       rt_merge_from_scale_update(p->merge_from_scale - delta_y, self);
1153     else if (g->curr_scale >= 0)
1154       rt_curr_scale_update(p->curr_scale - delta_y, self);
1155   }
1156 
1157   gtk_widget_queue_draw(g->wd_bar);
1158   return TRUE;
1159 }
1160 
rt_wdbar_motion_notify(GtkWidget * widget,GdkEventMotion * event,dt_iop_module_t * self)1161 static gboolean rt_wdbar_motion_notify(GtkWidget *widget, GdkEventMotion *event, dt_iop_module_t *self)
1162 {
1163   dt_iop_retouch_gui_data_t *g = (dt_iop_retouch_gui_data_t *)self->gui_data;
1164   dt_iop_retouch_params_t *p = (dt_iop_retouch_params_t *)self->params;
1165 
1166   GtkAllocation allocation;
1167   gtk_widget_get_allocation(widget, &allocation);
1168   const int inset = round(RT_WDBAR_INSET * allocation.height);
1169   const float box_w = (allocation.width - 2.0f * inset) / (float)RETOUCH_NO_SCALES;
1170   const float sh = 3.0f * lw + inset;
1171 
1172 
1173   /* record mouse position within control */
1174   g->wdbar_mouse_x = CLAMP(event->x - inset, 0, allocation.width - 2.0f * inset - 1.0f);
1175   g->wdbar_mouse_y = event->y;
1176 
1177   g->curr_scale = g->wdbar_mouse_x / box_w;
1178   g->lower_cursor = g->upper_cursor = FALSE;
1179   g->lower_margin = g->upper_margin = FALSE;
1180   if(g->wdbar_mouse_y <= sh)
1181   {
1182     g->upper_margin = TRUE;
1183     float middle = box_w * (0.5f + (float)p->merge_from_scale);
1184     g->upper_cursor = (g->wdbar_mouse_x >= (middle - inset)) && (g->wdbar_mouse_x <= (middle + inset));
1185     if (!(g->is_dragging)) g->curr_scale = -1;
1186   }
1187   else if (g->wdbar_mouse_y >= allocation.height - sh)
1188   {
1189     g->lower_margin = TRUE;
1190     float middle = box_w * (0.5f + (float)p->num_scales);
1191     g->lower_cursor = (g->wdbar_mouse_x >= (middle - inset)) && (g->wdbar_mouse_x <= (middle + inset));
1192     if (!(g->is_dragging)) g->curr_scale = -1;
1193   }
1194 
1195   if(g->is_dragging == DT_IOP_RETOUCH_WDBAR_DRAG_BOTTOM)
1196     rt_num_scales_update(g->curr_scale, self);
1197 
1198   if(g->is_dragging == DT_IOP_RETOUCH_WDBAR_DRAG_TOP)
1199     rt_merge_from_scale_update(g->curr_scale, self);
1200 
1201   gtk_widget_queue_draw(g->wd_bar);
1202   return TRUE;
1203 }
1204 
rt_scale_has_shapes(dt_iop_retouch_params_t * p,const int scale)1205 static int rt_scale_has_shapes(dt_iop_retouch_params_t *p, const int scale)
1206 {
1207   int has_shapes = 0;
1208 
1209   for(int i = 0; i < RETOUCH_NO_FORMS && has_shapes == 0; i++)
1210     has_shapes = (p->rt_forms[i].formid != 0 && p->rt_forms[i].scale == scale);
1211 
1212   return has_shapes;
1213 }
1214 
rt_wdbar_draw(GtkWidget * widget,cairo_t * crf,dt_iop_module_t * self)1215 static gboolean rt_wdbar_draw(GtkWidget *widget, cairo_t *crf, dt_iop_module_t *self)
1216 {
1217   dt_iop_retouch_gui_data_t *g = (dt_iop_retouch_gui_data_t *)self->gui_data;
1218   dt_iop_retouch_params_t *p = (dt_iop_retouch_params_t *)self->params;
1219 
1220 
1221   GdkRGBA border      = {0.066, 0.066, 0.066, 1};
1222   GdkRGBA original    = {.1, .1, .1, 1};
1223   GdkRGBA inactive    = {.15, .15, .15, 1};
1224   GdkRGBA active      = {.35, .35, .35, 1};
1225   GdkRGBA merge_from  = {.5, .5, .5, 1};
1226   GdkRGBA residual    = {.8, .8, .8, 1};
1227   GdkRGBA shapes      = {.75, .5, .0, 1};
1228   GdkRGBA color;
1229 
1230   float middle;
1231   const int first_scale_visible = (g->first_scale_visible > 0) ? g->first_scale_visible : RETOUCH_MAX_SCALES;
1232 
1233   GtkAllocation allocation;
1234   gtk_widget_get_allocation(widget, &allocation);
1235 
1236   cairo_surface_t *cst = dt_cairo_image_surface_create(CAIRO_FORMAT_ARGB32, allocation.width, allocation.height);
1237   cairo_t *cr = cairo_create(cst);
1238 
1239   // clear background
1240   gdk_cairo_set_source_rgba(cr, &inactive);
1241   cairo_paint(cr);
1242   cairo_save(cr);
1243 
1244   // geometry
1245   const int inset = round(RT_WDBAR_INSET * allocation.height);
1246   const int mk = 2 * inset;
1247   const float sh = 3.0f * lw + inset;
1248   const float box_w = (allocation.width - 2.0f * inset) / (float)RETOUCH_NO_SCALES;
1249   const float box_h = allocation.height - 2.0f * sh;
1250 
1251   // render the boxes
1252   cairo_set_antialias(cr, CAIRO_ANTIALIAS_NONE);
1253   for(int i = 0; i < RETOUCH_NO_SCALES; i++)
1254   {
1255     // draw box background
1256     if(i == 0)
1257       color = original;
1258     else if(i == p->num_scales + 1)
1259       color = residual;
1260     else if(i >= p->merge_from_scale && i <= p->num_scales && p->merge_from_scale > 0)
1261       color = merge_from;
1262     else if(i <= p->num_scales)
1263       color = active;
1264     else
1265       color = inactive;
1266 
1267     gdk_cairo_set_source_rgba(cr, &color);
1268     cairo_rectangle(cr, box_w * i + inset, sh, box_w, box_h);
1269     cairo_fill(cr);
1270 
1271     // if detail scale is visible at current zoom level inform it
1272     if(i >= first_scale_visible && i <= p->num_scales)
1273     {
1274       gdk_cairo_set_source_rgba(cr, &merge_from);
1275       cairo_rectangle(cr, box_w * i + inset, lw, box_w, 2.0f * lw);
1276       cairo_fill(cr);
1277     }
1278 
1279     // if the scale has shapes inform it
1280     if(rt_scale_has_shapes(p, i))
1281     {
1282       cairo_set_line_width(cr, lw);
1283       gdk_cairo_set_source_rgba(cr, &shapes);
1284       cairo_rectangle(cr, box_w * i + inset + lw / 2.0f, allocation.height - sh, box_w - lw, 2.0f * lw);
1285       cairo_fill(cr);
1286     }
1287 
1288     // draw the border
1289     cairo_set_line_width(cr, lw);
1290     gdk_cairo_set_source_rgba(cr, &border);
1291     cairo_rectangle(cr, box_w * i + inset, sh, box_w, box_h);
1292     cairo_stroke(cr);
1293   }
1294 
1295   cairo_set_antialias(cr, CAIRO_ANTIALIAS_DEFAULT);
1296   cairo_restore(cr);
1297 
1298   // dot for the current scale
1299   if(p->curr_scale >= p->merge_from_scale && p->curr_scale <= p->num_scales && p->merge_from_scale > 0)
1300     color = active;
1301   else
1302     color = merge_from;
1303 
1304   if(p->curr_scale >= 0 && p->curr_scale < RETOUCH_NO_SCALES)
1305   {
1306     cairo_set_line_width(cr, lw);
1307     gdk_cairo_set_source_rgba(cr, &color);
1308     middle = box_w * (0.5f + (float)p->curr_scale);
1309     cairo_arc(cr, middle + inset, 0.5f * box_h + sh, 0.5f * inset, 0, 2.0f * M_PI);
1310     cairo_fill(cr);
1311     cairo_stroke(cr);
1312   }
1313 
1314   // mouse hover on a scale
1315   if(g->curr_scale >= 0)
1316   {
1317     cairo_set_line_width(cr, lw);
1318     if(g->curr_scale == p->num_scales + 1) color = inactive;
1319     else color = residual;
1320     gdk_cairo_set_source_rgba(cr, &color);
1321     cairo_rectangle(cr, box_w * g->curr_scale + inset + lw, sh + lw, box_w - 2.0f * lw, box_h - 2.0f * lw);
1322     cairo_stroke(cr);
1323   }
1324 
1325   /* render control points handles */
1326 
1327   // draw number of scales arrow (bottom arrow)
1328   middle = box_w * (0.5f + (float)p->num_scales);
1329   if(g->lower_cursor || g->is_dragging == DT_IOP_RETOUCH_WDBAR_DRAG_BOTTOM)
1330   {
1331     cairo_set_source_rgb(cr, 0.67, 0.67, 0.67);
1332     dtgtk_cairo_paint_solid_triangle(cr, middle, box_h + 5.0f * lw, mk, mk, CPF_DIRECTION_UP, NULL);
1333   }
1334   else
1335   {
1336     cairo_set_source_rgb(cr, 0.54, 0.54, 0.54);
1337     dtgtk_cairo_paint_triangle(cr, middle, box_h + 5.0f * lw, mk, mk, CPF_DIRECTION_UP, NULL);
1338   }
1339 
1340   // draw merge scales arrow (top arrow)
1341   middle = box_w * (0.5f + (float)p->merge_from_scale);
1342   if(g->upper_cursor || g->is_dragging == DT_IOP_RETOUCH_WDBAR_DRAG_TOP)
1343   {
1344     cairo_set_source_rgb(cr, 0.67, 0.67, 0.67);
1345     dtgtk_cairo_paint_solid_triangle(cr, middle, 3.0f * lw, mk, mk, CPF_DIRECTION_DOWN, NULL);
1346   }
1347   else
1348   {
1349     cairo_set_source_rgb(cr, 0.54, 0.54, 0.54);
1350     dtgtk_cairo_paint_triangle(cr, middle, 3.0f * lw, mk, mk, CPF_DIRECTION_DOWN, NULL);
1351   }
1352 
1353   /* push mem surface into widget */
1354   cairo_destroy(cr);
1355   cairo_set_source_surface(crf, cst, 0, 0);
1356   cairo_paint(crf);
1357   cairo_surface_destroy(cst);
1358 
1359   return TRUE;
1360 }
1361 
rt_gslider_scale_callback(GtkWidget * self,float inval,int dir)1362 static float rt_gslider_scale_callback(GtkWidget *self, float inval, int dir)
1363 {
1364   float outval;
1365   switch(dir)
1366   {
1367     case GRADIENT_SLIDER_SET:
1368       outval = (inval - RETOUCH_PREVIEW_LVL_MIN) / (RETOUCH_PREVIEW_LVL_MAX - RETOUCH_PREVIEW_LVL_MIN);
1369       break;
1370     case GRADIENT_SLIDER_GET:
1371       outval = (RETOUCH_PREVIEW_LVL_MAX - RETOUCH_PREVIEW_LVL_MIN) * inval + RETOUCH_PREVIEW_LVL_MIN;
1372       break;
1373     default:
1374       outval = inval;
1375   }
1376   return outval;
1377 }
1378 
1379 
rt_gslider_changed(GtkDarktableGradientSlider * gslider,dt_iop_module_t * self)1380 static void rt_gslider_changed(GtkDarktableGradientSlider *gslider, dt_iop_module_t *self)
1381 {
1382   dt_iop_retouch_params_t *p = (dt_iop_retouch_params_t *)self->params;
1383 
1384   double dlevels[3];
1385 
1386   if(darktable.gui->reset) return;
1387 
1388   dtgtk_gradient_slider_multivalue_get_values(gslider, dlevels);
1389 
1390   for (int i = 0; i < 3; i++) p->preview_levels[i] = dlevels[i];
1391 
1392   dt_dev_add_history_item(darktable.develop, self, TRUE);
1393 
1394 }
1395 
1396 
color_picker_apply(dt_iop_module_t * self,GtkWidget * picker,dt_dev_pixelpipe_iop_t * piece)1397 void color_picker_apply(dt_iop_module_t *self, GtkWidget *picker, dt_dev_pixelpipe_iop_t *piece)
1398 {
1399   dt_iop_retouch_gui_data_t *g = (dt_iop_retouch_gui_data_t *)self->gui_data;
1400   dt_iop_retouch_params_t *p = (dt_iop_retouch_params_t *)self->params;
1401 
1402   if(fabsf(p->fill_color[0] - self->picked_output_color[0]) < 0.0001f
1403      && fabsf(p->fill_color[1] - self->picked_output_color[1]) < 0.0001f
1404      && fabsf(p->fill_color[2] - self->picked_output_color[2]) < 0.0001f)
1405   {
1406     // interrupt infinite loops
1407     return;
1408   }
1409 
1410   p->fill_color[0] = self->picked_output_color[0];
1411   p->fill_color[1] = self->picked_output_color[1];
1412   p->fill_color[2] = self->picked_output_color[2];
1413 
1414   const int index = rt_get_selected_shape_index(p);
1415   if(index >= 0)
1416   {
1417     if(p->rt_forms[index].algorithm == DT_IOP_RETOUCH_FILL)
1418     {
1419       p->rt_forms[index].fill_color[0] = p->fill_color[0];
1420       p->rt_forms[index].fill_color[1] = p->fill_color[1];
1421       p->rt_forms[index].fill_color[2] = p->fill_color[2];
1422     }
1423   }
1424 
1425   rt_display_selected_fill_color(g, p);
1426 
1427   dt_dev_add_history_item(darktable.develop, self, TRUE);
1428 }
1429 
rt_copypaste_scale_callback(GtkToggleButton * togglebutton,GdkEventButton * event,dt_iop_module_t * self)1430 static gboolean rt_copypaste_scale_callback(GtkToggleButton *togglebutton, GdkEventButton *event, dt_iop_module_t *self)
1431 {
1432   if(darktable.gui->reset) return TRUE;
1433 
1434   ++darktable.gui->reset;
1435 
1436   int scale_copied = 0;
1437   const int active = !gtk_toggle_button_get_active(togglebutton);
1438   dt_iop_retouch_params_t *p = (dt_iop_retouch_params_t *)self->params;
1439   dt_iop_retouch_gui_data_t *g = (dt_iop_retouch_gui_data_t *)self->gui_data;
1440 
1441   if(togglebutton == (GtkToggleButton *)g->bt_copy_scale)
1442   {
1443     g->copied_scale = (active) ? p->curr_scale : -1;
1444   }
1445   else if(togglebutton == (GtkToggleButton *)g->bt_paste_scale)
1446   {
1447     rt_paste_forms_from_scale(p, g->copied_scale, p->curr_scale);
1448     rt_show_forms_for_current_scale(self);
1449 
1450     scale_copied = 1;
1451     g->copied_scale = -1;
1452   }
1453 
1454   gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(g->bt_copy_scale), g->copied_scale >= 0);
1455   gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(g->bt_paste_scale), g->copied_scale >= 0);
1456   gtk_widget_set_sensitive(g->bt_paste_scale, g->copied_scale >= 0);
1457 
1458   --darktable.gui->reset;
1459 
1460   if(scale_copied) dt_dev_add_history_item(darktable.develop, self, TRUE);
1461 
1462   return TRUE;
1463 }
1464 
rt_display_wavelet_scale_callback(GtkToggleButton * togglebutton,GdkEventButton * event,dt_iop_module_t * self)1465 static gboolean rt_display_wavelet_scale_callback(GtkToggleButton *togglebutton, GdkEventButton *event, dt_iop_module_t *self)
1466 {
1467   if(darktable.gui->reset) return TRUE;
1468 
1469   dt_iop_retouch_params_t *p = (dt_iop_retouch_params_t *)self->params;
1470   dt_iop_retouch_gui_data_t *g = (dt_iop_retouch_gui_data_t *)self->gui_data;
1471 
1472   // if blend module is displaying mask do not display wavelet scales
1473   if(self->request_mask_display && !g->mask_display)
1474   {
1475     dt_control_log(_("cannot display scales when the blending mask is displayed"));
1476 
1477     ++darktable.gui->reset;
1478     gtk_toggle_button_set_active(togglebutton, FALSE);
1479     --darktable.gui->reset;
1480     return TRUE;
1481   }
1482 
1483   if(self->off) gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(self->off), 1);
1484   dt_iop_request_focus(self);
1485 
1486   g->display_wavelet_scale = !gtk_toggle_button_get_active(togglebutton);
1487 
1488   rt_show_hide_controls(self);
1489 
1490   // compute auto levels only the first time display wavelet scale is used,
1491   // only if levels values are the default
1492   // and a detail scale is displayed
1493   dt_iop_gui_enter_critical_section(self);
1494   if(g->displayed_wavelet_scale == 0 && p->preview_levels[0] == RETOUCH_PREVIEW_LVL_MIN
1495      && p->preview_levels[1] == 0.f && p->preview_levels[2] == RETOUCH_PREVIEW_LVL_MAX
1496      && g->preview_auto_levels == 0 && p->curr_scale > 0 && p->curr_scale <= p->num_scales)
1497   {
1498     g->preview_auto_levels = 1;
1499     g->displayed_wavelet_scale = 1;
1500   }
1501   dt_iop_gui_leave_critical_section(self);
1502 
1503   dt_dev_reprocess_center(self->dev);
1504 
1505   gtk_toggle_button_set_active(togglebutton, g->display_wavelet_scale);
1506   return TRUE;
1507 }
1508 
rt_develop_ui_pipe_finished_callback(gpointer instance,gpointer user_data)1509 static void rt_develop_ui_pipe_finished_callback(gpointer instance, gpointer user_data)
1510 {
1511   dt_iop_module_t *self = (dt_iop_module_t *)user_data;
1512   dt_iop_retouch_params_t *p = (dt_iop_retouch_params_t *)self->params;
1513   dt_iop_retouch_gui_data_t *g = (dt_iop_retouch_gui_data_t *)self->gui_data;
1514 
1515   // FIXME: this doesn't seems the right place to update params and GUI ...
1516   // update auto levels
1517   dt_iop_gui_enter_critical_section(self);
1518   if(g->preview_auto_levels == 2)
1519   {
1520     g->preview_auto_levels = -1;
1521 
1522     dt_iop_gui_leave_critical_section(self);
1523 
1524     for(int i = 0; i < 3; i++) p->preview_levels[i] = g->preview_levels[i];
1525 
1526     dt_dev_add_history_item(darktable.develop, self, TRUE);
1527 
1528     dt_iop_gui_enter_critical_section(self);
1529 
1530     // update the gradient slider
1531     double dlevels[3];
1532     for(int i = 0; i < 3; i++) dlevels[i] = p->preview_levels[i];
1533 
1534     ++darktable.gui->reset;
1535     dtgtk_gradient_slider_multivalue_set_values(g->preview_levels_gslider, dlevels);
1536     --darktable.gui->reset;
1537 
1538     g->preview_auto_levels = 0;
1539 
1540     dt_iop_gui_leave_critical_section(self);
1541 
1542   }
1543   else
1544   {
1545     dt_iop_gui_leave_critical_section(self);
1546   }
1547 
1548   // just in case zoom level has changed
1549   gtk_widget_queue_draw(GTK_WIDGET(g->wd_bar));
1550 }
1551 
rt_auto_levels_callback(GtkToggleButton * togglebutton,GdkEventButton * event,dt_iop_module_t * self)1552 static gboolean rt_auto_levels_callback(GtkToggleButton *togglebutton, GdkEventButton *event, dt_iop_module_t *self)
1553 {
1554   if(darktable.gui->reset) return FALSE;
1555 
1556   dt_iop_retouch_gui_data_t *g = (dt_iop_retouch_gui_data_t *)self->gui_data;
1557 
1558   if(self->off) gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(self->off), 1);
1559   dt_iop_request_focus(self);
1560 
1561   dt_iop_gui_enter_critical_section(self);
1562   if(g->preview_auto_levels == 0)
1563   {
1564     g->preview_auto_levels = 1;
1565   }
1566   dt_iop_gui_leave_critical_section(self);
1567 
1568   dt_iop_refresh_center(self);
1569 
1570   return TRUE;
1571 }
1572 
rt_mask_opacity_callback(GtkWidget * slider,dt_iop_module_t * self)1573 static void rt_mask_opacity_callback(GtkWidget *slider, dt_iop_module_t *self)
1574 {
1575   if(darktable.gui->reset) return;
1576 
1577   const int shape_id = rt_get_selected_shape_id();
1578 
1579   if(shape_id > 0)
1580   {
1581     const float opacity = dt_bauhaus_slider_get(slider);
1582     rt_masks_form_change_opacity(self, shape_id, opacity);
1583   }
1584 
1585   dt_dev_add_history_item(darktable.develop, self, TRUE);
1586 }
1587 
gui_post_expose(struct dt_iop_module_t * self,cairo_t * cr,int32_t width,int32_t height,int32_t pointerx,int32_t pointery)1588 void gui_post_expose (struct dt_iop_module_t *self,
1589                       cairo_t *cr,
1590                       int32_t width,
1591                       int32_t height,
1592                       int32_t pointerx,
1593                       int32_t pointery)
1594 {
1595   dt_iop_retouch_gui_data_t *g = (dt_iop_retouch_gui_data_t *)self->gui_data;
1596 
1597   const int shape_id = rt_get_selected_shape_id();
1598 
1599   if(shape_id > 0)
1600   {
1601     ++darktable.gui->reset;
1602     dt_bauhaus_slider_set(g->sl_mask_opacity, rt_masks_form_get_opacity(self, shape_id));
1603     --darktable.gui->reset;
1604   }
1605 }
1606 
rt_edit_masks_callback(GtkWidget * widget,GdkEventButton * event,dt_iop_module_t * self)1607 static gboolean rt_edit_masks_callback(GtkWidget *widget, GdkEventButton *event, dt_iop_module_t *self)
1608 {
1609   if(darktable.gui->reset) return FALSE;
1610 
1611   // if we don't have the focus, request for it and quit, gui_focus() do the rest
1612   if(darktable.develop->gui_module != self)
1613   {
1614     dt_iop_request_focus(self);
1615     return FALSE;
1616   }
1617 
1618   dt_iop_gui_blend_data_t *bd = (dt_iop_gui_blend_data_t *)self->blend_data;
1619   dt_iop_retouch_gui_data_t *g = (dt_iop_retouch_gui_data_t *)self->gui_data;
1620 
1621   //hide all shapes and free if some are in creation
1622   if(darktable.develop->form_gui->creation && darktable.develop->form_gui->creation_module == self)
1623     dt_masks_change_form_gui(NULL);
1624 
1625   if(darktable.develop->form_gui->creation_continuous_module == self)
1626   {
1627     darktable.develop->form_gui->creation_continuous = FALSE;
1628     darktable.develop->form_gui->creation_continuous_module = NULL;
1629   }
1630 
1631   gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(g->bt_path), FALSE);
1632   gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(g->bt_circle), FALSE);
1633   gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(g->bt_ellipse), FALSE);
1634   gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(g->bt_brush), FALSE);
1635 
1636   if(event->button == 1)
1637   {
1638     ++darktable.gui->reset;
1639 
1640     dt_iop_color_picker_reset(self, TRUE);
1641 
1642     dt_masks_form_t *grp = dt_masks_get_from_id(darktable.develop, self->blend_params->mask_id);
1643     if(grp && (grp->type & DT_MASKS_GROUP) && grp->points)
1644     {
1645       const gboolean control_button_pressed = dt_modifier_is(event->state, GDK_CONTROL_MASK);
1646 
1647       switch(bd->masks_shown)
1648       {
1649         case DT_MASKS_EDIT_FULL:
1650           bd->masks_shown = control_button_pressed ? DT_MASKS_EDIT_RESTRICTED : DT_MASKS_EDIT_OFF;
1651           break;
1652 
1653         case DT_MASKS_EDIT_RESTRICTED:
1654           bd->masks_shown = !control_button_pressed ? DT_MASKS_EDIT_FULL : DT_MASKS_EDIT_OFF;
1655           break;
1656 
1657         default:
1658         case DT_MASKS_EDIT_OFF:
1659           bd->masks_shown = control_button_pressed ? DT_MASKS_EDIT_RESTRICTED : DT_MASKS_EDIT_FULL;
1660           break;
1661       }
1662     }
1663     else
1664       bd->masks_shown = DT_MASKS_EDIT_OFF;
1665 
1666     rt_show_forms_for_current_scale(self);
1667 
1668     gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(g->bt_edit_masks),
1669                                  (bd->masks_shown != DT_MASKS_EDIT_OFF) && (darktable.develop->gui_module == self));
1670 
1671     --darktable.gui->reset;
1672 
1673     return TRUE;
1674   }
1675 
1676   return TRUE;
1677 }
1678 
rt_add_shape_callback(GtkWidget * widget,GdkEventButton * e,dt_iop_module_t * self)1679 static gboolean rt_add_shape_callback(GtkWidget *widget, GdkEventButton *e, dt_iop_module_t *self)
1680 {
1681   dt_iop_retouch_gui_data_t *g = (dt_iop_retouch_gui_data_t *)self->gui_data;
1682 
1683   if(darktable.gui->reset) return FALSE;
1684 
1685   const int creation_continuous = dt_modifier_is(e->state, GDK_CONTROL_MASK);
1686 
1687   rt_add_shape(widget, creation_continuous, self);
1688 
1689   gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(g->bt_circle), rt_shape_is_being_added(self, DT_MASKS_CIRCLE));
1690   gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(g->bt_path), rt_shape_is_being_added(self, DT_MASKS_PATH));
1691   gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(g->bt_ellipse), rt_shape_is_being_added(self, DT_MASKS_ELLIPSE));
1692   gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(g->bt_brush), rt_shape_is_being_added(self, DT_MASKS_BRUSH));
1693 
1694   return TRUE;
1695 }
1696 
rt_select_algorithm_callback(GtkToggleButton * togglebutton,GdkEventButton * e,dt_iop_module_t * self)1697 static gboolean rt_select_algorithm_callback(GtkToggleButton *togglebutton, GdkEventButton *e,
1698                                              dt_iop_module_t *self)
1699 {
1700   if(darktable.gui->reset) return FALSE;
1701 
1702   ++darktable.gui->reset;
1703 
1704   dt_iop_retouch_params_t *p = (dt_iop_retouch_params_t *)self->params;
1705   dt_iop_retouch_gui_data_t *g = (dt_iop_retouch_gui_data_t *)self->gui_data;
1706 
1707   dt_iop_retouch_algo_type_t new_algo = DT_IOP_RETOUCH_HEAL;
1708 
1709   if(togglebutton == (GtkToggleButton *)g->bt_blur)
1710     new_algo = DT_IOP_RETOUCH_BLUR;
1711   else if(togglebutton == (GtkToggleButton *)g->bt_clone)
1712     new_algo = DT_IOP_RETOUCH_CLONE;
1713   else if(togglebutton == (GtkToggleButton *)g->bt_heal)
1714     new_algo = DT_IOP_RETOUCH_HEAL;
1715   else if(togglebutton == (GtkToggleButton *)g->bt_fill)
1716     new_algo = DT_IOP_RETOUCH_FILL;
1717 
1718   // check if we have to do something
1719   gboolean accept = TRUE;
1720 
1721   const int index = rt_get_selected_shape_index(p);
1722   if(index >= 0 && dt_modifier_is(e->state, GDK_CONTROL_MASK))
1723   {
1724     if(new_algo != p->rt_forms[index].algorithm)
1725     {
1726       // we restrict changes to clone<->heal and blur<->fill
1727       if((new_algo == DT_IOP_RETOUCH_CLONE && p->rt_forms[index].algorithm != DT_IOP_RETOUCH_HEAL)
1728          || (new_algo == DT_IOP_RETOUCH_HEAL && p->rt_forms[index].algorithm != DT_IOP_RETOUCH_CLONE)
1729          || (new_algo == DT_IOP_RETOUCH_BLUR && p->rt_forms[index].algorithm != DT_IOP_RETOUCH_FILL)
1730          || (new_algo == DT_IOP_RETOUCH_FILL && p->rt_forms[index].algorithm != DT_IOP_RETOUCH_BLUR))
1731       {
1732         accept = FALSE;
1733       }
1734     }
1735   }
1736 
1737   if(accept) p->algorithm = new_algo;
1738 
1739   gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(g->bt_clone), (p->algorithm == DT_IOP_RETOUCH_CLONE));
1740   gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(g->bt_heal), (p->algorithm == DT_IOP_RETOUCH_HEAL));
1741   gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(g->bt_blur), (p->algorithm == DT_IOP_RETOUCH_BLUR));
1742   gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(g->bt_fill), (p->algorithm == DT_IOP_RETOUCH_FILL));
1743 
1744   rt_show_hide_controls(self);
1745 
1746   if(!accept)
1747   {
1748     --darktable.gui->reset;
1749     return FALSE;
1750   }
1751 
1752   if(index >= 0 && dt_modifier_is(e->state, GDK_CONTROL_MASK))
1753   {
1754     if(p->algorithm != p->rt_forms[index].algorithm)
1755     {
1756       p->rt_forms[index].algorithm = p->algorithm;
1757       dt_control_queue_redraw_center();
1758     }
1759   }
1760   else if(darktable.develop->form_gui->creation && (darktable.develop->form_gui->creation_module == self))
1761   {
1762     dt_iop_request_focus(self);
1763 
1764     dt_masks_type_t type = DT_MASKS_CIRCLE;
1765     if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(g->bt_path)))
1766       type = DT_MASKS_PATH;
1767     else if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(g->bt_circle)))
1768       type = DT_MASKS_CIRCLE;
1769     else if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(g->bt_ellipse)))
1770       type = DT_MASKS_ELLIPSE;
1771     else if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(g->bt_brush)))
1772       type = DT_MASKS_BRUSH;
1773 
1774     dt_masks_form_t *spot = NULL;
1775     if(p->algorithm == DT_IOP_RETOUCH_CLONE || p->algorithm == DT_IOP_RETOUCH_HEAL)
1776       spot = dt_masks_create(type | DT_MASKS_CLONE);
1777     else
1778       spot = dt_masks_create(type | DT_MASKS_NON_CLONE);
1779     dt_masks_change_form_gui(spot);
1780 
1781     darktable.develop->form_gui->creation = TRUE;
1782     darktable.develop->form_gui->creation_module = self;
1783     dt_control_queue_redraw_center();
1784   }
1785 
1786   --darktable.gui->reset;
1787 
1788   dt_dev_add_history_item(darktable.develop, self, TRUE);
1789 
1790   // if we have the shift key pressed, we set it as default
1791   if(dt_modifier_is(e->state, GDK_SHIFT_MASK))
1792   {
1793     dt_conf_set_int("plugins/darkroom/retouch/default_algo", p->algorithm);
1794     // and we show a toat msg to confirm
1795     if(p->algorithm == DT_IOP_RETOUCH_CLONE)
1796       dt_control_log(N_("default tool changed to %s"), N_("cloning"));
1797     else if(p->algorithm == DT_IOP_RETOUCH_HEAL)
1798       dt_control_log(N_("default tool changed to %s"), N_("healing"));
1799     else if(p->algorithm == DT_IOP_RETOUCH_FILL)
1800       dt_control_log(N_("default tool changed to %s"), N_("blur"));
1801     else if(p->algorithm == DT_IOP_RETOUCH_BLUR)
1802       dt_control_log(N_("default tool changed to %s"), N_("fill"));
1803   }
1804 
1805   return TRUE;
1806 }
1807 
rt_showmask_callback(GtkToggleButton * togglebutton,GdkEventButton * event,dt_iop_module_t * module)1808 static gboolean rt_showmask_callback(GtkToggleButton *togglebutton, GdkEventButton *event, dt_iop_module_t *module)
1809 {
1810   if(darktable.gui->reset) return TRUE;
1811 
1812   dt_iop_retouch_gui_data_t *g = (dt_iop_retouch_gui_data_t *)module->gui_data;
1813 
1814   // if blend module is displaying mask do not display it here
1815   if(module->request_mask_display && !g->mask_display)
1816   {
1817     dt_control_log(_("cannot display masks when the blending mask is displayed"));
1818 
1819     gtk_toggle_button_set_active(togglebutton, FALSE);
1820     return TRUE;
1821   }
1822 
1823   g->mask_display = !gtk_toggle_button_get_active(togglebutton);
1824 
1825   if(module->off) gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(module->off), 1);
1826   dt_iop_request_focus(module);
1827 
1828   dt_iop_refresh_center(module);
1829 
1830   gtk_toggle_button_set_active(togglebutton, g->mask_display);
1831   return TRUE;
1832 }
1833 
rt_suppress_callback(GtkToggleButton * togglebutton,GdkEventButton * event,dt_iop_module_t * module)1834 static gboolean rt_suppress_callback(GtkToggleButton *togglebutton, GdkEventButton *event, dt_iop_module_t *module)
1835 {
1836   if(darktable.gui->reset) return TRUE;
1837 
1838   dt_iop_retouch_gui_data_t *g = (dt_iop_retouch_gui_data_t *)module->gui_data;
1839   g->suppress_mask = !gtk_toggle_button_get_active(togglebutton);
1840 
1841   if(module->off) gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(module->off), 1);
1842   dt_iop_request_focus(module);
1843 
1844   dt_iop_refresh_center(module);
1845 
1846   gtk_toggle_button_set_active(togglebutton, g->suppress_mask);
1847   return TRUE;
1848 }
1849 
gui_changed(dt_iop_module_t * self,GtkWidget * w,void * previous)1850 void gui_changed(dt_iop_module_t *self, GtkWidget *w, void *previous)
1851 {
1852   dt_iop_retouch_params_t *p = (dt_iop_retouch_params_t *)self->params;
1853   dt_iop_retouch_gui_data_t *g = (dt_iop_retouch_gui_data_t *)self->gui_data;
1854 
1855   if(w == g->cmb_fill_mode)
1856   {
1857     ++darktable.gui->reset;
1858     rt_show_hide_controls(self);
1859     --darktable.gui->reset;
1860   }
1861   else
1862   {
1863     const int index = rt_get_selected_shape_index(p);
1864     if(index >= 0)
1865     {
1866       if(p->rt_forms[index].algorithm == DT_IOP_RETOUCH_BLUR)
1867       {
1868         p->rt_forms[index].blur_type = p->blur_type;
1869         p->rt_forms[index].blur_radius = p->blur_radius;
1870       }
1871       else if(p->rt_forms[index].algorithm == DT_IOP_RETOUCH_FILL)
1872       {
1873         p->rt_forms[index].fill_mode = p->fill_mode;
1874         p->rt_forms[index].fill_brightness = p->fill_brightness;
1875       }
1876     }
1877   }
1878 }
1879 
1880 //--------------------------------------------------------------------------------------------------
1881 // GUI
1882 //--------------------------------------------------------------------------------------------------
1883 
masks_selection_changed(struct dt_iop_module_t * self,const int form_selected_id)1884 void masks_selection_changed(struct dt_iop_module_t *self, const int form_selected_id)
1885 {
1886   dt_iop_retouch_gui_data_t *g = (dt_iop_retouch_gui_data_t *)self->gui_data;
1887   if(!g) return;
1888 
1889   dt_iop_gui_enter_critical_section(self);
1890   rt_shape_selection_changed(self);
1891   dt_iop_gui_leave_critical_section(self);
1892 }
1893 
init(dt_iop_module_t * module)1894 void init(dt_iop_module_t *module)
1895 {
1896   dt_iop_default_init(module);
1897 
1898   dt_iop_retouch_params_t *d = module->default_params;
1899 
1900   d->preview_levels[0] = RETOUCH_PREVIEW_LVL_MIN;
1901   d->preview_levels[1] = 0.f;
1902   d->preview_levels[2] = RETOUCH_PREVIEW_LVL_MAX;
1903   d->algorithm = dt_conf_get_int("plugins/darkroom/retouch/default_algo");
1904 }
1905 
init_global(dt_iop_module_so_t * module)1906 void init_global(dt_iop_module_so_t *module)
1907 {
1908   const int program = 21; // retouch.cl, from programs.conf
1909   dt_iop_retouch_global_data_t *gd = (dt_iop_retouch_global_data_t *)malloc(sizeof(dt_iop_retouch_global_data_t));
1910   module->data = gd;
1911   gd->kernel_retouch_clear_alpha = dt_opencl_create_kernel(program, "retouch_clear_alpha");
1912   gd->kernel_retouch_copy_alpha = dt_opencl_create_kernel(program, "retouch_copy_alpha");
1913   gd->kernel_retouch_copy_buffer_to_buffer = dt_opencl_create_kernel(program, "retouch_copy_buffer_to_buffer");
1914   gd->kernel_retouch_copy_buffer_to_image = dt_opencl_create_kernel(program, "retouch_copy_buffer_to_image");
1915   gd->kernel_retouch_fill = dt_opencl_create_kernel(program, "retouch_fill");
1916   gd->kernel_retouch_copy_image_to_buffer_masked
1917       = dt_opencl_create_kernel(program, "retouch_copy_image_to_buffer_masked");
1918   gd->kernel_retouch_copy_buffer_to_buffer_masked
1919       = dt_opencl_create_kernel(program, "retouch_copy_buffer_to_buffer_masked");
1920   gd->kernel_retouch_image_rgb2lab = dt_opencl_create_kernel(program, "retouch_image_rgb2lab");
1921   gd->kernel_retouch_image_lab2rgb = dt_opencl_create_kernel(program, "retouch_image_lab2rgb");
1922   gd->kernel_retouch_copy_mask_to_alpha = dt_opencl_create_kernel(program, "retouch_copy_mask_to_alpha");
1923 }
1924 
cleanup_global(dt_iop_module_so_t * module)1925 void cleanup_global(dt_iop_module_so_t *module)
1926 {
1927   dt_iop_retouch_global_data_t *gd = (dt_iop_retouch_global_data_t *)module->data;
1928 
1929   dt_opencl_free_kernel(gd->kernel_retouch_clear_alpha);
1930   dt_opencl_free_kernel(gd->kernel_retouch_copy_alpha);
1931   dt_opencl_free_kernel(gd->kernel_retouch_copy_buffer_to_buffer);
1932   dt_opencl_free_kernel(gd->kernel_retouch_copy_buffer_to_image);
1933   dt_opencl_free_kernel(gd->kernel_retouch_fill);
1934   dt_opencl_free_kernel(gd->kernel_retouch_copy_image_to_buffer_masked);
1935   dt_opencl_free_kernel(gd->kernel_retouch_copy_buffer_to_buffer_masked);
1936   dt_opencl_free_kernel(gd->kernel_retouch_image_rgb2lab);
1937   dt_opencl_free_kernel(gd->kernel_retouch_image_lab2rgb);
1938   dt_opencl_free_kernel(gd->kernel_retouch_copy_mask_to_alpha);
1939 
1940   free(module->data);
1941   module->data = NULL;
1942 }
1943 
gui_focus(struct dt_iop_module_t * self,gboolean in)1944 void gui_focus(struct dt_iop_module_t *self, gboolean in)
1945 {
1946   if(self->enabled && !darktable.develop->image_loading)
1947   {
1948     dt_iop_retouch_gui_data_t *g = (dt_iop_retouch_gui_data_t *)self->gui_data;
1949 
1950     if(in)
1951     {
1952       dt_iop_gui_blend_data_t *bd = (dt_iop_gui_blend_data_t *)self->blend_data;
1953       //only show shapes if shapes exist
1954       dt_masks_form_t *grp = dt_masks_get_from_id(darktable.develop, self->blend_params->mask_id);
1955       if(grp && (grp->type & DT_MASKS_GROUP) && grp->points)
1956       {
1957         // got focus, show all shapes
1958         if(bd->masks_shown == DT_MASKS_EDIT_OFF)
1959           dt_masks_set_edit_mode(self, DT_MASKS_EDIT_FULL);
1960 
1961         rt_show_forms_for_current_scale(self);
1962 
1963         gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(g->bt_edit_masks),
1964                                      (bd->masks_shown != DT_MASKS_EDIT_OFF)
1965                                          && (darktable.develop->gui_module == self));
1966       }
1967     }
1968     else
1969     {
1970       // lost focus, hide all shapes and free if some are in creation
1971       if(darktable.develop->form_gui->creation && darktable.develop->form_gui->creation_module == self)
1972         dt_masks_change_form_gui(NULL);
1973 
1974       if(darktable.develop->form_gui->creation_continuous_module == self)
1975       {
1976         darktable.develop->form_gui->creation_continuous = FALSE;
1977         darktable.develop->form_gui->creation_continuous_module = NULL;
1978       }
1979 
1980       gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(g->bt_path), FALSE);
1981       gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(g->bt_circle), FALSE);
1982       gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(g->bt_ellipse), FALSE);
1983       gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(g->bt_brush), FALSE);
1984       gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(g->bt_edit_masks), FALSE);
1985 
1986       dt_masks_set_edit_mode(self, DT_MASKS_EDIT_OFF);
1987     }
1988 
1989     // if we are switching between display modes we have to reprocess the main image
1990     if(g->display_wavelet_scale || g->mask_display || g->suppress_mask)
1991       dt_iop_refresh_center(self);
1992   }
1993 }
1994 
1995 /** commit is the synch point between core and gui, so it copies params to pipe data. */
commit_params(struct dt_iop_module_t * self,dt_iop_params_t * params,dt_dev_pixelpipe_t * pipe,dt_dev_pixelpipe_iop_t * piece)1996 void commit_params(struct dt_iop_module_t *self, dt_iop_params_t *params, dt_dev_pixelpipe_t *pipe,
1997                    dt_dev_pixelpipe_iop_t *piece)
1998 {
1999   memcpy(piece->data, params, sizeof(dt_iop_retouch_params_t));
2000 }
2001 
init_pipe(struct dt_iop_module_t * self,dt_dev_pixelpipe_t * pipe,dt_dev_pixelpipe_iop_t * piece)2002 void init_pipe(struct dt_iop_module_t *self, dt_dev_pixelpipe_t *pipe, dt_dev_pixelpipe_iop_t *piece)
2003 {
2004   piece->data = malloc(sizeof(dt_iop_retouch_data_t));
2005 }
2006 
cleanup_pipe(struct dt_iop_module_t * self,dt_dev_pixelpipe_t * pipe,dt_dev_pixelpipe_iop_t * piece)2007 void cleanup_pipe(struct dt_iop_module_t *self, dt_dev_pixelpipe_t *pipe, dt_dev_pixelpipe_iop_t *piece)
2008 {
2009   free(piece->data);
2010   piece->data = NULL;
2011 }
2012 
gui_update(dt_iop_module_t * self)2013 void gui_update(dt_iop_module_t *self)
2014 {
2015   dt_iop_retouch_gui_data_t *g = (dt_iop_retouch_gui_data_t *)self->gui_data;
2016   dt_iop_retouch_params_t *p = (dt_iop_retouch_params_t *)self->params;
2017 
2018   // check if there is new or deleted forms
2019   rt_resynch_params(self);
2020 
2021   if(darktable.develop->form_gui->creation_continuous
2022      && darktable.develop->form_gui->creation_continuous_module == self && !rt_allow_create_form(self))
2023   {
2024     dt_masks_change_form_gui(NULL);
2025     darktable.develop->form_gui->creation_continuous = FALSE;
2026     darktable.develop->form_gui->creation_continuous_module = NULL;
2027   }
2028 
2029   // update clones count
2030   const dt_masks_form_t *grp = dt_masks_get_from_id(self->dev, self->blend_params->mask_id);
2031   guint nb = 0;
2032   if(grp && (grp->type & DT_MASKS_GROUP)) nb = g_list_length(grp->points);
2033   gchar *str = g_strdup_printf("%d", nb);
2034   gtk_label_set_text(g->label_form, str);
2035   g_free(str);
2036 
2037   // update wavelet decompose labels
2038   rt_update_wd_bar_labels(p, g);
2039 
2040   // update selected shape label
2041   rt_display_selected_shapes_lbl(g);
2042 
2043   // show the shapes for the current scale
2044   rt_show_forms_for_current_scale(self);
2045 
2046   // enable/disable algorithm toolbar
2047   gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(g->bt_clone), p->algorithm == DT_IOP_RETOUCH_CLONE);
2048   gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(g->bt_blur), p->algorithm == DT_IOP_RETOUCH_BLUR);
2049   gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(g->bt_heal), p->algorithm == DT_IOP_RETOUCH_HEAL);
2050   gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(g->bt_fill), p->algorithm == DT_IOP_RETOUCH_FILL);
2051 
2052   // enable/disable shapes toolbar
2053   gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(g->bt_circle), rt_shape_is_being_added(self, DT_MASKS_CIRCLE));
2054   gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(g->bt_path), rt_shape_is_being_added(self, DT_MASKS_PATH));
2055   gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(g->bt_ellipse), rt_shape_is_being_added(self, DT_MASKS_ELLIPSE));
2056   gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(g->bt_brush), rt_shape_is_being_added(self, DT_MASKS_BRUSH));
2057 
2058   // update the rest of the fields
2059   gtk_widget_queue_draw(GTK_WIDGET(g->wd_bar));
2060 
2061   dt_bauhaus_combobox_set(g->cmb_blur_type, p->blur_type);
2062   dt_bauhaus_slider_set(g->sl_blur_radius, p->blur_radius);
2063   dt_bauhaus_slider_set(g->sl_fill_brightness, p->fill_brightness);
2064   dt_bauhaus_combobox_set(g->cmb_fill_mode, p->fill_mode);
2065 
2066   rt_display_selected_fill_color(g, p);
2067 
2068   gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(g->bt_display_wavelet_scale), g->display_wavelet_scale);
2069   gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(g->bt_copy_scale), g->copied_scale >= 0);
2070   gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(g->bt_paste_scale), g->copied_scale >= 0);
2071   gtk_widget_set_sensitive(g->bt_paste_scale, g->copied_scale >= 0);
2072 
2073   // show/hide some fields
2074   rt_show_hide_controls(self);
2075 
2076   // update edit shapes status
2077   dt_iop_gui_blend_data_t *bd = (dt_iop_gui_blend_data_t *)self->blend_data;
2078   if(darktable.develop->history_updating) bd->masks_shown = DT_MASKS_EDIT_OFF;
2079 
2080   //only toggle shape show button if shapes exist
2081   if(grp && (grp->type & DT_MASKS_GROUP) && grp->points)
2082   {
2083     gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(g->bt_edit_masks),
2084                                  (bd->masks_shown != DT_MASKS_EDIT_OFF) && (darktable.develop->gui_module == self));
2085   }
2086   else
2087   {
2088     gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(g->bt_edit_masks), FALSE);
2089   }
2090 
2091   // update the gradient slider
2092   double dlevels[3];
2093   for(int i = 0; i < 3; i++) dlevels[i] = p->preview_levels[i];
2094   dtgtk_gradient_slider_multivalue_set_values(g->preview_levels_gslider, dlevels);
2095 }
2096 
change_image(struct dt_iop_module_t * self)2097 void change_image(struct dt_iop_module_t *self)
2098 {
2099   dt_iop_retouch_gui_data_t *g = (dt_iop_retouch_gui_data_t *)self->gui_data;
2100   if(g)
2101   {
2102     g->copied_scale = -1;
2103     g->mask_display = 0;
2104     g->suppress_mask = 0;
2105     g->display_wavelet_scale = 0;
2106     g->displayed_wavelet_scale = 0;
2107     g->first_scale_visible = RETOUCH_MAX_SCALES + 1;
2108 
2109     g->preview_auto_levels = 0;
2110     g->preview_levels[0] = RETOUCH_PREVIEW_LVL_MIN;
2111     g->preview_levels[1] = 0.f;
2112     g->preview_levels[2] = RETOUCH_PREVIEW_LVL_MAX;
2113 
2114     g->is_dragging = 0;
2115     g->wdbar_mouse_x = g->wdbar_mouse_y = -1;
2116     g->curr_scale = -1;
2117     g->lower_cursor = g->upper_cursor = FALSE;
2118     g->lower_margin = g->upper_margin = FALSE;
2119   }
2120 }
2121 
gui_init(dt_iop_module_t * self)2122 void gui_init(dt_iop_module_t *self)
2123 {
2124   dt_iop_retouch_gui_data_t *g = IOP_GUI_ALLOC(retouch);
2125   dt_iop_retouch_params_t *p = (dt_iop_retouch_params_t *)self->default_params;
2126 
2127   change_image(self);
2128 
2129   // shapes toolbar
2130   GtkWidget *hbox_shapes = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
2131 
2132   gtk_box_pack_start(GTK_BOX(hbox_shapes), dt_ui_label_new(_("shapes:")), FALSE, TRUE, 0);
2133   g->label_form = GTK_LABEL(gtk_label_new("-1"));
2134   gtk_box_pack_start(GTK_BOX(hbox_shapes), GTK_WIDGET(g->label_form), FALSE, TRUE, DT_PIXEL_APPLY_DPI(5));
2135   gtk_widget_set_tooltip_text(hbox_shapes,
2136                _("to add a shape select an algorithm and a shape type and click on the image.\n"
2137                  "shapes are added to the current scale"));
2138 
2139   g->bt_edit_masks = dt_iop_togglebutton_new(self, N_("editing"), N_("show and edit shapes on the current scale"),
2140                                                                   N_("show and edit shapes in restricted mode"),
2141                                              G_CALLBACK(rt_edit_masks_callback), TRUE, 0, 0,
2142                                              dtgtk_cairo_paint_masks_eye, hbox_shapes);
2143 
2144   g->bt_brush = dt_iop_togglebutton_new(self, N_("shapes"), N_("add brush"), N_("add multiple brush strokes"),
2145                                         G_CALLBACK(rt_add_shape_callback), TRUE, 0, 0,
2146                                         dtgtk_cairo_paint_masks_brush, hbox_shapes);
2147 
2148   g->bt_path = dt_iop_togglebutton_new(self, N_("shapes"), N_("add path"), N_("add multiple paths"),
2149                                        G_CALLBACK(rt_add_shape_callback), TRUE, 0, 0,
2150                                        dtgtk_cairo_paint_masks_path, hbox_shapes);
2151 
2152   g->bt_ellipse = dt_iop_togglebutton_new(self, N_("shapes"), N_("add ellipse"), N_("add multiple ellipses"),
2153                                           G_CALLBACK(rt_add_shape_callback), TRUE, 0, 0,
2154                                           dtgtk_cairo_paint_masks_ellipse, hbox_shapes);
2155 
2156   g->bt_circle = dt_iop_togglebutton_new(self, N_("shapes"), N_("add circle"), N_("add multiple circles"),
2157                                          G_CALLBACK(rt_add_shape_callback), TRUE, 0, 0,
2158                                          dtgtk_cairo_paint_masks_circle, hbox_shapes);
2159 
2160   // algorithm toolbar
2161   GtkWidget *hbox_algo = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
2162 
2163   gtk_box_pack_start(GTK_BOX(hbox_algo), dt_ui_label_new(_("algorithms:")), FALSE, TRUE, 0);
2164 
2165   g->bt_blur = dt_iop_togglebutton_new(
2166       self, N_("tools"), N_("activate blur tool"), N_("change algorithm for current form"),
2167       G_CALLBACK(rt_select_algorithm_callback), TRUE, 0, 0, dtgtk_cairo_paint_tool_blur, hbox_algo);
2168 
2169   g->bt_fill = dt_iop_togglebutton_new(
2170       self, N_("tools"), N_("activate fill tool"), N_("change algorithm for current form"),
2171       G_CALLBACK(rt_select_algorithm_callback), TRUE, 0, 0, dtgtk_cairo_paint_tool_fill, hbox_algo);
2172 
2173   g->bt_clone = dt_iop_togglebutton_new(
2174       self, N_("tools"), N_("activate cloning tool"), N_("change algorithm for current form"),
2175       G_CALLBACK(rt_select_algorithm_callback), TRUE, 0, 0, dtgtk_cairo_paint_tool_clone, hbox_algo);
2176 
2177   g->bt_heal = dt_iop_togglebutton_new(
2178       self, N_("tools"), N_("activate healing tool"), N_("change algorithm for current form"),
2179       G_CALLBACK(rt_select_algorithm_callback), TRUE, 0, 0, dtgtk_cairo_paint_tool_heal, hbox_algo);
2180 
2181   // overwrite tooltip ourself to handle shift+click
2182   gchar *tt2 = dt_util_dstrcat(NULL, "%s\n%s", _("ctrl+click to change tool for current form"),
2183                                _("shift+click to set the tool as default"));
2184   gchar *tt = dt_util_dstrcat(NULL, "%s\n%s", _("activate blur tool"), tt2);
2185   gtk_widget_set_tooltip_text(g->bt_blur, tt);
2186   g_free(tt);
2187   tt = dt_util_dstrcat(NULL, "%s\n%s", _("activate fill tool"), tt2);
2188   gtk_widget_set_tooltip_text(g->bt_fill, tt);
2189   g_free(tt);
2190   tt = dt_util_dstrcat(NULL, "%s\n%s", _("activate cloning tool"), tt2);
2191   gtk_widget_set_tooltip_text(g->bt_clone, tt);
2192   g_free(tt);
2193   tt = dt_util_dstrcat(NULL, "%s\n%s", _("activate healing tool"), tt2);
2194   gtk_widget_set_tooltip_text(g->bt_heal, tt);
2195   g_free(tt);
2196   g_free(tt2);
2197 
2198   // wavelet decompose bar labels
2199   GtkWidget *grid_wd_labels = gtk_grid_new();
2200   gtk_grid_set_column_homogeneous(GTK_GRID(grid_wd_labels), FALSE);
2201 
2202   gtk_grid_attach(GTK_GRID(grid_wd_labels), dt_ui_label_new(_("scales:")), 0, 0, 1, 1);
2203   g->lbl_num_scales = GTK_LABEL(dt_ui_label_new(NULL));
2204   gtk_label_set_width_chars(g->lbl_num_scales, 2);
2205   gtk_grid_attach(GTK_GRID(grid_wd_labels), GTK_WIDGET(g->lbl_num_scales), 1, 0, 1, 1);
2206 
2207   gtk_grid_attach(GTK_GRID(grid_wd_labels), dt_ui_label_new(_("current:")), 0, 1, 1, 1);
2208   g->lbl_curr_scale = GTK_LABEL(dt_ui_label_new(NULL));
2209   gtk_label_set_width_chars(g->lbl_curr_scale, 2);
2210   gtk_grid_attach(GTK_GRID(grid_wd_labels), GTK_WIDGET(g->lbl_curr_scale), 1, 1, 1, 1);
2211 
2212   gtk_grid_attach(GTK_GRID(grid_wd_labels), dt_ui_label_new(_("merge from:")), 0, 2, 1, 1);
2213   g->lbl_merge_from_scale = GTK_LABEL(dt_ui_label_new(NULL));
2214   gtk_label_set_width_chars(g->lbl_merge_from_scale, 2);
2215   gtk_grid_attach(GTK_GRID(grid_wd_labels), GTK_WIDGET(g->lbl_merge_from_scale), 1, 2, 1, 1);
2216 
2217   // wavelet decompose bar
2218   g->wd_bar = gtk_drawing_area_new();
2219 
2220   gtk_widget_set_tooltip_text(g->wd_bar, _("top slider adjusts where the merge scales start\n"
2221                                            "bottom slider adjusts the number of scales\n"
2222                                            "dot indicates the current scale\n"
2223                                            "top line indicates that the scale is visible at current zoom level\n"
2224                                            "bottom line indicates that the scale has shapes on it"));
2225   g_signal_connect(G_OBJECT(g->wd_bar), "draw", G_CALLBACK(rt_wdbar_draw), self);
2226   g_signal_connect(G_OBJECT(g->wd_bar), "motion-notify-event", G_CALLBACK(rt_wdbar_motion_notify), self);
2227   g_signal_connect(G_OBJECT(g->wd_bar), "leave-notify-event", G_CALLBACK(rt_wdbar_leave_notify), self);
2228   g_signal_connect(G_OBJECT(g->wd_bar), "button-press-event", G_CALLBACK(rt_wdbar_button_press), self);
2229   g_signal_connect(G_OBJECT(g->wd_bar), "button-release-event", G_CALLBACK(rt_wdbar_button_release), self);
2230   g_signal_connect(G_OBJECT(g->wd_bar), "scroll-event", G_CALLBACK(rt_wdbar_scrolled), self);
2231   gtk_widget_add_events(GTK_WIDGET(g->wd_bar), GDK_POINTER_MOTION_MASK
2232                                                    | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK
2233                                                    | GDK_LEAVE_NOTIFY_MASK | darktable.gui->scroll_mask);
2234   gtk_widget_set_size_request(g->wd_bar, -1, DT_PIXEL_APPLY_DPI(40));
2235 
2236   // toolbar display current scale / cut&paste / suppress&display masks
2237   GtkWidget *hbox_scale = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 5);
2238 
2239   // display & suppress masks
2240   g->bt_showmask = dt_iop_togglebutton_new(self, N_("editing"), N_("display masks"), NULL,
2241                                            G_CALLBACK(rt_showmask_callback), TRUE, 0, 0,
2242                                            dtgtk_cairo_paint_showmask, hbox_scale);
2243 
2244   g->bt_suppress = dt_iop_togglebutton_new(self, N_("editing"), N_("temporarily switch off shapes"), NULL,
2245                                            G_CALLBACK(rt_suppress_callback), TRUE, 0, 0,
2246                                            dtgtk_cairo_paint_eye_toggle, hbox_scale);
2247 
2248   gtk_box_pack_end(GTK_BOX(hbox_scale), gtk_grid_new(), TRUE, TRUE, 0);
2249 
2250   // copy/paste shapes
2251   g->bt_paste_scale = dt_iop_togglebutton_new(self, N_("editing"), N_("paste cut shapes to current scale"), NULL,
2252                                               G_CALLBACK(rt_copypaste_scale_callback), TRUE, 0, 0,
2253                                               dtgtk_cairo_paint_paste_forms, hbox_scale);
2254 
2255   g->bt_copy_scale = dt_iop_togglebutton_new(self, N_("editing"), N_("cut shapes from current scale"), NULL,
2256                                              G_CALLBACK(rt_copypaste_scale_callback), TRUE, 0, 0,
2257                                              dtgtk_cairo_paint_cut_forms, hbox_scale);
2258 
2259   gtk_box_pack_end(GTK_BOX(hbox_scale), gtk_grid_new(), TRUE, TRUE, 0);
2260 
2261   // display final image/current scale
2262   g->bt_display_wavelet_scale = dt_iop_togglebutton_new(self, N_("editing"), N_("display wavelet scale"), NULL,
2263                                                         G_CALLBACK(rt_display_wavelet_scale_callback), TRUE, 0, 0,
2264                                                         dtgtk_cairo_paint_display_wavelet_scale, hbox_scale);
2265 
2266   // preview single scale
2267   g->vbox_preview_scale = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
2268 
2269   GtkWidget *lbl_psc = dt_ui_section_label_new(_("preview single scale"));
2270   gtk_box_pack_start(GTK_BOX(g->vbox_preview_scale), lbl_psc, FALSE, TRUE, 0);
2271 
2272   GtkWidget *prev_lvl = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
2273 
2274   // gradient slider
2275   #define NEUTRAL_GRAY 0.5
2276   static const GdkRGBA _gradient_L[]
2277       = { { 0, 0, 0, 1.0 }, { NEUTRAL_GRAY, NEUTRAL_GRAY, NEUTRAL_GRAY, 1.0 } };
2278   g->preview_levels_gslider = DTGTK_GRADIENT_SLIDER_MULTIVALUE(
2279       dtgtk_gradient_slider_multivalue_new_with_color_and_name(_gradient_L[0], _gradient_L[1], 3, "preview-levels"));
2280   gtk_widget_set_tooltip_text(GTK_WIDGET(g->preview_levels_gslider), _("adjust preview levels"));
2281   dtgtk_gradient_slider_multivalue_set_marker(g->preview_levels_gslider, GRADIENT_SLIDER_MARKER_LOWER_OPEN_BIG, 0);
2282   dtgtk_gradient_slider_multivalue_set_marker(g->preview_levels_gslider, GRADIENT_SLIDER_MARKER_LOWER_FILLED_BIG, 1);
2283   dtgtk_gradient_slider_multivalue_set_marker(g->preview_levels_gslider, GRADIENT_SLIDER_MARKER_LOWER_OPEN_BIG, 2);
2284   (g->preview_levels_gslider)->scale_callback = rt_gslider_scale_callback;
2285   double vdefault[3] = {RETOUCH_PREVIEW_LVL_MIN, (RETOUCH_PREVIEW_LVL_MIN + RETOUCH_PREVIEW_LVL_MAX) / 2.0, RETOUCH_PREVIEW_LVL_MAX};
2286   dtgtk_gradient_slider_multivalue_set_values(g->preview_levels_gslider, vdefault);
2287   dtgtk_gradient_slider_multivalue_set_resetvalues(g->preview_levels_gslider, vdefault);
2288   (g->preview_levels_gslider)->markers_type = PROPORTIONAL_MARKERS;
2289   (g->preview_levels_gslider)->min_spacing = 0.05;
2290   g_signal_connect(G_OBJECT(g->preview_levels_gslider), "value-changed", G_CALLBACK(rt_gslider_changed), self);
2291 
2292   gtk_box_pack_start(GTK_BOX(prev_lvl), GTK_WIDGET(g->preview_levels_gslider), TRUE, TRUE, 0);
2293 
2294   // auto-levels button
2295   g->bt_auto_levels = dt_iop_togglebutton_new(self, N_("editing"), N_("auto levels"), NULL,
2296                                               G_CALLBACK(rt_auto_levels_callback), TRUE, 0, 0,
2297                                               dtgtk_cairo_paint_auto_levels, prev_lvl);
2298 
2299   gtk_box_pack_start(GTK_BOX(g->vbox_preview_scale), prev_lvl, TRUE, TRUE, 0);
2300 
2301   // shapes selected (label)
2302   GtkWidget *hbox_shape_sel = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
2303   GtkWidget *label1 = gtk_label_new(_("shape selected:"));
2304   gtk_label_set_ellipsize(GTK_LABEL(label1), PANGO_ELLIPSIZE_START);
2305   gtk_box_pack_start(GTK_BOX(hbox_shape_sel), label1, FALSE, TRUE, 0);
2306   g->label_form_selected = GTK_LABEL(gtk_label_new("-1"));
2307   gtk_widget_set_tooltip_text(hbox_shape_sel,
2308                               _("click on a shape to select it,\nto unselect click on an empty space"));
2309   gtk_box_pack_start(GTK_BOX(hbox_shape_sel), GTK_WIDGET(g->label_form_selected), FALSE, TRUE, 0);
2310 
2311   // fill properties
2312   g->vbox_fill = self->widget = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
2313 
2314   g->cmb_fill_mode = dt_bauhaus_combobox_from_params(self, "fill_mode");
2315   gtk_widget_set_tooltip_text(g->cmb_fill_mode, _("erase the detail or fills with chosen color"));
2316 
2317   // color for fill algorithm
2318   GdkRGBA color
2319       = (GdkRGBA){.red = p->fill_color[0], .green = p->fill_color[1], .blue = p->fill_color[2], .alpha = 1.0 };
2320 
2321   g->hbox_color_pick = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
2322   GtkWidget *lbl_fill_color = dt_ui_label_new(_("fill color: "));
2323   gtk_box_pack_start(GTK_BOX(g->hbox_color_pick), lbl_fill_color, FALSE, TRUE, 0);
2324 
2325   g->colorpick = gtk_color_button_new_with_rgba(&color);
2326   gtk_color_chooser_set_use_alpha(GTK_COLOR_CHOOSER(g->colorpick), FALSE);
2327   gtk_color_button_set_title(GTK_COLOR_BUTTON(g->colorpick), _("select fill color"));
2328   gtk_widget_set_tooltip_text(g->colorpick, _("select fill color"));
2329   g_signal_connect(G_OBJECT(g->colorpick), "color-set", G_CALLBACK(rt_colorpick_color_set_callback), self);
2330   gtk_box_pack_start(GTK_BOX(g->hbox_color_pick), GTK_WIDGET(g->colorpick), TRUE, TRUE, 0);
2331 
2332   g->colorpicker = dt_color_picker_new(self, DT_COLOR_PICKER_POINT, g->hbox_color_pick);
2333   gtk_widget_set_tooltip_text(g->colorpicker, _("pick fill color from image"));
2334 
2335   gtk_box_pack_start(GTK_BOX(g->vbox_fill), g->hbox_color_pick, TRUE, TRUE, 0);
2336 
2337   g->sl_fill_brightness = dt_bauhaus_slider_from_params(self, "fill_brightness");
2338   dt_bauhaus_slider_set_digits(g->sl_fill_brightness, 4);
2339   gtk_widget_set_tooltip_text(g->sl_fill_brightness,
2340                               _("adjusts color brightness to fine-tune it. works with erase as well"));
2341 
2342   // blur properties
2343   g->vbox_blur = self->widget = gtk_box_new(GTK_ORIENTATION_VERTICAL, 5);
2344 
2345   g->cmb_blur_type = dt_bauhaus_combobox_from_params(self, "blur_type");
2346   gtk_widget_set_tooltip_text(g->cmb_blur_type, _("type for the blur algorithm"));
2347 
2348   g->sl_blur_radius = dt_bauhaus_slider_from_params(self, "blur_radius");
2349   dt_bauhaus_slider_set_step(g->sl_blur_radius, 0.1);
2350   gtk_widget_set_tooltip_text(g->sl_blur_radius, _("radius of the selected blur type"));
2351 
2352   // mask opacity
2353   g->sl_mask_opacity = dt_bauhaus_slider_new_with_range(self, 0.0, 1.0, 0.05, 1., 3);
2354   dt_bauhaus_widget_set_label(g->sl_mask_opacity, NULL, N_("mask opacity"));
2355   gtk_widget_set_tooltip_text(g->sl_mask_opacity, _("set the opacity on the selected shape"));
2356   g_signal_connect(G_OBJECT(g->sl_mask_opacity), "value-changed", G_CALLBACK(rt_mask_opacity_callback), self);
2357 
2358   // start building top level widget
2359   self->widget = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
2360 
2361   GtkWidget *lbl_rt_tools = dt_ui_section_label_new(_("retouch tools"));
2362   gtk_box_pack_start(GTK_BOX(self->widget), lbl_rt_tools, FALSE, TRUE, 0);
2363 
2364   // shapes toolbar
2365   gtk_box_pack_start(GTK_BOX(self->widget), hbox_shapes, TRUE, TRUE, 0);
2366   // algorithms toolbar
2367   gtk_box_pack_start(GTK_BOX(self->widget), hbox_algo, TRUE, TRUE, 0);
2368 
2369   // wavelet decompose
2370   GtkWidget *lbl_wd = dt_ui_section_label_new(_("wavelet decompose"));
2371   gtk_box_pack_start(GTK_BOX(self->widget), lbl_wd, FALSE, TRUE, 0);
2372 
2373   // wavelet decompose bar & labels
2374   gtk_box_pack_start(GTK_BOX(self->widget), grid_wd_labels, TRUE, TRUE, 0);
2375   gtk_box_pack_start(GTK_BOX(self->widget), g->wd_bar, TRUE, TRUE, DT_PIXEL_APPLY_DPI(3));
2376 
2377   // preview scale & cut/paste scale
2378   gtk_box_pack_start(GTK_BOX(self->widget), hbox_scale, TRUE, TRUE, 0);
2379 
2380   // preview single scale
2381   gtk_box_pack_start(GTK_BOX(self->widget), g->vbox_preview_scale, TRUE, TRUE, 0);
2382 
2383   // shapes
2384   GtkWidget *lbl_shapes = dt_ui_section_label_new(_("shapes"));
2385   gtk_box_pack_start(GTK_BOX(self->widget), lbl_shapes, FALSE, TRUE, 0);
2386 
2387   // shape selected
2388   gtk_box_pack_start(GTK_BOX(self->widget), hbox_shape_sel, TRUE, TRUE, 0);
2389   // blur radius
2390   gtk_box_pack_start(GTK_BOX(self->widget), g->vbox_blur, TRUE, TRUE, 0);
2391   // fill color
2392   gtk_box_pack_start(GTK_BOX(self->widget), g->vbox_fill, TRUE, TRUE, 0);
2393   // mask (shape) opacity
2394   gtk_box_pack_start(GTK_BOX(self->widget), g->sl_mask_opacity, TRUE, TRUE, 0);
2395 
2396   /* add signal handler for preview pipe finish to redraw the preview */
2397   DT_DEBUG_CONTROL_SIGNAL_CONNECT(darktable.signals, DT_SIGNAL_DEVELOP_UI_PIPE_FINISHED,
2398                             G_CALLBACK(rt_develop_ui_pipe_finished_callback), self);
2399 }
2400 
gui_reset(struct dt_iop_module_t * self)2401 void gui_reset(struct dt_iop_module_t *self)
2402 {
2403   // hide the previous masks
2404   dt_masks_reset_form_gui();
2405   // set the algo to the default one
2406   dt_iop_retouch_params_t *p = (dt_iop_retouch_params_t *)self->params;
2407   p->algorithm = dt_conf_get_int("plugins/darkroom/retouch/default_algo");
2408 }
2409 
reload_defaults(dt_iop_module_t * self)2410 void reload_defaults(dt_iop_module_t *self)
2411 {
2412   // set the algo to the default one
2413   dt_iop_retouch_params_t *p = (dt_iop_retouch_params_t *)self->default_params;
2414   p->algorithm = dt_conf_get_int("plugins/darkroom/retouch/default_algo");
2415 }
2416 
gui_cleanup(dt_iop_module_t * self)2417 void gui_cleanup(dt_iop_module_t *self)
2418 {
2419   DT_DEBUG_CONTROL_SIGNAL_DISCONNECT(darktable.signals, G_CALLBACK(rt_develop_ui_pipe_finished_callback), self);
2420 
2421   IOP_GUI_FREE;
2422 }
2423 
modify_roi_out(struct dt_iop_module_t * self,struct dt_dev_pixelpipe_iop_t * piece,dt_iop_roi_t * roi_out,const dt_iop_roi_t * roi_in)2424 void modify_roi_out(struct dt_iop_module_t *self, struct dt_dev_pixelpipe_iop_t *piece, dt_iop_roi_t *roi_out,
2425                     const dt_iop_roi_t *roi_in)
2426 {
2427   *roi_out = *roi_in;
2428 }
2429 
rt_compute_roi_in(struct dt_iop_module_t * self,struct dt_dev_pixelpipe_iop_t * piece,dt_iop_roi_t * roi_in,int * _roir,int * _roib,int * _roix,int * _roiy)2430 static void rt_compute_roi_in(struct dt_iop_module_t *self, struct dt_dev_pixelpipe_iop_t *piece,
2431                               dt_iop_roi_t *roi_in, int *_roir, int *_roib, int *_roix, int *_roiy)
2432 {
2433   dt_iop_retouch_params_t *p = (dt_iop_retouch_params_t *)piece->data;
2434   dt_develop_blend_params_t *bp = self->blend_params;
2435 
2436   int roir = *_roir;
2437   int roib = *_roib;
2438   int roix = *_roix;
2439   int roiy = *_roiy;
2440 
2441   // We iterate through all forms
2442   const dt_masks_form_t *grp = dt_masks_get_from_id_ext(piece->pipe->forms, bp->mask_id);
2443   if(grp && (grp->type & DT_MASKS_GROUP))
2444   {
2445     for(const GList *forms = grp->points; forms; forms = g_list_next(forms))
2446     {
2447       const dt_masks_point_group_t *grpt = (dt_masks_point_group_t *)forms->data;
2448       if(grpt)
2449       {
2450         const int formid = grpt->formid;
2451         const int index = rt_get_index_from_formid(p, formid);
2452         if(p->rt_forms[index].algorithm == DT_IOP_RETOUCH_FILL)
2453         {
2454           continue;
2455         }
2456 
2457         // we get the spot
2458         dt_masks_form_t *form = dt_masks_get_from_id_ext(piece->pipe->forms, formid);
2459         if(form)
2460         {
2461           // if the form is outside the roi, we just skip it
2462           // we get the area for the form
2463           int fl, ft, fw, fh;
2464           if(!dt_masks_get_area(self, piece, form, &fw, &fh, &fl, &ft))
2465           {
2466             continue;
2467           }
2468 
2469           // is the form outside of the roi?
2470           fw *= roi_in->scale, fh *= roi_in->scale, fl *= roi_in->scale, ft *= roi_in->scale;
2471           if(ft >= roi_in->y + roi_in->height || ft + fh <= roi_in->y || fl >= roi_in->x + roi_in->width
2472              || fl + fw <= roi_in->x)
2473           {
2474             continue;
2475           }
2476 
2477           // heal need the entire area
2478           if(p->rt_forms[index].algorithm == DT_IOP_RETOUCH_HEAL)
2479           {
2480             // we enlarge the roi if needed
2481             roiy = fminf(ft, roiy);
2482             roix = fminf(fl, roix);
2483             roir = fmaxf(fl + fw, roir);
2484             roib = fmaxf(ft + fh, roib);
2485           }
2486           // blur need an overlap of 4 * radius (scaled)
2487           if(p->rt_forms[index].algorithm == DT_IOP_RETOUCH_BLUR)
2488           {
2489             if(index >= 0)
2490             {
2491               const int overlap = ceilf(4 * (p->rt_forms[index].blur_radius * roi_in->scale / piece->iscale));
2492               if(roiy > ft) roiy = MAX(roiy - overlap, ft);
2493               if(roix > fl) roix = MAX(roix - overlap, fl);
2494               if(roir < fl + fw) roir = MAX(roir + overlap, fl + fw);
2495               if(roib < ft + fh) roib = MAX(roib + overlap, ft + fh);
2496             }
2497           }
2498           // heal and clone need both source and destination areas
2499           if(p->rt_forms[index].algorithm == DT_IOP_RETOUCH_HEAL
2500              || p->rt_forms[index].algorithm == DT_IOP_RETOUCH_CLONE)
2501           {
2502             int dx = 0, dy = 0;
2503             if(rt_masks_get_delta_to_destination(self, piece, roi_in, form, &dx, &dy,
2504                                                  p->rt_forms[index].distort_mode))
2505             {
2506               roiy = fminf(ft - dy, roiy);
2507               roix = fminf(fl - dx, roix);
2508               roir = fmaxf(fl + fw - dx, roir);
2509               roib = fmaxf(ft + fh - dy, roib);
2510             }
2511           }
2512         }
2513       }
2514     }
2515   }
2516 
2517   *_roir = roir;
2518   *_roib = roib;
2519   *_roix = roix;
2520   *_roiy = roiy;
2521 }
2522 
2523 // for a given form, if a previous clone/heal destination intersects the source area,
2524 // include that area in roi_in too
rt_extend_roi_in_from_source_clones(struct dt_iop_module_t * self,struct dt_dev_pixelpipe_iop_t * piece,dt_iop_roi_t * roi_in,const int formid_src,const int fl_src,const int ft_src,const int fw_src,const int fh_src,int * _roir,int * _roib,int * _roix,int * _roiy)2525 static void rt_extend_roi_in_from_source_clones(struct dt_iop_module_t *self, struct dt_dev_pixelpipe_iop_t *piece,
2526                                                 dt_iop_roi_t *roi_in, const int formid_src, const int fl_src,
2527                                                 const int ft_src, const int fw_src, const int fh_src, int *_roir,
2528                                                 int *_roib, int *_roix, int *_roiy)
2529 {
2530   dt_iop_retouch_params_t *p = (dt_iop_retouch_params_t *)piece->data;
2531   dt_develop_blend_params_t *bp = self->blend_params;
2532 
2533   int roir = *_roir;
2534   int roib = *_roib;
2535   int roix = *_roix;
2536   int roiy = *_roiy;
2537 
2538   // We iterate through all forms
2539   const dt_masks_form_t *grp = dt_masks_get_from_id_ext(piece->pipe->forms, bp->mask_id);
2540   if(grp && (grp->type & DT_MASKS_GROUP))
2541   {
2542     for(const GList *forms = grp->points; forms; forms = g_list_next(forms))
2543     {
2544       const dt_masks_point_group_t *grpt = (dt_masks_point_group_t *)forms->data;
2545       if(grpt)
2546       {
2547         const int formid = grpt->formid;
2548 
2549         // just need the previous forms
2550         if(formid == formid_src) break;
2551 
2552         const int index = rt_get_index_from_formid(p, formid);
2553 
2554         // only process clone and heal
2555         if(p->rt_forms[index].algorithm != DT_IOP_RETOUCH_HEAL
2556            && p->rt_forms[index].algorithm != DT_IOP_RETOUCH_CLONE)
2557         {
2558           continue;
2559         }
2560 
2561         // we get the spot
2562         dt_masks_form_t *form = dt_masks_get_from_id_ext(piece->pipe->forms, formid);
2563         if(form)
2564         {
2565           // we get the source area
2566           int fl, ft, fw, fh;
2567           if(!dt_masks_get_source_area(self, piece, form, &fw, &fh, &fl, &ft))
2568           {
2569             continue;
2570           }
2571           fw *= roi_in->scale, fh *= roi_in->scale, fl *= roi_in->scale, ft *= roi_in->scale;
2572 
2573           // get the destination area
2574           int fl_dest, ft_dest;
2575           int dx = 0, dy = 0;
2576           if(!rt_masks_get_delta_to_destination(self, piece, roi_in, form, &dx, &dy,
2577                                                 p->rt_forms[index].distort_mode))
2578           {
2579             continue;
2580           }
2581 
2582           ft_dest = ft + dy;
2583           fl_dest = fl + dx;
2584 
2585           // check if the destination of this form intersects the source of the formid_src
2586           const int intersects = !(ft_dest + fh < ft_src || ft_src + fh_src < ft_dest || fl_dest + fw < fl_src
2587                                    || fl_src + fw_src < fl_dest);
2588           if(intersects)
2589           {
2590             // we enlarge the roi if needed
2591             roiy = fminf(ft, roiy);
2592             roix = fminf(fl, roix);
2593             roir = fmaxf(fl + fw, roir);
2594             roib = fmaxf(ft + fh, roib);
2595 
2596             // need both source and destination areas
2597             roiy = fminf(ft + dy, roiy);
2598             roix = fminf(fl + dx, roix);
2599             roir = fmaxf(fl + fw + dx, roir);
2600             roib = fmaxf(ft + fh + dy, roib);
2601           }
2602         }
2603       }
2604     }
2605   }
2606 
2607   *_roir = roir;
2608   *_roib = roib;
2609   *_roix = roix;
2610   *_roiy = roiy;
2611 }
2612 
2613 // for clone and heal, if the source area is the destination from another clone/heal,
2614 // we also need the area from that previous clone/heal
rt_extend_roi_in_for_clone(struct dt_iop_module_t * self,struct dt_dev_pixelpipe_iop_t * piece,dt_iop_roi_t * roi_in,int * _roir,int * _roib,int * _roix,int * _roiy)2615 static void rt_extend_roi_in_for_clone(struct dt_iop_module_t *self, struct dt_dev_pixelpipe_iop_t *piece,
2616                                        dt_iop_roi_t *roi_in, int *_roir, int *_roib, int *_roix, int *_roiy)
2617 {
2618   dt_iop_retouch_params_t *p = (dt_iop_retouch_params_t *)piece->data;
2619   dt_develop_blend_params_t *bp = self->blend_params;
2620 
2621   int roir = *_roir;
2622   int roib = *_roib;
2623   int roix = *_roix;
2624   int roiy = *_roiy;
2625 
2626   // go through all clone and heal forms
2627   const dt_masks_form_t *grp = dt_masks_get_from_id_ext(piece->pipe->forms, bp->mask_id);
2628   if(grp && (grp->type & DT_MASKS_GROUP))
2629   {
2630     for(const GList *forms = grp->points; forms; forms = g_list_next(forms))
2631     {
2632       dt_masks_point_group_t *grpt = (dt_masks_point_group_t *)forms->data;
2633       if(grpt)
2634       {
2635         const int formid = grpt->formid;
2636         const int index = rt_get_index_from_formid(p, formid);
2637 
2638         if(p->rt_forms[index].algorithm != DT_IOP_RETOUCH_HEAL
2639            && p->rt_forms[index].algorithm != DT_IOP_RETOUCH_CLONE)
2640         {
2641           continue;
2642         }
2643 
2644         // we get the spot
2645         dt_masks_form_t *form = dt_masks_get_from_id_ext(piece->pipe->forms, formid);
2646         if(form == NULL)
2647         {
2648           continue;
2649         }
2650 
2651         // get the source area
2652         int fl_src, ft_src, fw_src, fh_src;
2653         if(!dt_masks_get_source_area(self, piece, form, &fw_src, &fh_src, &fl_src, &ft_src))
2654         {
2655           continue;
2656         }
2657 
2658         fw_src *= roi_in->scale, fh_src *= roi_in->scale, fl_src *= roi_in->scale, ft_src *= roi_in->scale;
2659 
2660         // we only want to process forms already in roi_in
2661         const int intersects
2662             = !(roib < ft_src || ft_src + fh_src < roiy || roir < fl_src || fl_src + fw_src < roix);
2663         if(intersects)
2664           rt_extend_roi_in_from_source_clones(self, piece, roi_in, formid, fl_src, ft_src, fw_src, fh_src, &roir,
2665                                               &roib, &roix, &roiy);
2666       }
2667     }
2668   }
2669 
2670   *_roir = roir;
2671   *_roib = roib;
2672   *_roix = roix;
2673   *_roiy = roiy;
2674 }
2675 
2676 // needed if mask dest is in roi and mask src is not
modify_roi_in(struct dt_iop_module_t * self,struct dt_dev_pixelpipe_iop_t * piece,const dt_iop_roi_t * roi_out,dt_iop_roi_t * roi_in)2677 void modify_roi_in(struct dt_iop_module_t *self, struct dt_dev_pixelpipe_iop_t *piece, const dt_iop_roi_t *roi_out,
2678                    dt_iop_roi_t *roi_in)
2679 {
2680   *roi_in = *roi_out;
2681 
2682   int roir = roi_in->width + roi_in->x;
2683   int roib = roi_in->height + roi_in->y;
2684   int roix = roi_in->x;
2685   int roiy = roi_in->y;
2686 
2687   rt_compute_roi_in(self, piece, roi_in, &roir, &roib, &roix, &roiy);
2688 
2689   int roir_prev = -1, roib_prev = -1, roix_prev = -1, roiy_prev = -1;
2690 
2691   while(roir != roir_prev || roib != roib_prev || roix != roix_prev || roiy != roiy_prev)
2692   {
2693     roir_prev = roir;
2694     roib_prev = roib;
2695     roix_prev = roix;
2696     roiy_prev = roiy;
2697 
2698     rt_extend_roi_in_for_clone(self, piece, roi_in, &roir, &roib, &roix, &roiy);
2699   }
2700 
2701   // now we set the values
2702   const float scwidth = piece->buf_in.width * roi_in->scale, scheight = piece->buf_in.height * roi_in->scale;
2703   roi_in->x = CLAMP(roix, 0, scwidth - 1);
2704   roi_in->y = CLAMP(roiy, 0, scheight - 1);
2705   roi_in->width = CLAMP(roir - roi_in->x, 1, scwidth + .5f - roi_in->x);
2706   roi_in->height = CLAMP(roib - roi_in->y, 1, scheight + .5f - roi_in->y);
2707 }
2708 
2709 //--------------------------------------------------------------------------------------------------
2710 // process
2711 //--------------------------------------------------------------------------------------------------
2712 
2713 #ifdef __SSE2__
2714 /** uses D50 white point. */
2715 // see http://www.brucelindbloom.com/Eqn_RGB_XYZ_Matrix.html for the transformation matrices
dt_XYZ_to_RGB_sse2(__m128 XYZ)2716 static inline __m128 dt_XYZ_to_RGB_sse2(__m128 XYZ)
2717 {
2718   // XYZ -> sRGB matrix, D65
2719   const __m128 xyz_to_srgb_0 = _mm_setr_ps(3.1338561f, -0.9787684f, 0.0719453f, 0.0f);
2720   const __m128 xyz_to_srgb_1 = _mm_setr_ps(-1.6168667f, 1.9161415f, -0.2289914f, 0.0f);
2721   const __m128 xyz_to_srgb_2 = _mm_setr_ps(-0.4906146f, 0.0334540f, 1.4052427f, 0.0f);
2722 
2723   __m128 rgb
2724       = _mm_add_ps(_mm_mul_ps(xyz_to_srgb_0, _mm_shuffle_ps(XYZ, XYZ, _MM_SHUFFLE(0, 0, 0, 0))),
2725                    _mm_add_ps(_mm_mul_ps(xyz_to_srgb_1, _mm_shuffle_ps(XYZ, XYZ, _MM_SHUFFLE(1, 1, 1, 1))),
2726                               _mm_mul_ps(xyz_to_srgb_2, _mm_shuffle_ps(XYZ, XYZ, _MM_SHUFFLE(2, 2, 2, 2)))));
2727 
2728   return rgb;
2729 }
2730 
dt_RGB_to_XYZ_sse2(__m128 rgb)2731 static inline __m128 dt_RGB_to_XYZ_sse2(__m128 rgb)
2732 {
2733   // sRGB -> XYZ matrix, D65
2734   const __m128 srgb_to_xyz_0 = _mm_setr_ps(0.4360747f, 0.2225045f, 0.0139322f, 0.0f);
2735   const __m128 srgb_to_xyz_1 = _mm_setr_ps(0.3850649f, 0.7168786f, 0.0971045f, 0.0f);
2736   const __m128 srgb_to_xyz_2 = _mm_setr_ps(0.1430804f, 0.0606169f, 0.7141733f, 0.0f);
2737 
2738   __m128 XYZ
2739       = _mm_add_ps(_mm_mul_ps(srgb_to_xyz_0, _mm_shuffle_ps(rgb, rgb, _MM_SHUFFLE(0, 0, 0, 0))),
2740                    _mm_add_ps(_mm_mul_ps(srgb_to_xyz_1, _mm_shuffle_ps(rgb, rgb, _MM_SHUFFLE(1, 1, 1, 1))),
2741                               _mm_mul_ps(srgb_to_xyz_2, _mm_shuffle_ps(rgb, rgb, _MM_SHUFFLE(2, 2, 2, 2)))));
2742   return XYZ;
2743 }
2744 #endif
2745 
dt_linearRGB_to_XYZ(const float * const linearRGB,float * XYZ)2746 static inline void dt_linearRGB_to_XYZ(const float *const linearRGB, float *XYZ)
2747 {
2748   const float srgb_to_xyz[3][3] = { { 0.4360747, 0.3850649, 0.1430804 },
2749                                     { 0.2225045, 0.7168786, 0.0606169 },
2750                                     { 0.0139322, 0.0971045, 0.7141733 } };
2751 
2752   // sRGB -> XYZ
2753   XYZ[0] = XYZ[1] = XYZ[2] = 0.0;
2754   for(int r = 0; r < 3; r++)
2755     for(int c = 0; c < 3; c++) XYZ[r] += srgb_to_xyz[r][c] * linearRGB[c];
2756 }
2757 
dt_XYZ_to_linearRGB(const float * const XYZ,float * linearRGB)2758 static inline void dt_XYZ_to_linearRGB(const float *const XYZ, float *linearRGB)
2759 {
2760   const float xyz_to_srgb_matrix[3][3] = { { 3.1338561, -1.6168667, -0.4906146 },
2761                                            { -0.9787684, 1.9161415, 0.0334540 },
2762                                            { 0.0719453, -0.2289914, 1.4052427 } };
2763 
2764   // XYZ -> sRGB
2765   linearRGB[0] = linearRGB[1] = linearRGB[2] = 0.f;
2766   for(int r = 0; r < 3; r++)
2767     for(int c = 0; c < 3; c++) linearRGB[r] += xyz_to_srgb_matrix[r][c] * XYZ[c];
2768 }
2769 
image_rgb2lab(float * img_src,const int width,const int height,const int ch,const int use_sse)2770 static void image_rgb2lab(float *img_src, const int width, const int height, const int ch, const int use_sse)
2771 {
2772   const int stride = width * height * ch;
2773 
2774 #if defined(__SSE__)
2775   if(ch == 4 && use_sse)
2776   {
2777 #ifdef _OPENMP
2778 #pragma omp parallel for default(none) \
2779     dt_omp_firstprivate(ch, stride) \
2780     shared(img_src) \
2781     schedule(static)
2782 #endif
2783     for(int i = 0; i < stride; i += ch)
2784     {
2785       // RGB -> XYZ
2786       __m128 rgb = _mm_load_ps(img_src + i);
2787       __m128 XYZ = dt_RGB_to_XYZ_sse2(rgb);
2788       // XYZ -> Lab
2789       _mm_store_ps(img_src + i, dt_XYZ_to_Lab_sse2(XYZ));
2790     }
2791 
2792     return;
2793   }
2794 #endif
2795 
2796 #ifdef _OPENMP
2797 #pragma omp parallel for default(none) \
2798   dt_omp_firstprivate(ch, stride) \
2799   shared(img_src) \
2800   schedule(static)
2801 #endif
2802   for(int i = 0; i < stride; i += ch)
2803   {
2804     float DT_ALIGNED_PIXEL XYZ[4];
2805 
2806     dt_linearRGB_to_XYZ(img_src + i, XYZ);
2807     dt_XYZ_to_Lab(XYZ, img_src + i);
2808   }
2809 }
2810 
image_lab2rgb(float * img_src,const int width,const int height,const int ch,const int use_sse)2811 static void image_lab2rgb(float *img_src, const int width, const int height, const int ch, const int use_sse)
2812 {
2813   const int stride = width * height * ch;
2814 
2815 #if defined(__SSE__)
2816   if(ch == 4 && use_sse)
2817   {
2818 #ifdef _OPENMP
2819 #pragma omp parallel for default(none) \
2820   dt_omp_firstprivate(ch, stride) \
2821   shared(img_src) \
2822   schedule(static)
2823 #endif
2824     for(int i = 0; i < stride; i += ch)
2825     {
2826       // Lab -> XYZ
2827       __m128 Lab = _mm_load_ps(img_src + i);
2828       __m128 XYZ = dt_Lab_to_XYZ_sse2(Lab);
2829       // XYZ -> RGB
2830       _mm_store_ps(img_src + i, dt_XYZ_to_RGB_sse2(XYZ));
2831     }
2832 
2833     return;
2834   }
2835 #endif
2836 
2837 #ifdef _OPENMP
2838 #pragma omp parallel for default(none) \
2839   dt_omp_firstprivate(ch, stride) \
2840   shared(img_src) \
2841   schedule(static)
2842 #endif
2843   for(int i = 0; i < stride; i += ch)
2844   {
2845     float DT_ALIGNED_PIXEL XYZ[4];
2846 
2847     dt_Lab_to_XYZ(img_src + i, XYZ);
2848     dt_XYZ_to_linearRGB(XYZ, img_src + i);
2849   }
2850 }
2851 
rt_process_stats(struct dt_iop_module_t * self,dt_dev_pixelpipe_iop_t * piece,float * const img_src,const int width,const int height,const int ch,float levels[3],int use_sse)2852 static void rt_process_stats(struct dt_iop_module_t *self, dt_dev_pixelpipe_iop_t *piece, float *const img_src,
2853                              const int width, const int height, const int ch, float levels[3], int use_sse)
2854 {
2855   const int size = width * height * ch;
2856   float l_max = -INFINITY;
2857   float l_min = INFINITY;
2858   float l_sum = 0.f;
2859   int count = 0;
2860   const dt_iop_order_iccprofile_info_t *const work_profile = dt_ioppr_get_pipe_work_profile_info(piece->pipe);
2861 
2862 #ifdef _OPENMP
2863 #pragma omp parallel for default(none) \
2864   dt_omp_firstprivate(ch, img_src, size, work_profile) \
2865   schedule(static) \
2866   reduction(+ : count, l_sum) \
2867   reduction(max : l_max) \
2868   reduction(min : l_min)
2869 #endif
2870   for(int i = 0; i < size; i += ch)
2871   {
2872     float Lab[3] = { 0 };
2873 
2874     if(work_profile)
2875     {
2876       dt_ioppr_rgb_matrix_to_lab(img_src + i, Lab, work_profile->matrix_in,
2877                                   work_profile->lut_in, work_profile->unbounded_coeffs_in,
2878                                   work_profile->lutsize, work_profile->nonlinearlut);
2879     }
2880     else
2881     {
2882       float DT_ALIGNED_PIXEL XYZ[4];
2883       dt_linearRGB_to_XYZ(img_src + i, XYZ);
2884       dt_XYZ_to_Lab(XYZ, Lab);
2885     }
2886 
2887     l_max = MAX(l_max, Lab[0]);
2888     l_min = MIN(l_min, Lab[0]);
2889     l_sum += Lab[0];
2890     count++;
2891   }
2892 
2893   levels[0] = l_min / 100.f;
2894   levels[2] = l_max / 100.f;
2895   levels[1] = (l_sum / (float)count) / 100.f;
2896 }
2897 
rt_adjust_levels(dt_iop_module_t * self,dt_dev_pixelpipe_iop_t * piece,float * img_src,const int width,const int height,const int ch,const float levels[3],int use_sse)2898 static void rt_adjust_levels(dt_iop_module_t *self, dt_dev_pixelpipe_iop_t *piece, float *img_src, const int width,
2899                              const int height, const int ch, const float levels[3], int use_sse)
2900 {
2901   const int size = width * height * ch;
2902   const dt_iop_order_iccprofile_info_t *const work_profile = dt_ioppr_get_pipe_work_profile_info(piece->pipe);
2903 
2904   const float left = levels[0];
2905   const float middle = levels[1];
2906   const float right = levels[2];
2907 
2908   if(left == RETOUCH_PREVIEW_LVL_MIN && middle == 0.f && right == RETOUCH_PREVIEW_LVL_MAX) return;
2909 
2910   const float delta = (right - left) / 2.0f;
2911   const float mid = left + delta;
2912   const float tmp = (middle - mid) / delta;
2913   const float in_inv_gamma = powf(10, tmp);
2914 
2915 #ifdef _OPENMP
2916 #pragma omp parallel for default(none) \
2917   dt_omp_firstprivate(ch, in_inv_gamma, left, right, size, work_profile) \
2918   shared(img_src) \
2919   schedule(static)
2920 #endif
2921   for(int i = 0; i < size; i += ch)
2922   {
2923     if(work_profile)
2924     {
2925       dt_ioppr_rgb_matrix_to_lab(img_src + i, img_src + i, work_profile->matrix_in,
2926                                   work_profile->lut_in, work_profile->unbounded_coeffs_in,
2927                                   work_profile->lutsize, work_profile->nonlinearlut);
2928     }
2929     else
2930     {
2931       float DT_ALIGNED_PIXEL XYZ[4];
2932 
2933       dt_linearRGB_to_XYZ(img_src + i, XYZ);
2934       dt_XYZ_to_Lab(XYZ, img_src + i);
2935     }
2936 
2937     for(int c = 0; c < 1; c++)
2938     {
2939       const float L_in = img_src[i + c] / 100.0f;
2940 
2941       if(L_in <= left)
2942       {
2943         img_src[i + c] = 0.f;
2944       }
2945       else
2946       {
2947         const float percentage = (L_in - left) / (right - left);
2948         img_src[i + c] = 100.0f * powf(percentage, in_inv_gamma);
2949       }
2950     }
2951 
2952     if(work_profile)
2953     {
2954       dt_ioppr_lab_to_rgb_matrix(img_src + i, img_src + i, work_profile->matrix_out,
2955                                  work_profile->lut_out, work_profile->unbounded_coeffs_out,
2956                                  work_profile->lutsize, work_profile->nonlinearlut);;
2957     }
2958     else
2959     {
2960       float DT_ALIGNED_PIXEL XYZ[4];
2961 
2962       dt_Lab_to_XYZ(img_src + i, XYZ);
2963       dt_XYZ_to_linearRGB(XYZ, img_src + i);
2964     }
2965   }
2966 }
2967 
2968 #undef RT_WDBAR_INSET
2969 
2970 #undef RETOUCH_NO_FORMS
2971 #undef RETOUCH_MAX_SCALES
2972 #undef RETOUCH_NO_SCALES
2973 
2974 #undef RETOUCH_PREVIEW_LVL_MIN
2975 #undef RETOUCH_PREVIEW_LVL_MAX
2976 
rt_intersect_2_rois(dt_iop_roi_t * const roi_1,dt_iop_roi_t * const roi_2,const int dx,const int dy,const int padding,dt_iop_roi_t * roi_dest)2977 static void rt_intersect_2_rois(dt_iop_roi_t *const roi_1, dt_iop_roi_t *const roi_2, const int dx, const int dy,
2978                                 const int padding, dt_iop_roi_t *roi_dest)
2979 {
2980   const int x_from = MAX(MAX((roi_1->x + 1 - padding), roi_2->x), (roi_2->x + dx));
2981   const int x_to
2982       = MIN(MIN((roi_1->x + roi_1->width + 1 + padding), roi_2->x + roi_2->width), (roi_2->x + roi_2->width + dx));
2983 
2984   const int y_from = MAX(MAX((roi_1->y + 1 - padding), roi_2->y), (roi_2->y + dy));
2985   const int y_to = MIN(MIN((roi_1->y + roi_1->height + 1 + padding), (roi_2->y + roi_2->height)),
2986                        (roi_2->y + roi_2->height + dy));
2987 
2988   roi_dest->x = x_from;
2989   roi_dest->y = y_from;
2990   roi_dest->width = x_to - x_from;
2991   roi_dest->height = y_to - y_from;
2992 }
2993 
rt_copy_in_to_out(const float * const in,const struct dt_iop_roi_t * const roi_in,float * const out,const struct dt_iop_roi_t * const roi_out,const int ch,const int dx,const int dy)2994 static void rt_copy_in_to_out(const float *const in, const struct dt_iop_roi_t *const roi_in, float *const out,
2995                               const struct dt_iop_roi_t *const roi_out, const int ch, const int dx, const int dy)
2996 {
2997   const size_t rowsize = sizeof(float) * ch * MIN(roi_out->width, roi_in->width);
2998   const int xoffs = roi_out->x - roi_in->x - dx;
2999   const int yoffs = roi_out->y - roi_in->y - dy;
3000   const int y_to = MIN(roi_out->height, roi_in->height);
3001 
3002 #ifdef _OPENMP
3003 #pragma omp parallel for default(none) \
3004   dt_omp_firstprivate(ch, in, out, roi_in, roi_out, rowsize, xoffs,  yoffs, \
3005                       y_to) \
3006   schedule(static)
3007 #endif
3008   for(int y = 0; y < y_to; y++)
3009   {
3010     const size_t iindex = ((size_t)(y + yoffs) * roi_in->width + xoffs) * ch;
3011     const size_t oindex = (size_t)y * roi_out->width * ch;
3012     float *in1 = (float *)in + iindex;
3013     float *out1 = (float *)out + oindex;
3014 
3015     memcpy(out1, in1, rowsize);
3016   }
3017 }
3018 
rt_build_scaled_mask(float * const mask,dt_iop_roi_t * const roi_mask,float ** mask_scaled,dt_iop_roi_t * roi_mask_scaled,dt_iop_roi_t * const roi_in,const int dx,const int dy,const int algo)3019 static void rt_build_scaled_mask(float *const mask, dt_iop_roi_t *const roi_mask, float **mask_scaled,
3020                                  dt_iop_roi_t *roi_mask_scaled, dt_iop_roi_t *const roi_in, const int dx,
3021                                  const int dy, const int algo)
3022 {
3023   float *mask_tmp = NULL;
3024 
3025   const int padding = (algo == DT_IOP_RETOUCH_HEAL) ? 1 : 0;
3026 
3027   *roi_mask_scaled = *roi_mask;
3028 
3029   roi_mask_scaled->x = roi_mask->x * roi_in->scale;
3030   roi_mask_scaled->y = roi_mask->y * roi_in->scale;
3031   roi_mask_scaled->width = ((roi_mask->width * roi_in->scale) + .5f);
3032   roi_mask_scaled->height = ((roi_mask->height * roi_in->scale) + .5f);
3033   roi_mask_scaled->scale = roi_in->scale;
3034 
3035   rt_intersect_2_rois(roi_mask_scaled, roi_in, dx, dy, padding, roi_mask_scaled);
3036   if(roi_mask_scaled->width < 1 || roi_mask_scaled->height < 1) goto cleanup;
3037 
3038   const int x_to = roi_mask_scaled->width + roi_mask_scaled->x;
3039   const int y_to = roi_mask_scaled->height + roi_mask_scaled->y;
3040 
3041   mask_tmp = dt_alloc_align_float((size_t)roi_mask_scaled->width * roi_mask_scaled->height);
3042   if(mask_tmp == NULL)
3043   {
3044     fprintf(stderr, "rt_build_scaled_mask: error allocating memory\n");
3045     goto cleanup;
3046   }
3047   dt_iop_image_fill(mask_tmp, 0.0f, roi_mask_scaled->width, roi_mask_scaled->height, 1);
3048 
3049 #ifdef _OPENMP
3050 #pragma omp parallel for default(none) \
3051   dt_omp_firstprivate(mask, roi_in, roi_mask, x_to, y_to) \
3052   shared(mask_tmp, roi_mask_scaled) \
3053   schedule(static)
3054 #endif
3055   for(int yy = roi_mask_scaled->y; yy < y_to; yy++)
3056   {
3057     const int mask_index = ((int)(yy / roi_in->scale)) - roi_mask->y;
3058     if(mask_index < 0 || mask_index >= roi_mask->height) continue;
3059 
3060     const int mask_scaled_index = (yy - roi_mask_scaled->y) * roi_mask_scaled->width;
3061 
3062     const float *m = mask + mask_index * roi_mask->width;
3063     float *ms = mask_tmp + mask_scaled_index;
3064 
3065     for(int xx = roi_mask_scaled->x; xx < x_to; xx++, ms++)
3066     {
3067       const int mx = ((int)(xx / roi_in->scale)) - roi_mask->x;
3068       if(mx < 0 || mx >= roi_mask->width) continue;
3069 
3070       *ms = m[mx];
3071     }
3072   }
3073 
3074 cleanup:
3075   *mask_scaled = mask_tmp;
3076 }
3077 
3078 // img_src and mask_scaled must have the same roi
rt_copy_image_masked(float * const img_src,float * img_dest,dt_iop_roi_t * const roi_dest,const int ch,float * const mask_scaled,dt_iop_roi_t * const roi_mask_scaled,const float opacity,const int use_sse)3079 static void rt_copy_image_masked(float *const img_src, float *img_dest, dt_iop_roi_t *const roi_dest, const int ch,
3080                                  float *const mask_scaled, dt_iop_roi_t *const roi_mask_scaled,
3081                                  const float opacity, const int use_sse)
3082 {
3083 #if defined(__SSE__)
3084   if(ch == 4 && use_sse)
3085   {
3086 #ifdef _OPENMP
3087 #pragma omp parallel for default(none) \
3088     dt_omp_firstprivate(ch, img_src, mask_scaled, opacity, roi_dest, roi_mask_scaled) \
3089     shared(img_dest) \
3090     schedule(static)
3091 #endif
3092     for(int yy = 0; yy < roi_mask_scaled->height; yy++)
3093     {
3094       const int mask_index = yy * roi_mask_scaled->width;
3095       const int src_index = mask_index * ch;
3096       const int dest_index
3097           = (((yy + roi_mask_scaled->y - roi_dest->y) * roi_dest->width) + (roi_mask_scaled->x - roi_dest->x))
3098             * ch;
3099 
3100       const float *s = img_src + src_index;
3101       const float *m = mask_scaled + mask_index;
3102       float *d = img_dest + dest_index;
3103 
3104       for(int xx = 0; xx < roi_mask_scaled->width; xx++, s += ch, d += ch, m++)
3105       {
3106         const float f = (*m) * opacity;
3107 
3108         const __m128 val1_f = _mm_set1_ps(1.0f - f);
3109         const __m128 valf = _mm_set1_ps(f);
3110 
3111         _mm_store_ps(d, _mm_add_ps(_mm_mul_ps(_mm_load_ps(d), val1_f), _mm_mul_ps(_mm_load_ps(s), valf)));
3112       }
3113     }
3114   }
3115   else
3116 #endif
3117   {
3118     const int ch1 = (ch == 4) ? ch - 1 : ch;
3119 
3120 #ifdef _OPENMP
3121 #pragma omp parallel for default(none) \
3122     dt_omp_firstprivate(ch, ch1, img_src, mask_scaled, opacity, roi_dest, roi_mask_scaled) \
3123     shared(img_dest) \
3124     schedule(static)
3125 #endif
3126     for(int yy = 0; yy < roi_mask_scaled->height; yy++)
3127     {
3128       const int mask_index = yy * roi_mask_scaled->width;
3129       const int src_index = mask_index * ch;
3130       const int dest_index
3131           = (((yy + roi_mask_scaled->y - roi_dest->y) * roi_dest->width) + (roi_mask_scaled->x - roi_dest->x))
3132             * ch;
3133 
3134       const float *s = img_src + src_index;
3135       const float *m = mask_scaled + mask_index;
3136       float *d = img_dest + dest_index;
3137 
3138       for(int xx = 0; xx < roi_mask_scaled->width; xx++, s += ch, d += ch, m++)
3139       {
3140         const float f = (*m) * opacity;
3141 
3142         for(int c = 0; c < ch1; c++)
3143         {
3144           d[c] = d[c] * (1.0f - f) + s[c] * f;
3145         }
3146       }
3147     }
3148   }
3149 }
3150 
rt_copy_mask_to_alpha(float * const img,dt_iop_roi_t * const roi_img,const int ch,float * const mask_scaled,dt_iop_roi_t * const roi_mask_scaled,const float opacity)3151 static void rt_copy_mask_to_alpha(float *const img, dt_iop_roi_t *const roi_img, const int ch,
3152                                   float *const mask_scaled, dt_iop_roi_t *const roi_mask_scaled,
3153                                   const float opacity)
3154 {
3155 #ifdef _OPENMP
3156 #pragma omp parallel for default(none) \
3157   dt_omp_firstprivate(ch, img, mask_scaled, opacity, roi_img, roi_mask_scaled) \
3158   schedule(static)
3159 #endif
3160   for(int yy = 0; yy < roi_mask_scaled->height; yy++)
3161   {
3162     const int mask_index = yy * roi_mask_scaled->width;
3163     const int dest_index
3164         = (((yy + roi_mask_scaled->y - roi_img->y) * roi_img->width) + (roi_mask_scaled->x - roi_img->x)) * ch;
3165 
3166     float *d = img + dest_index;
3167     const float *m = mask_scaled + mask_index;
3168 
3169     for(int xx = 0; xx < roi_mask_scaled->width; xx++, d += ch, m++)
3170     {
3171       const float f = (*m) * opacity;
3172       if(f > d[3]) d[3] = f;
3173     }
3174   }
3175 }
3176 
3177 #if defined(__SSE__)
retouch_fill_sse(float * const in,dt_iop_roi_t * const roi_in,float * const mask_scaled,dt_iop_roi_t * const roi_mask_scaled,const float opacity,const float * const fill_color)3178 static void retouch_fill_sse(float *const in, dt_iop_roi_t *const roi_in, float *const mask_scaled,
3179                              dt_iop_roi_t *const roi_mask_scaled, const float opacity,
3180                              const float *const fill_color)
3181 {
3182   const int ch = 4;
3183 
3184   const float valf4_fill[4] = { fill_color[0], fill_color[1], fill_color[2], 0.f };
3185   const __m128 val_fill = _mm_load_ps(valf4_fill);
3186 
3187 #ifdef _OPENMP
3188 #pragma omp parallel for default(none) \
3189   dt_omp_firstprivate(ch, in, mask_scaled, opacity, roi_in, roi_mask_scaled, val_fill) \
3190   schedule(static)
3191 #endif
3192   for(int yy = 0; yy < roi_mask_scaled->height; yy++)
3193   {
3194     const int mask_index = yy * roi_mask_scaled->width;
3195     const int dest_index
3196         = (((yy + roi_mask_scaled->y - roi_in->y) * roi_in->width) + (roi_mask_scaled->x - roi_in->x)) * ch;
3197 
3198     float *d = in + dest_index;
3199     const float *m = mask_scaled + mask_index;
3200 
3201     for(int xx = 0; xx < roi_mask_scaled->width; xx++, d += ch, m++)
3202     {
3203       const float f = (*m) * opacity;
3204 
3205       const __m128 val1_f = _mm_set1_ps(1.0f - f);
3206       const __m128 valf = _mm_set1_ps(f);
3207 
3208       _mm_store_ps(d, _mm_add_ps(_mm_mul_ps(_mm_load_ps(d), val1_f), _mm_mul_ps(val_fill, valf)));
3209     }
3210   }
3211 }
3212 #endif
3213 
retouch_fill(float * const in,dt_iop_roi_t * const roi_in,const int ch,float * const mask_scaled,dt_iop_roi_t * const roi_mask_scaled,const float opacity,const float * const fill_color,const int use_sse)3214 static void retouch_fill(float *const in, dt_iop_roi_t *const roi_in, const int ch, float *const mask_scaled,
3215                          dt_iop_roi_t *const roi_mask_scaled, const float opacity, const float *const fill_color,
3216                          const int use_sse)
3217 {
3218 #if defined(__SSE__)
3219   if(ch == 4 && use_sse)
3220   {
3221     retouch_fill_sse(in, roi_in, mask_scaled, roi_mask_scaled, opacity, fill_color);
3222     return;
3223   }
3224 #endif
3225   const int ch1 = (ch == 4) ? ch - 1 : ch;
3226 
3227 #ifdef _OPENMP
3228 #pragma omp parallel for default(none) \
3229   dt_omp_firstprivate(ch, ch1, fill_color, in, mask_scaled, opacity, roi_in, roi_mask_scaled) \
3230   schedule(static)
3231 #endif
3232   for(int yy = 0; yy < roi_mask_scaled->height; yy++)
3233   {
3234     const int mask_index = yy * roi_mask_scaled->width;
3235     const int dest_index
3236         = (((yy + roi_mask_scaled->y - roi_in->y) * roi_in->width) + (roi_mask_scaled->x - roi_in->x)) * ch;
3237 
3238     float *d = in + dest_index;
3239     const float *m = mask_scaled + mask_index;
3240 
3241     for(int xx = 0; xx < roi_mask_scaled->width; xx++, d += ch, m++)
3242     {
3243       const float f = (*m) * opacity;
3244 
3245       for(int c = 0; c < ch1; c++) d[c] = d[c] * (1.0f - f) + fill_color[c] * f;
3246     }
3247   }
3248 }
3249 
retouch_clone(float * const in,dt_iop_roi_t * const roi_in,const int ch,float * const mask_scaled,dt_iop_roi_t * const roi_mask_scaled,const int dx,const int dy,const float opacity,const int use_sse)3250 static void retouch_clone(float *const in, dt_iop_roi_t *const roi_in, const int ch, float *const mask_scaled,
3251                           dt_iop_roi_t *const roi_mask_scaled, const int dx, const int dy, const float opacity,
3252                           const int use_sse)
3253 {
3254   // alloc temp image to avoid issues when areas self-intersects
3255   float *img_src = dt_alloc_align_float((size_t)ch * roi_mask_scaled->width * roi_mask_scaled->height);
3256   if(img_src == NULL)
3257   {
3258     fprintf(stderr, "retouch_clone: error allocating memory for cloning\n");
3259     goto cleanup;
3260   }
3261 
3262   // copy source image to tmp
3263   rt_copy_in_to_out(in, roi_in, img_src, roi_mask_scaled, ch, dx, dy);
3264 
3265   // clone it
3266   rt_copy_image_masked(img_src, in, roi_in, ch, mask_scaled, roi_mask_scaled, opacity, use_sse);
3267 
3268 cleanup:
3269   if(img_src) dt_free_align(img_src);
3270 }
3271 
retouch_blur(dt_iop_module_t * self,float * const in,dt_iop_roi_t * const roi_in,const int ch,float * const mask_scaled,dt_iop_roi_t * const roi_mask_scaled,const float opacity,const int blur_type,const float blur_radius,dt_dev_pixelpipe_iop_t * piece,const int use_sse)3272 static void retouch_blur(dt_iop_module_t *self, float *const in, dt_iop_roi_t *const roi_in, const int ch, float *const mask_scaled,
3273                          dt_iop_roi_t *const roi_mask_scaled, const float opacity, const int blur_type,
3274                          const float blur_radius, dt_dev_pixelpipe_iop_t *piece, const int use_sse)
3275 {
3276   if(fabsf(blur_radius) <= 0.1f) return;
3277 
3278   const float sigma = blur_radius * roi_in->scale / piece->iscale;
3279 
3280   float *img_dest = NULL;
3281 
3282   // alloc temp image to blur
3283   img_dest = dt_alloc_align_float((size_t)ch * roi_mask_scaled->width * roi_mask_scaled->height);
3284   if(img_dest == NULL)
3285   {
3286     fprintf(stderr, "retouch_blur: error allocating memory for blurring\n");
3287     goto cleanup;
3288   }
3289 
3290   // copy source image so we blur just the mask area (at least the smallest rect that covers it)
3291   rt_copy_in_to_out(in, roi_in, img_dest, roi_mask_scaled, ch, 0, 0);
3292 
3293   if(blur_type == DT_IOP_RETOUCH_BLUR_GAUSSIAN && fabsf(blur_radius) > 0.1f)
3294   {
3295     float Labmax[] = { INFINITY, INFINITY, INFINITY, INFINITY };
3296     float Labmin[] = { -INFINITY, -INFINITY, -INFINITY, -INFINITY };
3297 
3298     dt_gaussian_t *g = dt_gaussian_init(roi_mask_scaled->width, roi_mask_scaled->height, ch, Labmax, Labmin, sigma,
3299                                         DT_IOP_GAUSSIAN_ZERO);
3300     if(g)
3301     {
3302       if(ch == 4)
3303         dt_gaussian_blur_4c(g, img_dest, img_dest);
3304       else
3305         dt_gaussian_blur(g, img_dest, img_dest);
3306       dt_gaussian_free(g);
3307     }
3308   }
3309   else if(blur_type == DT_IOP_RETOUCH_BLUR_BILATERAL && fabsf(blur_radius) > 0.1f)
3310   {
3311     const float sigma_r = 100.0f; // does not depend on scale
3312     const float sigma_s = sigma;
3313     const float detail = -1.0f; // we want the bilateral base layer
3314 
3315     dt_bilateral_t *b = dt_bilateral_init(roi_mask_scaled->width, roi_mask_scaled->height, sigma_s, sigma_r);
3316     if(b)
3317     {
3318       int converted_cst;
3319       const dt_iop_order_iccprofile_info_t *const work_profile = dt_ioppr_get_pipe_work_profile_info(piece->pipe);
3320 
3321       if(work_profile)
3322         dt_ioppr_transform_image_colorspace(self, img_dest, img_dest, roi_mask_scaled->width,
3323                                             roi_mask_scaled->height, iop_cs_rgb, iop_cs_Lab, &converted_cst,
3324                                             work_profile);
3325       else
3326         image_rgb2lab(img_dest, roi_mask_scaled->width, roi_mask_scaled->height, ch, use_sse);
3327 
3328       dt_bilateral_splat(b, img_dest);
3329       dt_bilateral_blur(b);
3330       dt_bilateral_slice(b, img_dest, img_dest, detail);
3331       dt_bilateral_free(b);
3332 
3333       if(work_profile)
3334         dt_ioppr_transform_image_colorspace(self, img_dest, img_dest, roi_mask_scaled->width,
3335                                             roi_mask_scaled->height, iop_cs_Lab, iop_cs_rgb, &converted_cst,
3336                                             work_profile);
3337       else
3338         image_lab2rgb(img_dest, roi_mask_scaled->width, roi_mask_scaled->height, ch, use_sse);
3339     }
3340   }
3341 
3342   // copy blurred (temp) image to destination image
3343   rt_copy_image_masked(img_dest, in, roi_in, ch, mask_scaled, roi_mask_scaled, opacity, use_sse);
3344 
3345 cleanup:
3346   if(img_dest) dt_free_align(img_dest);
3347 }
3348 
retouch_heal(float * const in,dt_iop_roi_t * const roi_in,const int ch,float * const mask_scaled,dt_iop_roi_t * const roi_mask_scaled,const int dx,const int dy,const float opacity,int use_sse)3349 static void retouch_heal(float *const in, dt_iop_roi_t *const roi_in, const int ch, float *const mask_scaled,
3350                          dt_iop_roi_t *const roi_mask_scaled, const int dx, const int dy, const float opacity,
3351                          int use_sse)
3352 {
3353   float *img_src = NULL;
3354   float *img_dest = NULL;
3355 
3356   // alloc temp images for source and destination
3357   img_src  = dt_alloc_align_float((size_t)ch * roi_mask_scaled->width * roi_mask_scaled->height);
3358   img_dest = dt_alloc_align_float((size_t)ch * roi_mask_scaled->width * roi_mask_scaled->height);
3359   if((img_src == NULL) || (img_dest == NULL))
3360   {
3361     fprintf(stderr, "retouch_heal: error allocating memory for healing\n");
3362     goto cleanup;
3363   }
3364 
3365   // copy source and destination to temp images
3366   rt_copy_in_to_out(in, roi_in, img_src, roi_mask_scaled, ch, dx, dy);
3367   rt_copy_in_to_out(in, roi_in, img_dest, roi_mask_scaled, ch, 0, 0);
3368 
3369   // heal it
3370   dt_heal(img_src, img_dest, mask_scaled, roi_mask_scaled->width, roi_mask_scaled->height, ch, use_sse);
3371 
3372   // copy healed (temp) image to destination image
3373   rt_copy_image_masked(img_dest, in, roi_in, ch, mask_scaled, roi_mask_scaled, opacity, use_sse);
3374 
3375 cleanup:
3376   if(img_src) dt_free_align(img_src);
3377   if(img_dest) dt_free_align(img_dest);
3378 }
3379 
rt_process_forms(float * layer,dwt_params_t * const wt_p,const int scale1)3380 static void rt_process_forms(float *layer, dwt_params_t *const wt_p, const int scale1)
3381 {
3382   int scale = scale1;
3383   retouch_user_data_t *usr_d = (retouch_user_data_t *)wt_p->user_data;
3384   dt_iop_module_t *self = usr_d->self;
3385   dt_dev_pixelpipe_iop_t *piece = usr_d->piece;
3386 
3387   // if preview a single scale, just process that scale and original image
3388   // unless merge is activated
3389   if(wt_p->merge_from_scale == 0 && wt_p->return_layer > 0 && scale != wt_p->return_layer && scale != 0) return;
3390   // do not process the reconstructed image
3391   if(scale > wt_p->scales + 1) return;
3392 
3393   dt_develop_blend_params_t *bp = (dt_develop_blend_params_t *)piece->blendop_data;
3394   dt_iop_retouch_params_t *p = (dt_iop_retouch_params_t *)piece->data;
3395   dt_iop_roi_t *roi_layer = &usr_d->roi;
3396   const int mask_display = usr_d->mask_display && (scale == usr_d->display_scale);
3397 
3398   // when the requested scales is grather than max scales the residual image index will be different from the one
3399   // defined by the user,
3400   // so we need to adjust it here, otherwise we will be using the shapes from a scale on the residual image
3401   if(wt_p->scales < p->num_scales && wt_p->return_layer == 0 && scale == wt_p->scales + 1)
3402   {
3403     scale = p->num_scales + 1;
3404   }
3405 
3406   // iterate through all forms
3407   if(!usr_d->suppress_mask)
3408   {
3409     const dt_masks_form_t *grp = dt_masks_get_from_id_ext(piece->pipe->forms, bp->mask_id);
3410     if(grp && (grp->type & DT_MASKS_GROUP))
3411     {
3412       for(const GList *forms = grp->points; forms; forms = g_list_next(forms))
3413       {
3414         const dt_masks_point_group_t *grpt = (dt_masks_point_group_t *)forms->data;
3415         if(grpt == NULL)
3416         {
3417           fprintf(stderr, "rt_process_forms: invalid form\n");
3418           continue;
3419         }
3420         const int formid = grpt->formid;
3421         const float form_opacity = grpt->opacity;
3422         if(formid == 0)
3423         {
3424           fprintf(stderr, "rt_process_forms: form is null\n");
3425           continue;
3426         }
3427         const int index = rt_get_index_from_formid(p, formid);
3428         if(index == -1)
3429         {
3430           // FIXME: we get this error when user go back in history, so forms are the same but the array has changed
3431           fprintf(stderr, "rt_process_forms: missing form=%i from array\n", formid);
3432           continue;
3433         }
3434 
3435         // only process current scale
3436         if(p->rt_forms[index].scale != scale)
3437         {
3438           continue;
3439         }
3440 
3441         // get the spot
3442         dt_masks_form_t *form = dt_masks_get_from_id_ext(piece->pipe->forms, formid);
3443         if(form == NULL)
3444         {
3445           fprintf(stderr, "rt_process_forms: missing form=%i from masks\n", formid);
3446           continue;
3447         }
3448 
3449         // if the form is outside the roi, we just skip it
3450         if(!rt_masks_form_is_in_roi(self, piece, form, roi_layer, roi_layer))
3451         {
3452           continue;
3453         }
3454 
3455         // get the mask
3456         float *mask = NULL;
3457         dt_iop_roi_t roi_mask = { 0 };
3458 
3459         dt_masks_get_mask(self, piece, form, &mask, &roi_mask.width, &roi_mask.height, &roi_mask.x, &roi_mask.y);
3460         if(mask == NULL)
3461         {
3462           fprintf(stderr, "rt_process_forms: error retrieving mask\n");
3463           continue;
3464         }
3465 
3466         // search the delta with the source
3467         const dt_iop_retouch_algo_type_t algo = p->rt_forms[index].algorithm;
3468         int dx = 0, dy = 0;
3469 
3470         if(algo != DT_IOP_RETOUCH_BLUR && algo != DT_IOP_RETOUCH_FILL)
3471         {
3472           if(!rt_masks_get_delta_to_destination(self, piece, roi_layer, form, &dx, &dy,
3473                                                 p->rt_forms[index].distort_mode))
3474           {
3475             if(mask) dt_free_align(mask);
3476             continue;
3477           }
3478         }
3479 
3480         // scale the mask
3481         float *mask_scaled = NULL;
3482         dt_iop_roi_t roi_mask_scaled = { 0 };
3483 
3484         rt_build_scaled_mask(mask, &roi_mask, &mask_scaled, &roi_mask_scaled, roi_layer, dx, dy, algo);
3485 
3486         // we don't need the original mask anymore
3487         if(mask)
3488         {
3489           dt_free_align(mask);
3490           mask = NULL;
3491         }
3492 
3493         if(mask_scaled == NULL)
3494         {
3495           continue;
3496         }
3497 
3498         if((dx != 0 || dy != 0 || algo == DT_IOP_RETOUCH_BLUR || algo == DT_IOP_RETOUCH_FILL)
3499            && ((roi_mask_scaled.width > 2) && (roi_mask_scaled.height > 2)))
3500         {
3501           if(algo == DT_IOP_RETOUCH_CLONE)
3502           {
3503             retouch_clone(layer, roi_layer, wt_p->ch, mask_scaled, &roi_mask_scaled, dx, dy, form_opacity,
3504                           wt_p->use_sse);
3505           }
3506           else if(algo == DT_IOP_RETOUCH_HEAL)
3507           {
3508             retouch_heal(layer, roi_layer, wt_p->ch, mask_scaled, &roi_mask_scaled, dx, dy, form_opacity,
3509                          wt_p->use_sse);
3510           }
3511           else if(algo == DT_IOP_RETOUCH_BLUR)
3512           {
3513             retouch_blur(self, layer, roi_layer, wt_p->ch, mask_scaled, &roi_mask_scaled, form_opacity,
3514                          p->rt_forms[index].blur_type, p->rt_forms[index].blur_radius, piece, wt_p->use_sse);
3515           }
3516           else if(algo == DT_IOP_RETOUCH_FILL)
3517           {
3518             // add a brightness to the color so it can be fine-adjusted by the user
3519             float fill_color[3];
3520 
3521             if(p->rt_forms[index].fill_mode == DT_IOP_RETOUCH_FILL_ERASE)
3522             {
3523               fill_color[0] = fill_color[1] = fill_color[2] = p->rt_forms[index].fill_brightness;
3524             }
3525             else
3526             {
3527               fill_color[0] = p->rt_forms[index].fill_color[0] + p->rt_forms[index].fill_brightness;
3528               fill_color[1] = p->rt_forms[index].fill_color[1] + p->rt_forms[index].fill_brightness;
3529               fill_color[2] = p->rt_forms[index].fill_color[2] + p->rt_forms[index].fill_brightness;
3530             }
3531 
3532             retouch_fill(layer, roi_layer, wt_p->ch, mask_scaled, &roi_mask_scaled, form_opacity, fill_color,
3533                          wt_p->use_sse);
3534           }
3535           else
3536             fprintf(stderr, "rt_process_forms: unknown algorithm %i\n", algo);
3537 
3538           if(mask_display)
3539             rt_copy_mask_to_alpha(layer, roi_layer, wt_p->ch, mask_scaled, &roi_mask_scaled, form_opacity);
3540         }
3541 
3542         if(mask) dt_free_align(mask);
3543         if(mask_scaled) dt_free_align(mask_scaled);
3544       }
3545     }
3546   }
3547 }
3548 
process_internal(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,const int use_sse)3549 static void process_internal(struct dt_iop_module_t *self, dt_dev_pixelpipe_iop_t *piece, const void *const ivoid,
3550                              void *const ovoid, const dt_iop_roi_t *const roi_in,
3551                              const dt_iop_roi_t *const roi_out, const int use_sse)
3552 {
3553   dt_iop_retouch_params_t *p = (dt_iop_retouch_params_t *)piece->data;
3554   dt_iop_retouch_gui_data_t *g = (dt_iop_retouch_gui_data_t *)self->gui_data;
3555 
3556   float *in_retouch = NULL;
3557 
3558   dt_iop_roi_t roi_retouch = *roi_in;
3559   dt_iop_roi_t *roi_rt = &roi_retouch;
3560 
3561   const int ch = piece->colors;
3562   retouch_user_data_t usr_data = { 0 };
3563   dwt_params_t *dwt_p = NULL;
3564 
3565   const int gui_active = (self->dev) ? (self == self->dev->gui_module) : 0;
3566   const int display_wavelet_scale = (g && gui_active) ? g->display_wavelet_scale : 0;
3567 
3568   // we will do all the clone, heal, etc on the input image,
3569   // this way the source for one algorithm can be the destination from a previous one
3570   in_retouch = dt_alloc_align_float((size_t)ch * roi_rt->width * roi_rt->height);
3571   if(in_retouch == NULL) goto cleanup;
3572 
3573   dt_iop_image_copy_by_size(in_retouch, ivoid, roi_rt->width, roi_rt->height, ch);
3574 
3575   // user data passed from the decompose routine to the one that process each scale
3576   usr_data.self = self;
3577   usr_data.piece = piece;
3578   usr_data.roi = *roi_rt;
3579   usr_data.mask_display = 0;
3580   usr_data.suppress_mask = (g && g->suppress_mask && self->dev->gui_attached && (self == self->dev->gui_module)
3581                             && (piece->pipe == self->dev->pipe));
3582   usr_data.display_scale = p->curr_scale;
3583 
3584   // init the decompose routine
3585   dwt_p = dt_dwt_init(in_retouch, roi_rt->width, roi_rt->height, ch, p->num_scales,
3586                       (!display_wavelet_scale || (piece->pipe->type & DT_DEV_PIXELPIPE_FULL) != DT_DEV_PIXELPIPE_FULL) ? 0 : p->curr_scale,
3587                       p->merge_from_scale, &usr_data,
3588                       roi_in->scale / piece->iscale, use_sse);
3589   if(dwt_p == NULL) goto cleanup;
3590 
3591   // check if this module should expose mask.
3592   if((piece->pipe->type & DT_DEV_PIXELPIPE_FULL) == DT_DEV_PIXELPIPE_FULL && g
3593      && (g->mask_display || display_wavelet_scale) && self->dev->gui_attached
3594      && (self == self->dev->gui_module) && (piece->pipe == self->dev->pipe))
3595   {
3596     for(size_t j = 0; j < (size_t)roi_rt->width * roi_rt->height * ch; j += ch) in_retouch[j + 3] = 0.f;
3597 
3598     piece->pipe->mask_display = g->mask_display ? DT_DEV_PIXELPIPE_DISPLAY_MASK : DT_DEV_PIXELPIPE_DISPLAY_PASSTHRU;
3599     piece->pipe->bypass_blendif = 1;
3600     usr_data.mask_display = 1;
3601   }
3602 
3603   if((piece->pipe->type & DT_DEV_PIXELPIPE_FULL) == DT_DEV_PIXELPIPE_FULL)
3604   {
3605     // check if the image support this number of scales
3606     if(gui_active)
3607     {
3608       const int max_scales = dwt_get_max_scale(dwt_p);
3609       if(dwt_p->scales > max_scales)
3610       {
3611         dt_control_log(_("max scale is %i for this image size"), max_scales);
3612       }
3613     }
3614     // get first scale visible at this zoom level
3615     if(g) g->first_scale_visible = dt_dwt_first_scale_visible(dwt_p);
3616   }
3617 
3618   // decompose it
3619   dwt_decompose(dwt_p, rt_process_forms);
3620 
3621   float levels[3] = { 0.f };
3622   levels[0] = p->preview_levels[0];
3623   levels[1] = p->preview_levels[1];
3624   levels[2] = p->preview_levels[2];
3625 
3626   // process auto levels
3627   if(g && (piece->pipe->type & DT_DEV_PIXELPIPE_FULL) == DT_DEV_PIXELPIPE_FULL)
3628   {
3629     dt_iop_gui_enter_critical_section(self);
3630     if(g->preview_auto_levels == 1 && !darktable.gui->reset)
3631     {
3632       g->preview_auto_levels = -1;
3633 
3634       dt_iop_gui_leave_critical_section(self);
3635 
3636       levels[0] = levels[1] = levels[2] = 0;
3637       rt_process_stats(self, piece, in_retouch, roi_rt->width, roi_rt->height, ch, levels, use_sse);
3638       rt_clamp_minmax(levels, levels);
3639 
3640       for(int i = 0; i < 3; i++) g->preview_levels[i] = levels[i];
3641 
3642       dt_iop_gui_enter_critical_section(self);
3643       g->preview_auto_levels = 2;
3644       dt_iop_gui_leave_critical_section(self);
3645     }
3646     else
3647     {
3648       dt_iop_gui_leave_critical_section(self);
3649     }
3650   }
3651 
3652   // if user wants to preview a detail scale adjust levels
3653   if(dwt_p->return_layer > 0 && dwt_p->return_layer < dwt_p->scales + 1)
3654   {
3655     rt_adjust_levels(self, piece, in_retouch, roi_rt->width, roi_rt->height, ch, levels, use_sse);
3656   }
3657 
3658   // copy alpha channel if needed
3659   if((piece->pipe->mask_display & DT_DEV_PIXELPIPE_DISPLAY_MASK) && g && !g->mask_display)
3660   {
3661     dt_iop_alpha_copy(ivoid, in_retouch, roi_rt->width, roi_rt->height);
3662   }
3663 
3664   // return final image
3665   rt_copy_in_to_out(in_retouch, roi_rt, ovoid, roi_out, ch, 0, 0);
3666 
3667 cleanup:
3668   if(in_retouch) dt_free_align(in_retouch);
3669   if(dwt_p) dt_dwt_free(dwt_p);
3670 }
3671 
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)3672 void process(struct dt_iop_module_t *self, dt_dev_pixelpipe_iop_t *piece, const void *const ivoid,
3673              void *const ovoid, const dt_iop_roi_t *const roi_in, const dt_iop_roi_t *const roi_out)
3674 {
3675   process_internal(self, piece, ivoid, ovoid, roi_in, roi_out, 0);
3676 }
3677 
3678 #if defined(__SSE__)
process_sse2(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)3679 void process_sse2(struct dt_iop_module_t *self, dt_dev_pixelpipe_iop_t *piece, const void *const ivoid,
3680                   void *const ovoid, const dt_iop_roi_t *const roi_in, const dt_iop_roi_t *const roi_out)
3681 {
3682   process_internal(self, piece, ivoid, ovoid, roi_in, roi_out, 1);
3683 }
3684 #endif
3685 
distort_mask(struct dt_iop_module_t * self,struct dt_dev_pixelpipe_iop_t * piece,const float * const in,float * const out,const dt_iop_roi_t * const roi_in,const dt_iop_roi_t * const roi_out)3686 void distort_mask(struct dt_iop_module_t *self, struct dt_dev_pixelpipe_iop_t *piece, const float *const in,
3687                   float *const out, const dt_iop_roi_t *const roi_in, const dt_iop_roi_t *const roi_out)
3688 {
3689   rt_copy_in_to_out(in, roi_in, out, roi_out, 1, 0, 0);
3690 }
3691 
3692 #ifdef HAVE_OPENCL
3693 
rt_process_stats_cl(struct dt_iop_module_t * self,dt_dev_pixelpipe_iop_t * piece,const int devid,cl_mem dev_img,const int width,const int height,float levels[3])3694 cl_int rt_process_stats_cl(struct dt_iop_module_t *self, dt_dev_pixelpipe_iop_t *piece, const int devid, cl_mem dev_img,
3695                            const int width, const int height, float levels[3])
3696 {
3697   cl_int err = CL_SUCCESS;
3698 
3699   const int ch = 4;
3700 
3701   float *src_buffer = NULL;
3702 
3703   src_buffer = dt_alloc_align_float((size_t)ch * width * height);
3704   if(src_buffer == NULL)
3705   {
3706     fprintf(stderr, "dt_heal_cl: error allocating memory for healing\n");
3707     err = CL_MEM_OBJECT_ALLOCATION_FAILURE;
3708     goto cleanup;
3709   }
3710 
3711   err = dt_opencl_read_buffer_from_device(devid, (void *)src_buffer, dev_img, 0,
3712                                           (size_t)width * height * ch * sizeof(float), CL_TRUE);
3713   if(err != CL_SUCCESS)
3714   {
3715     goto cleanup;
3716   }
3717 
3718   // just call the CPU version for now
3719   rt_process_stats(self, piece, src_buffer, width, height, ch, levels, 1);
3720 
3721   err = dt_opencl_write_buffer_to_device(devid, src_buffer, dev_img, 0, sizeof(float) * ch * width * height, TRUE);
3722   if(err != CL_SUCCESS)
3723   {
3724     goto cleanup;
3725   }
3726 
3727 cleanup:
3728   if(src_buffer) dt_free_align(src_buffer);
3729 
3730   return err;
3731 }
3732 
rt_adjust_levels_cl(struct dt_iop_module_t * self,dt_dev_pixelpipe_iop_t * piece,const int devid,cl_mem dev_img,const int width,const int height,const float levels[3])3733 cl_int rt_adjust_levels_cl(struct dt_iop_module_t *self, dt_dev_pixelpipe_iop_t *piece, const int devid, cl_mem dev_img,
3734                            const int width, const int height, const float levels[3])
3735 {
3736   cl_int err = CL_SUCCESS;
3737 
3738   const int ch = 4;
3739 
3740   float *src_buffer = NULL;
3741 
3742   src_buffer = dt_alloc_align_float((size_t)ch * width * height);
3743   if(src_buffer == NULL)
3744   {
3745     fprintf(stderr, "dt_heal_cl: error allocating memory for healing\n");
3746     err = CL_MEM_OBJECT_ALLOCATION_FAILURE;
3747     goto cleanup;
3748   }
3749 
3750   err = dt_opencl_read_buffer_from_device(devid, (void *)src_buffer, dev_img, 0,
3751                                           (size_t)width * height * ch * sizeof(float), CL_TRUE);
3752   if(err != CL_SUCCESS)
3753   {
3754     goto cleanup;
3755   }
3756 
3757   // just call the CPU version for now
3758   rt_adjust_levels(self, piece, src_buffer, width, height, ch, levels, 1);
3759 
3760   err = dt_opencl_write_buffer_to_device(devid, src_buffer, dev_img, 0, sizeof(float) * ch * width * height, TRUE);
3761   if(err != CL_SUCCESS)
3762   {
3763     goto cleanup;
3764   }
3765 
3766 cleanup:
3767   if(src_buffer) dt_free_align(src_buffer);
3768 
3769   return err;
3770 }
3771 
rt_copy_in_to_out_cl(const int devid,cl_mem dev_in,const struct dt_iop_roi_t * const roi_in,cl_mem dev_out,const struct dt_iop_roi_t * const roi_out,const int dx,const int dy,const int kernel)3772 static cl_int rt_copy_in_to_out_cl(const int devid, cl_mem dev_in, const struct dt_iop_roi_t *const roi_in,
3773                                    cl_mem dev_out, const struct dt_iop_roi_t *const roi_out, const int dx,
3774                                    const int dy, const int kernel)
3775 {
3776   cl_int err = CL_SUCCESS;
3777 
3778   const int xoffs = roi_out->x - roi_in->x - dx;
3779   const int yoffs = roi_out->y - roi_in->y - dy;
3780 
3781   cl_mem dev_roi_in = NULL;
3782   cl_mem dev_roi_out = NULL;
3783 
3784   const size_t sizes[]
3785       = { ROUNDUPWD(MIN(roi_out->width, roi_in->width)), ROUNDUPHT(MIN(roi_out->height, roi_in->height)), 1 };
3786 
3787   dev_roi_in = dt_opencl_copy_host_to_device_constant(devid, sizeof(dt_iop_roi_t), (void *)roi_in);
3788   dev_roi_out = dt_opencl_copy_host_to_device_constant(devid, sizeof(dt_iop_roi_t), (void *)roi_out);
3789   if(dev_roi_in == NULL || dev_roi_out == NULL)
3790   {
3791     fprintf(stderr, "rt_copy_in_to_out_cl error 1\n");
3792     err = CL_MEM_OBJECT_ALLOCATION_FAILURE;
3793     goto cleanup;
3794   }
3795 
3796   dt_opencl_set_kernel_arg(devid, kernel, 0, sizeof(cl_mem), (void *)&dev_in);
3797   dt_opencl_set_kernel_arg(devid, kernel, 1, sizeof(cl_mem), (void *)&dev_roi_in);
3798   dt_opencl_set_kernel_arg(devid, kernel, 2, sizeof(cl_mem), (void *)&dev_out);
3799   dt_opencl_set_kernel_arg(devid, kernel, 3, sizeof(cl_mem), (void *)&dev_roi_out);
3800   dt_opencl_set_kernel_arg(devid, kernel, 4, sizeof(int), (void *)&xoffs);
3801   dt_opencl_set_kernel_arg(devid, kernel, 5, sizeof(int), (void *)&yoffs);
3802   err = dt_opencl_enqueue_kernel_2d(devid, kernel, sizes);
3803   if(err != CL_SUCCESS)
3804   {
3805     fprintf(stderr, "rt_copy_in_to_out_cl error 2\n");
3806     goto cleanup;
3807   }
3808 
3809 cleanup:
3810   if(dev_roi_in) dt_opencl_release_mem_object(dev_roi_in);
3811   if(dev_roi_out) dt_opencl_release_mem_object(dev_roi_out);
3812 
3813   return err;
3814 }
3815 
rt_build_scaled_mask_cl(const int devid,float * const mask,dt_iop_roi_t * const roi_mask,float ** mask_scaled,cl_mem * p_dev_mask_scaled,dt_iop_roi_t * roi_mask_scaled,dt_iop_roi_t * const roi_in,const int dx,const int dy,const int algo)3816 static cl_int rt_build_scaled_mask_cl(const int devid, float *const mask, dt_iop_roi_t *const roi_mask,
3817                                       float **mask_scaled, cl_mem *p_dev_mask_scaled,
3818                                       dt_iop_roi_t *roi_mask_scaled, dt_iop_roi_t *const roi_in, const int dx,
3819                                       const int dy, const int algo)
3820 {
3821   cl_int err = CL_SUCCESS;
3822 
3823   rt_build_scaled_mask(mask, roi_mask, mask_scaled, roi_mask_scaled, roi_in, dx, dy, algo);
3824   if(*mask_scaled == NULL)
3825   {
3826     goto cleanup;
3827   }
3828 
3829   const cl_mem dev_mask_scaled
3830       = dt_opencl_alloc_device_buffer(devid, sizeof(float) * roi_mask_scaled->width * roi_mask_scaled->height);
3831   if(dev_mask_scaled == NULL)
3832   {
3833     fprintf(stderr, "rt_build_scaled_mask_cl error 2\n");
3834     err = CL_MEM_OBJECT_ALLOCATION_FAILURE;
3835     goto cleanup;
3836   }
3837 
3838   err = dt_opencl_write_buffer_to_device(devid, *mask_scaled, dev_mask_scaled, 0,
3839                                          sizeof(float) * roi_mask_scaled->width * roi_mask_scaled->height, TRUE);
3840   if(err != CL_SUCCESS)
3841   {
3842     fprintf(stderr, "rt_build_scaled_mask_cl error 4\n");
3843     goto cleanup;
3844   }
3845 
3846   *p_dev_mask_scaled = dev_mask_scaled;
3847 
3848 cleanup:
3849   if(err != CL_SUCCESS) fprintf(stderr, "rt_build_scaled_mask_cl error\n");
3850 
3851   return err;
3852 }
3853 
rt_copy_image_masked_cl(const int devid,cl_mem dev_src,cl_mem dev_dest,dt_iop_roi_t * const roi_dest,cl_mem dev_mask_scaled,dt_iop_roi_t * const roi_mask_scaled,const float opacity,const int kernel)3854 static cl_int rt_copy_image_masked_cl(const int devid, cl_mem dev_src, cl_mem dev_dest,
3855                                       dt_iop_roi_t *const roi_dest, cl_mem dev_mask_scaled,
3856                                       dt_iop_roi_t *const roi_mask_scaled, const float opacity, const int kernel)
3857 {
3858   cl_int err = CL_SUCCESS;
3859 
3860   const size_t sizes[] = { ROUNDUPWD(roi_mask_scaled->width), ROUNDUPHT(roi_mask_scaled->height), 1 };
3861 
3862   const cl_mem dev_roi_dest =
3863     dt_opencl_copy_host_to_device_constant(devid, sizeof(dt_iop_roi_t), (void *)roi_dest);
3864 
3865   const cl_mem dev_roi_mask_scaled
3866       = dt_opencl_copy_host_to_device_constant(devid, sizeof(dt_iop_roi_t), (void *)roi_mask_scaled);
3867 
3868   if(dev_roi_dest == NULL || dev_roi_mask_scaled == NULL)
3869   {
3870     err = CL_MEM_OBJECT_ALLOCATION_FAILURE;
3871     goto cleanup;
3872   }
3873 
3874   dt_opencl_set_kernel_arg(devid, kernel, 0, sizeof(cl_mem), (void *)&dev_src);
3875   dt_opencl_set_kernel_arg(devid, kernel, 1, sizeof(cl_mem), (void *)&dev_dest);
3876   dt_opencl_set_kernel_arg(devid, kernel, 2, sizeof(cl_mem), (void *)&dev_roi_dest);
3877   dt_opencl_set_kernel_arg(devid, kernel, 3, sizeof(cl_mem), (void *)&dev_mask_scaled);
3878   dt_opencl_set_kernel_arg(devid, kernel, 4, sizeof(cl_mem), (void *)&dev_roi_mask_scaled);
3879   dt_opencl_set_kernel_arg(devid, kernel, 5, sizeof(float), (void *)&opacity);
3880   err = dt_opencl_enqueue_kernel_2d(devid, kernel, sizes);
3881   if(err != CL_SUCCESS) goto cleanup;
3882 
3883 cleanup:
3884   if(dev_roi_dest) dt_opencl_release_mem_object(dev_roi_dest);
3885   if(dev_roi_mask_scaled) dt_opencl_release_mem_object(dev_roi_mask_scaled);
3886 
3887   return err;
3888 }
3889 
rt_copy_mask_to_alpha_cl(const int devid,cl_mem dev_layer,dt_iop_roi_t * const roi_layer,cl_mem dev_mask_scaled,dt_iop_roi_t * const roi_mask_scaled,const float opacity,dt_iop_retouch_global_data_t * gd)3890 static cl_int rt_copy_mask_to_alpha_cl(const int devid, cl_mem dev_layer, dt_iop_roi_t *const roi_layer,
3891                                        cl_mem dev_mask_scaled, dt_iop_roi_t *const roi_mask_scaled,
3892                                        const float opacity, dt_iop_retouch_global_data_t *gd)
3893 {
3894   cl_int err = CL_SUCCESS;
3895 
3896   // fill it
3897   const int kernel = gd->kernel_retouch_copy_mask_to_alpha;
3898   const size_t sizes[] = { ROUNDUPWD(roi_mask_scaled->width), ROUNDUPHT(roi_mask_scaled->height), 1 };
3899 
3900   const cl_mem  dev_roi_layer = dt_opencl_copy_host_to_device_constant(devid, sizeof(dt_iop_roi_t), (void *)roi_layer);
3901   const cl_mem dev_roi_mask_scaled
3902       = dt_opencl_copy_host_to_device_constant(devid, sizeof(dt_iop_roi_t), (void *)roi_mask_scaled);
3903   if(dev_roi_layer == NULL || dev_roi_mask_scaled == NULL)
3904   {
3905     err = CL_MEM_OBJECT_ALLOCATION_FAILURE;
3906     goto cleanup;
3907   }
3908 
3909   dt_opencl_set_kernel_arg(devid, kernel, 0, sizeof(cl_mem), (void *)&dev_layer);
3910   dt_opencl_set_kernel_arg(devid, kernel, 1, sizeof(cl_mem), (void *)&dev_roi_layer);
3911   dt_opencl_set_kernel_arg(devid, kernel, 2, sizeof(cl_mem), (void *)&dev_mask_scaled);
3912   dt_opencl_set_kernel_arg(devid, kernel, 3, sizeof(cl_mem), (void *)&dev_roi_mask_scaled);
3913   dt_opencl_set_kernel_arg(devid, kernel, 4, sizeof(float), (void *)&opacity);
3914   err = dt_opencl_enqueue_kernel_2d(devid, kernel, sizes);
3915   if(err != CL_SUCCESS) goto cleanup;
3916 
3917 
3918 cleanup:
3919   dt_opencl_release_mem_object(dev_roi_layer);
3920   dt_opencl_release_mem_object(dev_roi_mask_scaled);
3921 
3922   return err;
3923 }
3924 
retouch_clone_cl(const int devid,cl_mem dev_layer,dt_iop_roi_t * const roi_layer,cl_mem dev_mask_scaled,dt_iop_roi_t * const roi_mask_scaled,const int dx,const int dy,const float opacity,dt_iop_retouch_global_data_t * gd)3925 static cl_int retouch_clone_cl(const int devid, cl_mem dev_layer, dt_iop_roi_t *const roi_layer,
3926                                cl_mem dev_mask_scaled, dt_iop_roi_t *const roi_mask_scaled, const int dx,
3927                                const int dy, const float opacity, dt_iop_retouch_global_data_t *gd)
3928 {
3929   cl_int err = CL_SUCCESS;
3930 
3931   const int ch = 4;
3932 
3933   // alloc source temp image to avoid issues when areas self-intersects
3934   const cl_mem dev_src = dt_opencl_alloc_device_buffer(devid,
3935                                           sizeof(float) * ch * roi_mask_scaled->width * roi_mask_scaled->height);
3936   if(dev_src == NULL)
3937   {
3938     fprintf(stderr, "retouch_clone_cl error 2\n");
3939     err = CL_MEM_OBJECT_ALLOCATION_FAILURE;
3940     goto cleanup;
3941   }
3942 
3943   // copy source image to tmp
3944   err = rt_copy_in_to_out_cl(devid, dev_layer, roi_layer, dev_src, roi_mask_scaled, dx, dy,
3945                              gd->kernel_retouch_copy_buffer_to_buffer);
3946   if(err != CL_SUCCESS)
3947   {
3948     fprintf(stderr, "retouch_clone_cl error 4\n");
3949     goto cleanup;
3950   }
3951 
3952   // clone it
3953   err = rt_copy_image_masked_cl(devid, dev_src, dev_layer, roi_layer, dev_mask_scaled, roi_mask_scaled, opacity,
3954                                 gd->kernel_retouch_copy_buffer_to_buffer_masked);
3955   if(err != CL_SUCCESS)
3956   {
3957     fprintf(stderr, "retouch_clone_cl error 5\n");
3958     goto cleanup;
3959   }
3960 
3961 cleanup:
3962   if(dev_src) dt_opencl_release_mem_object(dev_src);
3963 
3964   return err;
3965 }
3966 
retouch_fill_cl(const int devid,cl_mem dev_layer,dt_iop_roi_t * const roi_layer,cl_mem dev_mask_scaled,dt_iop_roi_t * const roi_mask_scaled,const float opacity,float * color,dt_iop_retouch_global_data_t * gd)3967 static cl_int retouch_fill_cl(const int devid, cl_mem dev_layer, dt_iop_roi_t *const roi_layer,
3968                               cl_mem dev_mask_scaled, dt_iop_roi_t *const roi_mask_scaled, const float opacity,
3969                               float *color, dt_iop_retouch_global_data_t *gd)
3970 {
3971   cl_int err = CL_SUCCESS;
3972 
3973   // fill it
3974   const int kernel = gd->kernel_retouch_fill;
3975   const size_t sizes[] = { ROUNDUPWD(roi_mask_scaled->width), ROUNDUPHT(roi_mask_scaled->height), 1 };
3976 
3977   const cl_mem dev_roi_layer = dt_opencl_copy_host_to_device_constant(devid, sizeof(dt_iop_roi_t), (void *)roi_layer);
3978   const cl_mem dev_roi_mask_scaled
3979       = dt_opencl_copy_host_to_device_constant(devid, sizeof(dt_iop_roi_t), (void *)roi_mask_scaled);
3980   if(dev_roi_layer == NULL || dev_roi_mask_scaled == NULL)
3981   {
3982     err = CL_MEM_OBJECT_ALLOCATION_FAILURE;
3983     goto cleanup;
3984   }
3985 
3986   dt_opencl_set_kernel_arg(devid, kernel, 0, sizeof(cl_mem), (void *)&dev_layer);
3987   dt_opencl_set_kernel_arg(devid, kernel, 1, sizeof(cl_mem), (void *)&dev_roi_layer);
3988   dt_opencl_set_kernel_arg(devid, kernel, 2, sizeof(cl_mem), (void *)&dev_mask_scaled);
3989   dt_opencl_set_kernel_arg(devid, kernel, 3, sizeof(cl_mem), (void *)&dev_roi_mask_scaled);
3990   dt_opencl_set_kernel_arg(devid, kernel, 4, sizeof(float), (void *)&opacity);
3991   dt_opencl_set_kernel_arg(devid, kernel, 5, sizeof(float), (void *)&(color[0]));
3992   dt_opencl_set_kernel_arg(devid, kernel, 6, sizeof(float), (void *)&(color[1]));
3993   dt_opencl_set_kernel_arg(devid, kernel, 7, sizeof(float), (void *)&(color[2]));
3994   err = dt_opencl_enqueue_kernel_2d(devid, kernel, sizes);
3995   if(err != CL_SUCCESS) goto cleanup;
3996 
3997 
3998 cleanup:
3999   dt_opencl_release_mem_object(dev_roi_layer);
4000   dt_opencl_release_mem_object(dev_roi_mask_scaled);
4001 
4002   return err;
4003 }
4004 
retouch_blur_cl(const int devid,cl_mem dev_layer,dt_iop_roi_t * const roi_layer,cl_mem dev_mask_scaled,dt_iop_roi_t * const roi_mask_scaled,const float opacity,const int blur_type,const float blur_radius,dt_dev_pixelpipe_iop_t * piece,dt_iop_retouch_global_data_t * gd)4005 static cl_int retouch_blur_cl(const int devid, cl_mem dev_layer, dt_iop_roi_t *const roi_layer,
4006                               cl_mem dev_mask_scaled, dt_iop_roi_t *const roi_mask_scaled, const float opacity,
4007                               const int blur_type, const float blur_radius, dt_dev_pixelpipe_iop_t *piece,
4008                               dt_iop_retouch_global_data_t *gd)
4009 {
4010   cl_int err = CL_SUCCESS;
4011 
4012   if(fabsf(blur_radius) <= 0.1f) return err;
4013 
4014   const float sigma = blur_radius * roi_layer->scale / piece->iscale;
4015   const int ch = 4;
4016 
4017   const cl_mem dev_dest =
4018     dt_opencl_alloc_device(devid, roi_mask_scaled->width, roi_mask_scaled->height, sizeof(float) * ch);
4019   if(dev_dest == NULL)
4020   {
4021     fprintf(stderr, "retouch_blur_cl error 2\n");
4022     err = CL_MEM_OBJECT_ALLOCATION_FAILURE;
4023     goto cleanup;
4024   }
4025 
4026   if(blur_type == DT_IOP_RETOUCH_BLUR_BILATERAL)
4027   {
4028     const int kernel = gd->kernel_retouch_image_rgb2lab;
4029     size_t sizes[] = { ROUNDUPWD(roi_layer->width), ROUNDUPHT(roi_layer->height), 1 };
4030 
4031     dt_opencl_set_kernel_arg(devid, kernel, 0, sizeof(cl_mem), (void *)&dev_layer);
4032     dt_opencl_set_kernel_arg(devid, kernel, 1, sizeof(int), (void *)&(roi_layer->width));
4033     dt_opencl_set_kernel_arg(devid, kernel, 2, sizeof(int), (void *)&(roi_layer->height));
4034     err = dt_opencl_enqueue_kernel_2d(devid, kernel, sizes);
4035     if(err != CL_SUCCESS) goto cleanup;
4036   }
4037 
4038   err = rt_copy_in_to_out_cl(devid, dev_layer, roi_layer, dev_dest, roi_mask_scaled, 0, 0,
4039                              gd->kernel_retouch_copy_buffer_to_image);
4040   if(err != CL_SUCCESS)
4041   {
4042     fprintf(stderr, "retouch_blur_cl error 4\n");
4043     goto cleanup;
4044   }
4045 
4046   if(blur_type == DT_IOP_RETOUCH_BLUR_GAUSSIAN && fabsf(blur_radius) > 0.1f)
4047   {
4048     float Labmax[] = { INFINITY, INFINITY, INFINITY, INFINITY };
4049     float Labmin[] = { -INFINITY, -INFINITY, -INFINITY, -INFINITY };
4050 
4051     dt_gaussian_cl_t *g = dt_gaussian_init_cl(devid, roi_mask_scaled->width, roi_mask_scaled->height, ch, Labmax,
4052                                               Labmin, sigma, DT_IOP_GAUSSIAN_ZERO);
4053     if(g)
4054     {
4055       err = dt_gaussian_blur_cl(g, dev_dest, dev_dest);
4056       dt_gaussian_free_cl(g);
4057       if(err != CL_SUCCESS) goto cleanup;
4058     }
4059   }
4060   else if(blur_type == DT_IOP_RETOUCH_BLUR_BILATERAL && fabsf(blur_radius) > 0.1f)
4061   {
4062     const float sigma_r = 100.0f; // does not depend on scale
4063     const float sigma_s = sigma;
4064     const float detail = -1.0f; // we want the bilateral base layer
4065 
4066     dt_bilateral_cl_t *b
4067         = dt_bilateral_init_cl(devid, roi_mask_scaled->width, roi_mask_scaled->height, sigma_s, sigma_r);
4068     if(b)
4069     {
4070       err = dt_bilateral_splat_cl(b, dev_dest);
4071       if(err == CL_SUCCESS) err = dt_bilateral_blur_cl(b);
4072       if(err == CL_SUCCESS) err = dt_bilateral_slice_cl(b, dev_dest, dev_dest, detail);
4073 
4074       dt_bilateral_free_cl(b);
4075     }
4076   }
4077 
4078   // copy blurred (temp) image to destination image
4079   err = rt_copy_image_masked_cl(devid, dev_dest, dev_layer, roi_layer, dev_mask_scaled, roi_mask_scaled, opacity,
4080                                 gd->kernel_retouch_copy_image_to_buffer_masked);
4081   if(err != CL_SUCCESS)
4082   {
4083     fprintf(stderr, "retouch_blur_cl error 5\n");
4084     goto cleanup;
4085   }
4086 
4087   if(blur_type == DT_IOP_RETOUCH_BLUR_BILATERAL)
4088   {
4089     const int kernel = gd->kernel_retouch_image_lab2rgb;
4090     const size_t sizes[] = { ROUNDUPWD(roi_layer->width), ROUNDUPHT(roi_layer->height), 1 };
4091 
4092     dt_opencl_set_kernel_arg(devid, kernel, 0, sizeof(cl_mem), (void *)&dev_layer);
4093     dt_opencl_set_kernel_arg(devid, kernel, 1, sizeof(int), (void *)&(roi_layer->width));
4094     dt_opencl_set_kernel_arg(devid, kernel, 2, sizeof(int), (void *)&(roi_layer->height));
4095     err = dt_opencl_enqueue_kernel_2d(devid, kernel, sizes);
4096     if(err != CL_SUCCESS) goto cleanup;
4097   }
4098 
4099 cleanup:
4100   if(dev_dest) dt_opencl_release_mem_object(dev_dest);
4101 
4102   return err;
4103 }
4104 
retouch_heal_cl(const int devid,cl_mem dev_layer,dt_iop_roi_t * const roi_layer,float * mask_scaled,cl_mem dev_mask_scaled,dt_iop_roi_t * const roi_mask_scaled,const int dx,const int dy,const float opacity,dt_iop_retouch_global_data_t * gd)4105 static cl_int retouch_heal_cl(const int devid, cl_mem dev_layer, dt_iop_roi_t *const roi_layer, float *mask_scaled,
4106                               cl_mem dev_mask_scaled, dt_iop_roi_t *const roi_mask_scaled, const int dx,
4107                               const int dy, const float opacity, dt_iop_retouch_global_data_t *gd)
4108 {
4109   cl_int err = CL_SUCCESS;
4110 
4111   const int ch = 4;
4112 
4113   cl_mem dev_dest = NULL;
4114   cl_mem dev_src = dt_opencl_alloc_device_buffer(devid,
4115                                           sizeof(float) * ch * roi_mask_scaled->width * roi_mask_scaled->height);
4116   if(dev_src == NULL)
4117   {
4118     fprintf(stderr, "retouch_heal_cl: error allocating memory for healing\n");
4119     err = CL_MEM_OBJECT_ALLOCATION_FAILURE;
4120     goto cleanup;
4121   }
4122 
4123   dev_dest = dt_opencl_alloc_device_buffer(devid,
4124                                            sizeof(float) * ch * roi_mask_scaled->width * roi_mask_scaled->height);
4125   if(dev_dest == NULL)
4126   {
4127     fprintf(stderr, "retouch_heal_cl: error allocating memory for healing\n");
4128     err = CL_MEM_OBJECT_ALLOCATION_FAILURE;
4129     goto cleanup;
4130   }
4131 
4132   err = rt_copy_in_to_out_cl(devid, dev_layer, roi_layer, dev_src, roi_mask_scaled, dx, dy,
4133                              gd->kernel_retouch_copy_buffer_to_buffer);
4134   if(err != CL_SUCCESS)
4135   {
4136     fprintf(stderr, "retouch_heal_cl error 4\n");
4137     goto cleanup;
4138   }
4139 
4140   err = rt_copy_in_to_out_cl(devid, dev_layer, roi_layer, dev_dest, roi_mask_scaled, 0, 0,
4141                              gd->kernel_retouch_copy_buffer_to_buffer);
4142   if(err != CL_SUCCESS)
4143   {
4144     fprintf(stderr, "retouch_heal_cl error 4\n");
4145     goto cleanup;
4146   }
4147 
4148   // heal it
4149   heal_params_cl_t *hp = dt_heal_init_cl(devid);
4150   if(hp)
4151   {
4152     err = dt_heal_cl(hp, dev_src, dev_dest, mask_scaled, roi_mask_scaled->width, roi_mask_scaled->height);
4153     dt_heal_free_cl(hp);
4154 
4155     dt_opencl_release_mem_object(dev_src);
4156     dev_src = NULL;
4157 
4158     if(err != CL_SUCCESS) goto cleanup;
4159   }
4160 
4161   // copy healed (temp) image to destination image
4162   err = rt_copy_image_masked_cl(devid, dev_dest, dev_layer, roi_layer, dev_mask_scaled, roi_mask_scaled, opacity,
4163                                 gd->kernel_retouch_copy_buffer_to_buffer_masked);
4164   if(err != CL_SUCCESS)
4165   {
4166     fprintf(stderr, "retouch_heal_cl error 6\n");
4167     goto cleanup;
4168   }
4169 
4170 cleanup:
4171   if(dev_src) dt_opencl_release_mem_object(dev_src);
4172   if(dev_dest) dt_opencl_release_mem_object(dev_dest);
4173 
4174   return err;
4175 }
4176 
rt_process_forms_cl(cl_mem dev_layer,dwt_params_cl_t * const wt_p,const int scale1)4177 static cl_int rt_process_forms_cl(cl_mem dev_layer, dwt_params_cl_t *const wt_p, const int scale1)
4178 {
4179   cl_int err = CL_SUCCESS;
4180 
4181   int scale = scale1;
4182   retouch_user_data_t *usr_d = (retouch_user_data_t *)wt_p->user_data;
4183   dt_iop_module_t *self = usr_d->self;
4184   dt_dev_pixelpipe_iop_t *piece = usr_d->piece;
4185 
4186   // if preview a single scale, just process that scale and original image
4187   // unless merge is activated
4188   if(wt_p->merge_from_scale == 0 && wt_p->return_layer > 0 && scale != wt_p->return_layer && scale != 0)
4189     return err;
4190   // do not process the reconstructed image
4191   if(scale > wt_p->scales + 1) return err;
4192 
4193   dt_develop_blend_params_t *bp = (dt_develop_blend_params_t *)piece->blendop_data;
4194   dt_iop_retouch_params_t *p = (dt_iop_retouch_params_t *)piece->data;
4195   dt_iop_retouch_global_data_t *gd = (dt_iop_retouch_global_data_t *)self->global_data;
4196   const int devid = piece->pipe->devid;
4197   dt_iop_roi_t *roi_layer = &usr_d->roi;
4198   const int mask_display = usr_d->mask_display && (scale == usr_d->display_scale);
4199 
4200   // when the requested scales is grather than max scales the residual image index will be different from the one
4201   // defined by the user,
4202   // so we need to adjust it here, otherwise we will be using the shapes from a scale on the residual image
4203   if(wt_p->scales < p->num_scales && wt_p->return_layer == 0 && scale == wt_p->scales + 1)
4204   {
4205     scale = p->num_scales + 1;
4206   }
4207 
4208   // iterate through all forms
4209   if(!usr_d->suppress_mask)
4210   {
4211     dt_masks_form_t *grp = dt_masks_get_from_id_ext(piece->pipe->forms, bp->mask_id);
4212     if(grp && (grp->type & DT_MASKS_GROUP))
4213     {
4214       for(const GList *forms = grp->points; forms && err == CL_SUCCESS; forms = g_list_next(forms))
4215       {
4216         dt_masks_point_group_t *grpt = (dt_masks_point_group_t *)forms->data;
4217         if(grpt == NULL)
4218         {
4219           fprintf(stderr, "rt_process_forms: invalid form\n");
4220           continue;
4221         }
4222         const int formid = grpt->formid;
4223         const float form_opacity = grpt->opacity;
4224         if(formid == 0)
4225         {
4226           fprintf(stderr, "rt_process_forms: form is null\n");
4227           continue;
4228         }
4229         const int index = rt_get_index_from_formid(p, formid);
4230         if(index == -1)
4231         {
4232           // FIXME: we get this error when user go back in history, so forms are the same but the array has changed
4233           fprintf(stderr, "rt_process_forms: missing form=%i from array\n", formid);
4234           continue;
4235         }
4236 
4237         // only process current scale
4238         if(p->rt_forms[index].scale != scale)
4239         {
4240           continue;
4241         }
4242 
4243         // get the spot
4244         dt_masks_form_t *form = dt_masks_get_from_id_ext(piece->pipe->forms, formid);
4245         if(form == NULL)
4246         {
4247           fprintf(stderr, "rt_process_forms: missing form=%i from masks\n", formid);
4248           continue;
4249         }
4250 
4251         // if the form is outside the roi, we just skip it
4252         if(!rt_masks_form_is_in_roi(self, piece, form, roi_layer, roi_layer))
4253         {
4254           continue;
4255         }
4256 
4257         // get the mask
4258         float *mask = NULL;
4259         dt_iop_roi_t roi_mask = { 0 };
4260 
4261         dt_masks_get_mask(self, piece, form, &mask, &roi_mask.width, &roi_mask.height, &roi_mask.x, &roi_mask.y);
4262         if(mask == NULL)
4263         {
4264           fprintf(stderr, "rt_process_forms: error retrieving mask\n");
4265           continue;
4266         }
4267 
4268         int dx = 0, dy = 0;
4269 
4270         // search the delta with the source
4271         const dt_iop_retouch_algo_type_t algo = p->rt_forms[index].algorithm;
4272         if(algo != DT_IOP_RETOUCH_BLUR && algo != DT_IOP_RETOUCH_FILL)
4273         {
4274           if(!rt_masks_get_delta_to_destination(self, piece, roi_layer, form, &dx, &dy,
4275                                                 p->rt_forms[index].distort_mode))
4276           {
4277             if(mask) dt_free_align(mask);
4278             continue;
4279           }
4280         }
4281 
4282         // scale the mask
4283         cl_mem dev_mask_scaled = NULL;
4284         float *mask_scaled = NULL;
4285         dt_iop_roi_t roi_mask_scaled = { 0 };
4286 
4287         err = rt_build_scaled_mask_cl(devid, mask, &roi_mask, &mask_scaled, &dev_mask_scaled, &roi_mask_scaled,
4288                                       roi_layer, dx, dy, algo);
4289 
4290         // only heal needs mask scaled
4291         if(algo != DT_IOP_RETOUCH_HEAL && mask_scaled != NULL)
4292         {
4293           dt_free_align(mask_scaled);
4294           mask_scaled = NULL;
4295         }
4296 
4297         // we don't need the original mask anymore
4298         if(mask)
4299         {
4300           dt_free_align(mask);
4301           mask = NULL;
4302         }
4303 
4304         if(mask_scaled == NULL && algo == DT_IOP_RETOUCH_HEAL)
4305         {
4306           if(dev_mask_scaled) dt_opencl_release_mem_object(dev_mask_scaled);
4307           dev_mask_scaled = NULL;
4308           continue;
4309         }
4310 
4311         if((err == CL_SUCCESS)
4312            && (dx != 0 || dy != 0 || algo == DT_IOP_RETOUCH_BLUR || algo == DT_IOP_RETOUCH_FILL)
4313            && ((roi_mask_scaled.width > 2) && (roi_mask_scaled.height > 2)))
4314         {
4315           if(algo == DT_IOP_RETOUCH_CLONE)
4316           {
4317             err = retouch_clone_cl(devid, dev_layer, roi_layer, dev_mask_scaled, &roi_mask_scaled, dx, dy,
4318                                    form_opacity, gd);
4319           }
4320           else if(algo == DT_IOP_RETOUCH_HEAL)
4321           {
4322             err = retouch_heal_cl(devid, dev_layer, roi_layer, mask_scaled, dev_mask_scaled, &roi_mask_scaled, dx,
4323                                   dy, form_opacity, gd);
4324           }
4325           else if(algo == DT_IOP_RETOUCH_BLUR)
4326           {
4327             err = retouch_blur_cl(devid, dev_layer, roi_layer, dev_mask_scaled, &roi_mask_scaled, form_opacity,
4328                                   p->rt_forms[index].blur_type, p->rt_forms[index].blur_radius, piece, gd);
4329           }
4330           else if(algo == DT_IOP_RETOUCH_FILL)
4331           {
4332             // add a brightness to the color so it can be fine-adjusted by the user
4333             float fill_color[3];
4334 
4335             if(p->rt_forms[index].fill_mode == DT_IOP_RETOUCH_FILL_ERASE)
4336             {
4337               fill_color[0] = fill_color[1] = fill_color[2] = p->rt_forms[index].fill_brightness;
4338             }
4339             else
4340             {
4341               fill_color[0] = p->rt_forms[index].fill_color[0] + p->rt_forms[index].fill_brightness;
4342               fill_color[1] = p->rt_forms[index].fill_color[1] + p->rt_forms[index].fill_brightness;
4343               fill_color[2] = p->rt_forms[index].fill_color[2] + p->rt_forms[index].fill_brightness;
4344             }
4345 
4346             err = retouch_fill_cl(devid, dev_layer, roi_layer, dev_mask_scaled, &roi_mask_scaled, form_opacity,
4347                                   fill_color, gd);
4348           }
4349           else
4350             fprintf(stderr, "rt_process_forms: unknown algorithm %i\n", algo);
4351 
4352           if(mask_display)
4353             rt_copy_mask_to_alpha_cl(devid, dev_layer, roi_layer, dev_mask_scaled, &roi_mask_scaled, form_opacity,
4354                                      gd);
4355         }
4356 
4357         if(mask) dt_free_align(mask);
4358         if(mask_scaled) dt_free_align(mask_scaled);
4359         if(dev_mask_scaled) dt_opencl_release_mem_object(dev_mask_scaled);
4360       }
4361     }
4362   }
4363 
4364   return err;
4365 }
4366 
process_cl(struct dt_iop_module_t * self,dt_dev_pixelpipe_iop_t * piece,cl_mem dev_in,cl_mem dev_out,const dt_iop_roi_t * const roi_in,const dt_iop_roi_t * const roi_out)4367 int process_cl(struct dt_iop_module_t *self, dt_dev_pixelpipe_iop_t *piece, cl_mem dev_in, cl_mem dev_out,
4368                const dt_iop_roi_t *const roi_in, const dt_iop_roi_t *const roi_out)
4369 {
4370   dt_iop_retouch_params_t *p = (dt_iop_retouch_params_t *)piece->data;
4371   dt_iop_retouch_global_data_t *gd = (dt_iop_retouch_global_data_t *)self->global_data;
4372   dt_iop_retouch_gui_data_t *g = (dt_iop_retouch_gui_data_t *)self->gui_data;
4373 
4374   cl_int err = CL_SUCCESS;
4375   const int devid = piece->pipe->devid;
4376 
4377   dt_iop_roi_t roi_retouch = *roi_in;
4378   dt_iop_roi_t *roi_rt = &roi_retouch;
4379 
4380   const int ch = piece->colors;
4381   retouch_user_data_t usr_data = { 0 };
4382   dwt_params_cl_t *dwt_p = NULL;
4383 
4384   const int gui_active = (self->dev) ? (self == self->dev->gui_module) : 0;
4385   const int display_wavelet_scale = (g && gui_active) ? g->display_wavelet_scale : 0;
4386 
4387   // we will do all the clone, heal, etc on the input image,
4388   // this way the source for one algorithm can be the destination from a previous one
4389   const cl_mem in_retouch = dt_opencl_alloc_device_buffer(devid, sizeof(float) * ch * roi_rt->width * roi_rt->height);
4390   if(in_retouch == NULL)
4391   {
4392     fprintf(stderr, "process_internal: error allocating memory for wavelet decompose\n");
4393     err = CL_MEM_OBJECT_ALLOCATION_FAILURE;
4394     goto cleanup;
4395   }
4396 
4397   // copy input image to the new buffer
4398   {
4399     size_t origin[] = { 0, 0, 0 };
4400     size_t region[] = { roi_rt->width, roi_rt->height, 1 };
4401     err = dt_opencl_enqueue_copy_image_to_buffer(devid, dev_in, in_retouch, origin, region, 0);
4402     if(err != CL_SUCCESS) goto cleanup;
4403   }
4404 
4405   // user data passed from the decompose routine to the one that process each scale
4406   usr_data.self = self;
4407   usr_data.piece = piece;
4408   usr_data.roi = *roi_rt;
4409   usr_data.mask_display = 0;
4410   usr_data.suppress_mask = (g && g->suppress_mask && self->dev->gui_attached && (self == self->dev->gui_module)
4411                             && (piece->pipe == self->dev->pipe));
4412   usr_data.display_scale = p->curr_scale;
4413 
4414   // init the decompose routine
4415   dwt_p = dt_dwt_init_cl(devid, in_retouch, roi_rt->width, roi_rt->height, p->num_scales,
4416                          (!display_wavelet_scale
4417                           || (piece->pipe->type & DT_DEV_PIXELPIPE_FULL) != DT_DEV_PIXELPIPE_FULL) ? 0 : p->curr_scale,
4418                          p->merge_from_scale, &usr_data,
4419                          roi_in->scale / piece->iscale);
4420   if(dwt_p == NULL)
4421   {
4422     fprintf(stderr, "process_internal: error initializing wavelet decompose\n");
4423     err = CL_MEM_OBJECT_ALLOCATION_FAILURE;
4424     goto cleanup;
4425   }
4426 
4427   // check if this module should expose mask.
4428   if((piece->pipe->type & DT_DEV_PIXELPIPE_FULL) == DT_DEV_PIXELPIPE_FULL && g && g->mask_display && self->dev->gui_attached
4429      && (self == self->dev->gui_module) && (piece->pipe == self->dev->pipe))
4430   {
4431     const int kernel = gd->kernel_retouch_clear_alpha;
4432     const size_t sizes[] = { ROUNDUPWD(roi_rt->width), ROUNDUPHT(roi_rt->height), 1 };
4433 
4434     dt_opencl_set_kernel_arg(devid, kernel, 0, sizeof(cl_mem), (void *)&in_retouch);
4435     dt_opencl_set_kernel_arg(devid, kernel, 1, sizeof(int), (void *)&(roi_rt->width));
4436     dt_opencl_set_kernel_arg(devid, kernel, 2, sizeof(int), (void *)&(roi_rt->height));
4437     err = dt_opencl_enqueue_kernel_2d(devid, kernel, sizes);
4438     if(err != CL_SUCCESS) goto cleanup;
4439 
4440     piece->pipe->mask_display = DT_DEV_PIXELPIPE_DISPLAY_MASK;
4441     piece->pipe->bypass_blendif = 1;
4442     usr_data.mask_display = 1;
4443   }
4444 
4445   if((piece->pipe->type & DT_DEV_PIXELPIPE_FULL) == DT_DEV_PIXELPIPE_FULL)
4446   {
4447     // check if the image support this number of scales
4448     if(gui_active)
4449     {
4450       const int max_scales = dwt_get_max_scale_cl(dwt_p);
4451       if(dwt_p->scales > max_scales)
4452       {
4453         dt_control_log(_("max scale is %i for this image size"), max_scales);
4454       }
4455     }
4456     // get first scale visible at this zoom level
4457     if(g) g->first_scale_visible = dt_dwt_first_scale_visible_cl(dwt_p);
4458   }
4459 
4460   // decompose it
4461   err = dwt_decompose_cl(dwt_p, rt_process_forms_cl);
4462   if(err != CL_SUCCESS) goto cleanup;
4463 
4464   float levels[3] = { 0.f };
4465   levels[0] = p->preview_levels[0];
4466   levels[1] = p->preview_levels[1];
4467   levels[2] = p->preview_levels[2];
4468 
4469   // process auto levels
4470   if(g && (piece->pipe->type & DT_DEV_PIXELPIPE_FULL) == DT_DEV_PIXELPIPE_FULL)
4471   {
4472     dt_iop_gui_enter_critical_section(self);
4473     if(g->preview_auto_levels == 1 && !darktable.gui->reset)
4474     {
4475       g->preview_auto_levels = -1;
4476 
4477       dt_iop_gui_leave_critical_section(self);
4478 
4479       levels[0] = levels[1] = levels[2] = 0;
4480       err = rt_process_stats_cl(self, piece, devid, in_retouch, roi_rt->width, roi_rt->height, levels);
4481       if(err != CL_SUCCESS) goto cleanup;
4482 
4483       rt_clamp_minmax(levels, levels);
4484 
4485       for(int i = 0; i < 3; i++) g->preview_levels[i] = levels[i];
4486 
4487       dt_iop_gui_enter_critical_section(self);
4488       g->preview_auto_levels = 2;
4489       dt_iop_gui_leave_critical_section(self);
4490     }
4491     else
4492     {
4493       dt_iop_gui_leave_critical_section(self);
4494     }
4495   }
4496 
4497   // if user wants to preview a detail scale adjust levels
4498   if(dwt_p->return_layer > 0 && dwt_p->return_layer < dwt_p->scales + 1)
4499   {
4500     err = rt_adjust_levels_cl(self, piece, devid, in_retouch, roi_rt->width, roi_rt->height, levels);
4501     if(err != CL_SUCCESS) goto cleanup;
4502   }
4503 
4504   // copy alpha channel if needed
4505   if((piece->pipe->mask_display & DT_DEV_PIXELPIPE_DISPLAY_MASK) && g && !g->mask_display)
4506   {
4507     const int kernel = gd->kernel_retouch_copy_alpha;
4508     const size_t sizes[] = { ROUNDUPWD(roi_rt->width), ROUNDUPHT(roi_rt->height), 1 };
4509 
4510     dt_opencl_set_kernel_arg(devid, kernel, 0, sizeof(cl_mem), (void *)&dev_in);
4511     dt_opencl_set_kernel_arg(devid, kernel, 1, sizeof(cl_mem), (void *)&in_retouch);
4512     dt_opencl_set_kernel_arg(devid, kernel, 2, sizeof(int), (void *)&(roi_rt->width));
4513     dt_opencl_set_kernel_arg(devid, kernel, 3, sizeof(int), (void *)&(roi_rt->height));
4514     err = dt_opencl_enqueue_kernel_2d(devid, kernel, sizes);
4515     if(err != CL_SUCCESS) goto cleanup;
4516   }
4517 
4518   // return final image
4519   err = rt_copy_in_to_out_cl(devid, in_retouch, roi_in, dev_out, roi_out, 0, 0,
4520                              gd->kernel_retouch_copy_buffer_to_image);
4521 
4522 cleanup:
4523   if(dwt_p) dt_dwt_free_cl(dwt_p);
4524 
4525   if(in_retouch) dt_opencl_release_mem_object(in_retouch);
4526 
4527   if(err != CL_SUCCESS) dt_print(DT_DEBUG_OPENCL, "[opencl_retouch] couldn't enqueue kernel! %d\n", err);
4528 
4529   return (err == CL_SUCCESS) ? TRUE : FALSE;
4530 }
4531 #endif
4532 
4533 // modelines: These editor modelines have been set for all relevant files by tools/update_modelines.sh
4534 // vim: shiftwidth=2 expandtab tabstop=2 cindent
4535 // kate: tab-indents: off; indent-width 2; replace-tabs on; indent-mode cstyle; remove-trailing-space on;
4536