1 /*
2     This file is part of darktable,
3     Copyright (C) 2013-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 #include "develop/masks.h"
19 #include "bauhaus/bauhaus.h"
20 #include "common/debug.h"
21 #include "common/mipmap_cache.h"
22 #include "control/conf.h"
23 #include "control/control.h"
24 #include "common/undo.h"
25 #include "develop/blend.h"
26 #include "develop/imageop.h"
27 #include "develop/imageop_gui.h"
28 
29 #pragma GCC diagnostic ignored "-Wshadow"
30 
dt_masks_dup_masks_form(const dt_masks_form_t * form)31 dt_masks_form_t *dt_masks_dup_masks_form(const dt_masks_form_t *form)
32 {
33   if (!form) return NULL;
34 
35   dt_masks_form_t *new_form = malloc(sizeof(struct dt_masks_form_t));
36   memcpy(new_form, form, sizeof(struct dt_masks_form_t));
37 
38   // then duplicate the GList *points
39 
40   GList* newpoints = NULL;
41 
42   if (form->points)
43   {
44     int size_item = (form->functions) ? form->functions->point_struct_size : 0;
45 
46     if (size_item != 0)
47     {
48       for (GList *pt = form->points; pt; pt = g_list_next(pt))
49       {
50         void *item = malloc(size_item);
51         memcpy(item, pt->data, size_item);
52         newpoints = g_list_prepend(newpoints, item);
53       }
54     }
55   }
56   new_form->points = g_list_reverse(newpoints);  // list was built in reverse order, so un-reverse it
57 
58   return new_form;
59 }
60 
_dup_masks_form_cb(const void * formdata,gpointer user_data)61 static void *_dup_masks_form_cb(const void *formdata, gpointer user_data)
62 {
63   // duplicate the main form struct
64   dt_masks_form_t *form = (dt_masks_form_t *)formdata;
65   dt_masks_form_t *uform = (dt_masks_form_t *)user_data;
66   const dt_masks_form_t *f = uform == NULL || form->formid != uform->formid ? form : uform;
67   return (void *)dt_masks_dup_masks_form(f);
68 }
69 
70 // duplicate the list of forms, replace item in the list with form with the same formid
dt_masks_dup_forms_deep(GList * forms,dt_masks_form_t * form)71 GList *dt_masks_dup_forms_deep(GList *forms, dt_masks_form_t *form)
72 {
73   return (GList *)g_list_copy_deep(forms, _dup_masks_form_cb, (gpointer)form);
74 }
75 
_get_opacity(dt_masks_form_gui_t * gui,const dt_masks_form_t * form)76 static int _get_opacity(dt_masks_form_gui_t *gui, const dt_masks_form_t *form)
77 {
78   const dt_masks_point_group_t *fpt = (dt_masks_point_group_t *)g_list_nth_data(form->points, gui->group_edited);
79   const dt_masks_form_t *sel = dt_masks_get_from_id(darktable.develop, fpt->formid);
80   if(!sel) return 0;
81   const int formid = sel->formid;
82 
83   // look for apacity
84   const dt_masks_form_t *grp = dt_masks_get_from_id(darktable.develop, fpt->parentid);
85   if(!grp || !(grp->type & DT_MASKS_GROUP)) return 0;
86 
87   int opacity = 0;
88   for(GList *fpts = grp->points; fpts; fpts = g_list_next(fpts))
89   {
90     const dt_masks_point_group_t *fpt = (dt_masks_point_group_t *)fpts->data;
91     if(fpt->formid == formid)
92     {
93       opacity = fpt->opacity * 100;
94       break;
95     }
96   }
97 
98   return opacity;
99 }
100 
_get_all_types_in_group(dt_masks_form_t * form)101 static dt_masks_type_t _get_all_types_in_group(dt_masks_form_t *form)
102 {
103   if(form->type & DT_MASKS_GROUP)
104   {
105     dt_masks_type_t tp = 0;
106     for(GList *l = form->points; l; l = g_list_next(l))
107     {
108       const dt_masks_point_group_t *pt = (dt_masks_point_group_t *)l->data;
109       dt_masks_form_t *f = dt_masks_get_from_id(darktable.develop, pt->formid);
110       tp |= _get_all_types_in_group(f);
111     }
112     return tp;
113   }
114   else
115   {
116     return form->type;
117   }
118 }
119 
dt_masks_mouse_actions(dt_masks_form_t * form)120 GSList *dt_masks_mouse_actions(dt_masks_form_t *form)
121 {
122   dt_masks_type_t formtype = _get_all_types_in_group(form);
123   GSList *lm = NULL;
124 
125   if(form->functions && form->functions->setup_mouse_actions)
126   {
127     lm = form->functions->setup_mouse_actions(form);
128   }
129   // add the common action(s) shared by all shapes
130   if(formtype != 0)
131   {
132     lm = dt_mouse_action_create_simple(lm, DT_MOUSE_ACTION_RIGHT, 0,  _("[SHAPE] remove shape"));
133   }
134 
135   return lm;
136 }
137 
_set_hinter_message(dt_masks_form_gui_t * gui,const dt_masks_form_t * form)138 static void _set_hinter_message(dt_masks_form_gui_t *gui, const dt_masks_form_t *form)
139 {
140   char msg[256] = "";
141 
142   int ftype = form->type;
143 
144   int opacity = 100;
145 
146   const dt_masks_form_t *sel = form;
147   if((ftype & DT_MASKS_GROUP) && (gui->group_edited >= 0))
148   {
149     // we get the selected form
150     const dt_masks_point_group_t *fpt = (dt_masks_point_group_t *)g_list_nth_data(form->points, gui->group_edited);
151     sel = dt_masks_get_from_id(darktable.develop, fpt->formid);
152     if(!sel) return;
153 
154     opacity = _get_opacity(gui, form);
155   }
156   else
157   {
158     opacity = (int)(dt_conf_get_float("plugins/darkroom/masks/opacity") * 100);
159   }
160 
161   if(sel->functions && sel->functions->set_hint_message)
162   {
163     sel->functions->set_hint_message(gui, form, opacity, msg, sizeof(msg));
164   }
165 
166   dt_control_hinter_message(darktable.control, msg);
167 }
168 
dt_masks_init_form_gui(dt_masks_form_gui_t * gui)169 void dt_masks_init_form_gui(dt_masks_form_gui_t *gui)
170 {
171   memset(gui, 0, sizeof(dt_masks_form_gui_t));
172 
173   gui->posx = gui->posy = -1.0f;
174   gui->mouse_leaved_center = TRUE;
175   gui->posx_source = gui->posy_source = -1.0f;
176   gui->source_pos_type = DT_MASKS_SOURCE_POS_RELATIVE_TEMP;
177 }
178 
dt_masks_gui_form_create(dt_masks_form_t * form,dt_masks_form_gui_t * gui,int index,dt_iop_module_t * module)179 void dt_masks_gui_form_create(dt_masks_form_t *form, dt_masks_form_gui_t *gui, int index, dt_iop_module_t *module)
180 {
181   const int npoints = g_list_length(gui->points);
182   if(npoints == index)
183   {
184     dt_masks_form_gui_points_t *gpt2
185         = (dt_masks_form_gui_points_t *)calloc(1, sizeof(dt_masks_form_gui_points_t));
186     gui->points = g_list_append(gui->points, gpt2);
187   }
188   else if(npoints < index)
189     return;
190 
191   dt_masks_gui_form_remove(form, gui, index);
192 
193   dt_masks_form_gui_points_t *gpt = (dt_masks_form_gui_points_t *)g_list_nth_data(gui->points, index);
194   if(dt_masks_get_points_border(darktable.develop, form, &gpt->points, &gpt->points_count, &gpt->border,
195                                 &gpt->border_count, 0, NULL))
196   {
197     if(form->type & DT_MASKS_CLONE)
198       dt_masks_get_points_border(darktable.develop, form, &gpt->source, &gpt->source_count, NULL, NULL, 1, module);
199     gui->pipe_hash = darktable.develop->preview_pipe->backbuf_hash;
200     gui->formid = form->formid;
201   }
202 }
203 
dt_masks_form_gui_points_free(gpointer data)204 void dt_masks_form_gui_points_free(gpointer data)
205 {
206   if(!data) return;
207 
208   dt_masks_form_gui_points_t *gpt = (dt_masks_form_gui_points_t *)data;
209 
210   dt_free_align(gpt->points);
211   dt_free_align(gpt->border);
212   dt_free_align(gpt->source);
213   free(gpt);
214 }
215 
dt_masks_gui_form_remove(dt_masks_form_t * form,dt_masks_form_gui_t * gui,int index)216 void dt_masks_gui_form_remove(dt_masks_form_t *form, dt_masks_form_gui_t *gui, int index)
217 {
218   dt_masks_form_gui_points_t *gpt = (dt_masks_form_gui_points_t *)g_list_nth_data(gui->points, index);
219   gui->pipe_hash = gui->formid = 0;
220 
221   if(gpt)
222   {
223     gpt->points_count = gpt->border_count = gpt->source_count = 0;
224     dt_free_align(gpt->points);
225     gpt->points = NULL;
226     dt_free_align(gpt->border);
227     gpt->border = NULL;
228     dt_free_align(gpt->source);
229     gpt->source = NULL;
230   }
231 }
232 
dt_masks_gui_form_test_create(dt_masks_form_t * form,dt_masks_form_gui_t * gui,dt_iop_module_t * module)233 void dt_masks_gui_form_test_create(dt_masks_form_t *form, dt_masks_form_gui_t *gui, dt_iop_module_t *module)
234 {
235   // we test if the image has changed
236   if(gui->pipe_hash > 0)
237   {
238     if(gui->pipe_hash != darktable.develop->preview_pipe->backbuf_hash)
239     {
240       gui->pipe_hash = gui->formid = 0;
241       g_list_free_full(gui->points, dt_masks_form_gui_points_free);
242       gui->points = NULL;
243     }
244   }
245 
246   // we create the spots if needed
247   if(gui->pipe_hash == 0)
248   {
249     if(form->type & DT_MASKS_GROUP)
250     {
251       int pos = 0;
252       for(GList *fpts = form->points; fpts;  fpts = g_list_next(fpts))
253       {
254         dt_masks_point_group_t *fpt = (dt_masks_point_group_t *)fpts->data;
255         dt_masks_form_t *sel = dt_masks_get_from_id(darktable.develop, fpt->formid);
256         if (!sel) return;
257         dt_masks_gui_form_create(sel, gui, pos, module);
258         pos++;
259       }
260     }
261     else
262       dt_masks_gui_form_create(form, gui, 0, module);
263   }
264 }
265 
_check_id(dt_masks_form_t * form)266 static void _check_id(dt_masks_form_t *form)
267 {
268   int nid = 100;
269   for(GList *forms = darktable.develop->forms; forms; )
270   {
271     dt_masks_form_t *ff = (dt_masks_form_t *)forms->data;
272     if(ff->formid == form->formid)
273     {
274       form->formid = nid++;
275       forms = darktable.develop->forms; // jump back to start of list
276     }
277     else
278       forms = g_list_next(forms); // advance to next form
279   }
280 }
281 
_set_group_name_from_module(dt_iop_module_t * module,dt_masks_form_t * grp)282 static void _set_group_name_from_module(dt_iop_module_t *module, dt_masks_form_t *grp)
283 {
284   gchar *module_label = dt_history_item_get_name(module);
285   snprintf(grp->name, sizeof(grp->name), "grp %s", module_label);
286   g_free(module_label);
287 }
288 
_group_create(dt_develop_t * dev,dt_iop_module_t * module,dt_masks_type_t type)289 static dt_masks_form_t *_group_create(dt_develop_t *dev, dt_iop_module_t *module, dt_masks_type_t type)
290 {
291   dt_masks_form_t* grp = dt_masks_create(type);
292   _set_group_name_from_module(module, grp);
293   _check_id(grp);
294   dev->forms = g_list_append(dev->forms, grp);
295   module->blend_params->mask_id = grp->formid;
296   return grp;
297 }
298 
_group_from_module(dt_develop_t * dev,dt_iop_module_t * module)299 static dt_masks_form_t *_group_from_module(dt_develop_t *dev, dt_iop_module_t *module)
300 {
301   return dt_masks_get_from_id(dev, module->blend_params->mask_id);
302 }
303 
dt_masks_gui_form_save_creation(dt_develop_t * dev,dt_iop_module_t * module,dt_masks_form_t * form,dt_masks_form_gui_t * gui)304 void dt_masks_gui_form_save_creation(dt_develop_t *dev, dt_iop_module_t *module, dt_masks_form_t *form,
305                                      dt_masks_form_gui_t *gui)
306 {
307   // we check if the id is already registered
308   _check_id(form);
309 
310   if(gui) gui->creation = FALSE;
311 
312   // mask nb will be at least the length of the list
313   guint nb = 0;
314 
315   // count only the same forms to have a clean numbering
316   for(GList *l = dev->forms; l; l = g_list_next(l))
317   {
318     dt_masks_form_t *f = (dt_masks_form_t *)l->data;
319     if(f->type == form->type) nb++;
320   }
321 
322   gboolean exist = FALSE;
323 
324   // check that we do not have duplicate, in case some masks have been
325   // removed we can have hole and so nb could already exists.
326   do
327   {
328     exist = FALSE;
329     nb++;
330 
331     if(form->functions && form->functions->set_form_name)
332       form->functions->set_form_name(form, nb);
333 
334     for(GList *l = dev->forms; l; l = g_list_next(l))
335     {
336       dt_masks_form_t *f = (dt_masks_form_t *)l->data;
337       if(!strcmp(f->name, form->name))
338       {
339         exist = TRUE;
340         break;
341       }
342     }
343   } while(exist);
344 
345   dev->forms = g_list_append(dev->forms, form);
346 
347   dt_dev_add_masks_history_item(dev, module, TRUE);
348 
349   if(module)
350   {
351     // is there already a masks group for this module ?
352     dt_masks_form_t *grp = _group_from_module(dev, module);
353     if(!grp)
354     {
355       // we create a new group
356       if(form->type & (DT_MASKS_CLONE|DT_MASKS_NON_CLONE))
357         grp = _group_create(dev, module, DT_MASKS_GROUP | DT_MASKS_CLONE);
358       else
359         grp = _group_create(dev, module, DT_MASKS_GROUP);
360     }
361     // we add the form in this group
362     dt_masks_point_group_t *grpt = malloc(sizeof(dt_masks_point_group_t));
363     grpt->formid = form->formid;
364     grpt->parentid = grp->formid;
365     grpt->state = DT_MASKS_STATE_SHOW | DT_MASKS_STATE_USE;
366     if(grp->points) grpt->state |= DT_MASKS_STATE_UNION;
367     grpt->opacity = dt_conf_get_float("plugins/darkroom/masks/opacity");
368     grp->points = g_list_append(grp->points, grpt);
369     // we save the group
370     dt_dev_add_masks_history_item(dev, module, TRUE);
371     // we update module gui
372     if(gui) dt_masks_iop_update(module);
373     //dt_dev_add_history_item(dev, module, TRUE);
374   }
375   // show the form if needed
376   if(gui) dev->form_gui->formid = form->formid;
377 }
378 
dt_masks_form_duplicate(dt_develop_t * dev,int formid)379 int dt_masks_form_duplicate(dt_develop_t *dev, int formid)
380 {
381   // we create a new empty form
382   dt_masks_form_t *fbase = dt_masks_get_from_id(dev, formid);
383   if(!fbase) return -1;
384   dt_masks_form_t *fdest = dt_masks_create(fbase->type);
385   _check_id(fdest);
386 
387   // we copy the base values
388   fdest->source[0] = fbase->source[0];
389   fdest->source[1] = fbase->source[1];
390   fdest->version = fbase->version;
391   snprintf(fdest->name, sizeof(fdest->name), _("copy of %s"), fbase->name);
392 
393   darktable.develop->forms = g_list_append(dev->forms, fdest);
394 
395   // we copy all the points
396   if(fbase->functions)
397     fbase->functions->duplicate_points(dev, fbase, fdest);
398 
399   // we save the form
400   dt_dev_add_masks_history_item(dev, NULL, TRUE);
401 
402   // and we return its id
403   return fdest->formid;
404 }
405 
dt_masks_get_points_border(dt_develop_t * dev,dt_masks_form_t * form,float ** points,int * points_count,float ** border,int * border_count,int source,dt_iop_module_t * module)406 int dt_masks_get_points_border(dt_develop_t *dev, dt_masks_form_t *form, float **points, int *points_count,
407                                float **border, int *border_count, int source, dt_iop_module_t *module)
408 {
409   if(form->functions && form->functions->get_points_border)
410   {
411     return form->functions->get_points_border(dev, form, points, points_count, border, border_count, source,
412                                               module);
413   }
414   return 0;
415 }
416 
dt_masks_get_area(dt_iop_module_t * module,dt_dev_pixelpipe_iop_t * piece,dt_masks_form_t * form,int * width,int * height,int * posx,int * posy)417 int dt_masks_get_area(dt_iop_module_t *module, dt_dev_pixelpipe_iop_t *piece, dt_masks_form_t *form,
418                       int *width, int *height, int *posx, int *posy)
419 {
420   if(form->functions)
421     return form->functions->get_area(module, piece, form, width, height, posx, posy);
422 
423   return 0;
424 }
425 
dt_masks_get_source_area(dt_iop_module_t * module,dt_dev_pixelpipe_iop_t * piece,dt_masks_form_t * form,int * width,int * height,int * posx,int * posy)426 int dt_masks_get_source_area(dt_iop_module_t *module, dt_dev_pixelpipe_iop_t *piece, dt_masks_form_t *form,
427                              int *width, int *height, int *posx, int *posy)
428 {
429   *width = *height = *posx = *posy = 0;
430 
431   // must be a clone form
432   if(form->type & DT_MASKS_CLONE)
433   {
434     if(form->functions)
435       return form->functions->get_source_area(module, piece, form, width, height, posx, posy);
436   }
437   return 0;
438 }
439 
dt_masks_version(void)440 int dt_masks_version(void)
441 {
442   return DEVELOP_MASKS_VERSION;
443 }
444 
dt_masks_legacy_params_v1_to_v2(dt_develop_t * dev,void * params)445 static int dt_masks_legacy_params_v1_to_v2(dt_develop_t *dev, void *params)
446 {
447   /*
448    * difference: before v2 images were originally rotated on load, and then
449    * maybe in flip iop
450    * after v2: images are only rotated in flip iop.
451    */
452 
453   dt_masks_form_t *m = (dt_masks_form_t *)params;
454 
455   const dt_image_orientation_t ori = dt_image_orientation(&dev->image_storage);
456 
457   if(ori == ORIENTATION_NONE)
458   {
459     // image is not rotated, we're fine!
460     m->version = 2;
461     return 0;
462   }
463   else
464   {
465     if(dev->iop == NULL) return 1;
466 
467     const char *opname = "flip";
468     dt_iop_module_t *module = NULL;
469 
470     for(GList *modules = dev->iop; modules; modules = g_list_next(modules))
471     {
472       dt_iop_module_t *find_op = (dt_iop_module_t *)modules->data;
473       if(!strcmp(find_op->op, opname))
474       {
475         module = find_op;
476         break;
477       }
478     }
479 
480     if(module == NULL) return 1;
481 
482     dt_dev_pixelpipe_iop_t piece = { 0 };
483 
484     module->init_pipe(module, NULL, &piece);
485     module->commit_params(module, module->default_params, NULL, &piece);
486 
487     piece.buf_in.width = 1;
488     piece.buf_in.height = 1;
489 
490     GList *p = m->points;
491 
492     if(!p) return 1;
493 
494     if(m->type & DT_MASKS_CIRCLE)
495     {
496       dt_masks_point_circle_t *circle = (dt_masks_point_circle_t *)p->data;
497       module->distort_backtransform(module, &piece, circle->center, 1);
498     }
499     else if(m->type & DT_MASKS_PATH)
500     {
501       for(; p; p = g_list_next(p))
502       {
503         dt_masks_point_path_t *path = (dt_masks_point_path_t *)p->data;
504         module->distort_backtransform(module, &piece, path->corner, 1);
505         module->distort_backtransform(module, &piece, path->ctrl1, 1);
506         module->distort_backtransform(module, &piece, path->ctrl2, 1);
507       }
508     }
509     else if(m->type & DT_MASKS_GRADIENT)
510     { // TODO: new ones have wrong rotation.
511       dt_masks_point_gradient_t *gradient = (dt_masks_point_gradient_t *)p->data;
512       module->distort_backtransform(module, &piece, gradient->anchor, 1);
513 
514       if(ori == ORIENTATION_ROTATE_180_DEG)
515         gradient->rotation -= 180.0f;
516       else if(ori == ORIENTATION_ROTATE_CCW_90_DEG)
517         gradient->rotation -= 90.0f;
518       else if(ori == ORIENTATION_ROTATE_CW_90_DEG)
519         gradient->rotation -= -90.0f;
520     }
521     else if(m->type & DT_MASKS_ELLIPSE)
522     {
523       dt_masks_point_ellipse_t *ellipse = (dt_masks_point_ellipse_t *)p->data;
524       module->distort_backtransform(module, &piece, ellipse->center, 1);
525 
526       if(ori & ORIENTATION_SWAP_XY)
527       {
528         const float y = ellipse->radius[0];
529         ellipse->radius[0] = ellipse->radius[1];
530         ellipse->radius[1] = y;
531       }
532     }
533     else if(m->type & DT_MASKS_BRUSH)
534     {
535       for(; p; p = g_list_next(p))
536       {
537         dt_masks_point_brush_t *brush = (dt_masks_point_brush_t *)p->data;
538         module->distort_backtransform(module, &piece, brush->corner, 1);
539         module->distort_backtransform(module, &piece, brush->ctrl1, 1);
540         module->distort_backtransform(module, &piece, brush->ctrl2, 1);
541       }
542     }
543 
544     if(m->type & DT_MASKS_CLONE)
545     {
546       // NOTE: can be: DT_MASKS_CIRCLE, DT_MASKS_ELLIPSE, DT_MASKS_PATH
547       module->distort_backtransform(module, &piece, m->source, 1);
548     }
549 
550     m->version = 2;
551 
552     return 0;
553   }
554 }
555 
dt_masks_legacy_params_v2_to_v3_transform(const dt_image_t * img,float * points)556 static void dt_masks_legacy_params_v2_to_v3_transform(const dt_image_t *img, float *points)
557 {
558   const float w = (float)img->width, h = (float)img->height;
559 
560   const float cx = (float)img->crop_x, cy = (float)img->crop_y;
561 
562   const float cw = (float)(img->width - img->crop_x - img->crop_width),
563               ch = (float)(img->height - img->crop_y - img->crop_height);
564 
565   /*
566    * masks coordinates are normalized, so we need to:
567    * 1. de-normalize them by image original cropped dimensions
568    * 2. un-crop them by adding top-left crop coordinates
569    * 3. normalize them by the image fully uncropped dimensions
570    */
571   points[0] = ((points[0] * cw) + cx) / w;
572   points[1] = ((points[1] * ch) + cy) / h;
573 }
574 
dt_masks_legacy_params_v2_to_v3_transform_only_rescale(const dt_image_t * img,float * points,size_t points_count)575 static void dt_masks_legacy_params_v2_to_v3_transform_only_rescale(const dt_image_t *img, float *points,
576                                                                    size_t points_count)
577 {
578   const float w = (float)img->width, h = (float)img->height;
579 
580   const float cw = (float)(img->width - img->crop_x - img->crop_width),
581               ch = (float)(img->height - img->crop_y - img->crop_height);
582 
583   /*
584    * masks coordinates are normalized, so we need to:
585    * 1. de-normalize them by minimal of image original cropped dimensions
586    * 2. normalize them by the minimal of image fully uncropped dimensions
587    */
588   for(size_t i = 0; i < points_count; i++) points[i] = ((points[i] * MIN(cw, ch))) / MIN(w, h);
589 }
590 
dt_masks_legacy_params_v2_to_v3(dt_develop_t * dev,void * params)591 static int dt_masks_legacy_params_v2_to_v3(dt_develop_t *dev, void *params)
592 {
593   /*
594    * difference: before v3 images were originally cropped on load
595    * after v3: images are cropped in rawprepare iop.
596    */
597 
598   dt_masks_form_t *m = (dt_masks_form_t *)params;
599 
600   const dt_image_t *img = &(dev->image_storage);
601 
602   if(img->crop_x == 0 && img->crop_y == 0 && img->crop_width == 0 && img->crop_height == 0)
603   {
604     // image has no "raw cropping", we're fine!
605     m->version = 3;
606     return 0;
607   }
608   else
609   {
610     GList *p = m->points;
611 
612     if(!p) return 1;
613 
614     if(m->type & DT_MASKS_CIRCLE)
615     {
616       dt_masks_point_circle_t *circle = (dt_masks_point_circle_t *)p->data;
617       dt_masks_legacy_params_v2_to_v3_transform(img, circle->center);
618       dt_masks_legacy_params_v2_to_v3_transform_only_rescale(img, &circle->radius, 1);
619       dt_masks_legacy_params_v2_to_v3_transform_only_rescale(img, &circle->border, 1);
620     }
621     else if(m->type & DT_MASKS_PATH)
622     {
623       for(; p; p = g_list_next(p))
624       {
625         dt_masks_point_path_t *path = (dt_masks_point_path_t *)p->data;
626         dt_masks_legacy_params_v2_to_v3_transform(img, path->corner);
627         dt_masks_legacy_params_v2_to_v3_transform(img, path->ctrl1);
628         dt_masks_legacy_params_v2_to_v3_transform(img, path->ctrl2);
629         dt_masks_legacy_params_v2_to_v3_transform_only_rescale(img, path->border, 2);
630       }
631     }
632     else if(m->type & DT_MASKS_GRADIENT)
633     {
634       dt_masks_point_gradient_t *gradient = (dt_masks_point_gradient_t *)p->data;
635       dt_masks_legacy_params_v2_to_v3_transform(img, gradient->anchor);
636     }
637     else if(m->type & DT_MASKS_ELLIPSE)
638     {
639       dt_masks_point_ellipse_t *ellipse = (dt_masks_point_ellipse_t *)p->data;
640       dt_masks_legacy_params_v2_to_v3_transform(img, ellipse->center);
641       dt_masks_legacy_params_v2_to_v3_transform_only_rescale(img, ellipse->radius, 2);
642       dt_masks_legacy_params_v2_to_v3_transform_only_rescale(img, &ellipse->border, 1);
643     }
644     else if(m->type & DT_MASKS_BRUSH)
645     {
646       for(; p;  p = g_list_next(p))
647       {
648         dt_masks_point_brush_t *brush = (dt_masks_point_brush_t *)p->data;
649         dt_masks_legacy_params_v2_to_v3_transform(img, brush->corner);
650         dt_masks_legacy_params_v2_to_v3_transform(img, brush->ctrl1);
651         dt_masks_legacy_params_v2_to_v3_transform(img, brush->ctrl2);
652         dt_masks_legacy_params_v2_to_v3_transform_only_rescale(img, brush->border, 2);
653       }
654     }
655 
656     if(m->type & DT_MASKS_CLONE)
657     {
658       // NOTE: can be: DT_MASKS_CIRCLE, DT_MASKS_ELLIPSE, DT_MASKS_PATH
659       dt_masks_legacy_params_v2_to_v3_transform(img, m->source);
660     }
661 
662     m->version = 3;
663 
664     return 0;
665   }
666 }
667 
dt_masks_legacy_params_v3_to_v4(dt_develop_t * dev,void * params)668 static int dt_masks_legacy_params_v3_to_v4(dt_develop_t *dev, void *params)
669 {
670   /*
671    * difference affecting ellipse
672    * up to v3: only equidistant feathering
673    * after v4: choice between equidistant and proportional feathering
674    * type of feathering is defined in new flags parameter
675    */
676 
677   dt_masks_form_t *m = (dt_masks_form_t *)params;
678 
679   GList *p = m->points;
680 
681   if(!p) return 1;
682 
683   if(m->type & DT_MASKS_ELLIPSE)
684   {
685     dt_masks_point_ellipse_t *ellipse = (dt_masks_point_ellipse_t *)p->data;
686     ellipse->flags = DT_MASKS_ELLIPSE_EQUIDISTANT;
687   }
688 
689   m->version = 4;
690 
691   return 0;
692 }
693 
694 
dt_masks_legacy_params_v4_to_v5(dt_develop_t * dev,void * params)695 static int dt_masks_legacy_params_v4_to_v5(dt_develop_t *dev, void *params)
696 {
697   /*
698    * difference affecting gradient
699    * up to v4: only linear gradient (relative to input image)
700    * after v5: curved gradients
701    */
702 
703   dt_masks_form_t *m = (dt_masks_form_t *)params;
704 
705   GList *p = m->points;
706 
707   if(!p) return 1;
708 
709   if(m->type & DT_MASKS_GRADIENT)
710   {
711     dt_masks_point_gradient_t *gradient = (dt_masks_point_gradient_t *)p->data;
712     gradient->curvature = 0.0f;
713   }
714 
715   m->version = 5;
716 
717   return 0;
718 }
719 
dt_masks_legacy_params_v5_to_v6(dt_develop_t * dev,void * params)720 static int dt_masks_legacy_params_v5_to_v6(dt_develop_t *dev, void *params)
721 {
722   /*
723    * difference affecting gradient
724    * up to v5: linear transition
725    * after v5: linear or sigmoidal transition
726    */
727 
728   dt_masks_form_t *m = (dt_masks_form_t *)params;
729 
730   GList *p = m->points;
731 
732   if(!p) return 1;
733 
734   if(m->type & DT_MASKS_GRADIENT)
735   {
736     dt_masks_point_gradient_t *gradient = (dt_masks_point_gradient_t *)p->data;
737     gradient->state = DT_MASKS_GRADIENT_STATE_LINEAR;
738   }
739 
740   m->version = 6;
741 
742   return 0;
743 }
744 
745 
dt_masks_legacy_params(dt_develop_t * dev,void * params,const int old_version,const int new_version)746 int dt_masks_legacy_params(dt_develop_t *dev, void *params, const int old_version, const int new_version)
747 {
748   int res = 1;
749 #if 0 // we should not need this any longer
750   if(old_version == 1 && new_version == 2)
751   {
752     res = dt_masks_legacy_params_v1_to_v2(dev, params);
753   }
754 #endif
755 
756   if(old_version == 1 && new_version == 6)
757   {
758     res = dt_masks_legacy_params_v1_to_v2(dev, params);
759     if(!res) res = dt_masks_legacy_params_v2_to_v3(dev, params);
760     if(!res) res = dt_masks_legacy_params_v3_to_v4(dev, params);
761     if(!res) res = dt_masks_legacy_params_v4_to_v5(dev, params);
762     if(!res) res = dt_masks_legacy_params_v5_to_v6(dev, params);
763   }
764   else if(old_version == 2 && new_version == 6)
765   {
766     res = dt_masks_legacy_params_v2_to_v3(dev, params);
767     if(!res) res = dt_masks_legacy_params_v3_to_v4(dev, params);
768     if(!res) res = dt_masks_legacy_params_v4_to_v5(dev, params);
769     if(!res) res = dt_masks_legacy_params_v5_to_v6(dev, params);
770   }
771   else if(old_version == 3 && new_version == 6)
772   {
773     res = dt_masks_legacy_params_v3_to_v4(dev, params);
774     if(!res) res = dt_masks_legacy_params_v4_to_v5(dev, params);
775     if(!res) res = dt_masks_legacy_params_v5_to_v6(dev, params);
776   }
777   else if(old_version == 4 && new_version == 6)
778   {
779     res = dt_masks_legacy_params_v4_to_v5(dev, params);
780     if(!res) res = dt_masks_legacy_params_v5_to_v6(dev, params);
781   }
782   else if(old_version == 5 && new_version == 6)
783   {
784     res = dt_masks_legacy_params_v5_to_v6(dev, params);
785   }
786 
787   return res;
788 }
789 
790 static int form_id = 0;
791 
dt_masks_create(dt_masks_type_t type)792 dt_masks_form_t *dt_masks_create(dt_masks_type_t type)
793 {
794   dt_masks_form_t *form = (dt_masks_form_t *)calloc(1, sizeof(dt_masks_form_t));
795   if(!form) return NULL;
796 
797   form->type = type;
798   form->version = dt_masks_version();
799   form->formid = time(NULL) + form_id++;
800 
801   if (type & DT_MASKS_CIRCLE)
802     form->functions = &dt_masks_functions_circle;
803   else if (type & DT_MASKS_ELLIPSE)
804     form->functions = &dt_masks_functions_ellipse;
805   else if (type & DT_MASKS_BRUSH)
806     form->functions = &dt_masks_functions_brush;
807   else if (type & DT_MASKS_PATH)
808     form->functions = &dt_masks_functions_path;
809   else if (type & DT_MASKS_GRADIENT)
810     form->functions = &dt_masks_functions_gradient;
811   else if (type & DT_MASKS_GROUP)
812     form->functions = &dt_masks_functions_group;
813 
814   if (form->functions && form->functions->sanitize_config)
815     form->functions->sanitize_config(type);
816 
817   return form;
818 }
819 
dt_masks_create_ext(dt_masks_type_t type)820 dt_masks_form_t *dt_masks_create_ext(dt_masks_type_t type)
821 {
822   dt_masks_form_t *form = dt_masks_create(type);
823 
824   // all forms created here are registered in darktable.develop->allforms for later cleanup
825   if(form)
826   darktable.develop->allforms = g_list_append(darktable.develop->allforms, form);
827 
828   return form;
829 }
830 
dt_masks_replace_current_forms(dt_develop_t * dev,GList * forms)831 void dt_masks_replace_current_forms(dt_develop_t *dev, GList *forms)
832 {
833   GList *forms_tmp = dt_masks_dup_forms_deep(forms, NULL);
834 
835   while(dev->forms)
836   {
837     darktable.develop->allforms = g_list_append(darktable.develop->allforms, dev->forms->data);
838     dev->forms = g_list_delete_link(dev->forms, dev->forms);
839   }
840 
841   dev->forms = forms_tmp;
842 }
843 
dt_masks_get_from_id_ext(GList * forms,int id)844 dt_masks_form_t *dt_masks_get_from_id_ext(GList *forms, int id)
845 {
846   for(; forms; forms = g_list_next(forms))
847   {
848     dt_masks_form_t *form = (dt_masks_form_t *)forms->data;
849     if(form->formid == id) return form;
850   }
851   return NULL;
852 }
853 
dt_masks_get_from_id(dt_develop_t * dev,int id)854 dt_masks_form_t *dt_masks_get_from_id(dt_develop_t *dev, int id)
855 {
856   return dt_masks_get_from_id_ext(dev->forms, id);
857 }
858 
dt_masks_read_masks_history(dt_develop_t * dev,const int imgid)859 void dt_masks_read_masks_history(dt_develop_t *dev, const int imgid)
860 {
861   dt_dev_history_item_t *hist_item = NULL;
862   dt_dev_history_item_t *hist_item_last = NULL;
863   int num_prev = -1;
864 
865   sqlite3_stmt *stmt;
866   DT_DEBUG_SQLITE3_PREPARE_V2(
867       dt_database_get(darktable.db),
868       "SELECT imgid, formid, form, name, version, points, points_count, source, num "
869       "FROM main.masks_history WHERE imgid = ?1 ORDER BY num",
870       -1, &stmt, NULL);
871   DT_DEBUG_SQLITE3_BIND_INT(stmt, 1, imgid);
872 
873   while(sqlite3_step(stmt) == SQLITE_ROW)
874   {
875     // db record:
876     // 0-img, 1-formid, 2-form_type, 3-name, 4-version, 5-points, 6-points_count, 7-source, 8-num
877 
878     // we get the values
879 
880     const int formid = sqlite3_column_int(stmt, 1);
881     const int num = sqlite3_column_int(stmt, 8);
882     const dt_masks_type_t type = sqlite3_column_int(stmt, 2);
883     dt_masks_form_t *form = dt_masks_create(type);
884     form->formid = formid;
885     const char *name = (const char *)sqlite3_column_text(stmt, 3);
886     g_strlcpy(form->name, name, sizeof(form->name));
887     form->version = sqlite3_column_int(stmt, 4);
888     form->points = NULL;
889     const int nb_points = sqlite3_column_int(stmt, 6);
890     memcpy(form->source, sqlite3_column_blob(stmt, 7), sizeof(float) * 2);
891 
892     // and now we "read" the blob
893     if(form->functions)
894     {
895       const char *const ptbuf = (char *)sqlite3_column_blob(stmt, 5);
896       const size_t point_size = form->functions->point_struct_size;
897       for(int i = 0; i < nb_points; i++)
898       {
899         char *point = (char *)malloc(point_size);
900         memcpy(point, ptbuf + i*point_size, point_size);
901         form->points = g_list_append(form->points, point);
902       }
903     }
904 
905     if(form->version != dt_masks_version())
906     {
907       if(dt_masks_legacy_params(dev, form, form->version, dt_masks_version()))
908       {
909         const char *fname = dev->image_storage.filename + strlen(dev->image_storage.filename);
910         while(fname > dev->image_storage.filename && *fname != '/') fname--;
911         if(fname > dev->image_storage.filename) fname++;
912 
913         fprintf(stderr,
914                 "[_dev_read_masks_history] %s (imgid `%i'): mask version mismatch: history is %d, dt %d.\n",
915                 fname, imgid, form->version, dt_masks_version());
916         dt_control_log(_("%s: mask version mismatch: %d != %d"), fname, dt_masks_version(), form->version);
917 
918         continue;
919       }
920     }
921 
922     // if this is a new history entry let's find it
923     if(num_prev != num)
924     {
925       hist_item = NULL;
926       for(GList *history = dev->history; history; history = g_list_next(history))
927       {
928         dt_dev_history_item_t *hitem = (dt_dev_history_item_t *)(history->data);
929         if(hitem->num == num)
930         {
931           hist_item = hitem;
932           break;
933         }
934       }
935       num_prev = num;
936     }
937     // add the form to the history entry
938     if(hist_item)
939     {
940       hist_item->forms = g_list_append(hist_item->forms, form);
941     }
942     else
943       fprintf(stderr,
944               "[_dev_read_masks_history] can't find history entry %i while adding mask %s(%i)\n",
945               num, form->name, formid);
946 
947     if(num < dev->history_end) hist_item_last = hist_item;
948   }
949   sqlite3_finalize(stmt);
950 
951   // and we update the current forms snapshot
952   dt_masks_replace_current_forms(dev, (hist_item_last)?hist_item_last->forms:NULL);
953 }
954 
dt_masks_write_masks_history_item(const int imgid,const int num,dt_masks_form_t * form)955 void dt_masks_write_masks_history_item(const int imgid, const int num, dt_masks_form_t *form)
956 {
957   sqlite3_stmt *stmt;
958 
959   // write the form into the database
960   DT_DEBUG_SQLITE3_PREPARE_V2(dt_database_get(darktable.db), "INSERT INTO main.masks_history (imgid, num, formid, form, name, "
961                                                              "version, points, points_count,source) VALUES "
962                                                              "(?1, ?9, ?2, ?3, ?4, ?5, ?6, ?7, ?8)",
963                               -1, &stmt, NULL);
964   DT_DEBUG_SQLITE3_BIND_INT(stmt, 1, imgid);
965   DT_DEBUG_SQLITE3_BIND_INT(stmt, 9, num);
966   DT_DEBUG_SQLITE3_BIND_INT(stmt, 2, form->formid);
967   DT_DEBUG_SQLITE3_BIND_INT(stmt, 3, form->type);
968   DT_DEBUG_SQLITE3_BIND_TEXT(stmt, 4, form->name, -1, SQLITE_TRANSIENT);
969   DT_DEBUG_SQLITE3_BIND_BLOB(stmt, 8, form->source, 2 * sizeof(float), SQLITE_TRANSIENT);
970   DT_DEBUG_SQLITE3_BIND_INT(stmt, 5, form->version);
971   if(form->functions)
972   {
973     const size_t point_size = form->functions->point_struct_size;
974     const guint nb = g_list_length(form->points);
975     char *const restrict ptbuf = (char *)malloc(nb * point_size);
976     int pos = 0;
977     for (GList *points = form->points; points; points = g_list_next(points))
978     {
979       memcpy(ptbuf + pos, points->data, point_size);
980       pos += point_size;
981     }
982     DT_DEBUG_SQLITE3_BIND_BLOB(stmt, 6, ptbuf, nb * point_size, SQLITE_TRANSIENT);
983     DT_DEBUG_SQLITE3_BIND_INT(stmt, 7, nb);
984     sqlite3_step(stmt);
985     sqlite3_finalize(stmt);
986     free(ptbuf);
987   }
988 }
989 
dt_masks_free_form(dt_masks_form_t * form)990 void dt_masks_free_form(dt_masks_form_t *form)
991 {
992   if(!form) return;
993   g_list_free_full(form->points, free);
994   form->points = NULL;
995   free(form);
996 }
997 
dt_masks_events_mouse_leave(struct dt_iop_module_t * module)998 int dt_masks_events_mouse_leave(struct dt_iop_module_t *module)
999 {
1000   if(darktable.develop->form_gui)
1001   {
1002     dt_masks_form_gui_t *gui = darktable.develop->form_gui;
1003     gui->mouse_leaved_center = TRUE;
1004   }
1005   return 0;
1006 }
1007 
dt_masks_events_mouse_enter(struct dt_iop_module_t * module)1008 int dt_masks_events_mouse_enter(struct dt_iop_module_t *module)
1009 {
1010   if(darktable.develop->form_gui)
1011   {
1012     dt_masks_form_gui_t *gui = darktable.develop->form_gui;
1013     gui->mouse_leaved_center = FALSE;
1014   }
1015   return 0;
1016 }
1017 
dt_masks_events_mouse_moved(struct dt_iop_module_t * module,double x,double y,double pressure,int which)1018 int dt_masks_events_mouse_moved(struct dt_iop_module_t *module, double x, double y, double pressure, int which)
1019 {
1020   // record mouse position even if there are no masks visible
1021   dt_masks_form_gui_t *gui = darktable.develop->form_gui;
1022   dt_masks_form_t *form = darktable.develop->form_visible;
1023   float pzx = 0.0f, pzy = 0.0f;
1024 
1025   dt_dev_get_pointer_zoom_pos(darktable.develop, x, y, &pzx, &pzy);
1026   pzx += 0.5f;
1027   pzy += 0.5f;
1028 
1029   if(gui)
1030   {
1031     // This assume that if this event is generated the mouse is over the center window
1032     gui->mouse_leaved_center = FALSE;
1033     gui->posx = pzx * darktable.develop->preview_pipe->backbuf_width;
1034     gui->posy = pzy * darktable.develop->preview_pipe->backbuf_height;
1035   }
1036 
1037   // do not process if no forms visible
1038   if(!form) return 0;
1039 
1040   // add an option to allow skip mouse events while editing masks
1041   if(darktable.develop->darkroom_skip_mouse_events) return 0;
1042 
1043   int rep = 0;
1044   if(form->functions)
1045     rep = form->functions->mouse_moved(module, pzx, pzy, pressure, which, form, 0, gui, 0);
1046 
1047   if(gui) _set_hinter_message(gui, form);
1048 
1049   return rep;
1050 }
1051 
dt_masks_events_button_released(struct dt_iop_module_t * module,double x,double y,int which,uint32_t state)1052 int dt_masks_events_button_released(struct dt_iop_module_t *module, double x, double y, int which,
1053                                     uint32_t state)
1054 {
1055   // add an option to allow skip mouse events while editing masks
1056   if(darktable.develop->darkroom_skip_mouse_events) return 0;
1057 
1058   dt_masks_form_t *form = darktable.develop->form_visible;
1059   dt_masks_form_gui_t *gui = darktable.develop->form_gui;
1060   float pzx = 0.0f, pzy = 0.0f;
1061   dt_dev_get_pointer_zoom_pos(darktable.develop, x, y, &pzx, &pzy);
1062   pzx += 0.5f;
1063   pzy += 0.5f;
1064 
1065   if(darktable.develop->mask_form_selected_id)
1066     dt_dev_masks_selection_change(darktable.develop, module,
1067                                   darktable.develop->mask_form_selected_id, FALSE);
1068 
1069   if(form->functions)
1070     return form->functions->button_released(module, pzx, pzy, which, state, form, 0, gui, 0);
1071 
1072   return 0;
1073 }
1074 
dt_masks_events_button_pressed(struct dt_iop_module_t * module,double x,double y,double pressure,int which,int type,uint32_t state)1075 int dt_masks_events_button_pressed(struct dt_iop_module_t *module, double x, double y, double pressure,
1076                                    int which, int type, uint32_t state)
1077 {
1078   // add an option to allow skip mouse events while editing masks
1079   if(darktable.develop->darkroom_skip_mouse_events) return 0;
1080 
1081   dt_masks_form_t *form = darktable.develop->form_visible;
1082   dt_masks_form_gui_t *gui = darktable.develop->form_gui;
1083   float pzx = 0.0f, pzy = 0.0f;
1084   dt_dev_get_pointer_zoom_pos(darktable.develop, x, y, &pzx, &pzy);
1085   pzx += 0.5f;
1086   pzy += 0.5f;
1087 
1088   // allow to select a shape inside an iop
1089   if(gui && which == 1)
1090   {
1091     dt_masks_form_t *sel = NULL;
1092 
1093     if((gui->form_selected || gui->source_selected || gui->point_selected || gui->seg_selected
1094         || gui->feather_selected)
1095        && !gui->creation && gui->group_edited >= 0)
1096     {
1097       // we get the selected form
1098       dt_masks_point_group_t *fpt = (dt_masks_point_group_t *)g_list_nth_data(form->points, gui->group_edited);
1099       if(fpt)
1100       {
1101         sel = dt_masks_get_from_id(darktable.develop, fpt->formid);
1102       }
1103     }
1104 
1105     dt_masks_select_form(module, sel);
1106   }
1107 
1108   if(form->functions)
1109     return form->functions->button_pressed(module, pzx, pzy, pressure, which, type, state, form, 0, gui, 0);
1110 
1111   return 0;
1112 }
1113 
dt_masks_events_mouse_scrolled(struct dt_iop_module_t * module,double x,double y,int up,uint32_t state)1114 int dt_masks_events_mouse_scrolled(struct dt_iop_module_t *module, double x, double y, int up, uint32_t state)
1115 {
1116   // add an option to allow skip mouse events while editing masks
1117   if(darktable.develop->darkroom_skip_mouse_events) return 0;
1118 
1119   dt_masks_form_t *form = darktable.develop->form_visible;
1120   dt_masks_form_gui_t *gui = darktable.develop->form_gui;
1121   float pzx = 0.0f, pzy = 0.0f;
1122   dt_dev_get_pointer_zoom_pos(darktable.develop, x, y, &pzx, &pzy);
1123   pzx += 0.5f;
1124   pzy += 0.5f;
1125 
1126   int ret = 0;
1127   const gboolean incr = dt_mask_scroll_increases(up);
1128 
1129   if(form->functions)
1130     ret = form->functions->mouse_scrolled(module, pzx, pzy,
1131                                           incr ? 1 : 0,
1132                                           state, form, 0, gui, 0);
1133 
1134   if(gui)
1135   {
1136     // for brush, the opacity is the density of the masks, do not update opacity here for the brush.
1137     if(gui->creation && dt_modifier_is(state, GDK_CONTROL_MASK))
1138     {
1139       float opacity = dt_conf_get_float("plugins/darkroom/masks/opacity");
1140       const float amount = incr ? 0.05f : -0.05f;
1141 
1142       opacity = CLAMP(opacity + amount, 0.05f, 1.0f);
1143       dt_conf_set_float("plugins/darkroom/masks/opacity", opacity);
1144       const int opacitypercent = opacity * 100;
1145       dt_toast_log(_("opacity: %d%%"), opacitypercent);
1146       ret = 1;
1147     }
1148 
1149     _set_hinter_message(gui, form);
1150   }
1151 
1152   return ret;
1153 }
dt_masks_events_post_expose(struct dt_iop_module_t * module,cairo_t * cr,int32_t width,int32_t height,int32_t pointerx,int32_t pointery)1154 void dt_masks_events_post_expose(struct dt_iop_module_t *module, cairo_t *cr, int32_t width, int32_t height,
1155                                  int32_t pointerx, int32_t pointery)
1156 {
1157   dt_develop_t *dev = darktable.develop;
1158   dt_masks_form_t *form = dev->form_visible;
1159   dt_masks_form_gui_t *gui = dev->form_gui;
1160   if(!gui) return;
1161   if(!form) return;
1162 
1163   float wd = dev->preview_pipe->backbuf_width;
1164   float ht = dev->preview_pipe->backbuf_height;
1165   if(wd < 1.0 || ht < 1.0) return;
1166   float pzx = 0.0f, pzy = 0.0f;
1167   dt_dev_get_pointer_zoom_pos(dev, pointerx, pointery, &pzx, &pzy);
1168   pzx += 0.5f;
1169   pzy += 0.5f;
1170   float zoom_y = dt_control_get_dev_zoom_y();
1171   float zoom_x = dt_control_get_dev_zoom_x();
1172   dt_dev_zoom_t zoom = dt_control_get_dev_zoom();
1173   int closeup = dt_control_get_dev_closeup();
1174   float zoom_scale = dt_dev_get_zoom_scale(dev, zoom, 1<<closeup, 1);
1175 
1176   cairo_save(cr);
1177   cairo_set_source_rgb(cr, .3, .3, .3);
1178 
1179   cairo_translate(cr, width / 2.0, height / 2.0f);
1180   cairo_scale(cr, zoom_scale, zoom_scale);
1181   cairo_translate(cr, -.5f * wd - zoom_x * wd, -.5f * ht - zoom_y * ht);
1182 
1183   cairo_set_line_cap(cr, CAIRO_LINE_CAP_ROUND);
1184 
1185   // we update the form if needed
1186   // add preview when creating a circle, ellipse and gradient
1187   if(!(((form->type & DT_MASKS_CIRCLE) || (form->type & DT_MASKS_ELLIPSE) || (form->type & DT_MASKS_GRADIENT))
1188        && gui->creation))
1189     dt_masks_gui_form_test_create(form, gui, module);
1190 
1191   // draw form
1192   if(form->type & DT_MASKS_GROUP)
1193     dt_group_events_post_expose(cr, zoom_scale, form, gui);
1194   else if(form->functions)
1195     form->functions->post_expose(cr, zoom_scale, gui, 0, g_list_length(form->points));
1196 
1197   cairo_restore(cr);
1198 }
1199 
dt_masks_clear_form_gui(dt_develop_t * dev)1200 void dt_masks_clear_form_gui(dt_develop_t *dev)
1201 {
1202   if(!dev->form_gui) return;
1203   g_list_free_full(dev->form_gui->points, dt_masks_form_gui_points_free);
1204   dev->form_gui->points = NULL;
1205   dt_masks_dynbuf_free(dev->form_gui->guipoints);
1206   dev->form_gui->guipoints = NULL;
1207   dt_masks_dynbuf_free(dev->form_gui->guipoints_payload);
1208   dev->form_gui->guipoints_payload = NULL;
1209   dev->form_gui->guipoints_count = 0;
1210   dev->form_gui->pipe_hash = dev->form_gui->formid = 0;
1211   dev->form_gui->dx = dev->form_gui->dy = 0.0f;
1212   dev->form_gui->scrollx = dev->form_gui->scrolly = 0.0f;
1213   dev->form_gui->form_selected = dev->form_gui->border_selected = dev->form_gui->form_dragging
1214       = dev->form_gui->form_rotating = dev->form_gui->border_toggling = dev->form_gui->gradient_toggling = FALSE;
1215   dev->form_gui->source_selected = dev->form_gui->source_dragging = FALSE;
1216   dev->form_gui->pivot_selected = FALSE;
1217   dev->form_gui->point_border_selected = dev->form_gui->seg_selected = dev->form_gui->point_selected
1218       = dev->form_gui->feather_selected = -1;
1219   dev->form_gui->point_border_dragging = dev->form_gui->seg_dragging = dev->form_gui->feather_dragging
1220       = dev->form_gui->point_dragging = -1;
1221   dev->form_gui->creation_closing_form = dev->form_gui->creation = FALSE;
1222   dev->form_gui->pressure_sensitivity = DT_MASKS_PRESSURE_OFF;
1223   dev->form_gui->creation_module = NULL;
1224   dev->form_gui->point_edited = -1;
1225 
1226   dev->form_gui->group_edited = -1;
1227   dev->form_gui->group_selected = -1;
1228   dev->form_gui->edit_mode = DT_MASKS_EDIT_OFF;
1229   // allow to select a shape inside an iop
1230   dt_masks_select_form(NULL, NULL);
1231 }
1232 
dt_masks_change_form_gui(dt_masks_form_t * newform)1233 void dt_masks_change_form_gui(dt_masks_form_t *newform)
1234 {
1235   dt_masks_form_t *old = darktable.develop->form_visible;
1236 
1237   dt_masks_clear_form_gui(darktable.develop);
1238   darktable.develop->form_visible = newform;
1239 
1240   /* update sticky accels window */
1241   if(newform != old && darktable.view_manager->accels_window.window && darktable.view_manager->accels_window.sticky)
1242     dt_view_accels_refresh(darktable.view_manager);
1243 }
1244 
dt_masks_reset_form_gui(void)1245 void dt_masks_reset_form_gui(void)
1246 {
1247   dt_masks_change_form_gui(NULL);
1248   dt_iop_module_t *m = darktable.develop->gui_module;
1249   if(m && (m->flags() & IOP_FLAGS_SUPPORTS_BLENDING) && !(m->flags() & IOP_FLAGS_NO_MASKS)
1250     && m->blend_data)
1251   {
1252     dt_iop_gui_blend_data_t *bd = (dt_iop_gui_blend_data_t *)m->blend_data;
1253     bd->masks_shown = DT_MASKS_EDIT_OFF;
1254     gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(bd->masks_edit), 0);
1255     for(int n = 0; n < DEVELOP_MASKS_NB_SHAPES; n++)
1256       gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(bd->masks_shapes[n]), 0);
1257   }
1258 }
1259 
dt_masks_reset_show_masks_icons(void)1260 void dt_masks_reset_show_masks_icons(void)
1261 {
1262   if(darktable.develop->first_load) return;
1263   for(GList *modules = darktable.develop->iop; modules; modules = g_list_next(modules))
1264   {
1265     dt_iop_module_t *m = (dt_iop_module_t *)modules->data;
1266     if(m && (m->flags() & IOP_FLAGS_SUPPORTS_BLENDING) && !(m->flags() & IOP_FLAGS_NO_MASKS))
1267     {
1268       dt_iop_gui_blend_data_t *bd = (dt_iop_gui_blend_data_t *)m->blend_data;
1269       if(!bd) break;  // TODO: this doesn't look right. Why do we break the while look as soon as one module has no blend_data?
1270       bd->masks_shown = DT_MASKS_EDIT_OFF;
1271       gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(bd->masks_edit), FALSE);
1272       gtk_widget_queue_draw(bd->masks_edit);
1273       for(int n = 0; n < DEVELOP_MASKS_NB_SHAPES; n++)
1274       {
1275         gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(bd->masks_shapes[n]), 0);
1276         gtk_widget_queue_draw(bd->masks_shapes[n]);
1277       }
1278     }
1279   }
1280 }
1281 
dt_masks_get_edit_mode(struct dt_iop_module_t * module)1282 dt_masks_edit_mode_t dt_masks_get_edit_mode(struct dt_iop_module_t *module)
1283 {
1284   return darktable.develop->form_gui
1285     ? darktable.develop->form_gui->edit_mode
1286     : DT_MASKS_EDIT_OFF;
1287 }
1288 
dt_masks_set_edit_mode(struct dt_iop_module_t * module,dt_masks_edit_mode_t value)1289 void dt_masks_set_edit_mode(struct dt_iop_module_t *module, dt_masks_edit_mode_t value)
1290 {
1291   if(!module) return;
1292   dt_iop_gui_blend_data_t *bd = (dt_iop_gui_blend_data_t *)module->blend_data;
1293   if(!bd) return;
1294 
1295   dt_masks_form_t *grp = NULL;
1296   dt_masks_form_t *form = dt_masks_get_from_id(module->dev, module->blend_params->mask_id);
1297   if(value && form)
1298   {
1299     grp = dt_masks_create_ext(DT_MASKS_GROUP);
1300     grp->formid = 0;
1301     dt_masks_group_ungroup(grp, form);
1302   }
1303 
1304   if(bd) bd->masks_shown = value;
1305 
1306   dt_masks_change_form_gui(grp);
1307   darktable.develop->form_gui->edit_mode = value;
1308   if(value && form)
1309     dt_dev_masks_selection_change(darktable.develop, NULL, form->formid, FALSE);
1310   else
1311     dt_dev_masks_selection_change(darktable.develop, NULL, 0, FALSE);
1312 
1313   if(bd->masks_support)
1314     gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(bd->masks_edit),
1315                                  value == DT_MASKS_EDIT_OFF ? FALSE : TRUE);
1316 
1317   dt_control_queue_redraw_center();
1318 }
1319 
dt_masks_set_edit_mode_single_form(struct dt_iop_module_t * module,const int formid,dt_masks_edit_mode_t value)1320 void dt_masks_set_edit_mode_single_form(struct dt_iop_module_t *module, const int formid,
1321                                         dt_masks_edit_mode_t value)
1322 {
1323   if(!module) return;
1324 
1325   dt_masks_form_t *grp = dt_masks_create_ext(DT_MASKS_GROUP);
1326 
1327   const int grid = module->blend_params->mask_id;
1328   dt_masks_form_t *form = dt_masks_get_from_id(darktable.develop, formid);
1329   if(form)
1330   {
1331     dt_masks_point_group_t *fpt = (dt_masks_point_group_t *)malloc(sizeof(dt_masks_point_group_t));
1332     fpt->formid = formid;
1333     fpt->parentid = grid;
1334     fpt->state = DT_MASKS_STATE_USE;
1335     fpt->opacity = 1.0f;
1336     grp->points = g_list_append(grp->points, fpt);
1337   }
1338 
1339   dt_masks_form_t *grp2 = dt_masks_create_ext(DT_MASKS_GROUP);
1340   grp2->formid = 0;
1341   dt_masks_group_ungroup(grp2, grp);
1342   dt_masks_change_form_gui(grp2);
1343   darktable.develop->form_gui->edit_mode = value;
1344 
1345   if(value && form)
1346     dt_dev_masks_selection_change(darktable.develop, NULL, formid, FALSE);
1347   else
1348     dt_dev_masks_selection_change(darktable.develop, NULL, 0, FALSE);
1349 
1350   dt_control_queue_redraw_center();
1351 }
1352 
dt_masks_iop_edit_toggle_callback(GtkToggleButton * togglebutton,dt_iop_module_t * module)1353 void dt_masks_iop_edit_toggle_callback(GtkToggleButton *togglebutton, dt_iop_module_t *module)
1354 {
1355   if(!module) return;
1356   dt_iop_gui_blend_data_t *bd = (dt_iop_gui_blend_data_t *)module->blend_data;
1357   if(module->blend_params->mask_id == 0)
1358   {
1359     bd->masks_shown = DT_MASKS_EDIT_OFF;
1360     return;
1361   }
1362 
1363   // reset the gui
1364   dt_masks_set_edit_mode(module,
1365                          (bd->masks_shown == DT_MASKS_EDIT_OFF ? DT_MASKS_EDIT_FULL : DT_MASKS_EDIT_OFF));
1366 }
1367 
_menu_no_masks(struct dt_iop_module_t * module)1368 static void _menu_no_masks(struct dt_iop_module_t *module)
1369 {
1370   // we drop all the forms in the iop
1371   dt_masks_form_t *grp = _group_from_module(darktable.develop, module);
1372   if(grp) dt_masks_form_remove(module, NULL, grp);
1373   module->blend_params->mask_id = 0;
1374 
1375   // and we update the iop
1376   dt_masks_set_edit_mode(module, DT_MASKS_EDIT_OFF);
1377   dt_masks_iop_update(module);
1378 
1379   dt_dev_add_history_item(darktable.develop, module, TRUE);
1380 }
1381 
_menu_add_shape(struct dt_iop_module_t * module,dt_masks_type_t type)1382 static void _menu_add_shape(struct dt_iop_module_t *module, dt_masks_type_t type)
1383 {
1384   // we want to be sure that the iop has focus
1385   dt_iop_request_focus(module);
1386   // we create the new form
1387   dt_masks_form_t *form = dt_masks_create(type);
1388   dt_masks_change_form_gui(form);
1389   darktable.develop->form_gui->creation = TRUE;
1390   darktable.develop->form_gui->creation_module = module;
1391   dt_control_queue_redraw_center();
1392 }
1393 
_menu_add_exist(dt_iop_module_t * module,int formid)1394 static void _menu_add_exist(dt_iop_module_t *module, int formid)
1395 {
1396   if(!module) return;
1397   dt_masks_form_t *form = dt_masks_get_from_id(darktable.develop, formid);
1398   if(!form) return;
1399 
1400   // is there already a masks group for this module ?
1401   dt_masks_form_t *grp = _group_from_module(darktable.develop, module);
1402   if(!grp)
1403   {
1404     grp = _group_create(darktable.develop, module, DT_MASKS_GROUP);
1405   }
1406   // we add the form in this group
1407   dt_masks_group_add_form(grp, form);
1408   // we save the group
1409   // and we ensure that we are in edit mode
1410   dt_dev_add_masks_history_item(darktable.develop, module, TRUE);
1411   dt_masks_iop_update(module);
1412   dt_masks_set_edit_mode(module, DT_MASKS_EDIT_FULL);
1413 }
1414 
dt_masks_group_update_name(dt_iop_module_t * module)1415 void dt_masks_group_update_name(dt_iop_module_t *module)
1416 {
1417   dt_masks_form_t *grp = _group_from_module(darktable.develop, module);
1418   if (!grp)
1419     return;
1420 
1421   _set_group_name_from_module(module, grp);
1422   dt_dev_add_masks_history_item(darktable.develop, module, TRUE);
1423   dt_masks_iop_update(module);
1424 }
1425 
dt_masks_iop_use_same_as(dt_iop_module_t * module,dt_iop_module_t * src)1426 void dt_masks_iop_use_same_as(dt_iop_module_t *module, dt_iop_module_t *src)
1427 {
1428   if(!module || !src) return;
1429 
1430   // we get the source group
1431   int srcid = src->blend_params->mask_id;
1432   dt_masks_form_t *src_grp = dt_masks_get_from_id(darktable.develop, srcid);
1433   if(!src_grp || src_grp->type != DT_MASKS_GROUP) return;
1434 
1435   // is there already a masks group for this module ?
1436   dt_masks_form_t *grp = _group_from_module(darktable.develop, module);
1437   if(!grp)
1438   {
1439     grp = _group_create(darktable.develop, module, DT_MASKS_GROUP);
1440   }
1441   // we copy the src group in this group
1442   for(GList *points = src_grp->points; points; points = g_list_next(points))
1443   {
1444     dt_masks_point_group_t *pt = (dt_masks_point_group_t *)points->data;
1445     dt_masks_form_t *form = dt_masks_get_from_id(darktable.develop, pt->formid);
1446     if(form)
1447     {
1448       dt_masks_point_group_t *grpt = dt_masks_group_add_form(grp, form);
1449       if(grpt)
1450       {
1451         grpt->state = pt->state;
1452         grpt->opacity = pt->opacity;
1453       }
1454     }
1455   }
1456 
1457   // we save the group
1458   dt_dev_add_masks_history_item(darktable.develop, module, TRUE);
1459 }
1460 
dt_masks_iop_combo_populate(GtkWidget * w,struct dt_iop_module_t ** m)1461 void dt_masks_iop_combo_populate(GtkWidget *w, struct dt_iop_module_t **m)
1462 {
1463   // we ensure that the module has focus
1464   dt_iop_module_t *module = *m;
1465   dt_iop_request_focus(module);
1466   dt_iop_gui_blend_data_t *bd = (dt_iop_gui_blend_data_t *)module->blend_data;
1467 
1468   // we determine a higher approx of the entry number
1469   guint nbe = 5 + g_list_length(darktable.develop->forms) + g_list_length(darktable.develop->iop);
1470   free(bd->masks_combo_ids);
1471   bd->masks_combo_ids = malloc( sizeof(int) *nbe);
1472 
1473   int *cids = bd->masks_combo_ids;
1474   GtkWidget *combo = bd->masks_combo;
1475 
1476   // we remove all the combo entries except the first one
1477   while(dt_bauhaus_combobox_length(combo) > 1)
1478   {
1479     dt_bauhaus_combobox_remove_at(combo, 1);
1480   }
1481 
1482   int pos = 0;
1483   cids[pos++] = 0; // nothing to do for the first entry (already here)
1484 
1485 
1486   // add existing shapes
1487   int nb = 0;
1488   for(GList *forms = darktable.develop->forms; forms; forms = g_list_next(forms))
1489   {
1490     dt_masks_form_t *form = (dt_masks_form_t *)forms->data;
1491     if((form->type & (DT_MASKS_CLONE|DT_MASKS_NON_CLONE)) || form->formid == module->blend_params->mask_id)
1492     {
1493       continue;
1494     }
1495 
1496     // we search were this form is used in the current module
1497     int used = 0;
1498     dt_masks_form_t *grp = _group_from_module(darktable.develop, module);
1499     if(grp && (grp->type & DT_MASKS_GROUP))
1500     {
1501       for(GList *pts = grp->points; pts; pts = g_list_next(pts))
1502       {
1503         dt_masks_point_group_t *pt = (dt_masks_point_group_t *)pts->data;
1504         if(pt->formid == form->formid)
1505         {
1506           used = 1;
1507           break;
1508         }
1509       }
1510     }
1511     if(!used)
1512     {
1513       if(nb == 0)
1514       {
1515         dt_bauhaus_combobox_add_section(combo, _("add existing shape"));
1516         cids[pos++] = 0; // nothing to do
1517       }
1518       dt_bauhaus_combobox_add(combo, form->name);
1519       cids[pos++] = form->formid;
1520       nb++;
1521     }
1522   }
1523 
1524   // masks from other iops
1525   nb = 0;
1526   int pos2 = 1;
1527   for(GList *modules = darktable.develop->iop; modules; modules = g_list_next(modules))
1528   {
1529     dt_iop_module_t *other_mod = (dt_iop_module_t *)modules->data;
1530     if((other_mod != module) && (other_mod->flags() & IOP_FLAGS_SUPPORTS_BLENDING) && !(other_mod->flags() & IOP_FLAGS_NO_MASKS))
1531     {
1532       dt_masks_form_t *grp = _group_from_module(darktable.develop, other_mod);
1533       if(grp)
1534       {
1535         if(nb == 0)
1536         {
1537           dt_bauhaus_combobox_add_section(combo, _("use same shapes as"));
1538           cids[pos++] = 0; // nothing to do
1539         }
1540         gchar *module_label = dt_history_item_get_name(other_mod);
1541         dt_bauhaus_combobox_add(combo, module_label);
1542         g_free(module_label);
1543         cids[pos++] = -1 * pos2;
1544         nb++;
1545       }
1546     }
1547     pos2++;
1548   }
1549 }
1550 
dt_masks_iop_value_changed_callback(GtkWidget * widget,struct dt_iop_module_t * module)1551 void dt_masks_iop_value_changed_callback(GtkWidget *widget, struct dt_iop_module_t *module)
1552 {
1553   // we get the corresponding value
1554   dt_iop_gui_blend_data_t *bd = (dt_iop_gui_blend_data_t *)module->blend_data;
1555 
1556   int sel = dt_bauhaus_combobox_get(bd->masks_combo);
1557   if(sel == 0) return;
1558   if(sel == 1)
1559   {
1560     ++darktable.gui->reset;
1561     dt_bauhaus_combobox_set(bd->masks_combo, 0);
1562     --darktable.gui->reset;
1563     return;
1564   }
1565   if(sel > 0)
1566   {
1567     int val = bd->masks_combo_ids[sel];
1568     if(val == -1000000)
1569     {
1570       // delete all masks
1571       _menu_no_masks(module);
1572     }
1573     else if(val == -2000001)
1574     {
1575       // add a circle shape
1576       _menu_add_shape(module, DT_MASKS_CIRCLE);
1577     }
1578     else if(val == -2000002)
1579     {
1580       // add a path shape
1581       _menu_add_shape(module, DT_MASKS_PATH);
1582     }
1583     else if(val == -2000016)
1584     {
1585       // add a gradient shape
1586       _menu_add_shape(module, DT_MASKS_GRADIENT);
1587     }
1588     else if(val == -2000032)
1589     {
1590       // add a gradient shape
1591       _menu_add_shape(module, DT_MASKS_ELLIPSE);
1592     }
1593     else if(val == -2000064)
1594     {
1595       // add a brush shape
1596       _menu_add_shape(module, DT_MASKS_BRUSH);
1597     }
1598     else if(val < 0)
1599     {
1600       // use same shapes as another iop
1601       val = -1 * val - 1;
1602       if(val < g_list_length(module->dev->iop))
1603       {
1604         dt_iop_module_t *m = (dt_iop_module_t *)g_list_nth_data(module->dev->iop, val);
1605         dt_masks_iop_use_same_as(module, m);
1606         // and we ensure that we are in edit mode
1607         //dt_dev_add_history_item(darktable.develop, module, TRUE);
1608         dt_dev_add_masks_history_item(darktable.develop, module, TRUE);
1609         dt_masks_iop_update(module);
1610         dt_masks_set_edit_mode(module, DT_MASKS_EDIT_FULL);
1611       }
1612     }
1613     else if(val > 0)
1614     {
1615       // add an existing shape
1616       _menu_add_exist(module, val);
1617     }
1618     else
1619       return;
1620   }
1621   // we update the combo line
1622   dt_masks_iop_update(module);
1623 }
1624 
dt_masks_iop_update(struct dt_iop_module_t * module)1625 void dt_masks_iop_update(struct dt_iop_module_t *module)
1626 {
1627   if(!module) return;
1628 
1629   dt_iop_gui_update(module);
1630   dt_iop_gui_update_masks(module);
1631 }
1632 
dt_masks_form_remove(struct dt_iop_module_t * module,dt_masks_form_t * grp,dt_masks_form_t * form)1633 void dt_masks_form_remove(struct dt_iop_module_t *module, dt_masks_form_t *grp, dt_masks_form_t *form)
1634 {
1635   if(!form) return;
1636   int id = form->formid;
1637   if(grp && !(grp->type & DT_MASKS_GROUP)) return;
1638 
1639   if(!(form->type & (DT_MASKS_CLONE|DT_MASKS_NON_CLONE)) && grp)
1640   {
1641     // we try to remove the form from the masks group
1642     int ok = 0;
1643     for(GList *forms = grp->points; forms; forms = g_list_next(forms))
1644     {
1645       dt_masks_point_group_t *grpt = (dt_masks_point_group_t *)forms->data;
1646       if(grpt->formid == id)
1647       {
1648         ok = 1;
1649         grp->points = g_list_remove(grp->points, grpt);
1650         free(grpt);
1651         break;
1652       }
1653     }
1654     if(ok) dt_dev_add_masks_history_item(darktable.develop, module, TRUE);
1655     if(ok && module)
1656     {
1657       dt_masks_iop_update(module);
1658       dt_masks_update_image(darktable.develop);
1659     }
1660     if(ok && grp->points == NULL) dt_masks_form_remove(module, NULL, grp);
1661     return;
1662   }
1663 
1664   if(form->type & DT_MASKS_GROUP && form->type & DT_MASKS_CLONE)
1665   {
1666     // when removing a cloning group the children have to be removed, too, as they won't be shown in the mask manager
1667     // and are thus not accessible afterwards.
1668     while(form->points)
1669     {
1670       dt_masks_point_group_t *group_child = (dt_masks_point_group_t *)form->points->data;
1671       dt_masks_form_t *child = dt_masks_get_from_id(darktable.develop, group_child->formid);
1672       dt_masks_form_remove(module, form, child);
1673       // no need to do anything to form->points, the recursive call will have removed child from the list
1674     }
1675   }
1676 
1677   // if we are here that mean we have to permanently delete this form
1678   // we drop the form from all modules
1679   int form_removed = 0;
1680   for(GList *iops = darktable.develop->iop; iops; iops = g_list_next(iops))
1681   {
1682     dt_iop_module_t *m = (dt_iop_module_t *)iops->data;
1683     if(m->flags() & IOP_FLAGS_SUPPORTS_BLENDING)
1684     {
1685       // is the form the base group of the iop ?
1686       if(id == m->blend_params->mask_id)
1687       {
1688         m->blend_params->mask_id = 0;
1689         dt_masks_iop_update(m);
1690         dt_dev_add_history_item(darktable.develop, m, TRUE);
1691       }
1692       else
1693       {
1694         dt_masks_form_t *iopgrp = _group_from_module(darktable.develop, m);
1695         if(iopgrp && (iopgrp->type & DT_MASKS_GROUP))
1696         {
1697           int ok = 0;
1698           GList *forms = iopgrp->points;
1699           while(forms)
1700           {
1701             dt_masks_point_group_t *grpt = (dt_masks_point_group_t *)forms->data;
1702             if(grpt->formid == id)
1703             {
1704               ok = 1;
1705               iopgrp->points = g_list_remove(iopgrp->points, grpt);
1706               free(grpt);
1707               forms = iopgrp->points; // jump back to start of list
1708               continue;
1709             }
1710             forms = g_list_next(forms); // advance to next form
1711           }
1712           if(ok)
1713           {
1714             form_removed = 1;
1715             dt_masks_iop_update(m);
1716             dt_masks_update_image(darktable.develop);
1717             if(iopgrp->points == NULL) dt_masks_form_remove(m, NULL, iopgrp);
1718           }
1719         }
1720       }
1721     }
1722   }
1723   // we drop the form from the general list
1724   for(GList *forms = darktable.develop->forms; forms; forms = g_list_next(forms))
1725   {
1726     dt_masks_form_t *f = (dt_masks_form_t *)forms->data;
1727     if(f->formid == id)
1728     {
1729       darktable.develop->forms = g_list_remove(darktable.develop->forms, f);
1730       form_removed = 1;
1731       break;
1732     }
1733   }
1734   if(form_removed) dt_dev_add_masks_history_item(darktable.develop, module, TRUE);
1735 }
1736 
dt_masks_form_change_opacity(dt_masks_form_t * form,int parentid,int up)1737 void dt_masks_form_change_opacity(dt_masks_form_t *form, int parentid, int up)
1738 {
1739   if(!form) return;
1740   dt_masks_form_t *grp = dt_masks_get_from_id(darktable.develop, parentid);
1741   if(!grp || !(grp->type & DT_MASKS_GROUP)) return;
1742 
1743   // we first need to test if the opacity can be set to the form
1744   if(form->type & DT_MASKS_GROUP) return;
1745   const int id = form->formid;
1746   const float amount = up ? 0.05f : -0.05f;
1747 
1748   // so we change the value inside the group
1749   for(GList *fpts = grp->points; fpts; fpts = g_list_next(fpts))
1750   {
1751     dt_masks_point_group_t *fpt = (dt_masks_point_group_t *)fpts->data;
1752     if(fpt->formid == id)
1753     {
1754       const float opacity = CLAMP(fpt->opacity + amount, 0.05f, 1.0f);
1755       fpt->opacity = opacity;
1756       const int opacitypercent = opacity * 100;
1757       dt_toast_log(_("opacity: %d%%"), opacitypercent);
1758       dt_dev_add_masks_history_item(darktable.develop, NULL, TRUE);
1759       dt_masks_update_image(darktable.develop);
1760       break;
1761     }
1762   }
1763 }
1764 
dt_masks_form_move(dt_masks_form_t * grp,int formid,int up)1765 void dt_masks_form_move(dt_masks_form_t *grp, int formid, int up)
1766 {
1767   if(!grp || !(grp->type & DT_MASKS_GROUP)) return;
1768 
1769   // we search the form in the group
1770   dt_masks_point_group_t *grpt = NULL;
1771   guint pos = 0;
1772   for(GList *fpts = grp->points; fpts; fpts = g_list_next(fpts))
1773   {
1774     dt_masks_point_group_t *fpt = (dt_masks_point_group_t *)fpts->data;
1775     if(fpt->formid == formid)
1776     {
1777       grpt = fpt;
1778       break;
1779     }
1780     pos++;
1781   }
1782 
1783   // we remove the form and read it
1784   if(grpt)
1785   {
1786     if(!up && pos == 0) return;
1787     if(up && pos == g_list_length(grp->points) - 1) return;
1788 
1789     grp->points = g_list_remove(grp->points, grpt);
1790     if(!up)
1791       pos -= 1;
1792     else
1793       pos += 1;
1794     grp->points = g_list_insert(grp->points, grpt, pos);
1795     dt_dev_add_masks_history_item(darktable.develop, NULL, TRUE);
1796   }
1797 }
1798 
_find_in_group(dt_masks_form_t * grp,int formid)1799 static int _find_in_group(dt_masks_form_t *grp, int formid)
1800 {
1801   if(!(grp->type & DT_MASKS_GROUP)) return 0;
1802   if(grp->formid == formid) return 1;
1803   int nb = 0;
1804   for(GList *forms = grp->points; forms; forms = g_list_next(forms))
1805   {
1806     const dt_masks_point_group_t *grpt = (dt_masks_point_group_t *)forms->data;
1807     dt_masks_form_t *form = dt_masks_get_from_id(darktable.develop, grpt->formid);
1808     if(form)
1809     {
1810       if(form->type & DT_MASKS_GROUP) nb += _find_in_group(form, formid);
1811     }
1812   }
1813   return nb;
1814 }
1815 
dt_masks_group_add_form(dt_masks_form_t * grp,dt_masks_form_t * form)1816 dt_masks_point_group_t *dt_masks_group_add_form(dt_masks_form_t *grp, dt_masks_form_t *form)
1817 {
1818   // add a form to group and check for self inclusion
1819 
1820   if(!(grp->type & DT_MASKS_GROUP)) return NULL;
1821   // either the form to add is not a group, so no risk
1822   // or we go through all points of form to see if we find a ref to grp->formid
1823   if(!(form->type & DT_MASKS_GROUP) || _find_in_group(form, grp->formid) == 0)
1824   {
1825     dt_masks_point_group_t *grpt = malloc(sizeof(dt_masks_point_group_t));
1826     grpt->formid = form->formid;
1827     grpt->parentid = grp->formid;
1828     grpt->state = DT_MASKS_STATE_SHOW | DT_MASKS_STATE_USE;
1829     if(grp->points) grpt->state |= DT_MASKS_STATE_UNION;
1830     grpt->opacity = dt_conf_get_float("plugins/darkroom/masks/opacity");
1831     grp->points = g_list_append(grp->points, grpt);
1832     return grpt;
1833   }
1834 
1835   dt_control_log(_("masks can not contain themselves"));
1836   return NULL;
1837 }
1838 
dt_masks_group_ungroup(dt_masks_form_t * dest_grp,dt_masks_form_t * grp)1839 void dt_masks_group_ungroup(dt_masks_form_t *dest_grp, dt_masks_form_t *grp)
1840 {
1841   if(!grp || !dest_grp) return;
1842   if(!(grp->type & DT_MASKS_GROUP) || !(dest_grp->type & DT_MASKS_GROUP)) return;
1843 
1844   for(GList *forms = grp->points; forms; forms = g_list_next(forms))
1845   {
1846     dt_masks_point_group_t *grpt = (dt_masks_point_group_t *)forms->data;
1847     dt_masks_form_t *form = dt_masks_get_from_id(darktable.develop, grpt->formid);
1848     if(form)
1849     {
1850       if(form->type & DT_MASKS_GROUP)
1851       {
1852         dt_masks_group_ungroup(dest_grp, form);
1853       }
1854       else
1855       {
1856         dt_masks_point_group_t *fpt = (dt_masks_point_group_t *)malloc(sizeof(dt_masks_point_group_t));
1857         fpt->formid = grpt->formid;
1858         fpt->parentid = grpt->parentid;
1859         fpt->state = grpt->state;
1860         fpt->opacity = grpt->opacity;
1861         dest_grp->points = g_list_append(dest_grp->points, fpt);
1862       }
1863     }
1864   }
1865 }
1866 
dt_masks_group_get_hash_buffer_length(dt_masks_form_t * form)1867 int dt_masks_group_get_hash_buffer_length(dt_masks_form_t *form)
1868 {
1869   if(!form) return 0;
1870   int pos = 0;
1871   // basic infos
1872   pos += sizeof(dt_masks_type_t);
1873   pos += sizeof(int);
1874   pos += sizeof(int);
1875   pos += 2 * sizeof(float);
1876 
1877   for(GList *forms = form->points; forms; forms = g_list_next(forms))
1878   {
1879     if(form->type & DT_MASKS_GROUP)
1880     {
1881       dt_masks_point_group_t *grpt = (dt_masks_point_group_t *)forms->data;
1882       dt_masks_form_t *f = dt_masks_get_from_id(darktable.develop, grpt->formid);
1883       if(f)
1884       {
1885         // state & opacity
1886         pos += sizeof(int);
1887         pos += sizeof(float);
1888         // the form itself
1889         pos += dt_masks_group_get_hash_buffer_length(f);
1890       }
1891     }
1892     else if(form->functions)
1893     {
1894       pos += form->functions->point_struct_size;
1895     }
1896   }
1897   return pos;
1898 }
1899 
dt_masks_group_get_hash_buffer(dt_masks_form_t * form,char * str)1900 char *dt_masks_group_get_hash_buffer(dt_masks_form_t *form, char *str)
1901 {
1902   if(!form) return str;
1903   int pos = 0;
1904   // basic infos
1905   memcpy(str + pos, &form->type, sizeof(dt_masks_type_t));
1906   pos += sizeof(dt_masks_type_t);
1907   memcpy(str + pos, &form->formid, sizeof(int));
1908   pos += sizeof(int);
1909   memcpy(str + pos, &form->version, sizeof(int));
1910   pos += sizeof(int);
1911   memcpy(str + pos, &form->source, sizeof(float) * 2);
1912   pos += 2 * sizeof(float);
1913 
1914   for(const GList *forms = form->points; forms; forms = g_list_next(forms))
1915   {
1916     if(form->type & DT_MASKS_GROUP)
1917     {
1918       dt_masks_point_group_t *grpt = (dt_masks_point_group_t *)forms->data;
1919       dt_masks_form_t *f = dt_masks_get_from_id(darktable.develop, grpt->formid);
1920       if(f)
1921       {
1922         // state & opacity
1923         memcpy(str + pos, &grpt->state, sizeof(int));
1924         pos += sizeof(int);
1925         memcpy(str + pos, &grpt->opacity, sizeof(float));
1926         pos += sizeof(float);
1927         // the form itself
1928         str = dt_masks_group_get_hash_buffer(f, str + pos) - pos;
1929       }
1930     }
1931     else if(form->functions)
1932     {
1933       memcpy(str + pos,forms->data, form->functions->point_struct_size);
1934       pos += form->functions->point_struct_size;
1935     }
1936   }
1937   return str + pos;
1938 }
1939 
dt_masks_update_image(dt_develop_t * dev)1940 void dt_masks_update_image(dt_develop_t *dev)
1941 {
1942   /* invalidate image data*/
1943   // dt_similarity_image_dirty(dev->image_storage.id);
1944 
1945   // invalidate buffers and force redraw of darkroom
1946   dev->pipe->changed |= DT_DEV_PIPE_SYNCH;
1947   dev->preview_pipe->changed |= DT_DEV_PIPE_SYNCH;
1948   dev->preview2_pipe->changed |= DT_DEV_PIPE_SYNCH;
1949   dt_dev_invalidate_all(dev);
1950 }
1951 
1952 // adds formid to used array
1953 // if formid is a group it adds all the forms that belongs to that group
_cleanup_unused_recurs(GList * forms,int formid,int * used,int nb)1954 static void _cleanup_unused_recurs(GList *forms, int formid, int *used, int nb)
1955 {
1956   // first, we search for the formid in used table
1957   for(int i = 0; i < nb; i++)
1958   {
1959     if(used[i] == 0)
1960     {
1961       // we store the formid
1962       used[i] = formid;
1963       break;
1964     }
1965     if(used[i] == formid) break;
1966   }
1967 
1968   // if the form is a group, we iterate through the sub-forms
1969   dt_masks_form_t *form = dt_masks_get_from_id_ext(forms, formid);
1970   if(form && (form->type & DT_MASKS_GROUP))
1971   {
1972     for(GList *grpts = form->points; grpts; grpts = g_list_next(grpts))
1973     {
1974       dt_masks_point_group_t *grpt = (dt_masks_point_group_t *)grpts->data;
1975       _cleanup_unused_recurs(forms, grpt->formid, used, nb);
1976     }
1977   }
1978 }
1979 
1980 // removes from _forms all forms that are not used in history_list up to history_end
_masks_cleanup_unused(GList ** _forms,GList * history_list,const int history_end)1981 static int _masks_cleanup_unused(GList **_forms, GList *history_list, const int history_end)
1982 {
1983   int masks_removed = 0;
1984   GList *forms = *_forms;
1985 
1986   // we create a table to store the ids of used forms
1987   guint nbf = g_list_length(forms);
1988   int *used = calloc(nbf, sizeof(int));
1989 
1990   // check in history if the module has drawn masks and add it to used array
1991   int num = 0;
1992   for(GList *history = history_list; history && num < history_end; history = g_list_next(history))
1993   {
1994     dt_dev_history_item_t *hist = (dt_dev_history_item_t *)history->data;
1995     dt_develop_blend_params_t *blend_params = hist->blend_params;
1996     if(blend_params)
1997     {
1998       if(blend_params->mask_id > 0) _cleanup_unused_recurs(forms, blend_params->mask_id, used, nbf);
1999     }
2000     num++;
2001   }
2002 
2003   // and we delete all unused forms
2004   GList *shapes = forms;
2005   while(shapes)
2006   {
2007     dt_masks_form_t *f = (dt_masks_form_t *)shapes->data;
2008     int u = 0;
2009     for(int i = 0; i < nbf; i++)
2010     {
2011       if(used[i] == f->formid)
2012       {
2013         u = 1;
2014         break;
2015       }
2016       if(used[i] == 0) break;
2017     }
2018 
2019     shapes = g_list_next(shapes); // need to get 'next' now, because we may be removing the current node
2020 
2021     if(u == 0)
2022     {
2023       forms = g_list_remove(forms, f);
2024       // and add it to allforms for cleanup
2025       darktable.develop->allforms = g_list_append(darktable.develop->allforms, f);
2026       masks_removed = 1;
2027     }
2028   }
2029 
2030   free(used);
2031 
2032   *_forms = forms;
2033 
2034   return masks_removed;
2035 }
2036 
2037 // removes all unused form from history
2038 // if there are multiple hist->forms entries in history it may leave some unused forms
2039 // we do it like this so the user can go back in history
2040 // for a more accurate cleanup the user should compress history
dt_masks_cleanup_unused_from_list(GList * history_list)2041 void dt_masks_cleanup_unused_from_list(GList *history_list)
2042 {
2043   // a mask is used in a given hist->forms entry if it is used up to the next hist->forms
2044   // so we are going to remove for each hist->forms from the top
2045   int num = g_list_length(history_list);
2046   int history_end = num;
2047   for(const GList *history = g_list_last(history_list); history; history = g_list_previous(history))
2048   {
2049     dt_dev_history_item_t *hist = (dt_dev_history_item_t *)history->data;
2050     if(hist->forms && strcmp(hist->op_name, "mask_manager") == 0)
2051     {
2052       _masks_cleanup_unused(&hist->forms, history_list, history_end);
2053       history_end = num - 1;
2054     }
2055     num--;
2056   }
2057 }
2058 
dt_masks_cleanup_unused(dt_develop_t * dev)2059 void dt_masks_cleanup_unused(dt_develop_t *dev)
2060 {
2061   dt_masks_change_form_gui(NULL);
2062 
2063   // we remove the forms from history
2064   dt_masks_cleanup_unused_from_list(dev->history);
2065 
2066   // and we save all that
2067   GList *forms = NULL;
2068   dt_iop_module_t *module = NULL;
2069   int num = 0;
2070   for(const GList *history = dev->history; history && num < dev->history_end; history = g_list_next(history))
2071   {
2072     dt_dev_history_item_t *hist = (dt_dev_history_item_t *)history->data;
2073 
2074     if(hist->forms) forms = hist->forms;
2075     if(hist->module && strcmp(hist->op_name, "mask_manager") != 0) module = hist->module;
2076 
2077     num++;
2078   }
2079 
2080   dt_masks_replace_current_forms(dev, forms);
2081 
2082   if(module)
2083     dt_dev_add_history_item(dev, module, module->enabled);
2084   else
2085     dt_dev_add_masks_history_item(dev, NULL, TRUE);
2086 }
2087 
dt_masks_point_in_form_exact(float x,float y,float * points,int points_start,int points_count)2088 int dt_masks_point_in_form_exact(float x, float y, float *points, int points_start, int points_count)
2089 {
2090   // we use ray casting algorithm
2091   // to avoid most problems with horizontal segments, y should be rounded as int
2092   // so that there's very little chance than y==points...
2093 
2094   if(points_count > 2 + points_start)
2095   {
2096     int start = isnan(points[points_start * 2]) && !isnan(points[points_start * 2 + 1])
2097                     ? points[points_start * 2 + 1]
2098                     : points_start;
2099 
2100     float yf = (float)y;
2101     int nb = 0;
2102     for(int i = start, next = start + 1; i < points_count;)
2103     {
2104       float y1 = points[i * 2 + 1];
2105       float y2 = points[next * 2 + 1];
2106       //if we need to skip points (in case of deleted point, because of self-intersection)
2107       if(isnan(points[next * 2]))
2108       {
2109         next = isnan(y2) ? start : (int)y2;
2110         continue;
2111       }
2112       if(((yf <= y2 && yf > y1) || (yf >= y2 && yf < y1)) && (points[i * 2] > x)) nb++;
2113 
2114       if(next == start) break;
2115       i = next++;
2116       if(next >= points_count) next = start;
2117     }
2118     return (nb & 1);
2119   }
2120   return 0;
2121 }
2122 
dt_masks_point_in_form_near(float x,float y,float * points,int points_start,int points_count,float distance,int * near)2123 int dt_masks_point_in_form_near(float x, float y, float *points, int points_start, int points_count, float distance, int *near)
2124 {
2125   // we use ray casting algorithm
2126   // to avoid most problems with horizontal segments, y should be rounded as int
2127   // so that there's very little chance than y==points...
2128 
2129   // TODO : distance is only evaluated in x, not y...
2130 
2131   if(points_count > 2 + points_start)
2132   {
2133     const int start = isnan(points[points_start * 2]) && !isnan(points[points_start * 2 + 1])
2134                       ? points[points_start * 2 + 1]
2135                       : points_start;
2136 
2137     const float yf = (float)y;
2138     int nb = 0;
2139     for(int i = start, next = start + 1; i < points_count;)
2140     {
2141       const float y1 = points[i * 2 + 1];
2142       const float y2 = points[next * 2 + 1];
2143       //if we need to jump to skip points (in case of deleted point, because of self-intersection)
2144       if(isnan(points[next * 2]))
2145       {
2146         next = isnan(y2) ? start : (int)y2;
2147         continue;
2148       }
2149       if((yf <= y2 && yf > y1) || (yf >= y2 && yf < y1))
2150       {
2151         if(points[i * 2] > x) nb++;
2152         if(points[i * 2] - x < distance && points[i * 2] - x > -distance) *near = 1;
2153       }
2154 
2155       if(next == start) break;
2156       i = next++;
2157       if(next >= points_count) next = start;
2158     }
2159     return (nb & 1);
2160   }
2161   return 0;
2162 }
2163 
2164 // allow to select a shape inside an iop
dt_masks_select_form(struct dt_iop_module_t * module,dt_masks_form_t * sel)2165 void dt_masks_select_form(struct dt_iop_module_t *module, dt_masks_form_t *sel)
2166 {
2167   gboolean selection_changed = FALSE;
2168 
2169   if(sel)
2170   {
2171     if(sel->formid != darktable.develop->mask_form_selected_id)
2172     {
2173       darktable.develop->mask_form_selected_id = sel->formid;
2174       selection_changed = TRUE;
2175     }
2176   }
2177   else
2178   {
2179     if(darktable.develop->mask_form_selected_id != 0)
2180     {
2181       darktable.develop->mask_form_selected_id = 0;
2182       selection_changed = TRUE;
2183     }
2184   }
2185   if(selection_changed)
2186   {
2187     if(!module && darktable.develop->mask_form_selected_id == 0)
2188       module = darktable.develop->gui_module;
2189     if(module)
2190     {
2191       if(module->masks_selection_changed)
2192         module->masks_selection_changed(module, darktable.develop->mask_form_selected_id);
2193     }
2194   }
2195 }
2196 
2197 // draw a cross where the source position of a clone mask will be created
dt_masks_draw_clone_source_pos(cairo_t * cr,const float zoom_scale,const float x,const float y)2198 void dt_masks_draw_clone_source_pos(cairo_t *cr, const float zoom_scale, const float x, const float y)
2199 {
2200   const float dx = 3.5f / zoom_scale;
2201   const float dy = 3.5f / zoom_scale;
2202 
2203   double dashed[] = { 4.0, 4.0 };
2204   dashed[0] /= zoom_scale;
2205   dashed[1] /= zoom_scale;
2206 
2207   cairo_set_dash(cr, dashed, 0, 0);
2208   cairo_set_line_width(cr, 3.0 / zoom_scale);
2209   cairo_set_source_rgba(cr, .3, .3, .3, .8);
2210 
2211   cairo_move_to(cr, x + dx, y);
2212   cairo_line_to(cr, x - dx, y);
2213   cairo_move_to(cr, x, y + dy);
2214   cairo_line_to(cr, x, y - dy);
2215   cairo_stroke_preserve(cr);
2216 
2217   cairo_set_line_width(cr, 1.0 / zoom_scale);
2218   cairo_set_source_rgba(cr, .8, .8, .8, .8);
2219   cairo_stroke(cr);
2220 }
2221 
2222 // sets if the initial source position for a clone mask will be absolute or relative,
2223 // based on mouse position and key state
dt_masks_set_source_pos_initial_state(dt_masks_form_gui_t * gui,const uint32_t state,const float pzx,const float pzy)2224 void dt_masks_set_source_pos_initial_state(dt_masks_form_gui_t *gui, const uint32_t state, const float pzx,
2225                                            const float pzy)
2226 {
2227   if(dt_modifier_is(state, GDK_SHIFT_MASK | GDK_CONTROL_MASK))
2228     gui->source_pos_type = DT_MASKS_SOURCE_POS_ABSOLUTE;
2229   else if(dt_modifier_is(state, GDK_SHIFT_MASK))
2230     gui->source_pos_type = DT_MASKS_SOURCE_POS_RELATIVE_TEMP;
2231   else
2232     fprintf(stderr, "[dt_masks_set_source_pos_initial_state] unknown state for setting masks position type\n");
2233 
2234   // both source types record an absolute position,
2235   // for the relative type, the first time is used the position is recorded,
2236   // the second time a relative position is calculated based on that one
2237   gui->posx_source = pzx * darktable.develop->preview_pipe->backbuf_width;
2238   gui->posy_source = pzy * darktable.develop->preview_pipe->backbuf_height;
2239 }
2240 
2241 // set the initial source position value for a clone mask
dt_masks_set_source_pos_initial_value(dt_masks_form_gui_t * gui,const int mask_type,dt_masks_form_t * form,const float pzx,const float pzy)2242 void dt_masks_set_source_pos_initial_value(dt_masks_form_gui_t *gui, const int mask_type, dt_masks_form_t *form,
2243                                                    const float pzx, const float pzy)
2244 {
2245   const float wd = darktable.develop->preview_pipe->backbuf_width;
2246   const float ht = darktable.develop->preview_pipe->backbuf_height;
2247   const float iwd = darktable.develop->preview_pipe->iwidth;
2248   const float iht = darktable.develop->preview_pipe->iheight;
2249 
2250   // if this is the first time the relative pos is used
2251   if(gui->source_pos_type == DT_MASKS_SOURCE_POS_RELATIVE_TEMP)
2252   {
2253     // if it has not been defined by the user, set some default
2254     if(gui->posx_source == -1.0f && gui->posy_source == -1.0f)
2255     {
2256       if(form->functions && form->functions->initial_source_pos)
2257       {
2258         form->functions->initial_source_pos(iwd, iht, &gui->posx_source, &gui->posy_source);
2259       }
2260       else
2261         fprintf(stderr, "[dt_masks_set_source_pos_initial_value] unsupported masks type when calculating source position initial value\n");
2262 
2263       float pts[2] = { pzx * wd + gui->posx_source, pzy * ht + gui->posy_source };
2264       dt_dev_distort_backtransform(darktable.develop, pts, 1);
2265 
2266       form->source[0] = pts[0] / iwd;
2267       form->source[1] = pts[1] / iht;
2268     }
2269     else
2270     {
2271       // if a position was defined by the user, use the absolute value the first time
2272       float pts[2] = { gui->posx_source, gui->posy_source };
2273       dt_dev_distort_backtransform(darktable.develop, pts, 1);
2274 
2275       form->source[0] = pts[0] / iwd;
2276       form->source[1] = pts[1] / iht;
2277 
2278       gui->posx_source = gui->posx_source - pzx * wd;
2279       gui->posy_source = gui->posy_source - pzy * ht;
2280     }
2281 
2282     gui->source_pos_type = DT_MASKS_SOURCE_POS_RELATIVE;
2283   }
2284   else if(gui->source_pos_type == DT_MASKS_SOURCE_POS_RELATIVE)
2285   {
2286     // original pos was already defined and relative value calculated, just use it
2287     float pts[2] = { pzx * wd + gui->posx_source, pzy * ht + gui->posy_source };
2288     dt_dev_distort_backtransform(darktable.develop, pts, 1);
2289 
2290     form->source[0] = pts[0] / iwd;
2291     form->source[1] = pts[1] / iht;
2292   }
2293   else if(gui->source_pos_type == DT_MASKS_SOURCE_POS_ABSOLUTE)
2294   {
2295     // an absolute position was defined by the user
2296     float pts_src[2] = { gui->posx_source, gui->posy_source };
2297     dt_dev_distort_backtransform(darktable.develop, pts_src, 1);
2298 
2299     form->source[0] = pts_src[0] / iwd;
2300     form->source[1] = pts_src[1] / iht;
2301   }
2302   else
2303     fprintf(stderr, "[dt_masks_set_source_pos_initial_value] unknown source position type\n");
2304 }
2305 
2306 // calculates the source position value for preview drawing, on cairo coordinates
dt_masks_calculate_source_pos_value(dt_masks_form_gui_t * gui,const int mask_type,const float initial_xpos,const float initial_ypos,const float xpos,const float ypos,float * px,float * py,const int adding)2307 void dt_masks_calculate_source_pos_value(dt_masks_form_gui_t *gui, const int mask_type, const float initial_xpos,
2308                                          const float initial_ypos, const float xpos, const float ypos, float *px,
2309                                          float *py, const int adding)
2310 {
2311   float x = 0.0f, y = 0.0f;
2312   const float pr_d = darktable.develop->preview_downsampling;
2313   const float iwd = pr_d * darktable.develop->preview_pipe->iwidth;
2314   const float iht = pr_d * darktable.develop->preview_pipe->iheight;
2315 
2316   if(gui->source_pos_type == DT_MASKS_SOURCE_POS_RELATIVE)
2317   {
2318     x = xpos + gui->posx_source;
2319     y = ypos + gui->posy_source;
2320   }
2321   else if(gui->source_pos_type == DT_MASKS_SOURCE_POS_RELATIVE_TEMP)
2322   {
2323     if(gui->posx_source == -1.0f && gui->posy_source == -1.0f)
2324     {
2325 #if 0 //TODO: replace individual cases with this generic one (will require passing 'form' through multiple layers...)
2326       if(form->functions && form->functions->initial_source_pos)
2327       {
2328         form->functions->initial_source_pos(iwd, iht, &x, &y);
2329         x += xpos;
2330         y += ypos;
2331       }
2332 #else
2333       if(mask_type & DT_MASKS_CIRCLE)
2334       {
2335         dt_masks_functions_circle.initial_source_pos(iwd, iht, &x, &y);
2336         x += xpos;
2337         y += ypos;
2338       }
2339       else if(mask_type & DT_MASKS_ELLIPSE)
2340       {
2341         dt_masks_functions_ellipse.initial_source_pos(iwd, iht, &x, &y);
2342         x += xpos;
2343         y += ypos;
2344       }
2345       else if(mask_type & DT_MASKS_PATH)
2346       {
2347         dt_masks_functions_path.initial_source_pos(iwd, iht, &x, &y);
2348         x += xpos;
2349         y += ypos;
2350       }
2351       else if(mask_type & DT_MASKS_BRUSH)
2352       {
2353         dt_masks_functions_brush.initial_source_pos(iwd, iht, &x, &y);
2354         x += xpos;
2355         y += ypos;
2356       }
2357 #endif
2358       else
2359         fprintf(stderr, "[dt_masks_calculate_source_pos_value] unsupported masks type when calculating source position value\n");
2360     }
2361     else
2362     {
2363       x = gui->posx_source;
2364       y = gui->posy_source;
2365     }
2366   }
2367   else if(gui->source_pos_type == DT_MASKS_SOURCE_POS_ABSOLUTE)
2368   {
2369     // if the user is actually adding the mask follow the cursor
2370     if(adding)
2371     {
2372       x = xpos + gui->posx_source - initial_xpos;
2373       y = ypos + gui->posy_source - initial_ypos;
2374     }
2375     else
2376     {
2377       // if not added yet set the start position
2378       x = gui->posx_source;
2379       y = gui->posy_source;
2380     }
2381   }
2382   else
2383     fprintf(stderr, "[dt_masks_calculate_source_pos_value] unknown source position type for setting source position value\n");
2384 
2385   *px = x;
2386   *py = y;
2387 }
2388 
2389 #include "detail.c"
2390 
2391 // modelines: These editor modelines have been set for all relevant files by tools/update_modelines.sh
2392 // vim: shiftwidth=2 expandtab tabstop=2 cindent
2393 // kate: tab-indents: off; indent-width 2; replace-tabs on; indent-mode cstyle; remove-trailing-spaces modified;
2394