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 | IOP_FLAGS_GUIDES_WIDGET;
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   dt_boundingbox_t points;
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 = g_strdup_printf("%s\n%s", _("ctrl+click to change tool for current form"),
2183                                _("shift+click to set the tool as default"));
2184   gchar *tt = g_strdup_printf("%s\n%s", _("activate blur tool"), tt2);
2185   gtk_widget_set_tooltip_text(g->bt_blur, tt);
2186   g_free(tt);
2187   tt = g_strdup_printf("%s\n%s", _("activate fill tool"), tt2);
2188   gtk_widget_set_tooltip_text(g->bt_fill, tt);
2189   g_free(tt);
2190   tt = g_strdup_printf("%s\n%s", _("activate cloning tool"), tt2);
2191   gtk_widget_set_tooltip_text(g->bt_clone, tt);
2192   g_free(tt);
2193   tt = g_strdup_printf("%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   dt_bauhaus_slider_set_factor(g->sl_fill_brightness, 100.0f);
2340   dt_bauhaus_slider_set_format(g->sl_fill_brightness, "%+.2f%%");
2341   gtk_widget_set_tooltip_text(g->sl_fill_brightness,
2342                               _("adjusts color brightness to fine-tune it. works with erase as well"));
2343 
2344   // blur properties
2345   g->vbox_blur = self->widget = gtk_box_new(GTK_ORIENTATION_VERTICAL, 5);
2346 
2347   g->cmb_blur_type = dt_bauhaus_combobox_from_params(self, "blur_type");
2348   gtk_widget_set_tooltip_text(g->cmb_blur_type, _("type for the blur algorithm"));
2349 
2350   g->sl_blur_radius = dt_bauhaus_slider_from_params(self, "blur_radius");
2351   dt_bauhaus_slider_set_step(g->sl_blur_radius, 0.1);
2352   dt_bauhaus_slider_set_format(g->sl_blur_radius, "%.1f px");
2353   gtk_widget_set_tooltip_text(g->sl_blur_radius, _("radius of the selected blur type"));
2354 
2355   // mask opacity
2356   g->sl_mask_opacity = dt_bauhaus_slider_new_with_range(self, 0.0, 1.0, 0.05, 1., 3);
2357   dt_bauhaus_widget_set_label(g->sl_mask_opacity, NULL, N_("mask opacity"));
2358   dt_bauhaus_slider_set_factor(g->sl_mask_opacity, 100.0f);
2359   dt_bauhaus_slider_set_format(g->sl_mask_opacity, "%.2f%%");
2360   gtk_widget_set_tooltip_text(g->sl_mask_opacity, _("set the opacity on the selected shape"));
2361   g_signal_connect(G_OBJECT(g->sl_mask_opacity), "value-changed", G_CALLBACK(rt_mask_opacity_callback), self);
2362 
2363   // start building top level widget
2364   self->widget = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
2365 
2366   GtkWidget *lbl_rt_tools = dt_ui_section_label_new(_("retouch tools"));
2367   gtk_box_pack_start(GTK_BOX(self->widget), lbl_rt_tools, FALSE, TRUE, 0);
2368 
2369   // shapes toolbar
2370   gtk_box_pack_start(GTK_BOX(self->widget), hbox_shapes, TRUE, TRUE, 0);
2371   // algorithms toolbar
2372   gtk_box_pack_start(GTK_BOX(self->widget), hbox_algo, TRUE, TRUE, 0);
2373 
2374   // wavelet decompose
2375   GtkWidget *lbl_wd = dt_ui_section_label_new(_("wavelet decompose"));
2376   gtk_box_pack_start(GTK_BOX(self->widget), lbl_wd, FALSE, TRUE, 0);
2377 
2378   // wavelet decompose bar & labels
2379   gtk_box_pack_start(GTK_BOX(self->widget), grid_wd_labels, TRUE, TRUE, 0);
2380   gtk_box_pack_start(GTK_BOX(self->widget), g->wd_bar, TRUE, TRUE, DT_PIXEL_APPLY_DPI(3));
2381 
2382   // preview scale & cut/paste scale
2383   gtk_box_pack_start(GTK_BOX(self->widget), hbox_scale, TRUE, TRUE, 0);
2384 
2385   // preview single scale
2386   gtk_box_pack_start(GTK_BOX(self->widget), g->vbox_preview_scale, TRUE, TRUE, 0);
2387 
2388   // shapes
2389   GtkWidget *lbl_shapes = dt_ui_section_label_new(_("shapes"));
2390   gtk_box_pack_start(GTK_BOX(self->widget), lbl_shapes, FALSE, TRUE, 0);
2391 
2392   // shape selected
2393   gtk_box_pack_start(GTK_BOX(self->widget), hbox_shape_sel, TRUE, TRUE, 0);
2394   // blur radius
2395   gtk_box_pack_start(GTK_BOX(self->widget), g->vbox_blur, TRUE, TRUE, 0);
2396   // fill color
2397   gtk_box_pack_start(GTK_BOX(self->widget), g->vbox_fill, TRUE, TRUE, 0);
2398   // mask (shape) opacity
2399   gtk_box_pack_start(GTK_BOX(self->widget), g->sl_mask_opacity, TRUE, TRUE, 0);
2400 
2401   /* add signal handler for preview pipe finish to redraw the preview */
2402   DT_DEBUG_CONTROL_SIGNAL_CONNECT(darktable.signals, DT_SIGNAL_DEVELOP_UI_PIPE_FINISHED,
2403                             G_CALLBACK(rt_develop_ui_pipe_finished_callback), self);
2404 }
2405 
gui_reset(struct dt_iop_module_t * self)2406 void gui_reset(struct dt_iop_module_t *self)
2407 {
2408   // hide the previous masks
2409   dt_masks_reset_form_gui();
2410   // set the algo to the default one
2411   dt_iop_retouch_params_t *p = (dt_iop_retouch_params_t *)self->params;
2412   p->algorithm = dt_conf_get_int("plugins/darkroom/retouch/default_algo");
2413 }
2414 
reload_defaults(dt_iop_module_t * self)2415 void reload_defaults(dt_iop_module_t *self)
2416 {
2417   // set the algo to the default one
2418   dt_iop_retouch_params_t *p = (dt_iop_retouch_params_t *)self->default_params;
2419   p->algorithm = dt_conf_get_int("plugins/darkroom/retouch/default_algo");
2420 }
2421 
gui_cleanup(dt_iop_module_t * self)2422 void gui_cleanup(dt_iop_module_t *self)
2423 {
2424   DT_DEBUG_CONTROL_SIGNAL_DISCONNECT(darktable.signals, G_CALLBACK(rt_develop_ui_pipe_finished_callback), self);
2425 
2426   IOP_GUI_FREE;
2427 }
2428 
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)2429 void modify_roi_out(struct dt_iop_module_t *self, struct dt_dev_pixelpipe_iop_t *piece, dt_iop_roi_t *roi_out,
2430                     const dt_iop_roi_t *roi_in)
2431 {
2432   *roi_out = *roi_in;
2433 }
2434 
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)2435 static void rt_compute_roi_in(struct dt_iop_module_t *self, struct dt_dev_pixelpipe_iop_t *piece,
2436                               dt_iop_roi_t *roi_in, int *_roir, int *_roib, int *_roix, int *_roiy)
2437 {
2438   dt_iop_retouch_params_t *p = (dt_iop_retouch_params_t *)piece->data;
2439   dt_develop_blend_params_t *bp = self->blend_params;
2440 
2441   int roir = *_roir;
2442   int roib = *_roib;
2443   int roix = *_roix;
2444   int roiy = *_roiy;
2445 
2446   // We iterate through all forms
2447   const dt_masks_form_t *grp = dt_masks_get_from_id_ext(piece->pipe->forms, bp->mask_id);
2448   if(grp && (grp->type & DT_MASKS_GROUP))
2449   {
2450     for(const GList *forms = grp->points; forms; forms = g_list_next(forms))
2451     {
2452       const dt_masks_point_group_t *grpt = (dt_masks_point_group_t *)forms->data;
2453       if(grpt)
2454       {
2455         const int formid = grpt->formid;
2456         const int index = rt_get_index_from_formid(p, formid);
2457         if(p->rt_forms[index].algorithm == DT_IOP_RETOUCH_FILL)
2458         {
2459           continue;
2460         }
2461 
2462         // we get the spot
2463         dt_masks_form_t *form = dt_masks_get_from_id_ext(piece->pipe->forms, formid);
2464         if(form)
2465         {
2466           // if the form is outside the roi, we just skip it
2467           // we get the area for the form
2468           int fl, ft, fw, fh;
2469           if(!dt_masks_get_area(self, piece, form, &fw, &fh, &fl, &ft))
2470           {
2471             continue;
2472           }
2473 
2474           // is the form outside of the roi?
2475           fw *= roi_in->scale, fh *= roi_in->scale, fl *= roi_in->scale, ft *= roi_in->scale;
2476           if(ft >= roi_in->y + roi_in->height || ft + fh <= roi_in->y || fl >= roi_in->x + roi_in->width
2477              || fl + fw <= roi_in->x)
2478           {
2479             continue;
2480           }
2481 
2482           // heal need the entire area
2483           if(p->rt_forms[index].algorithm == DT_IOP_RETOUCH_HEAL)
2484           {
2485             // we enlarge the roi if needed
2486             roiy = fminf(ft, roiy);
2487             roix = fminf(fl, roix);
2488             roir = fmaxf(fl + fw, roir);
2489             roib = fmaxf(ft + fh, roib);
2490           }
2491           // blur need an overlap of 4 * radius (scaled)
2492           if(p->rt_forms[index].algorithm == DT_IOP_RETOUCH_BLUR)
2493           {
2494             if(index >= 0)
2495             {
2496               const int overlap = ceilf(4 * (p->rt_forms[index].blur_radius * roi_in->scale / piece->iscale));
2497               if(roiy > ft) roiy = MAX(roiy - overlap, ft);
2498               if(roix > fl) roix = MAX(roix - overlap, fl);
2499               if(roir < fl + fw) roir = MAX(roir + overlap, fl + fw);
2500               if(roib < ft + fh) roib = MAX(roib + overlap, ft + fh);
2501             }
2502           }
2503           // heal and clone need both source and destination areas
2504           if(p->rt_forms[index].algorithm == DT_IOP_RETOUCH_HEAL
2505              || p->rt_forms[index].algorithm == DT_IOP_RETOUCH_CLONE)
2506           {
2507             int dx = 0, dy = 0;
2508             if(rt_masks_get_delta_to_destination(self, piece, roi_in, form, &dx, &dy,
2509                                                  p->rt_forms[index].distort_mode))
2510             {
2511               roiy = fminf(ft - dy, roiy);
2512               roix = fminf(fl - dx, roix);
2513               roir = fmaxf(fl + fw - dx, roir);
2514               roib = fmaxf(ft + fh - dy, roib);
2515             }
2516           }
2517         }
2518       }
2519     }
2520   }
2521 
2522   *_roir = roir;
2523   *_roib = roib;
2524   *_roix = roix;
2525   *_roiy = roiy;
2526 }
2527 
2528 // for a given form, if a previous clone/heal destination intersects the source area,
2529 // 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)2530 static void rt_extend_roi_in_from_source_clones(struct dt_iop_module_t *self, struct dt_dev_pixelpipe_iop_t *piece,
2531                                                 dt_iop_roi_t *roi_in, const int formid_src, const int fl_src,
2532                                                 const int ft_src, const int fw_src, const int fh_src, int *_roir,
2533                                                 int *_roib, int *_roix, int *_roiy)
2534 {
2535   dt_iop_retouch_params_t *p = (dt_iop_retouch_params_t *)piece->data;
2536   dt_develop_blend_params_t *bp = self->blend_params;
2537 
2538   int roir = *_roir;
2539   int roib = *_roib;
2540   int roix = *_roix;
2541   int roiy = *_roiy;
2542 
2543   // We iterate through all forms
2544   const dt_masks_form_t *grp = dt_masks_get_from_id_ext(piece->pipe->forms, bp->mask_id);
2545   if(grp && (grp->type & DT_MASKS_GROUP))
2546   {
2547     for(const GList *forms = grp->points; forms; forms = g_list_next(forms))
2548     {
2549       const dt_masks_point_group_t *grpt = (dt_masks_point_group_t *)forms->data;
2550       if(grpt)
2551       {
2552         const int formid = grpt->formid;
2553 
2554         // just need the previous forms
2555         if(formid == formid_src) break;
2556 
2557         const int index = rt_get_index_from_formid(p, formid);
2558 
2559         // only process clone and heal
2560         if(p->rt_forms[index].algorithm != DT_IOP_RETOUCH_HEAL
2561            && p->rt_forms[index].algorithm != DT_IOP_RETOUCH_CLONE)
2562         {
2563           continue;
2564         }
2565 
2566         // we get the spot
2567         dt_masks_form_t *form = dt_masks_get_from_id_ext(piece->pipe->forms, formid);
2568         if(form)
2569         {
2570           // we get the source area
2571           int fl, ft, fw, fh;
2572           if(!dt_masks_get_source_area(self, piece, form, &fw, &fh, &fl, &ft))
2573           {
2574             continue;
2575           }
2576           fw *= roi_in->scale, fh *= roi_in->scale, fl *= roi_in->scale, ft *= roi_in->scale;
2577 
2578           // get the destination area
2579           int fl_dest, ft_dest;
2580           int dx = 0, dy = 0;
2581           if(!rt_masks_get_delta_to_destination(self, piece, roi_in, form, &dx, &dy,
2582                                                 p->rt_forms[index].distort_mode))
2583           {
2584             continue;
2585           }
2586 
2587           ft_dest = ft + dy;
2588           fl_dest = fl + dx;
2589 
2590           // check if the destination of this form intersects the source of the formid_src
2591           const int intersects = !(ft_dest + fh < ft_src || ft_src + fh_src < ft_dest || fl_dest + fw < fl_src
2592                                    || fl_src + fw_src < fl_dest);
2593           if(intersects)
2594           {
2595             // we enlarge the roi if needed
2596             roiy = fminf(ft, roiy);
2597             roix = fminf(fl, roix);
2598             roir = fmaxf(fl + fw, roir);
2599             roib = fmaxf(ft + fh, roib);
2600 
2601             // need both source and destination areas
2602             roiy = fminf(ft + dy, roiy);
2603             roix = fminf(fl + dx, roix);
2604             roir = fmaxf(fl + fw + dx, roir);
2605             roib = fmaxf(ft + fh + dy, roib);
2606           }
2607         }
2608       }
2609     }
2610   }
2611 
2612   *_roir = roir;
2613   *_roib = roib;
2614   *_roix = roix;
2615   *_roiy = roiy;
2616 }
2617 
2618 // for clone and heal, if the source area is the destination from another clone/heal,
2619 // 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)2620 static void rt_extend_roi_in_for_clone(struct dt_iop_module_t *self, struct dt_dev_pixelpipe_iop_t *piece,
2621                                        dt_iop_roi_t *roi_in, int *_roir, int *_roib, int *_roix, int *_roiy)
2622 {
2623   dt_iop_retouch_params_t *p = (dt_iop_retouch_params_t *)piece->data;
2624   dt_develop_blend_params_t *bp = self->blend_params;
2625 
2626   int roir = *_roir;
2627   int roib = *_roib;
2628   int roix = *_roix;
2629   int roiy = *_roiy;
2630 
2631   // go through all clone and heal forms
2632   const dt_masks_form_t *grp = dt_masks_get_from_id_ext(piece->pipe->forms, bp->mask_id);
2633   if(grp && (grp->type & DT_MASKS_GROUP))
2634   {
2635     for(const GList *forms = grp->points; forms; forms = g_list_next(forms))
2636     {
2637       dt_masks_point_group_t *grpt = (dt_masks_point_group_t *)forms->data;
2638       if(grpt)
2639       {
2640         const int formid = grpt->formid;
2641         const int index = rt_get_index_from_formid(p, formid);
2642 
2643         if(p->rt_forms[index].algorithm != DT_IOP_RETOUCH_HEAL
2644            && p->rt_forms[index].algorithm != DT_IOP_RETOUCH_CLONE)
2645         {
2646           continue;
2647         }
2648 
2649         // we get the spot
2650         dt_masks_form_t *form = dt_masks_get_from_id_ext(piece->pipe->forms, formid);
2651         if(form == NULL)
2652         {
2653           continue;
2654         }
2655 
2656         // get the source area
2657         int fl_src, ft_src, fw_src, fh_src;
2658         if(!dt_masks_get_source_area(self, piece, form, &fw_src, &fh_src, &fl_src, &ft_src))
2659         {
2660           continue;
2661         }
2662 
2663         fw_src *= roi_in->scale, fh_src *= roi_in->scale, fl_src *= roi_in->scale, ft_src *= roi_in->scale;
2664 
2665         // we only want to process forms already in roi_in
2666         const int intersects
2667             = !(roib < ft_src || ft_src + fh_src < roiy || roir < fl_src || fl_src + fw_src < roix);
2668         if(intersects)
2669           rt_extend_roi_in_from_source_clones(self, piece, roi_in, formid, fl_src, ft_src, fw_src, fh_src, &roir,
2670                                               &roib, &roix, &roiy);
2671       }
2672     }
2673   }
2674 
2675   *_roir = roir;
2676   *_roib = roib;
2677   *_roix = roix;
2678   *_roiy = roiy;
2679 }
2680 
2681 // 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)2682 void modify_roi_in(struct dt_iop_module_t *self, struct dt_dev_pixelpipe_iop_t *piece, const dt_iop_roi_t *roi_out,
2683                    dt_iop_roi_t *roi_in)
2684 {
2685   *roi_in = *roi_out;
2686 
2687   int roir = roi_in->width + roi_in->x;
2688   int roib = roi_in->height + roi_in->y;
2689   int roix = roi_in->x;
2690   int roiy = roi_in->y;
2691 
2692   rt_compute_roi_in(self, piece, roi_in, &roir, &roib, &roix, &roiy);
2693 
2694   int roir_prev = -1, roib_prev = -1, roix_prev = -1, roiy_prev = -1;
2695 
2696   while(roir != roir_prev || roib != roib_prev || roix != roix_prev || roiy != roiy_prev)
2697   {
2698     roir_prev = roir;
2699     roib_prev = roib;
2700     roix_prev = roix;
2701     roiy_prev = roiy;
2702 
2703     rt_extend_roi_in_for_clone(self, piece, roi_in, &roir, &roib, &roix, &roiy);
2704   }
2705 
2706   // now we set the values
2707   const float scwidth = piece->buf_in.width * roi_in->scale, scheight = piece->buf_in.height * roi_in->scale;
2708   roi_in->x = CLAMP(roix, 0, scwidth - 1);
2709   roi_in->y = CLAMP(roiy, 0, scheight - 1);
2710   roi_in->width = CLAMP(roir - roi_in->x, 1, scwidth + .5f - roi_in->x);
2711   roi_in->height = CLAMP(roib - roi_in->y, 1, scheight + .5f - roi_in->y);
2712 }
2713 
2714 //--------------------------------------------------------------------------------------------------
2715 // process
2716 //--------------------------------------------------------------------------------------------------
2717 
image_rgb2lab(float * img_src,const int width,const int height,const int ch,const int use_sse)2718 static void image_rgb2lab(float *img_src, const int width, const int height, const int ch, const int use_sse)
2719 {
2720   const int stride = width * height * ch;
2721 
2722 #if defined(__SSE__)
2723   if(ch == 4 && use_sse)
2724   {
2725 #ifdef _OPENMP
2726 #pragma omp parallel for default(none) \
2727     dt_omp_firstprivate(ch, stride) \
2728     shared(img_src) \
2729     schedule(static)
2730 #endif
2731     for(int i = 0; i < stride; i += ch)
2732     {
2733       // RGB -> XYZ
2734       __m128 rgb = _mm_load_ps(img_src + i);
2735       __m128 XYZ = dt_RGB_to_XYZ_sse2(rgb);
2736       // XYZ -> Lab
2737       _mm_store_ps(img_src + i, dt_XYZ_to_Lab_sse2(XYZ));
2738     }
2739 
2740     return;
2741   }
2742 #endif
2743 
2744 #ifdef _OPENMP
2745 #pragma omp parallel for default(none) \
2746   dt_omp_firstprivate(ch, stride) \
2747   shared(img_src) \
2748   schedule(static)
2749 #endif
2750   for(int i = 0; i < stride; i += ch)
2751   {
2752     dt_aligned_pixel_t XYZ;
2753 
2754     dt_linearRGB_to_XYZ(img_src + i, XYZ);
2755     dt_XYZ_to_Lab(XYZ, img_src + i);
2756   }
2757 }
2758 
image_lab2rgb(float * img_src,const int width,const int height,const int ch,const int use_sse)2759 static void image_lab2rgb(float *img_src, const int width, const int height, const int ch, const int use_sse)
2760 {
2761   const int stride = width * height * ch;
2762 
2763 #if defined(__SSE__)
2764   if(ch == 4 && use_sse)
2765   {
2766 #ifdef _OPENMP
2767 #pragma omp parallel for default(none) \
2768   dt_omp_firstprivate(ch, stride) \
2769   shared(img_src) \
2770   schedule(static)
2771 #endif
2772     for(int i = 0; i < stride; i += ch)
2773     {
2774       // Lab -> XYZ
2775       __m128 Lab = _mm_load_ps(img_src + i);
2776       __m128 XYZ = dt_Lab_to_XYZ_sse2(Lab);
2777       // XYZ -> RGB
2778       _mm_store_ps(img_src + i, dt_XYZ_to_RGB_sse2(XYZ));
2779     }
2780 
2781     return;
2782   }
2783 #endif
2784 
2785 #ifdef _OPENMP
2786 #pragma omp parallel for default(none) \
2787   dt_omp_firstprivate(ch, stride) \
2788   shared(img_src) \
2789   schedule(static)
2790 #endif
2791   for(int i = 0; i < stride; i += ch)
2792   {
2793     dt_aligned_pixel_t XYZ;
2794 
2795     dt_Lab_to_XYZ(img_src + i, XYZ);
2796     dt_XYZ_to_linearRGB(XYZ, img_src + i);
2797   }
2798 }
2799 
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])2800 static void rt_process_stats(struct dt_iop_module_t *self, dt_dev_pixelpipe_iop_t *piece, float *const img_src,
2801                              const int width, const int height, const int ch, float levels[3])
2802 {
2803   const int size = width * height * ch;
2804   float l_max = -INFINITY;
2805   float l_min = INFINITY;
2806   float l_sum = 0.f;
2807   int count = 0;
2808   const dt_iop_order_iccprofile_info_t *const work_profile = dt_ioppr_get_pipe_work_profile_info(piece->pipe);
2809 
2810 #ifdef _OPENMP
2811 #pragma omp parallel for default(none) \
2812   dt_omp_firstprivate(ch, img_src, size, work_profile) \
2813   schedule(static) \
2814   reduction(+ : count, l_sum) \
2815   reduction(max : l_max) \
2816   reduction(min : l_min)
2817 #endif
2818   for(int i = 0; i < size; i += ch)
2819   {
2820     dt_aligned_pixel_t Lab = { 0 };
2821 
2822     if(work_profile)
2823     {
2824       dt_ioppr_rgb_matrix_to_lab(img_src + i, Lab, work_profile->matrix_in_transposed,
2825                                   work_profile->lut_in, work_profile->unbounded_coeffs_in,
2826                                   work_profile->lutsize, work_profile->nonlinearlut);
2827     }
2828     else
2829     {
2830       dt_aligned_pixel_t XYZ;
2831       dt_linearRGB_to_XYZ(img_src + i, XYZ);
2832       dt_XYZ_to_Lab(XYZ, Lab);
2833     }
2834 
2835     l_max = MAX(l_max, Lab[0]);
2836     l_min = MIN(l_min, Lab[0]);
2837     l_sum += Lab[0];
2838     count++;
2839   }
2840 
2841   levels[0] = l_min / 100.f;
2842   levels[2] = l_max / 100.f;
2843   levels[1] = (l_sum / (float)count) / 100.f;
2844 }
2845 
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])2846 static void rt_adjust_levels(dt_iop_module_t *self, dt_dev_pixelpipe_iop_t *piece, float *img_src, const int width,
2847                              const int height, const int ch, const float levels[3])
2848 {
2849   const int size = width * height * ch;
2850   const dt_iop_order_iccprofile_info_t *const work_profile = dt_ioppr_get_pipe_work_profile_info(piece->pipe);
2851 
2852   const float left = levels[0];
2853   const float middle = levels[1];
2854   const float right = levels[2];
2855 
2856   if(left == RETOUCH_PREVIEW_LVL_MIN && middle == 0.f && right == RETOUCH_PREVIEW_LVL_MAX) return;
2857 
2858   const float delta = (right - left) / 2.0f;
2859   const float mid = left + delta;
2860   const float tmp = (middle - mid) / delta;
2861   const float in_inv_gamma = powf(10, tmp);
2862 
2863 #ifdef _OPENMP
2864 #pragma omp parallel for default(none) \
2865   dt_omp_firstprivate(ch, in_inv_gamma, left, right, size, work_profile) \
2866   shared(img_src) \
2867   schedule(static)
2868 #endif
2869   for(int i = 0; i < size; i += ch)
2870   {
2871     if(work_profile)
2872     {
2873       dt_ioppr_rgb_matrix_to_lab(img_src + i, img_src + i, work_profile->matrix_in_transposed,
2874                                   work_profile->lut_in, work_profile->unbounded_coeffs_in,
2875                                   work_profile->lutsize, work_profile->nonlinearlut);
2876     }
2877     else
2878     {
2879       dt_aligned_pixel_t XYZ;
2880 
2881       dt_linearRGB_to_XYZ(img_src + i, XYZ);
2882       dt_XYZ_to_Lab(XYZ, img_src + i);
2883     }
2884 
2885     for(int c = 0; c < 1; c++)
2886     {
2887       const float L_in = img_src[i + c] / 100.0f;
2888 
2889       if(L_in <= left)
2890       {
2891         img_src[i + c] = 0.f;
2892       }
2893       else
2894       {
2895         const float percentage = (L_in - left) / (right - left);
2896         img_src[i + c] = 100.0f * powf(percentage, in_inv_gamma);
2897       }
2898     }
2899 
2900     if(work_profile)
2901     {
2902       dt_ioppr_lab_to_rgb_matrix(img_src + i, img_src + i, work_profile->matrix_out_transposed,
2903                                  work_profile->lut_out, work_profile->unbounded_coeffs_out,
2904                                  work_profile->lutsize, work_profile->nonlinearlut);;
2905     }
2906     else
2907     {
2908       dt_aligned_pixel_t XYZ;
2909 
2910       dt_Lab_to_XYZ(img_src + i, XYZ);
2911       dt_XYZ_to_linearRGB(XYZ, img_src + i);
2912     }
2913   }
2914 }
2915 
2916 #undef RT_WDBAR_INSET
2917 
2918 #undef RETOUCH_NO_FORMS
2919 #undef RETOUCH_MAX_SCALES
2920 #undef RETOUCH_NO_SCALES
2921 
2922 #undef RETOUCH_PREVIEW_LVL_MIN
2923 #undef RETOUCH_PREVIEW_LVL_MAX
2924 
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)2925 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,
2926                                 const int padding, dt_iop_roi_t *roi_dest)
2927 {
2928   const int x_from = MAX(MAX((roi_1->x + 1 - padding), roi_2->x), (roi_2->x + dx));
2929   const int x_to
2930       = MIN(MIN((roi_1->x + roi_1->width + 1 + padding), roi_2->x + roi_2->width), (roi_2->x + roi_2->width + dx));
2931 
2932   const int y_from = MAX(MAX((roi_1->y + 1 - padding), roi_2->y), (roi_2->y + dy));
2933   const int y_to = MIN(MIN((roi_1->y + roi_1->height + 1 + padding), (roi_2->y + roi_2->height)),
2934                        (roi_2->y + roi_2->height + dy));
2935 
2936   roi_dest->x = x_from;
2937   roi_dest->y = y_from;
2938   roi_dest->width = x_to - x_from;
2939   roi_dest->height = y_to - y_from;
2940 }
2941 
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)2942 static void rt_copy_in_to_out(const float *const in, const struct dt_iop_roi_t *const roi_in, float *const out,
2943                               const struct dt_iop_roi_t *const roi_out, const int ch, const int dx, const int dy)
2944 {
2945   const size_t rowsize = sizeof(float) * ch * MIN(roi_out->width, roi_in->width);
2946   const int xoffs = roi_out->x - roi_in->x - dx;
2947   const int yoffs = roi_out->y - roi_in->y - dy;
2948   const int y_to = MIN(roi_out->height, roi_in->height);
2949 
2950 #ifdef _OPENMP
2951 #pragma omp parallel for default(none) \
2952   dt_omp_firstprivate(ch, in, out, roi_in, roi_out, rowsize, xoffs,  yoffs, \
2953                       y_to) \
2954   schedule(static)
2955 #endif
2956   for(int y = 0; y < y_to; y++)
2957   {
2958     const size_t iindex = ((size_t)(y + yoffs) * roi_in->width + xoffs) * ch;
2959     const size_t oindex = (size_t)y * roi_out->width * ch;
2960     float *in1 = (float *)in + iindex;
2961     float *out1 = (float *)out + oindex;
2962 
2963     memcpy(out1, in1, rowsize);
2964   }
2965 }
2966 
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)2967 static void rt_build_scaled_mask(float *const mask, dt_iop_roi_t *const roi_mask, float **mask_scaled,
2968                                  dt_iop_roi_t *roi_mask_scaled, dt_iop_roi_t *const roi_in, const int dx,
2969                                  const int dy, const int algo)
2970 {
2971   float *mask_tmp = NULL;
2972 
2973   const int padding = (algo == DT_IOP_RETOUCH_HEAL) ? 1 : 0;
2974 
2975   *roi_mask_scaled = *roi_mask;
2976 
2977   roi_mask_scaled->x = roi_mask->x * roi_in->scale;
2978   roi_mask_scaled->y = roi_mask->y * roi_in->scale;
2979   roi_mask_scaled->width = ((roi_mask->width * roi_in->scale) + .5f);
2980   roi_mask_scaled->height = ((roi_mask->height * roi_in->scale) + .5f);
2981   roi_mask_scaled->scale = roi_in->scale;
2982 
2983   rt_intersect_2_rois(roi_mask_scaled, roi_in, dx, dy, padding, roi_mask_scaled);
2984   if(roi_mask_scaled->width < 1 || roi_mask_scaled->height < 1) goto cleanup;
2985 
2986   const int x_to = roi_mask_scaled->width + roi_mask_scaled->x;
2987   const int y_to = roi_mask_scaled->height + roi_mask_scaled->y;
2988 
2989   mask_tmp = dt_alloc_align_float((size_t)roi_mask_scaled->width * roi_mask_scaled->height);
2990   if(mask_tmp == NULL)
2991   {
2992     fprintf(stderr, "rt_build_scaled_mask: error allocating memory\n");
2993     goto cleanup;
2994   }
2995   dt_iop_image_fill(mask_tmp, 0.0f, roi_mask_scaled->width, roi_mask_scaled->height, 1);
2996 
2997 #ifdef _OPENMP
2998 #pragma omp parallel for default(none) \
2999   dt_omp_firstprivate(mask, roi_in, roi_mask, x_to, y_to) \
3000   shared(mask_tmp, roi_mask_scaled) \
3001   schedule(static)
3002 #endif
3003   for(int yy = roi_mask_scaled->y; yy < y_to; yy++)
3004   {
3005     const int mask_index = ((int)(yy / roi_in->scale)) - roi_mask->y;
3006     if(mask_index < 0 || mask_index >= roi_mask->height) continue;
3007 
3008     const int mask_scaled_index = (yy - roi_mask_scaled->y) * roi_mask_scaled->width;
3009 
3010     const float *m = mask + mask_index * roi_mask->width;
3011     float *ms = mask_tmp + mask_scaled_index;
3012 
3013     for(int xx = roi_mask_scaled->x; xx < x_to; xx++, ms++)
3014     {
3015       const int mx = ((int)(xx / roi_in->scale)) - roi_mask->x;
3016       if(mx < 0 || mx >= roi_mask->width) continue;
3017 
3018       *ms = m[mx];
3019     }
3020   }
3021 
3022 cleanup:
3023   *mask_scaled = mask_tmp;
3024 }
3025 
3026 // 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,float * const mask_scaled,dt_iop_roi_t * const roi_mask_scaled,const float opacity)3027 static void rt_copy_image_masked(float *const img_src, float *img_dest, dt_iop_roi_t *const roi_dest,
3028                                  float *const mask_scaled, dt_iop_roi_t *const roi_mask_scaled,
3029                                  const float opacity)
3030 {
3031 #ifdef _OPENMP
3032 #pragma omp parallel for default(none) \
3033     dt_omp_firstprivate(img_src, mask_scaled, opacity, roi_dest, roi_mask_scaled) \
3034     shared(img_dest) \
3035     schedule(static)
3036 #endif
3037   for(int yy = 0; yy < roi_mask_scaled->height; yy++)
3038   {
3039     const int mask_index = yy * roi_mask_scaled->width;
3040     const int src_index = 4 * mask_index;
3041     const int dest_index
3042       = 4 * (((yy + roi_mask_scaled->y - roi_dest->y) * roi_dest->width) + (roi_mask_scaled->x - roi_dest->x));
3043 
3044     const float *s = img_src + src_index;
3045     const float *m = mask_scaled + mask_index;
3046     float *d = img_dest + dest_index;
3047 
3048     for(int xx = 0; xx < roi_mask_scaled->width; xx++)
3049     {
3050       const float f = m[xx] * opacity;
3051       const float f1 = (1.0f - f);
3052 
3053       for_each_channel(c,aligned(s,d))
3054       {
3055         d[4*xx + c] = d[4*xx + c] * f1 + s[4*xx + c] * f;
3056       }
3057     }
3058   }
3059 }
3060 
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)3061 static void rt_copy_mask_to_alpha(float *const img, dt_iop_roi_t *const roi_img, const int ch,
3062                                   float *const mask_scaled, dt_iop_roi_t *const roi_mask_scaled,
3063                                   const float opacity)
3064 {
3065 #ifdef _OPENMP
3066 #pragma omp parallel for default(none) \
3067   dt_omp_firstprivate(ch, img, mask_scaled, opacity, roi_img, roi_mask_scaled) \
3068   schedule(static)
3069 #endif
3070   for(int yy = 0; yy < roi_mask_scaled->height; yy++)
3071   {
3072     const int mask_index = yy * roi_mask_scaled->width;
3073     const int dest_index
3074         = (((yy + roi_mask_scaled->y - roi_img->y) * roi_img->width) + (roi_mask_scaled->x - roi_img->x)) * ch;
3075 
3076     float *d = img + dest_index;
3077     const float *m = mask_scaled + mask_index;
3078 
3079     for(int xx = 0; xx < roi_mask_scaled->width; xx++, d += ch, m++)
3080     {
3081       const float f = (*m) * opacity;
3082       if(f > d[3]) d[3] = f;
3083     }
3084   }
3085 }
3086 
retouch_fill(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)3087 static void retouch_fill(float *const in, dt_iop_roi_t *const roi_in, float *const mask_scaled,
3088                          dt_iop_roi_t *const roi_mask_scaled, const float opacity, const float *const fill_color)
3089 {
3090 #ifdef _OPENMP
3091 #pragma omp parallel for default(none) \
3092   dt_omp_firstprivate(fill_color, in, mask_scaled, opacity, roi_in, roi_mask_scaled) \
3093   schedule(static)
3094 #endif
3095   for(int yy = 0; yy < roi_mask_scaled->height; yy++)
3096   {
3097     const int mask_index = yy * roi_mask_scaled->width;
3098     const int dest_index
3099         = (((yy + roi_mask_scaled->y - roi_in->y) * roi_in->width) + (roi_mask_scaled->x - roi_in->x)) * 4;
3100 
3101     float *d = in + dest_index;
3102     const float *m = mask_scaled + mask_index;
3103 
3104     for(int xx = 0; xx < roi_mask_scaled->width; xx++)
3105     {
3106       const float f = m[xx] * opacity;
3107 
3108       for_each_channel(c,aligned(d,fill_color))
3109         d[4*xx + c] = d[4*xx + c] * (1.0f - f) + fill_color[c] * f;
3110     }
3111   }
3112 }
3113 
retouch_clone(float * const in,dt_iop_roi_t * const roi_in,float * const mask_scaled,dt_iop_roi_t * const roi_mask_scaled,const int dx,const int dy,const float opacity)3114 static void retouch_clone(float *const in, dt_iop_roi_t *const roi_in, float *const mask_scaled,
3115                           dt_iop_roi_t *const roi_mask_scaled, const int dx, const int dy, const float opacity)
3116 {
3117   // alloc temp image to avoid issues when areas self-intersects
3118   float *img_src = dt_alloc_align_float((size_t)4 * roi_mask_scaled->width * roi_mask_scaled->height);
3119   if(img_src == NULL)
3120   {
3121     fprintf(stderr, "retouch_clone: error allocating memory for cloning\n");
3122     goto cleanup;
3123   }
3124 
3125   // copy source image to tmp
3126   rt_copy_in_to_out(in, roi_in, img_src, roi_mask_scaled, 4, dx, dy);
3127 
3128   // clone it
3129   rt_copy_image_masked(img_src, in, roi_in, mask_scaled, roi_mask_scaled, opacity);
3130 
3131 cleanup:
3132   if(img_src) dt_free_align(img_src);
3133 }
3134 
retouch_blur(dt_iop_module_t * self,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 int blur_type,const float blur_radius,dt_dev_pixelpipe_iop_t * piece,const int use_sse)3135 static void retouch_blur(dt_iop_module_t *self, float *const in, dt_iop_roi_t *const roi_in, float *const mask_scaled,
3136                          dt_iop_roi_t *const roi_mask_scaled, const float opacity, const int blur_type,
3137                          const float blur_radius, dt_dev_pixelpipe_iop_t *piece, const int use_sse)
3138 {
3139   if(fabsf(blur_radius) <= 0.1f) return;
3140 
3141   const float sigma = blur_radius * roi_in->scale / piece->iscale;
3142 
3143   float *img_dest = NULL;
3144 
3145   // alloc temp image to blur
3146   img_dest = dt_alloc_align_float((size_t)4 * roi_mask_scaled->width * roi_mask_scaled->height);
3147   if(img_dest == NULL)
3148   {
3149     fprintf(stderr, "retouch_blur: error allocating memory for blurring\n");
3150     goto cleanup;
3151   }
3152 
3153   // copy source image so we blur just the mask area (at least the smallest rect that covers it)
3154   rt_copy_in_to_out(in, roi_in, img_dest, roi_mask_scaled, 4, 0, 0);
3155 
3156   if(blur_type == DT_IOP_RETOUCH_BLUR_GAUSSIAN && fabsf(blur_radius) > 0.1f)
3157   {
3158     float Labmax[] = { INFINITY, INFINITY, INFINITY, INFINITY };
3159     float Labmin[] = { -INFINITY, -INFINITY, -INFINITY, -INFINITY };
3160 
3161     dt_gaussian_t *g = dt_gaussian_init(roi_mask_scaled->width, roi_mask_scaled->height, 4, Labmax, Labmin, sigma,
3162                                         DT_IOP_GAUSSIAN_ZERO);
3163     if(g)
3164     {
3165       dt_gaussian_blur_4c(g, img_dest, img_dest);
3166       dt_gaussian_free(g);
3167     }
3168   }
3169   else if(blur_type == DT_IOP_RETOUCH_BLUR_BILATERAL && fabsf(blur_radius) > 0.1f)
3170   {
3171     const float sigma_r = 100.0f; // does not depend on scale
3172     const float sigma_s = sigma;
3173     const float detail = -1.0f; // we want the bilateral base layer
3174 
3175     dt_bilateral_t *b = dt_bilateral_init(roi_mask_scaled->width, roi_mask_scaled->height, sigma_s, sigma_r);
3176     if(b)
3177     {
3178       int converted_cst;
3179       const dt_iop_order_iccprofile_info_t *const work_profile = dt_ioppr_get_pipe_work_profile_info(piece->pipe);
3180 
3181       if(work_profile)
3182         dt_ioppr_transform_image_colorspace(self, img_dest, img_dest, roi_mask_scaled->width,
3183                                             roi_mask_scaled->height, iop_cs_rgb, iop_cs_Lab, &converted_cst,
3184                                             work_profile);
3185       else
3186         image_rgb2lab(img_dest, roi_mask_scaled->width, roi_mask_scaled->height, 4, use_sse);
3187 
3188       dt_bilateral_splat(b, img_dest);
3189       dt_bilateral_blur(b);
3190       dt_bilateral_slice(b, img_dest, img_dest, detail);
3191       dt_bilateral_free(b);
3192 
3193       if(work_profile)
3194         dt_ioppr_transform_image_colorspace(self, img_dest, img_dest, roi_mask_scaled->width,
3195                                             roi_mask_scaled->height, iop_cs_Lab, iop_cs_rgb, &converted_cst,
3196                                             work_profile);
3197       else
3198         image_lab2rgb(img_dest, roi_mask_scaled->width, roi_mask_scaled->height, 4, use_sse);
3199     }
3200   }
3201 
3202   // copy blurred (temp) image to destination image
3203   rt_copy_image_masked(img_dest, in, roi_in, mask_scaled, roi_mask_scaled, opacity);
3204 
3205 cleanup:
3206   if(img_dest) dt_free_align(img_dest);
3207 }
3208 
retouch_heal(float * const in,dt_iop_roi_t * const roi_in,float * const mask_scaled,dt_iop_roi_t * const roi_mask_scaled,const int dx,const int dy,const float opacity)3209 static void retouch_heal(float *const in, dt_iop_roi_t *const roi_in, float *const mask_scaled,
3210                          dt_iop_roi_t *const roi_mask_scaled, const int dx, const int dy, const float opacity)
3211 {
3212   float *img_src = NULL;
3213   float *img_dest = NULL;
3214 
3215   // alloc temp images for source and destination
3216   img_src  = dt_alloc_align_float((size_t)4 * roi_mask_scaled->width * roi_mask_scaled->height);
3217   img_dest = dt_alloc_align_float((size_t)4 * roi_mask_scaled->width * roi_mask_scaled->height);
3218   if((img_src == NULL) || (img_dest == NULL))
3219   {
3220     fprintf(stderr, "retouch_heal: error allocating memory for healing\n");
3221     goto cleanup;
3222   }
3223 
3224   // copy source and destination to temp images
3225   rt_copy_in_to_out(in, roi_in, img_src, roi_mask_scaled, 4, dx, dy);
3226   rt_copy_in_to_out(in, roi_in, img_dest, roi_mask_scaled, 4, 0, 0);
3227 
3228   // heal it
3229   dt_heal(img_src, img_dest, mask_scaled, roi_mask_scaled->width, roi_mask_scaled->height, 4);
3230 
3231   // copy healed (temp) image to destination image
3232   rt_copy_image_masked(img_dest, in, roi_in, mask_scaled, roi_mask_scaled, opacity);
3233 
3234 cleanup:
3235   if(img_src) dt_free_align(img_src);
3236   if(img_dest) dt_free_align(img_dest);
3237 }
3238 
rt_process_forms(float * layer,dwt_params_t * const wt_p,const int scale1)3239 static void rt_process_forms(float *layer, dwt_params_t *const wt_p, const int scale1)
3240 {
3241   int scale = scale1;
3242   retouch_user_data_t *usr_d = (retouch_user_data_t *)wt_p->user_data;
3243   dt_iop_module_t *self = usr_d->self;
3244   dt_dev_pixelpipe_iop_t *piece = usr_d->piece;
3245 
3246   // if preview a single scale, just process that scale and original image
3247   // unless merge is activated
3248   if(wt_p->merge_from_scale == 0 && wt_p->return_layer > 0 && scale != wt_p->return_layer && scale != 0) return;
3249   // do not process the reconstructed image
3250   if(scale > wt_p->scales + 1) return;
3251 
3252   dt_develop_blend_params_t *bp = (dt_develop_blend_params_t *)piece->blendop_data;
3253   dt_iop_retouch_params_t *p = (dt_iop_retouch_params_t *)piece->data;
3254   dt_iop_roi_t *roi_layer = &usr_d->roi;
3255   const int mask_display = usr_d->mask_display && (scale == usr_d->display_scale);
3256 
3257   // when the requested scales is grather than max scales the residual image index will be different from the one
3258   // defined by the user,
3259   // so we need to adjust it here, otherwise we will be using the shapes from a scale on the residual image
3260   if(wt_p->scales < p->num_scales && wt_p->return_layer == 0 && scale == wt_p->scales + 1)
3261   {
3262     scale = p->num_scales + 1;
3263   }
3264 
3265   // iterate through all forms
3266   if(!usr_d->suppress_mask)
3267   {
3268     const dt_masks_form_t *grp = dt_masks_get_from_id_ext(piece->pipe->forms, bp->mask_id);
3269     if(grp && (grp->type & DT_MASKS_GROUP))
3270     {
3271       for(const GList *forms = grp->points; forms; forms = g_list_next(forms))
3272       {
3273         const dt_masks_point_group_t *grpt = (dt_masks_point_group_t *)forms->data;
3274         if(grpt == NULL)
3275         {
3276           fprintf(stderr, "rt_process_forms: invalid form\n");
3277           continue;
3278         }
3279         const int formid = grpt->formid;
3280         const float form_opacity = grpt->opacity;
3281         if(formid == 0)
3282         {
3283           fprintf(stderr, "rt_process_forms: form is null\n");
3284           continue;
3285         }
3286         const int index = rt_get_index_from_formid(p, formid);
3287         if(index == -1)
3288         {
3289           // FIXME: we get this error when user go back in history, so forms are the same but the array has changed
3290           fprintf(stderr, "rt_process_forms: missing form=%i from array\n", formid);
3291           continue;
3292         }
3293 
3294         // only process current scale
3295         if(p->rt_forms[index].scale != scale)
3296         {
3297           continue;
3298         }
3299 
3300         // get the spot
3301         dt_masks_form_t *form = dt_masks_get_from_id_ext(piece->pipe->forms, formid);
3302         if(form == NULL)
3303         {
3304           fprintf(stderr, "rt_process_forms: missing form=%i from masks\n", formid);
3305           continue;
3306         }
3307 
3308         // if the form is outside the roi, we just skip it
3309         if(!rt_masks_form_is_in_roi(self, piece, form, roi_layer, roi_layer))
3310         {
3311           continue;
3312         }
3313 
3314         // get the mask
3315         float *mask = NULL;
3316         dt_iop_roi_t roi_mask = { 0 };
3317 
3318         dt_masks_get_mask(self, piece, form, &mask, &roi_mask.width, &roi_mask.height, &roi_mask.x, &roi_mask.y);
3319         if(mask == NULL)
3320         {
3321           fprintf(stderr, "rt_process_forms: error retrieving mask\n");
3322           continue;
3323         }
3324 
3325         // search the delta with the source
3326         const dt_iop_retouch_algo_type_t algo = p->rt_forms[index].algorithm;
3327         int dx = 0, dy = 0;
3328 
3329         if(algo != DT_IOP_RETOUCH_BLUR && algo != DT_IOP_RETOUCH_FILL)
3330         {
3331           if(!rt_masks_get_delta_to_destination(self, piece, roi_layer, form, &dx, &dy,
3332                                                 p->rt_forms[index].distort_mode))
3333           {
3334             if(mask) dt_free_align(mask);
3335             continue;
3336           }
3337         }
3338 
3339         // scale the mask
3340         float *mask_scaled = NULL;
3341         dt_iop_roi_t roi_mask_scaled = { 0 };
3342 
3343         rt_build_scaled_mask(mask, &roi_mask, &mask_scaled, &roi_mask_scaled, roi_layer, dx, dy, algo);
3344 
3345         // we don't need the original mask anymore
3346         if(mask)
3347         {
3348           dt_free_align(mask);
3349           mask = NULL;
3350         }
3351 
3352         if(mask_scaled == NULL)
3353         {
3354           continue;
3355         }
3356 
3357         if((dx != 0 || dy != 0 || algo == DT_IOP_RETOUCH_BLUR || algo == DT_IOP_RETOUCH_FILL)
3358            && ((roi_mask_scaled.width > 2) && (roi_mask_scaled.height > 2)))
3359         {
3360           if(algo == DT_IOP_RETOUCH_CLONE)
3361           {
3362             retouch_clone(layer, roi_layer, mask_scaled, &roi_mask_scaled, dx, dy, form_opacity);
3363           }
3364           else if(algo == DT_IOP_RETOUCH_HEAL)
3365           {
3366             retouch_heal(layer, roi_layer, mask_scaled, &roi_mask_scaled, dx, dy, form_opacity);
3367           }
3368           else if(algo == DT_IOP_RETOUCH_BLUR)
3369           {
3370             retouch_blur(self, layer, roi_layer, mask_scaled, &roi_mask_scaled, form_opacity,
3371                          p->rt_forms[index].blur_type, p->rt_forms[index].blur_radius, piece, wt_p->use_sse);
3372           }
3373           else if(algo == DT_IOP_RETOUCH_FILL)
3374           {
3375             // add a brightness to the color so it can be fine-adjusted by the user
3376             dt_aligned_pixel_t fill_color;
3377 
3378             if(p->rt_forms[index].fill_mode == DT_IOP_RETOUCH_FILL_ERASE)
3379             {
3380               fill_color[0] = fill_color[1] = fill_color[2] = p->rt_forms[index].fill_brightness;
3381             }
3382             else
3383             {
3384               fill_color[0] = p->rt_forms[index].fill_color[0] + p->rt_forms[index].fill_brightness;
3385               fill_color[1] = p->rt_forms[index].fill_color[1] + p->rt_forms[index].fill_brightness;
3386               fill_color[2] = p->rt_forms[index].fill_color[2] + p->rt_forms[index].fill_brightness;
3387             }
3388             fill_color[3] = 0.0f;
3389 
3390             retouch_fill(layer, roi_layer, mask_scaled, &roi_mask_scaled, form_opacity, fill_color);
3391           }
3392           else
3393             fprintf(stderr, "rt_process_forms: unknown algorithm %i\n", algo);
3394 
3395           if(mask_display)
3396             rt_copy_mask_to_alpha(layer, roi_layer, wt_p->ch, mask_scaled, &roi_mask_scaled, form_opacity);
3397         }
3398 
3399         if(mask) dt_free_align(mask);
3400         if(mask_scaled) dt_free_align(mask_scaled);
3401       }
3402     }
3403   }
3404 }
3405 
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)3406 static void process_internal(struct dt_iop_module_t *self, dt_dev_pixelpipe_iop_t *piece, const void *const ivoid,
3407                              void *const ovoid, const dt_iop_roi_t *const roi_in,
3408                              const dt_iop_roi_t *const roi_out, const int use_sse)
3409 {
3410   if (!dt_iop_have_required_input_format(4 /*we need full-color pixels*/, self, piece->colors,
3411                                          ivoid, ovoid, roi_in, roi_out))
3412     return;
3413 
3414   dt_iop_retouch_params_t *p = (dt_iop_retouch_params_t *)piece->data;
3415   dt_iop_retouch_gui_data_t *g = (dt_iop_retouch_gui_data_t *)self->gui_data;
3416 
3417   float *in_retouch = NULL;
3418 
3419   dt_iop_roi_t roi_retouch = *roi_in;
3420   dt_iop_roi_t *roi_rt = &roi_retouch;
3421 
3422   retouch_user_data_t usr_data = { 0 };
3423   dwt_params_t *dwt_p = NULL;
3424 
3425   const int gui_active = (self->dev) ? (self == self->dev->gui_module) : 0;
3426   const int display_wavelet_scale = (g && gui_active) ? g->display_wavelet_scale : 0;
3427 
3428   // we will do all the clone, heal, etc on the input image,
3429   // this way the source for one algorithm can be the destination from a previous one
3430   in_retouch = dt_alloc_align_float((size_t)4 * roi_rt->width * roi_rt->height);
3431   if(in_retouch == NULL) goto cleanup;
3432 
3433   dt_iop_image_copy_by_size(in_retouch, ivoid, roi_rt->width, roi_rt->height, 4);
3434 
3435   // user data passed from the decompose routine to the one that process each scale
3436   usr_data.self = self;
3437   usr_data.piece = piece;
3438   usr_data.roi = *roi_rt;
3439   usr_data.mask_display = 0;
3440   usr_data.suppress_mask = (g && g->suppress_mask && self->dev->gui_attached && (self == self->dev->gui_module)
3441                             && (piece->pipe == self->dev->pipe));
3442   usr_data.display_scale = p->curr_scale;
3443 
3444   // init the decompose routine
3445   dwt_p = dt_dwt_init(in_retouch, roi_rt->width, roi_rt->height, 4, p->num_scales,
3446                       (!display_wavelet_scale || (piece->pipe->type & DT_DEV_PIXELPIPE_FULL) != DT_DEV_PIXELPIPE_FULL) ? 0 : p->curr_scale,
3447                       p->merge_from_scale, &usr_data,
3448                       roi_in->scale / piece->iscale, use_sse);
3449   if(dwt_p == NULL) goto cleanup;
3450 
3451   // check if this module should expose mask.
3452   if((piece->pipe->type & DT_DEV_PIXELPIPE_FULL) == DT_DEV_PIXELPIPE_FULL && g
3453      && (g->mask_display || display_wavelet_scale) && self->dev->gui_attached
3454      && (self == self->dev->gui_module) && (piece->pipe == self->dev->pipe))
3455   {
3456     for(size_t j = 0; j < (size_t)roi_rt->width * roi_rt->height * 4; j += 4) in_retouch[j + 3] = 0.f;
3457 
3458     piece->pipe->mask_display = g->mask_display ? DT_DEV_PIXELPIPE_DISPLAY_MASK : DT_DEV_PIXELPIPE_DISPLAY_PASSTHRU;
3459     piece->pipe->bypass_blendif = 1;
3460     usr_data.mask_display = 1;
3461   }
3462 
3463   if((piece->pipe->type & DT_DEV_PIXELPIPE_FULL) == DT_DEV_PIXELPIPE_FULL)
3464   {
3465     // check if the image support this number of scales
3466     if(gui_active)
3467     {
3468       const int max_scales = dwt_get_max_scale(dwt_p);
3469       if(dwt_p->scales > max_scales)
3470       {
3471         dt_control_log(_("max scale is %i for this image size"), max_scales);
3472       }
3473     }
3474     // get first scale visible at this zoom level
3475     if(g) g->first_scale_visible = dt_dwt_first_scale_visible(dwt_p);
3476   }
3477 
3478   // decompose it
3479   dwt_decompose(dwt_p, rt_process_forms);
3480 
3481   dt_aligned_pixel_t levels = { p->preview_levels[0], p->preview_levels[1], p->preview_levels[2] };
3482 
3483   // process auto levels
3484   if(g && (piece->pipe->type & DT_DEV_PIXELPIPE_FULL) == DT_DEV_PIXELPIPE_FULL)
3485   {
3486     dt_iop_gui_enter_critical_section(self);
3487     if(g->preview_auto_levels == 1 && !darktable.gui->reset)
3488     {
3489       g->preview_auto_levels = -1;
3490 
3491       dt_iop_gui_leave_critical_section(self);
3492 
3493       levels[0] = levels[1] = levels[2] = 0;
3494       rt_process_stats(self, piece, in_retouch, roi_rt->width, roi_rt->height, 4, levels);
3495       rt_clamp_minmax(levels, levels);
3496 
3497       for(int i = 0; i < 3; i++) g->preview_levels[i] = levels[i];
3498 
3499       dt_iop_gui_enter_critical_section(self);
3500       g->preview_auto_levels = 2;
3501       dt_iop_gui_leave_critical_section(self);
3502     }
3503     else
3504     {
3505       dt_iop_gui_leave_critical_section(self);
3506     }
3507   }
3508 
3509   // if user wants to preview a detail scale adjust levels
3510   if(dwt_p->return_layer > 0 && dwt_p->return_layer < dwt_p->scales + 1)
3511   {
3512     rt_adjust_levels(self, piece, in_retouch, roi_rt->width, roi_rt->height, 4, levels);
3513   }
3514 
3515   // copy alpha channel if needed
3516   if((piece->pipe->mask_display & DT_DEV_PIXELPIPE_DISPLAY_MASK) && g && !g->mask_display)
3517   {
3518     dt_iop_alpha_copy(ivoid, in_retouch, roi_rt->width, roi_rt->height);
3519   }
3520 
3521   // return final image
3522   rt_copy_in_to_out(in_retouch, roi_rt, ovoid, roi_out, 4, 0, 0);
3523 
3524 cleanup:
3525   if(in_retouch) dt_free_align(in_retouch);
3526   if(dwt_p) dt_dwt_free(dwt_p);
3527 }
3528 
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)3529 void process(struct dt_iop_module_t *self, dt_dev_pixelpipe_iop_t *piece, const void *const ivoid,
3530              void *const ovoid, const dt_iop_roi_t *const roi_in, const dt_iop_roi_t *const roi_out)
3531 {
3532   process_internal(self, piece, ivoid, ovoid, roi_in, roi_out, 0);
3533 }
3534 
3535 #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)3536 void process_sse2(struct dt_iop_module_t *self, dt_dev_pixelpipe_iop_t *piece, const void *const ivoid,
3537                   void *const ovoid, const dt_iop_roi_t *const roi_in, const dt_iop_roi_t *const roi_out)
3538 {
3539   process_internal(self, piece, ivoid, ovoid, roi_in, roi_out, 1);
3540 }
3541 #endif
3542 
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)3543 void distort_mask(struct dt_iop_module_t *self, struct dt_dev_pixelpipe_iop_t *piece, const float *const in,
3544                   float *const out, const dt_iop_roi_t *const roi_in, const dt_iop_roi_t *const roi_out)
3545 {
3546   rt_copy_in_to_out(in, roi_in, out, roi_out, 1, 0, 0);
3547 }
3548 
3549 #ifdef HAVE_OPENCL
3550 
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])3551 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,
3552                            const int width, const int height, float levels[3])
3553 {
3554   cl_int err = CL_SUCCESS;
3555 
3556   const int ch = 4;
3557 
3558   float *src_buffer = NULL;
3559 
3560   src_buffer = dt_alloc_align_float((size_t)ch * width * height);
3561   if(src_buffer == NULL)
3562   {
3563     fprintf(stderr, "dt_heal_cl: error allocating memory for healing\n");
3564     err = CL_MEM_OBJECT_ALLOCATION_FAILURE;
3565     goto cleanup;
3566   }
3567 
3568   err = dt_opencl_read_buffer_from_device(devid, (void *)src_buffer, dev_img, 0,
3569                                           (size_t)width * height * ch * sizeof(float), CL_TRUE);
3570   if(err != CL_SUCCESS)
3571   {
3572     goto cleanup;
3573   }
3574 
3575   // just call the CPU version for now
3576   rt_process_stats(self, piece, src_buffer, width, height, ch, levels);
3577 
3578   err = dt_opencl_write_buffer_to_device(devid, src_buffer, dev_img, 0, sizeof(float) * ch * width * height, TRUE);
3579   if(err != CL_SUCCESS)
3580   {
3581     goto cleanup;
3582   }
3583 
3584 cleanup:
3585   if(src_buffer) dt_free_align(src_buffer);
3586 
3587   return err;
3588 }
3589 
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])3590 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,
3591                            const int width, const int height, const float levels[3])
3592 {
3593   cl_int err = CL_SUCCESS;
3594 
3595   const int ch = 4;
3596 
3597   float *src_buffer = NULL;
3598 
3599   src_buffer = dt_alloc_align_float((size_t)ch * width * height);
3600   if(src_buffer == NULL)
3601   {
3602     fprintf(stderr, "dt_heal_cl: error allocating memory for healing\n");
3603     err = CL_MEM_OBJECT_ALLOCATION_FAILURE;
3604     goto cleanup;
3605   }
3606 
3607   err = dt_opencl_read_buffer_from_device(devid, (void *)src_buffer, dev_img, 0,
3608                                           (size_t)width * height * ch * sizeof(float), CL_TRUE);
3609   if(err != CL_SUCCESS)
3610   {
3611     goto cleanup;
3612   }
3613 
3614   // just call the CPU version for now
3615   rt_adjust_levels(self, piece, src_buffer, width, height, ch, levels);
3616 
3617   err = dt_opencl_write_buffer_to_device(devid, src_buffer, dev_img, 0, sizeof(float) * ch * width * height, TRUE);
3618   if(err != CL_SUCCESS)
3619   {
3620     goto cleanup;
3621   }
3622 
3623 cleanup:
3624   if(src_buffer) dt_free_align(src_buffer);
3625 
3626   return err;
3627 }
3628 
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)3629 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,
3630                                    cl_mem dev_out, const struct dt_iop_roi_t *const roi_out, const int dx,
3631                                    const int dy, const int kernel)
3632 {
3633   cl_int err = CL_SUCCESS;
3634 
3635   const int xoffs = roi_out->x - roi_in->x - dx;
3636   const int yoffs = roi_out->y - roi_in->y - dy;
3637 
3638   cl_mem dev_roi_in = NULL;
3639   cl_mem dev_roi_out = NULL;
3640 
3641   const size_t sizes[]
3642       = { ROUNDUPWD(MIN(roi_out->width, roi_in->width)), ROUNDUPHT(MIN(roi_out->height, roi_in->height)), 1 };
3643 
3644   dev_roi_in = dt_opencl_copy_host_to_device_constant(devid, sizeof(dt_iop_roi_t), (void *)roi_in);
3645   dev_roi_out = dt_opencl_copy_host_to_device_constant(devid, sizeof(dt_iop_roi_t), (void *)roi_out);
3646   if(dev_roi_in == NULL || dev_roi_out == NULL)
3647   {
3648     fprintf(stderr, "rt_copy_in_to_out_cl error 1\n");
3649     err = CL_MEM_OBJECT_ALLOCATION_FAILURE;
3650     goto cleanup;
3651   }
3652 
3653   dt_opencl_set_kernel_arg(devid, kernel, 0, sizeof(cl_mem), (void *)&dev_in);
3654   dt_opencl_set_kernel_arg(devid, kernel, 1, sizeof(cl_mem), (void *)&dev_roi_in);
3655   dt_opencl_set_kernel_arg(devid, kernel, 2, sizeof(cl_mem), (void *)&dev_out);
3656   dt_opencl_set_kernel_arg(devid, kernel, 3, sizeof(cl_mem), (void *)&dev_roi_out);
3657   dt_opencl_set_kernel_arg(devid, kernel, 4, sizeof(int), (void *)&xoffs);
3658   dt_opencl_set_kernel_arg(devid, kernel, 5, sizeof(int), (void *)&yoffs);
3659   err = dt_opencl_enqueue_kernel_2d(devid, kernel, sizes);
3660   if(err != CL_SUCCESS)
3661   {
3662     fprintf(stderr, "rt_copy_in_to_out_cl error 2\n");
3663     goto cleanup;
3664   }
3665 
3666 cleanup:
3667   if(dev_roi_in) dt_opencl_release_mem_object(dev_roi_in);
3668   if(dev_roi_out) dt_opencl_release_mem_object(dev_roi_out);
3669 
3670   return err;
3671 }
3672 
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)3673 static cl_int rt_build_scaled_mask_cl(const int devid, float *const mask, dt_iop_roi_t *const roi_mask,
3674                                       float **mask_scaled, cl_mem *p_dev_mask_scaled,
3675                                       dt_iop_roi_t *roi_mask_scaled, dt_iop_roi_t *const roi_in, const int dx,
3676                                       const int dy, const int algo)
3677 {
3678   cl_int err = CL_SUCCESS;
3679 
3680   rt_build_scaled_mask(mask, roi_mask, mask_scaled, roi_mask_scaled, roi_in, dx, dy, algo);
3681   if(*mask_scaled == NULL)
3682   {
3683     goto cleanup;
3684   }
3685 
3686   const cl_mem dev_mask_scaled
3687       = dt_opencl_alloc_device_buffer(devid, sizeof(float) * roi_mask_scaled->width * roi_mask_scaled->height);
3688   if(dev_mask_scaled == NULL)
3689   {
3690     fprintf(stderr, "rt_build_scaled_mask_cl error 2\n");
3691     err = CL_MEM_OBJECT_ALLOCATION_FAILURE;
3692     goto cleanup;
3693   }
3694 
3695   err = dt_opencl_write_buffer_to_device(devid, *mask_scaled, dev_mask_scaled, 0,
3696                                          sizeof(float) * roi_mask_scaled->width * roi_mask_scaled->height, TRUE);
3697   if(err != CL_SUCCESS)
3698   {
3699     fprintf(stderr, "rt_build_scaled_mask_cl error 4\n");
3700     goto cleanup;
3701   }
3702 
3703   *p_dev_mask_scaled = dev_mask_scaled;
3704 
3705 cleanup:
3706   if(err != CL_SUCCESS) fprintf(stderr, "rt_build_scaled_mask_cl error\n");
3707 
3708   return err;
3709 }
3710 
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)3711 static cl_int rt_copy_image_masked_cl(const int devid, cl_mem dev_src, cl_mem dev_dest,
3712                                       dt_iop_roi_t *const roi_dest, cl_mem dev_mask_scaled,
3713                                       dt_iop_roi_t *const roi_mask_scaled, const float opacity, const int kernel)
3714 {
3715   cl_int err = CL_SUCCESS;
3716 
3717   const size_t sizes[] = { ROUNDUPWD(roi_mask_scaled->width), ROUNDUPHT(roi_mask_scaled->height), 1 };
3718 
3719   const cl_mem dev_roi_dest =
3720     dt_opencl_copy_host_to_device_constant(devid, sizeof(dt_iop_roi_t), (void *)roi_dest);
3721 
3722   const cl_mem dev_roi_mask_scaled
3723       = dt_opencl_copy_host_to_device_constant(devid, sizeof(dt_iop_roi_t), (void *)roi_mask_scaled);
3724 
3725   if(dev_roi_dest == NULL || dev_roi_mask_scaled == NULL)
3726   {
3727     err = CL_MEM_OBJECT_ALLOCATION_FAILURE;
3728     goto cleanup;
3729   }
3730 
3731   dt_opencl_set_kernel_arg(devid, kernel, 0, sizeof(cl_mem), (void *)&dev_src);
3732   dt_opencl_set_kernel_arg(devid, kernel, 1, sizeof(cl_mem), (void *)&dev_dest);
3733   dt_opencl_set_kernel_arg(devid, kernel, 2, sizeof(cl_mem), (void *)&dev_roi_dest);
3734   dt_opencl_set_kernel_arg(devid, kernel, 3, sizeof(cl_mem), (void *)&dev_mask_scaled);
3735   dt_opencl_set_kernel_arg(devid, kernel, 4, sizeof(cl_mem), (void *)&dev_roi_mask_scaled);
3736   dt_opencl_set_kernel_arg(devid, kernel, 5, sizeof(float), (void *)&opacity);
3737   err = dt_opencl_enqueue_kernel_2d(devid, kernel, sizes);
3738   if(err != CL_SUCCESS) goto cleanup;
3739 
3740 cleanup:
3741   if(dev_roi_dest) dt_opencl_release_mem_object(dev_roi_dest);
3742   if(dev_roi_mask_scaled) dt_opencl_release_mem_object(dev_roi_mask_scaled);
3743 
3744   return err;
3745 }
3746 
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)3747 static cl_int rt_copy_mask_to_alpha_cl(const int devid, cl_mem dev_layer, dt_iop_roi_t *const roi_layer,
3748                                        cl_mem dev_mask_scaled, dt_iop_roi_t *const roi_mask_scaled,
3749                                        const float opacity, dt_iop_retouch_global_data_t *gd)
3750 {
3751   cl_int err = CL_SUCCESS;
3752 
3753   // fill it
3754   const int kernel = gd->kernel_retouch_copy_mask_to_alpha;
3755   const size_t sizes[] = { ROUNDUPWD(roi_mask_scaled->width), ROUNDUPHT(roi_mask_scaled->height), 1 };
3756 
3757   const cl_mem  dev_roi_layer = dt_opencl_copy_host_to_device_constant(devid, sizeof(dt_iop_roi_t), (void *)roi_layer);
3758   const cl_mem dev_roi_mask_scaled
3759       = dt_opencl_copy_host_to_device_constant(devid, sizeof(dt_iop_roi_t), (void *)roi_mask_scaled);
3760   if(dev_roi_layer == NULL || dev_roi_mask_scaled == NULL)
3761   {
3762     err = CL_MEM_OBJECT_ALLOCATION_FAILURE;
3763     goto cleanup;
3764   }
3765 
3766   dt_opencl_set_kernel_arg(devid, kernel, 0, sizeof(cl_mem), (void *)&dev_layer);
3767   dt_opencl_set_kernel_arg(devid, kernel, 1, sizeof(cl_mem), (void *)&dev_roi_layer);
3768   dt_opencl_set_kernel_arg(devid, kernel, 2, sizeof(cl_mem), (void *)&dev_mask_scaled);
3769   dt_opencl_set_kernel_arg(devid, kernel, 3, sizeof(cl_mem), (void *)&dev_roi_mask_scaled);
3770   dt_opencl_set_kernel_arg(devid, kernel, 4, sizeof(float), (void *)&opacity);
3771   err = dt_opencl_enqueue_kernel_2d(devid, kernel, sizes);
3772   if(err != CL_SUCCESS) goto cleanup;
3773 
3774 
3775 cleanup:
3776   dt_opencl_release_mem_object(dev_roi_layer);
3777   dt_opencl_release_mem_object(dev_roi_mask_scaled);
3778 
3779   return err;
3780 }
3781 
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)3782 static cl_int retouch_clone_cl(const int devid, cl_mem dev_layer, dt_iop_roi_t *const roi_layer,
3783                                cl_mem dev_mask_scaled, dt_iop_roi_t *const roi_mask_scaled, const int dx,
3784                                const int dy, const float opacity, dt_iop_retouch_global_data_t *gd)
3785 {
3786   cl_int err = CL_SUCCESS;
3787 
3788   const int ch = 4;
3789 
3790   // alloc source temp image to avoid issues when areas self-intersects
3791   const cl_mem dev_src = dt_opencl_alloc_device_buffer(devid,
3792                                           sizeof(float) * ch * roi_mask_scaled->width * roi_mask_scaled->height);
3793   if(dev_src == NULL)
3794   {
3795     fprintf(stderr, "retouch_clone_cl error 2\n");
3796     err = CL_MEM_OBJECT_ALLOCATION_FAILURE;
3797     goto cleanup;
3798   }
3799 
3800   // copy source image to tmp
3801   err = rt_copy_in_to_out_cl(devid, dev_layer, roi_layer, dev_src, roi_mask_scaled, dx, dy,
3802                              gd->kernel_retouch_copy_buffer_to_buffer);
3803   if(err != CL_SUCCESS)
3804   {
3805     fprintf(stderr, "retouch_clone_cl error 4\n");
3806     goto cleanup;
3807   }
3808 
3809   // clone it
3810   err = rt_copy_image_masked_cl(devid, dev_src, dev_layer, roi_layer, dev_mask_scaled, roi_mask_scaled, opacity,
3811                                 gd->kernel_retouch_copy_buffer_to_buffer_masked);
3812   if(err != CL_SUCCESS)
3813   {
3814     fprintf(stderr, "retouch_clone_cl error 5\n");
3815     goto cleanup;
3816   }
3817 
3818 cleanup:
3819   if(dev_src) dt_opencl_release_mem_object(dev_src);
3820 
3821   return err;
3822 }
3823 
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)3824 static cl_int retouch_fill_cl(const int devid, cl_mem dev_layer, dt_iop_roi_t *const roi_layer,
3825                               cl_mem dev_mask_scaled, dt_iop_roi_t *const roi_mask_scaled, const float opacity,
3826                               float *color, dt_iop_retouch_global_data_t *gd)
3827 {
3828   cl_int err = CL_SUCCESS;
3829 
3830   // fill it
3831   const int kernel = gd->kernel_retouch_fill;
3832   const size_t sizes[] = { ROUNDUPWD(roi_mask_scaled->width), ROUNDUPHT(roi_mask_scaled->height), 1 };
3833 
3834   const cl_mem dev_roi_layer = dt_opencl_copy_host_to_device_constant(devid, sizeof(dt_iop_roi_t), (void *)roi_layer);
3835   const cl_mem dev_roi_mask_scaled
3836       = dt_opencl_copy_host_to_device_constant(devid, sizeof(dt_iop_roi_t), (void *)roi_mask_scaled);
3837   if(dev_roi_layer == NULL || dev_roi_mask_scaled == NULL)
3838   {
3839     err = CL_MEM_OBJECT_ALLOCATION_FAILURE;
3840     goto cleanup;
3841   }
3842 
3843   dt_opencl_set_kernel_arg(devid, kernel, 0, sizeof(cl_mem), (void *)&dev_layer);
3844   dt_opencl_set_kernel_arg(devid, kernel, 1, sizeof(cl_mem), (void *)&dev_roi_layer);
3845   dt_opencl_set_kernel_arg(devid, kernel, 2, sizeof(cl_mem), (void *)&dev_mask_scaled);
3846   dt_opencl_set_kernel_arg(devid, kernel, 3, sizeof(cl_mem), (void *)&dev_roi_mask_scaled);
3847   dt_opencl_set_kernel_arg(devid, kernel, 4, sizeof(float), (void *)&opacity);
3848   dt_opencl_set_kernel_arg(devid, kernel, 5, sizeof(float), (void *)&(color[0]));
3849   dt_opencl_set_kernel_arg(devid, kernel, 6, sizeof(float), (void *)&(color[1]));
3850   dt_opencl_set_kernel_arg(devid, kernel, 7, sizeof(float), (void *)&(color[2]));
3851   err = dt_opencl_enqueue_kernel_2d(devid, kernel, sizes);
3852   if(err != CL_SUCCESS) goto cleanup;
3853 
3854 
3855 cleanup:
3856   dt_opencl_release_mem_object(dev_roi_layer);
3857   dt_opencl_release_mem_object(dev_roi_mask_scaled);
3858 
3859   return err;
3860 }
3861 
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)3862 static cl_int retouch_blur_cl(const int devid, cl_mem dev_layer, dt_iop_roi_t *const roi_layer,
3863                               cl_mem dev_mask_scaled, dt_iop_roi_t *const roi_mask_scaled, const float opacity,
3864                               const int blur_type, const float blur_radius, dt_dev_pixelpipe_iop_t *piece,
3865                               dt_iop_retouch_global_data_t *gd)
3866 {
3867   cl_int err = CL_SUCCESS;
3868 
3869   if(fabsf(blur_radius) <= 0.1f) return err;
3870 
3871   const float sigma = blur_radius * roi_layer->scale / piece->iscale;
3872   const int ch = 4;
3873 
3874   const cl_mem dev_dest =
3875     dt_opencl_alloc_device(devid, roi_mask_scaled->width, roi_mask_scaled->height, sizeof(float) * ch);
3876   if(dev_dest == NULL)
3877   {
3878     fprintf(stderr, "retouch_blur_cl error 2\n");
3879     err = CL_MEM_OBJECT_ALLOCATION_FAILURE;
3880     goto cleanup;
3881   }
3882 
3883   if(blur_type == DT_IOP_RETOUCH_BLUR_BILATERAL)
3884   {
3885     const int kernel = gd->kernel_retouch_image_rgb2lab;
3886     size_t sizes[] = { ROUNDUPWD(roi_layer->width), ROUNDUPHT(roi_layer->height), 1 };
3887 
3888     dt_opencl_set_kernel_arg(devid, kernel, 0, sizeof(cl_mem), (void *)&dev_layer);
3889     dt_opencl_set_kernel_arg(devid, kernel, 1, sizeof(int), (void *)&(roi_layer->width));
3890     dt_opencl_set_kernel_arg(devid, kernel, 2, sizeof(int), (void *)&(roi_layer->height));
3891     err = dt_opencl_enqueue_kernel_2d(devid, kernel, sizes);
3892     if(err != CL_SUCCESS) goto cleanup;
3893   }
3894 
3895   err = rt_copy_in_to_out_cl(devid, dev_layer, roi_layer, dev_dest, roi_mask_scaled, 0, 0,
3896                              gd->kernel_retouch_copy_buffer_to_image);
3897   if(err != CL_SUCCESS)
3898   {
3899     fprintf(stderr, "retouch_blur_cl error 4\n");
3900     goto cleanup;
3901   }
3902 
3903   if(blur_type == DT_IOP_RETOUCH_BLUR_GAUSSIAN && fabsf(blur_radius) > 0.1f)
3904   {
3905     float Labmax[] = { INFINITY, INFINITY, INFINITY, INFINITY };
3906     float Labmin[] = { -INFINITY, -INFINITY, -INFINITY, -INFINITY };
3907 
3908     dt_gaussian_cl_t *g = dt_gaussian_init_cl(devid, roi_mask_scaled->width, roi_mask_scaled->height, ch, Labmax,
3909                                               Labmin, sigma, DT_IOP_GAUSSIAN_ZERO);
3910     if(g)
3911     {
3912       err = dt_gaussian_blur_cl(g, dev_dest, dev_dest);
3913       dt_gaussian_free_cl(g);
3914       if(err != CL_SUCCESS) goto cleanup;
3915     }
3916   }
3917   else if(blur_type == DT_IOP_RETOUCH_BLUR_BILATERAL && fabsf(blur_radius) > 0.1f)
3918   {
3919     const float sigma_r = 100.0f; // does not depend on scale
3920     const float sigma_s = sigma;
3921     const float detail = -1.0f; // we want the bilateral base layer
3922 
3923     dt_bilateral_cl_t *b
3924         = dt_bilateral_init_cl(devid, roi_mask_scaled->width, roi_mask_scaled->height, sigma_s, sigma_r);
3925     if(b)
3926     {
3927       err = dt_bilateral_splat_cl(b, dev_dest);
3928       if(err == CL_SUCCESS) err = dt_bilateral_blur_cl(b);
3929       if(err == CL_SUCCESS) err = dt_bilateral_slice_cl(b, dev_dest, dev_dest, detail);
3930 
3931       dt_bilateral_free_cl(b);
3932     }
3933   }
3934 
3935   // copy blurred (temp) image to destination image
3936   err = rt_copy_image_masked_cl(devid, dev_dest, dev_layer, roi_layer, dev_mask_scaled, roi_mask_scaled, opacity,
3937                                 gd->kernel_retouch_copy_image_to_buffer_masked);
3938   if(err != CL_SUCCESS)
3939   {
3940     fprintf(stderr, "retouch_blur_cl error 5\n");
3941     goto cleanup;
3942   }
3943 
3944   if(blur_type == DT_IOP_RETOUCH_BLUR_BILATERAL)
3945   {
3946     const int kernel = gd->kernel_retouch_image_lab2rgb;
3947     const size_t sizes[] = { ROUNDUPWD(roi_layer->width), ROUNDUPHT(roi_layer->height), 1 };
3948 
3949     dt_opencl_set_kernel_arg(devid, kernel, 0, sizeof(cl_mem), (void *)&dev_layer);
3950     dt_opencl_set_kernel_arg(devid, kernel, 1, sizeof(int), (void *)&(roi_layer->width));
3951     dt_opencl_set_kernel_arg(devid, kernel, 2, sizeof(int), (void *)&(roi_layer->height));
3952     err = dt_opencl_enqueue_kernel_2d(devid, kernel, sizes);
3953     if(err != CL_SUCCESS) goto cleanup;
3954   }
3955 
3956 cleanup:
3957   if(dev_dest) dt_opencl_release_mem_object(dev_dest);
3958 
3959   return err;
3960 }
3961 
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)3962 static cl_int retouch_heal_cl(const int devid, cl_mem dev_layer, dt_iop_roi_t *const roi_layer, float *mask_scaled,
3963                               cl_mem dev_mask_scaled, dt_iop_roi_t *const roi_mask_scaled, const int dx,
3964                               const int dy, const float opacity, dt_iop_retouch_global_data_t *gd)
3965 {
3966   cl_int err = CL_SUCCESS;
3967 
3968   const int ch = 4;
3969 
3970   cl_mem dev_dest = NULL;
3971   cl_mem dev_src = dt_opencl_alloc_device_buffer(devid,
3972                                           sizeof(float) * ch * roi_mask_scaled->width * roi_mask_scaled->height);
3973   if(dev_src == NULL)
3974   {
3975     fprintf(stderr, "retouch_heal_cl: error allocating memory for healing\n");
3976     err = CL_MEM_OBJECT_ALLOCATION_FAILURE;
3977     goto cleanup;
3978   }
3979 
3980   dev_dest = dt_opencl_alloc_device_buffer(devid,
3981                                            sizeof(float) * ch * roi_mask_scaled->width * roi_mask_scaled->height);
3982   if(dev_dest == NULL)
3983   {
3984     fprintf(stderr, "retouch_heal_cl: error allocating memory for healing\n");
3985     err = CL_MEM_OBJECT_ALLOCATION_FAILURE;
3986     goto cleanup;
3987   }
3988 
3989   err = rt_copy_in_to_out_cl(devid, dev_layer, roi_layer, dev_src, roi_mask_scaled, dx, dy,
3990                              gd->kernel_retouch_copy_buffer_to_buffer);
3991   if(err != CL_SUCCESS)
3992   {
3993     fprintf(stderr, "retouch_heal_cl error 4\n");
3994     goto cleanup;
3995   }
3996 
3997   err = rt_copy_in_to_out_cl(devid, dev_layer, roi_layer, dev_dest, roi_mask_scaled, 0, 0,
3998                              gd->kernel_retouch_copy_buffer_to_buffer);
3999   if(err != CL_SUCCESS)
4000   {
4001     fprintf(stderr, "retouch_heal_cl error 4\n");
4002     goto cleanup;
4003   }
4004 
4005   // heal it
4006   heal_params_cl_t *hp = dt_heal_init_cl(devid);
4007   if(hp)
4008   {
4009     err = dt_heal_cl(hp, dev_src, dev_dest, mask_scaled, roi_mask_scaled->width, roi_mask_scaled->height);
4010     dt_heal_free_cl(hp);
4011 
4012     dt_opencl_release_mem_object(dev_src);
4013     dev_src = NULL;
4014 
4015     if(err != CL_SUCCESS) goto cleanup;
4016   }
4017 
4018   // copy healed (temp) image to destination image
4019   err = rt_copy_image_masked_cl(devid, dev_dest, dev_layer, roi_layer, dev_mask_scaled, roi_mask_scaled, opacity,
4020                                 gd->kernel_retouch_copy_buffer_to_buffer_masked);
4021   if(err != CL_SUCCESS)
4022   {
4023     fprintf(stderr, "retouch_heal_cl error 6\n");
4024     goto cleanup;
4025   }
4026 
4027 cleanup:
4028   if(dev_src) dt_opencl_release_mem_object(dev_src);
4029   if(dev_dest) dt_opencl_release_mem_object(dev_dest);
4030 
4031   return err;
4032 }
4033 
rt_process_forms_cl(cl_mem dev_layer,dwt_params_cl_t * const wt_p,const int scale1)4034 static cl_int rt_process_forms_cl(cl_mem dev_layer, dwt_params_cl_t *const wt_p, const int scale1)
4035 {
4036   cl_int err = CL_SUCCESS;
4037 
4038   int scale = scale1;
4039   retouch_user_data_t *usr_d = (retouch_user_data_t *)wt_p->user_data;
4040   dt_iop_module_t *self = usr_d->self;
4041   dt_dev_pixelpipe_iop_t *piece = usr_d->piece;
4042 
4043   // if preview a single scale, just process that scale and original image
4044   // unless merge is activated
4045   if(wt_p->merge_from_scale == 0 && wt_p->return_layer > 0 && scale != wt_p->return_layer && scale != 0)
4046     return err;
4047   // do not process the reconstructed image
4048   if(scale > wt_p->scales + 1) return err;
4049 
4050   dt_develop_blend_params_t *bp = (dt_develop_blend_params_t *)piece->blendop_data;
4051   dt_iop_retouch_params_t *p = (dt_iop_retouch_params_t *)piece->data;
4052   dt_iop_retouch_global_data_t *gd = (dt_iop_retouch_global_data_t *)self->global_data;
4053   const int devid = piece->pipe->devid;
4054   dt_iop_roi_t *roi_layer = &usr_d->roi;
4055   const int mask_display = usr_d->mask_display && (scale == usr_d->display_scale);
4056 
4057   // when the requested scales is grather than max scales the residual image index will be different from the one
4058   // defined by the user,
4059   // so we need to adjust it here, otherwise we will be using the shapes from a scale on the residual image
4060   if(wt_p->scales < p->num_scales && wt_p->return_layer == 0 && scale == wt_p->scales + 1)
4061   {
4062     scale = p->num_scales + 1;
4063   }
4064 
4065   // iterate through all forms
4066   if(!usr_d->suppress_mask)
4067   {
4068     dt_masks_form_t *grp = dt_masks_get_from_id_ext(piece->pipe->forms, bp->mask_id);
4069     if(grp && (grp->type & DT_MASKS_GROUP))
4070     {
4071       for(const GList *forms = grp->points; forms && err == CL_SUCCESS; forms = g_list_next(forms))
4072       {
4073         dt_masks_point_group_t *grpt = (dt_masks_point_group_t *)forms->data;
4074         if(grpt == NULL)
4075         {
4076           fprintf(stderr, "rt_process_forms: invalid form\n");
4077           continue;
4078         }
4079         const int formid = grpt->formid;
4080         const float form_opacity = grpt->opacity;
4081         if(formid == 0)
4082         {
4083           fprintf(stderr, "rt_process_forms: form is null\n");
4084           continue;
4085         }
4086         const int index = rt_get_index_from_formid(p, formid);
4087         if(index == -1)
4088         {
4089           // FIXME: we get this error when user go back in history, so forms are the same but the array has changed
4090           fprintf(stderr, "rt_process_forms: missing form=%i from array\n", formid);
4091           continue;
4092         }
4093 
4094         // only process current scale
4095         if(p->rt_forms[index].scale != scale)
4096         {
4097           continue;
4098         }
4099 
4100         // get the spot
4101         dt_masks_form_t *form = dt_masks_get_from_id_ext(piece->pipe->forms, formid);
4102         if(form == NULL)
4103         {
4104           fprintf(stderr, "rt_process_forms: missing form=%i from masks\n", formid);
4105           continue;
4106         }
4107 
4108         // if the form is outside the roi, we just skip it
4109         if(!rt_masks_form_is_in_roi(self, piece, form, roi_layer, roi_layer))
4110         {
4111           continue;
4112         }
4113 
4114         // get the mask
4115         float *mask = NULL;
4116         dt_iop_roi_t roi_mask = { 0 };
4117 
4118         dt_masks_get_mask(self, piece, form, &mask, &roi_mask.width, &roi_mask.height, &roi_mask.x, &roi_mask.y);
4119         if(mask == NULL)
4120         {
4121           fprintf(stderr, "rt_process_forms: error retrieving mask\n");
4122           continue;
4123         }
4124 
4125         int dx = 0, dy = 0;
4126 
4127         // search the delta with the source
4128         const dt_iop_retouch_algo_type_t algo = p->rt_forms[index].algorithm;
4129         if(algo != DT_IOP_RETOUCH_BLUR && algo != DT_IOP_RETOUCH_FILL)
4130         {
4131           if(!rt_masks_get_delta_to_destination(self, piece, roi_layer, form, &dx, &dy,
4132                                                 p->rt_forms[index].distort_mode))
4133           {
4134             if(mask) dt_free_align(mask);
4135             continue;
4136           }
4137         }
4138 
4139         // scale the mask
4140         cl_mem dev_mask_scaled = NULL;
4141         float *mask_scaled = NULL;
4142         dt_iop_roi_t roi_mask_scaled = { 0 };
4143 
4144         err = rt_build_scaled_mask_cl(devid, mask, &roi_mask, &mask_scaled, &dev_mask_scaled, &roi_mask_scaled,
4145                                       roi_layer, dx, dy, algo);
4146 
4147         // only heal needs mask scaled
4148         if(algo != DT_IOP_RETOUCH_HEAL && mask_scaled != NULL)
4149         {
4150           dt_free_align(mask_scaled);
4151           mask_scaled = NULL;
4152         }
4153 
4154         // we don't need the original mask anymore
4155         if(mask)
4156         {
4157           dt_free_align(mask);
4158           mask = NULL;
4159         }
4160 
4161         if(mask_scaled == NULL && algo == DT_IOP_RETOUCH_HEAL)
4162         {
4163           if(dev_mask_scaled) dt_opencl_release_mem_object(dev_mask_scaled);
4164           dev_mask_scaled = NULL;
4165           continue;
4166         }
4167 
4168         if((err == CL_SUCCESS)
4169            && (dx != 0 || dy != 0 || algo == DT_IOP_RETOUCH_BLUR || algo == DT_IOP_RETOUCH_FILL)
4170            && ((roi_mask_scaled.width > 2) && (roi_mask_scaled.height > 2)))
4171         {
4172           if(algo == DT_IOP_RETOUCH_CLONE)
4173           {
4174             err = retouch_clone_cl(devid, dev_layer, roi_layer, dev_mask_scaled, &roi_mask_scaled, dx, dy,
4175                                    form_opacity, gd);
4176           }
4177           else if(algo == DT_IOP_RETOUCH_HEAL)
4178           {
4179             err = retouch_heal_cl(devid, dev_layer, roi_layer, mask_scaled, dev_mask_scaled, &roi_mask_scaled, dx,
4180                                   dy, form_opacity, gd);
4181           }
4182           else if(algo == DT_IOP_RETOUCH_BLUR)
4183           {
4184             err = retouch_blur_cl(devid, dev_layer, roi_layer, dev_mask_scaled, &roi_mask_scaled, form_opacity,
4185                                   p->rt_forms[index].blur_type, p->rt_forms[index].blur_radius, piece, gd);
4186           }
4187           else if(algo == DT_IOP_RETOUCH_FILL)
4188           {
4189             // add a brightness to the color so it can be fine-adjusted by the user
4190             dt_aligned_pixel_t fill_color;
4191 
4192             if(p->rt_forms[index].fill_mode == DT_IOP_RETOUCH_FILL_ERASE)
4193             {
4194               fill_color[0] = fill_color[1] = fill_color[2] = p->rt_forms[index].fill_brightness;
4195             }
4196             else
4197             {
4198               fill_color[0] = p->rt_forms[index].fill_color[0] + p->rt_forms[index].fill_brightness;
4199               fill_color[1] = p->rt_forms[index].fill_color[1] + p->rt_forms[index].fill_brightness;
4200               fill_color[2] = p->rt_forms[index].fill_color[2] + p->rt_forms[index].fill_brightness;
4201             }
4202 
4203             err = retouch_fill_cl(devid, dev_layer, roi_layer, dev_mask_scaled, &roi_mask_scaled, form_opacity,
4204                                   fill_color, gd);
4205           }
4206           else
4207             fprintf(stderr, "rt_process_forms: unknown algorithm %i\n", algo);
4208 
4209           if(mask_display)
4210             rt_copy_mask_to_alpha_cl(devid, dev_layer, roi_layer, dev_mask_scaled, &roi_mask_scaled, form_opacity,
4211                                      gd);
4212         }
4213 
4214         if(mask) dt_free_align(mask);
4215         if(mask_scaled) dt_free_align(mask_scaled);
4216         if(dev_mask_scaled) dt_opencl_release_mem_object(dev_mask_scaled);
4217       }
4218     }
4219   }
4220 
4221   return err;
4222 }
4223 
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)4224 int process_cl(struct dt_iop_module_t *self, dt_dev_pixelpipe_iop_t *piece, cl_mem dev_in, cl_mem dev_out,
4225                const dt_iop_roi_t *const roi_in, const dt_iop_roi_t *const roi_out)
4226 {
4227   dt_iop_retouch_params_t *p = (dt_iop_retouch_params_t *)piece->data;
4228   dt_iop_retouch_global_data_t *gd = (dt_iop_retouch_global_data_t *)self->global_data;
4229   dt_iop_retouch_gui_data_t *g = (dt_iop_retouch_gui_data_t *)self->gui_data;
4230 
4231   cl_int err = CL_SUCCESS;
4232   const int devid = piece->pipe->devid;
4233 
4234   dt_iop_roi_t roi_retouch = *roi_in;
4235   dt_iop_roi_t *roi_rt = &roi_retouch;
4236 
4237   const int ch = piece->colors;
4238   retouch_user_data_t usr_data = { 0 };
4239   dwt_params_cl_t *dwt_p = NULL;
4240 
4241   const int gui_active = (self->dev) ? (self == self->dev->gui_module) : 0;
4242   const int display_wavelet_scale = (g && gui_active) ? g->display_wavelet_scale : 0;
4243 
4244   // we will do all the clone, heal, etc on the input image,
4245   // this way the source for one algorithm can be the destination from a previous one
4246   const cl_mem in_retouch = dt_opencl_alloc_device_buffer(devid, sizeof(float) * ch * roi_rt->width * roi_rt->height);
4247   if(in_retouch == NULL)
4248   {
4249     fprintf(stderr, "process_internal: error allocating memory for wavelet decompose\n");
4250     err = CL_MEM_OBJECT_ALLOCATION_FAILURE;
4251     goto cleanup;
4252   }
4253 
4254   // copy input image to the new buffer
4255   {
4256     size_t origin[] = { 0, 0, 0 };
4257     size_t region[] = { roi_rt->width, roi_rt->height, 1 };
4258     err = dt_opencl_enqueue_copy_image_to_buffer(devid, dev_in, in_retouch, origin, region, 0);
4259     if(err != CL_SUCCESS) goto cleanup;
4260   }
4261 
4262   // user data passed from the decompose routine to the one that process each scale
4263   usr_data.self = self;
4264   usr_data.piece = piece;
4265   usr_data.roi = *roi_rt;
4266   usr_data.mask_display = 0;
4267   usr_data.suppress_mask = (g && g->suppress_mask && self->dev->gui_attached && (self == self->dev->gui_module)
4268                             && (piece->pipe == self->dev->pipe));
4269   usr_data.display_scale = p->curr_scale;
4270 
4271   // init the decompose routine
4272   dwt_p = dt_dwt_init_cl(devid, in_retouch, roi_rt->width, roi_rt->height, p->num_scales,
4273                          (!display_wavelet_scale
4274                           || (piece->pipe->type & DT_DEV_PIXELPIPE_FULL) != DT_DEV_PIXELPIPE_FULL) ? 0 : p->curr_scale,
4275                          p->merge_from_scale, &usr_data,
4276                          roi_in->scale / piece->iscale);
4277   if(dwt_p == NULL)
4278   {
4279     fprintf(stderr, "process_internal: error initializing wavelet decompose\n");
4280     err = CL_MEM_OBJECT_ALLOCATION_FAILURE;
4281     goto cleanup;
4282   }
4283 
4284   // check if this module should expose mask.
4285   if((piece->pipe->type & DT_DEV_PIXELPIPE_FULL) == DT_DEV_PIXELPIPE_FULL && g && g->mask_display && self->dev->gui_attached
4286      && (self == self->dev->gui_module) && (piece->pipe == self->dev->pipe))
4287   {
4288     const int kernel = gd->kernel_retouch_clear_alpha;
4289     const size_t sizes[] = { ROUNDUPWD(roi_rt->width), ROUNDUPHT(roi_rt->height), 1 };
4290 
4291     dt_opencl_set_kernel_arg(devid, kernel, 0, sizeof(cl_mem), (void *)&in_retouch);
4292     dt_opencl_set_kernel_arg(devid, kernel, 1, sizeof(int), (void *)&(roi_rt->width));
4293     dt_opencl_set_kernel_arg(devid, kernel, 2, sizeof(int), (void *)&(roi_rt->height));
4294     err = dt_opencl_enqueue_kernel_2d(devid, kernel, sizes);
4295     if(err != CL_SUCCESS) goto cleanup;
4296 
4297     piece->pipe->mask_display = DT_DEV_PIXELPIPE_DISPLAY_MASK;
4298     piece->pipe->bypass_blendif = 1;
4299     usr_data.mask_display = 1;
4300   }
4301 
4302   if((piece->pipe->type & DT_DEV_PIXELPIPE_FULL) == DT_DEV_PIXELPIPE_FULL)
4303   {
4304     // check if the image support this number of scales
4305     if(gui_active)
4306     {
4307       const int max_scales = dwt_get_max_scale_cl(dwt_p);
4308       if(dwt_p->scales > max_scales)
4309       {
4310         dt_control_log(_("max scale is %i for this image size"), max_scales);
4311       }
4312     }
4313     // get first scale visible at this zoom level
4314     if(g) g->first_scale_visible = dt_dwt_first_scale_visible_cl(dwt_p);
4315   }
4316 
4317   // decompose it
4318   err = dwt_decompose_cl(dwt_p, rt_process_forms_cl);
4319   if(err != CL_SUCCESS) goto cleanup;
4320 
4321   dt_aligned_pixel_t levels = { p->preview_levels[0], p->preview_levels[1], p->preview_levels[2] };
4322 
4323   // process auto levels
4324   if(g && (piece->pipe->type & DT_DEV_PIXELPIPE_FULL) == DT_DEV_PIXELPIPE_FULL)
4325   {
4326     dt_iop_gui_enter_critical_section(self);
4327     if(g->preview_auto_levels == 1 && !darktable.gui->reset)
4328     {
4329       g->preview_auto_levels = -1;
4330 
4331       dt_iop_gui_leave_critical_section(self);
4332 
4333       levels[0] = levels[1] = levels[2] = 0;
4334       err = rt_process_stats_cl(self, piece, devid, in_retouch, roi_rt->width, roi_rt->height, levels);
4335       if(err != CL_SUCCESS) goto cleanup;
4336 
4337       rt_clamp_minmax(levels, levels);
4338 
4339       for(int i = 0; i < 3; i++) g->preview_levels[i] = levels[i];
4340 
4341       dt_iop_gui_enter_critical_section(self);
4342       g->preview_auto_levels = 2;
4343       dt_iop_gui_leave_critical_section(self);
4344     }
4345     else
4346     {
4347       dt_iop_gui_leave_critical_section(self);
4348     }
4349   }
4350 
4351   // if user wants to preview a detail scale adjust levels
4352   if(dwt_p->return_layer > 0 && dwt_p->return_layer < dwt_p->scales + 1)
4353   {
4354     err = rt_adjust_levels_cl(self, piece, devid, in_retouch, roi_rt->width, roi_rt->height, levels);
4355     if(err != CL_SUCCESS) goto cleanup;
4356   }
4357 
4358   // copy alpha channel if needed
4359   if((piece->pipe->mask_display & DT_DEV_PIXELPIPE_DISPLAY_MASK) && g && !g->mask_display)
4360   {
4361     const int kernel = gd->kernel_retouch_copy_alpha;
4362     const size_t sizes[] = { ROUNDUPWD(roi_rt->width), ROUNDUPHT(roi_rt->height), 1 };
4363 
4364     dt_opencl_set_kernel_arg(devid, kernel, 0, sizeof(cl_mem), (void *)&dev_in);
4365     dt_opencl_set_kernel_arg(devid, kernel, 1, sizeof(cl_mem), (void *)&in_retouch);
4366     dt_opencl_set_kernel_arg(devid, kernel, 2, sizeof(int), (void *)&(roi_rt->width));
4367     dt_opencl_set_kernel_arg(devid, kernel, 3, sizeof(int), (void *)&(roi_rt->height));
4368     err = dt_opencl_enqueue_kernel_2d(devid, kernel, sizes);
4369     if(err != CL_SUCCESS) goto cleanup;
4370   }
4371 
4372   // return final image
4373   err = rt_copy_in_to_out_cl(devid, in_retouch, roi_in, dev_out, roi_out, 0, 0,
4374                              gd->kernel_retouch_copy_buffer_to_image);
4375 
4376 cleanup:
4377   if(dwt_p) dt_dwt_free_cl(dwt_p);
4378 
4379   if(in_retouch) dt_opencl_release_mem_object(in_retouch);
4380 
4381   if(err != CL_SUCCESS) dt_print(DT_DEBUG_OPENCL, "[opencl_retouch] couldn't enqueue kernel! %d\n", err);
4382 
4383   return (err == CL_SUCCESS) ? TRUE : FALSE;
4384 }
4385 #endif
4386 
4387 // modelines: These editor modelines have been set for all relevant files by tools/update_modelines.sh
4388 // vim: shiftwidth=2 expandtab tabstop=2 cindent
4389 // kate: tab-indents: off; indent-width 2; replace-tabs on; indent-mode cstyle; remove-trailing-space on;
4390