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 "bauhaus/bauhaus.h"
19 #include "common/debug.h"
20 #include "common/imagebuf.h"
21 #include "common/undo.h"
22 #include "control/conf.h"
23 #include "control/control.h"
24 #include "develop/blend.h"
25 #include "develop/imageop.h"
26 #include "develop/masks.h"
27 #include "develop/openmp_maths.h"
28 #include <assert.h>
29 
30 static void _path_bounding_box_raw(const float *const points, const float *border, const int nb_corner, const int num_points, int num_borders,
31                                    float *x_min, float *x_max, float *y_min, float *y_max);
32 
33 /** get the point of the path at pos t [0,1]  */
_path_get_XY(float p0x,float p0y,float p1x,float p1y,float p2x,float p2y,float p3x,float p3y,float t,float * x,float * y)34 static void _path_get_XY(float p0x, float p0y, float p1x, float p1y, float p2x, float p2y, float p3x,
35                          float p3y, float t, float *x, float *y)
36 {
37   const float ti = 1.0f - t;
38   const float a = ti * ti * ti;
39   const float b = 3.0f * t * ti * ti;
40   const float c = 3.0f * t * t * ti;
41   const float d = t * t * t;
42   *x = p0x * a + p1x * b + p2x * c + p3x * d;
43   *y = p0y * a + p1y * b + p2y * c + p3y * d;
44 }
45 
46 /** get the point of the path at pos t [0,1]  AND the corresponding border point */
_path_border_get_XY(float p0x,float p0y,float p1x,float p1y,float p2x,float p2y,float p3x,float p3y,float t,float rad,float * xc,float * yc,float * xb,float * yb)47 static void _path_border_get_XY(float p0x, float p0y, float p1x, float p1y, float p2x, float p2y, float p3x,
48                                 float p3y, float t, float rad, float *xc, float *yc, float *xb, float *yb)
49 {
50   // we get the point
51   _path_get_XY(p0x, p0y, p1x, p1y, p2x, p2y, p3x, p3y, t, xc, yc);
52 
53   // now we get derivative points
54   const float ti = 1.0f - t;
55   const float a = 3.0f * ti * ti;
56   const float b = 3.0f * (ti * ti - 2.0f * t * ti);
57   const float c = 3.0f * (2.0f * t * ti - t * t);
58   const float d = 3.0f * sqf(t);
59 
60   const float dx = -p0x * a + p1x * b + p2x * c + p3x * d;
61   const float dy = -p0y * a + p1y * b + p2y * c + p3y * d;
62 
63   // so we can have the resulting point
64   if(dx == 0 && dy == 0)
65   {
66     *xb = NAN;
67     *yb = NAN;
68     return;
69   }
70   const float l = 1.0f / sqrtf(sqf(dx) + sqf(dy));
71   *xb = (*xc) + rad * dy * l;
72   *yb = (*yc) - rad * dx * l;
73 }
74 
75 /** get feather extremity from the control point n°2 */
76 /** the values should be in orthonormal space */
_path_ctrl2_to_feather(int ptx,int pty,int ctrlx,int ctrly,int * fx,int * fy,gboolean clockwise)77 static void _path_ctrl2_to_feather(int ptx, int pty, int ctrlx, int ctrly, int *fx, int *fy,
78                                    gboolean clockwise)
79 {
80   if(clockwise)
81   {
82     *fx = ptx + ctrly - pty;
83     *fy = pty + ptx - ctrlx;
84   }
85   else
86   {
87     *fx = ptx - ctrly + pty;
88     *fy = pty - ptx + ctrlx;
89   }
90 }
91 
92 /** get bezier control points from feather extremity */
93 /** the values should be in orthonormal space */
_path_feather_to_ctrl(int ptx,int pty,int fx,int fy,int * ctrl1x,int * ctrl1y,int * ctrl2x,int * ctrl2y,gboolean clockwise)94 static void _path_feather_to_ctrl(int ptx, int pty, int fx, int fy, int *ctrl1x, int *ctrl1y, int *ctrl2x,
95                                   int *ctrl2y, gboolean clockwise)
96 {
97   if(clockwise)
98   {
99     *ctrl2x = ptx + pty - fy;
100     *ctrl2y = pty + fx - ptx;
101     *ctrl1x = ptx - pty + fy;
102     *ctrl1y = pty - fx + ptx;
103   }
104   else
105   {
106     *ctrl1x = ptx + pty - fy;
107     *ctrl1y = pty + fx - ptx;
108     *ctrl2x = ptx - pty + fy;
109     *ctrl2y = pty - fx + ptx;
110   }
111 }
112 
113 /** Get the control points of a segment to match exactly a catmull-rom spline */
_path_catmull_to_bezier(float x1,float y1,float x2,float y2,float x3,float y3,float x4,float y4,float * bx1,float * by1,float * bx2,float * by2)114 static void _path_catmull_to_bezier(float x1, float y1, float x2, float y2, float x3, float y3, float x4,
115                                     float y4, float *bx1, float *by1, float *bx2, float *by2)
116 {
117   *bx1 = (-x1 + 6 * x2 + x3) / 6;
118   *by1 = (-y1 + 6 * y2 + y3) / 6;
119   *bx2 = (x2 + 6 * x3 - x4) / 6;
120   *by2 = (y2 + 6 * y3 - y4) / 6;
121 }
122 
123 /** initialise all control points to eventually match a catmull-rom like spline */
_path_init_ctrl_points(dt_masks_form_t * form)124 static void _path_init_ctrl_points(dt_masks_form_t *form)
125 {
126   // if we have less that 3 points, what to do ??
127   const guint nb = g_list_length(form->points);
128   if(nb < 2) return;
129 
130   const GList *form_points = form->points;
131   for(int k = 0; k < nb; k++)
132   {
133     dt_masks_point_path_t *point3 = (dt_masks_point_path_t *)form_points->data;
134     // if the point has not been set manually, we redefine it
135     if(point3->state & DT_MASKS_POINT_STATE_NORMAL)
136     {
137       // we want to get point-2 (into pt1), point-1 (into pt2), point+1 (into pt4), point+2 (into pt5), wrapping
138       // around to the other end of the list
139       const GList *pt2 = g_list_prev_wraparound(form_points); // prev, wrapping around if already on first element
140       const GList *pt1 = g_list_prev_wraparound(pt2);
141       const GList *pt4 = g_list_next_wraparound(form_points, form->points); // next, wrapping around if on last element
142       const GList *pt5 = g_list_next_wraparound(pt4, form->points);
143       dt_masks_point_path_t *point1 = (dt_masks_point_path_t *)pt1->data;
144       dt_masks_point_path_t *point2 = (dt_masks_point_path_t *)pt2->data;
145       dt_masks_point_path_t *point4 = (dt_masks_point_path_t *)pt4->data;
146       dt_masks_point_path_t *point5 = (dt_masks_point_path_t *)pt5->data;
147 
148       float bx1 = 0.0f, by1 = 0.0f, bx2 = 0.0f, by2 = 0.0f;
149       _path_catmull_to_bezier(point1->corner[0], point1->corner[1], point2->corner[0], point2->corner[1],
150                               point3->corner[0], point3->corner[1], point4->corner[0], point4->corner[1],
151                               &bx1, &by1, &bx2, &by2);
152       if(point2->ctrl2[0] == -1.0) point2->ctrl2[0] = bx1;
153       if(point2->ctrl2[1] == -1.0) point2->ctrl2[1] = by1;
154       point3->ctrl1[0] = bx2;
155       point3->ctrl1[1] = by2;
156       _path_catmull_to_bezier(point2->corner[0], point2->corner[1], point3->corner[0], point3->corner[1],
157                               point4->corner[0], point4->corner[1], point5->corner[0], point5->corner[1],
158                               &bx1, &by1, &bx2, &by2);
159       if(point4->ctrl1[0] == -1.0) point4->ctrl1[0] = bx2;
160       if(point4->ctrl1[1] == -1.0) point4->ctrl1[1] = by2;
161       point3->ctrl2[0] = bx1;
162       point3->ctrl2[1] = by1;
163     }
164     // keep form_points tracking the kth element of form->points
165     form_points = g_list_next(form_points);
166   }
167 }
168 
_path_is_clockwise(dt_masks_form_t * form)169 static gboolean _path_is_clockwise(dt_masks_form_t *form)
170 {
171   if(!g_list_shorter_than(form->points,3)) // if we have at least three points...
172   {
173     float sum = 0.0f;
174     for(const GList *form_points = form->points; form_points; form_points = g_list_next(form_points))
175     {
176       const GList *next = g_list_next_wraparound(form_points, form->points); // next, wrapping around if on last elt
177       dt_masks_point_path_t *point1 = (dt_masks_point_path_t *)form_points->data; // kth element of form->points
178       dt_masks_point_path_t *point2 = (dt_masks_point_path_t *)next->data;
179       sum += (point2->corner[0] - point1->corner[0]) * (point2->corner[1] + point1->corner[1]);
180       ;
181     }
182     return (sum < 0);
183   }
184   // return dummy answer
185   return TRUE;
186 }
187 
188 /** fill eventual gaps between 2 points with a line */
_path_fill_gaps(int lastx,int lasty,int x,int y,dt_masks_dynbuf_t * points)189 static int _path_fill_gaps(int lastx, int lasty, int x, int y, dt_masks_dynbuf_t *points)
190 {
191   dt_masks_dynbuf_reset(points);
192   dt_masks_dynbuf_add_2(points, x, y);
193 
194   // now we want to be sure everything is continuous
195   if(x - lastx > 1)
196   {
197     for(int j = x - 1; j > lastx; j--)
198     {
199       const int yyy = (j - lastx) * (y - lasty) / (float)(x - lastx) + lasty;
200       const int lasty2 = dt_masks_dynbuf_get(points, -1);
201       if(lasty2 - yyy > 1)
202       {
203         for(int jj = lasty2 + 1; jj < yyy; jj++)
204         {
205           dt_masks_dynbuf_add_2(points, j, jj);
206         }
207       }
208       else if(lasty2 - yyy < -1)
209       {
210         for(int jj = lasty2 - 1; jj > yyy; jj--)
211         {
212           dt_masks_dynbuf_add_2(points, j, jj);
213         }
214       }
215       dt_masks_dynbuf_add_2(points, j, yyy);
216     }
217   }
218   else if(x - lastx < -1)
219   {
220     for(int j = x + 1; j < lastx; j++)
221     {
222       int yyy = (j - lastx) * (y - lasty) / (float)(x - lastx) + lasty;
223       int lasty2 = dt_masks_dynbuf_get(points, -1);
224       if(lasty2 - yyy > 1)
225       {
226         for(int jj = lasty2 + 1; jj < yyy; jj++)
227         {
228           dt_masks_dynbuf_add_2(points, j, jj);
229         }
230       }
231       else if(lasty2 - yyy < -1)
232       {
233         for(int jj = lasty2 - 1; jj > yyy; jj--)
234         {
235           dt_masks_dynbuf_add_2(points, j, jj);
236         }
237       }
238       dt_masks_dynbuf_add_2(points, j, yyy);
239     }
240   }
241   return 1;
242 }
243 
244 /** fill the gap between 2 points with an arc of circle */
245 /** this function is here because we can have gap in border, esp. if the corner is very sharp */
_path_points_recurs_border_gaps(float * cmax,float * bmin,float * bmin2,float * bmax,dt_masks_dynbuf_t * dpoints,dt_masks_dynbuf_t * dborder,gboolean clockwise)246 static void _path_points_recurs_border_gaps(float *cmax, float *bmin, float *bmin2, float *bmax, dt_masks_dynbuf_t *dpoints,
247                                             dt_masks_dynbuf_t *dborder, gboolean clockwise)
248 {
249   // we want to find the start and end angles
250   double a1 = atan2f(bmin[1] - cmax[1], bmin[0] - cmax[0]);
251   double a2 = atan2f(bmax[1] - cmax[1], bmax[0] - cmax[0]);
252   if(a1 == a2) return;
253 
254   // we have to be sure that we turn in the correct direction
255   if(a2 < a1 && clockwise)
256   {
257     a2 += 2 * M_PI;
258   }
259   if(a2 > a1 && !clockwise)
260   {
261     a1 += 2 * M_PI;
262   }
263 
264   // we determine start and end radius too
265   const float r1 = sqrtf((bmin[1] - cmax[1]) * (bmin[1] - cmax[1]) + (bmin[0] - cmax[0]) * (bmin[0] - cmax[0]));
266   const float r2 = sqrtf((bmax[1] - cmax[1]) * (bmax[1] - cmax[1]) + (bmax[0] - cmax[0]) * (bmax[0] - cmax[0]));
267 
268   // and the max length of the circle arc
269   int l = 0;
270   if(a2 > a1)
271     l = (a2 - a1) * fmaxf(r1, r2);
272   else
273     l = (a1 - a2) * fmaxf(r1, r2);
274   if(l < 2) return;
275 
276   // and now we add the points
277   const float incra = (a2 - a1) / l;
278   const float incrr = (r2 - r1) / l;
279   float rr = r1 + incrr;
280   float aa = a1 + incra;
281   // allocate entries in the dynbufs
282   float *dpoints_ptr = dt_masks_dynbuf_reserve_n(dpoints, 2*(l-1));
283   float *dborder_ptr = dborder ? dt_masks_dynbuf_reserve_n(dborder, 2*(l-1)) : NULL;
284   // and fill them in: the same center pos for each point in dpoints, and the corresponding border point at
285   //  successive angular positions for dborder
286   if (dpoints_ptr)
287   {
288     for(int i = 1; i < l; i++)
289     {
290       *dpoints_ptr++ = cmax[0];
291       *dpoints_ptr++ = cmax[1];
292       if (dborder_ptr)
293       {
294         *dborder_ptr++ = cmax[0] + rr * cosf(aa);
295         *dborder_ptr++ = cmax[1] + rr * sinf(aa);
296       }
297       rr += incrr;
298       aa += incra;
299     }
300   }
301 }
302 
303 /** recursive function to get all points of the path AND all point of the border */
304 /** the function take care to avoid big gaps between points */
_path_points_recurs(float * p1,float * p2,double tmin,double tmax,float * path_min,float * path_max,float * border_min,float * border_max,float * rpath,float * rborder,dt_masks_dynbuf_t * dpoints,dt_masks_dynbuf_t * dborder,int withborder)305 static void _path_points_recurs(float *p1, float *p2, double tmin, double tmax, float *path_min,
306                                 float *path_max, float *border_min, float *border_max, float *rpath,
307                                 float *rborder, dt_masks_dynbuf_t *dpoints, dt_masks_dynbuf_t *dborder,
308                                 int withborder)
309 {
310   // we calculate points if needed
311   if(isnan(path_min[0]))
312   {
313     _path_border_get_XY(p1[0], p1[1], p1[2], p1[3], p2[2], p2[3], p2[0], p2[1], tmin,
314                         p1[4] + (p2[4] - p1[4]) * tmin * tmin * (3.0 - 2.0 * tmin), path_min, path_min + 1,
315                         border_min, border_min + 1);
316   }
317   if(isnan(path_max[0]))
318   {
319     _path_border_get_XY(p1[0], p1[1], p1[2], p1[3], p2[2], p2[3], p2[0], p2[1], tmax,
320                         p1[4] + (p2[4] - p1[4]) * tmax * tmax * (3.0 - 2.0 * tmax), path_max, path_max + 1,
321                         border_max, border_max + 1);
322   }
323   // are the points near ?
324   if((tmax - tmin < 0.0001)
325      || ((int)path_min[0] - (int)path_max[0] < 1 && (int)path_min[0] - (int)path_max[0] > -1
326          && (int)path_min[1] - (int)path_max[1] < 1 && (int)path_min[1] - (int)path_max[1] > -1
327          && (!withborder
328              || ((int)border_min[0] - (int)border_max[0] < 1 && (int)border_min[0] - (int)border_max[0] > -1
329                  && (int)border_min[1] - (int)border_max[1] < 1
330                  && (int)border_min[1] - (int)border_max[1] > -1))))
331   {
332     dt_masks_dynbuf_add_2(dpoints, path_max[0], path_max[1]);
333     rpath[0] = path_max[0];
334     rpath[1] = path_max[1];
335 
336     if(withborder)
337     {
338       dt_masks_dynbuf_add_2(dborder, border_max[0], border_max[1]);
339       rborder[0] = border_max[0];
340       rborder[1] = border_max[1];
341     }
342     return;
343   }
344 
345   // we split in two part
346   double tx = (tmin + tmax) / 2.0;
347   float c[2] = { NAN, NAN }, b[2] = { NAN, NAN };
348   float rc[2] = { 0 }, rb[2] = { 0 };
349   _path_points_recurs(p1, p2, tmin, tx, path_min, c, border_min, b, rc, rb, dpoints, dborder, withborder);
350   _path_points_recurs(p1, p2, tx, tmax, rc, path_max, rb, border_max, rpath, rborder, dpoints, dborder, withborder);
351 }
352 
353 /** find all self intersections in a path */
_path_find_self_intersection(dt_masks_dynbuf_t * inter,int nb_corners,float * border,int border_count)354 static int _path_find_self_intersection(dt_masks_dynbuf_t *inter, int nb_corners, float *border, int border_count)
355 {
356   if(nb_corners == 0 || border_count == 0) return 0;
357 
358   int inter_count = 0;
359 
360   // we search extreme points in x and y
361   int xmin = INT_MAX, xmax = INT_MIN, ymin = INT_MAX, ymax = INT_MIN;
362   int posextr[4] = { -1 }; // xmin,xmax,ymin,ymax
363 
364   for(int i = nb_corners * 3; i < border_count; i++)
365   {
366     if(isnan(border[i * 2]) || isnan(border[i * 2 + 1]))
367     {
368       border[i * 2] = border[i * 2 - 2];
369       border[i * 2 + 1] = border[i * 2 - 1];
370     }
371     if(xmin > border[i * 2])
372     {
373       xmin = border[i * 2];
374       posextr[0] = i;
375     }
376     if(xmax < border[i * 2])
377     {
378       xmax = border[i * 2];
379       posextr[1] = i;
380     }
381     if(ymin > border[i * 2 + 1])
382     {
383       ymin = border[i * 2 + 1];
384       posextr[2] = i;
385     }
386     if(ymax < border[i * 2 + 1])
387     {
388       ymax = border[i * 2 + 1];
389       posextr[3] = i;
390     }
391   }
392   xmin -= 1, ymin -= 1;
393   xmax += 1, ymax += 1;
394   const int hb = ymax - ymin;
395   const int wb = xmax - xmin;
396 
397   // we allocate the buffer
398   const size_t ss = (size_t)hb * wb;
399   if(ss < 10 || hb < 0 || wb < 0) return 0;
400 
401   int *binter = dt_alloc_align(64, sizeof(int) * ss);
402   if(binter == NULL) return 0;
403   memset(binter, 0, sizeof(int) * ss);
404 
405   dt_masks_dynbuf_t *extra = dt_masks_dynbuf_init(100000, "path extra");
406   if(extra == NULL)
407   {
408     dt_free_align(binter);
409     return 0;
410   }
411 
412   // we'll iterate through all border points, but we can't start at point[0]
413   // because it may be in a self-intersected section
414   // so we choose a point where we are sure there's no intersection:
415   // one from border shape extrema (here x_max)
416   int lastx = border[(posextr[1] - 1) * 2];
417   int lasty = border[(posextr[1] - 1) * 2 + 1];
418 
419   for(int ii = nb_corners * 3; ii < border_count; ii++)
420   {
421     // we want to loop from one border extremity
422     int i = ii - nb_corners * 3 + posextr[1];
423     if(i >= border_count) i = i - border_count + nb_corners * 3;
424 
425     if(inter_count >= nb_corners * 4) break;
426 
427     // we want to be sure everything is continuous
428     _path_fill_gaps(lastx, lasty, border[i * 2], border[i * 2 + 1], extra);
429 
430     // extra represent all the points between the last one and the current one
431     // for all the points in extra, we'll check for self-intersection
432     // and "register" them in binter
433     for(int j = dt_masks_dynbuf_position(extra) / 2 - 1; j >= 0; j--)
434     {
435       const int xx = (dt_masks_dynbuf_buffer(extra))[j * 2];
436       const int yy = (dt_masks_dynbuf_buffer(extra))[j * 2 + 1];
437 
438       // we check also 2 points around to be sure catching intersection
439       int v[3] = { 0 };
440       const int idx = (yy - ymin) * wb + (xx - xmin);
441       if(idx < 0 || idx > ss)
442       {
443         dt_free_align(binter);
444         return 0;
445       }
446       v[0] = binter[idx];
447       if(xx > xmin) v[1] = binter[idx - 1];
448       if(yy > ymin) v[2] = binter[idx - wb];
449 
450       for(int k = 0; k < 3; k++)
451       {
452         if(v[k] > 0)
453         {
454           // there's already a border point "registered" at this coordinate.
455           // so we've potentially found a self-intersection portion between v[k] and i
456           if((xx == lastx && yy == lasty) || v[k] == i - 1)
457           {
458             // we haven't move from last point.
459             // this is not a real self-interesection, so we just update binter
460             binter[idx] = i;
461           }
462           else if((i > v[k]
463                    && ((posextr[0] < v[k] || posextr[0] > i) && (posextr[1] < v[k] || posextr[1] > i)
464                        && (posextr[2] < v[k] || posextr[2] > i) && (posextr[3] < v[k] || posextr[3] > i)))
465                   || (i < v[k] && posextr[0] < v[k] && posextr[0] > i && posextr[1] < v[k] && posextr[1] > i
466                       && posextr[2] < v[k] && posextr[2] > i && posextr[3] < v[k] && posextr[3] > i))
467           {
468             // we have found a self-intersection portion, between v[k] and i
469             // and we are sure that this portion doesn't include one of the shape extrema
470             if(inter_count > 0)
471             {
472               if((v[k] - i) * ((int)dt_masks_dynbuf_get(inter, -2) - (int)dt_masks_dynbuf_get(inter, -1)) > 0
473                  && (int)dt_masks_dynbuf_get(inter, -2) >= v[k] && (int)dt_masks_dynbuf_get(inter, -1) <= i)
474               {
475                 // we find an self-intersection portion which include the last one
476                 // we just update it
477                 dt_masks_dynbuf_set(inter, -2, v[k]);
478                 dt_masks_dynbuf_set(inter, -1, i);
479               }
480               else
481               {
482                 // we find a new self-intersection portion
483                 dt_masks_dynbuf_add_2(inter, v[k], i);
484                 inter_count++;
485               }
486             }
487             else
488             {
489               // we find a new self-intersection portion
490               dt_masks_dynbuf_add_2(inter, v[k], i);
491               inter_count++;
492             }
493           }
494         }
495         else
496         {
497           // there wasn't anything "registered" at this place in binter
498           // we do it now
499           binter[idx] = i;
500         }
501       }
502       lastx = xx;
503       lasty = yy;
504     }
505   }
506 
507   dt_masks_dynbuf_free(extra);
508   dt_free_align(binter);
509 
510   // and we return the number of self-intersection found
511   return inter_count;
512 }
513 
514 /** get all points of the path and the border */
515 /** this take care of gaps and self-intersection and iop distortions */
_path_get_pts_border(dt_develop_t * dev,dt_masks_form_t * form,const double iop_order,const int transf_direction,dt_dev_pixelpipe_t * pipe,float ** points,int * points_count,float ** border,int * border_count,int source)516 static int _path_get_pts_border(dt_develop_t *dev, dt_masks_form_t *form, const double iop_order, const int transf_direction,
517                                    dt_dev_pixelpipe_t *pipe, float **points, int *points_count,
518                                    float **border, int *border_count, int source)
519 {
520   double start2 = 0.0;
521 
522   if(darktable.unmuted & DT_DEBUG_PERF) start2 = dt_get_wtime();
523 
524   const float wd = pipe->iwidth, ht = pipe->iheight;
525   const guint nb = g_list_length(form->points);
526 
527   dt_masks_dynbuf_t *dpoints = NULL, *dborder = NULL, *intersections = NULL;
528 
529   *points = NULL;
530   *points_count = 0;
531   if(border) *border = NULL;
532   if(border) *border_count = 0;
533 
534   dpoints = dt_masks_dynbuf_init(1000000, "path dpoints");
535   if(dpoints == NULL) return 0;
536 
537   if(border)
538   {
539     dborder = dt_masks_dynbuf_init(1000000, "path dborder");
540     if(dborder == NULL)
541     {
542       dt_masks_dynbuf_free(dpoints);
543       return 0;
544     }
545   }
546 
547   intersections = dt_masks_dynbuf_init(10 * MAX(nb, 1), "path intersections");
548   if(intersections == NULL)
549   {
550     dt_masks_dynbuf_free(dpoints);
551     dt_masks_dynbuf_free(dborder);
552     return 0;
553   }
554 
555   // we store all points
556   float dx = 0.0f, dy = 0.0f;
557 
558   if(source && nb > 0 && transf_direction != DT_DEV_TRANSFORM_DIR_ALL)
559   {
560     dt_masks_point_path_t *pt = (dt_masks_point_path_t *)form->points->data;
561     dx = (pt->corner[0] - form->source[0]) * wd;
562     dy = (pt->corner[1] - form->source[1]) * ht;
563   }
564   for(const GList *l = form->points; l; l = g_list_next(l))
565   {
566     const dt_masks_point_path_t *const pt = (dt_masks_point_path_t *)l->data;
567     float *const buf = dt_masks_dynbuf_reserve_n(dpoints, 6);
568     if (buf)
569     {
570       buf[0] = pt->ctrl1[0] * wd - dx;
571       buf[1] = pt->ctrl1[1] * ht - dy;
572       buf[2] = pt->corner[0] * wd - dx;
573       buf[3] = pt->corner[1] * ht - dy;
574       buf[4] = pt->ctrl2[0] * wd - dx;
575       buf[5] = pt->ctrl2[1] * ht - dy;
576     }
577   }
578   // for the border, we store value too
579   if(dborder)
580   {
581     dt_masks_dynbuf_add_zeros(dborder, 6 * nb);  // need six zeros for each border point
582   }
583 
584   float *border_init = dt_alloc_align_float((size_t)6 * nb);
585   int cw = _path_is_clockwise(form);
586   if(cw == 0) cw = -1;
587 
588   if(darktable.unmuted & DT_DEBUG_PERF)
589   {
590     dt_print(DT_DEBUG_MASKS, "[masks %s] path_points init took %0.04f sec\n", form->name,
591              dt_get_wtime() - start2);
592     start2 = dt_get_wtime();
593   }
594 
595   // we render all segments
596   const GList *form_points = form->points;
597   for(int k = 0; k < nb; k++)
598   {
599     int pb = dborder ? dt_masks_dynbuf_position(dborder) : 0;
600     border_init[k * 6 + 2] = -pb;
601     const GList *pt2 = g_list_next_wraparound(form_points, form->points); // next, wrapping around if on last element
602     const GList *pt3 = g_list_next_wraparound(pt2, form->points);
603     dt_masks_point_path_t *point1 = (dt_masks_point_path_t *)form_points->data; // kth element of form->points
604     dt_masks_point_path_t *point2 = (dt_masks_point_path_t *)pt2->data;
605     dt_masks_point_path_t *point3 = (dt_masks_point_path_t *)pt3->data;
606     float p1[5] = { point1->corner[0] * wd - dx, point1->corner[1] * ht - dy, point1->ctrl2[0] * wd - dx,
607                     point1->ctrl2[1] * ht - dy, cw * point1->border[1] * MIN(wd, ht) };
608     float p2[5] = { point2->corner[0] * wd - dx, point2->corner[1] * ht - dy, point2->ctrl1[0] * wd - dx,
609                     point2->ctrl1[1] * ht - dy, cw * point2->border[0] * MIN(wd, ht) };
610     float p3[5] = { point2->corner[0] * wd - dx, point2->corner[1] * ht - dy, point2->ctrl2[0] * wd - dx,
611                     point2->ctrl2[1] * ht - dy, cw * point2->border[1] * MIN(wd, ht) };
612     float p4[5] = { point3->corner[0] * wd - dx, point3->corner[1] * ht - dy, point3->ctrl1[0] * wd - dx,
613                     point3->ctrl1[1] * ht - dy, cw * point3->border[0] * MIN(wd, ht) };
614 
615     // advance form_points for next iteration so that it tracks the kth element of form->points
616     form_points = g_list_next(form_points);
617 
618     // and we determine all points by recursion (to be sure the distance between 2 points is <=1)
619     float rc[2] = { 0 }, rb[2] = { 0 };
620     float bmin[2] = { NAN, NAN };
621     float bmax[2] = { NAN, NAN };
622     float cmin[2] = { NAN, NAN };
623     float cmax[2] = { NAN, NAN };
624 
625     _path_points_recurs(p1, p2, 0.0, 1.0, cmin, cmax, bmin, bmax, rc, rb, dpoints, dborder, border && (nb >= 3));
626 
627     // we check gaps in the border (sharp edges)
628     if(dborder && (fabs(dt_masks_dynbuf_get(dborder, -2) - rb[0]) > 1.0f ||
629                    fabs(dt_masks_dynbuf_get(dborder, -1) - rb[1]) > 1.0f))
630     {
631       bmin[0] = dt_masks_dynbuf_get(dborder, -2);
632       bmin[1] = dt_masks_dynbuf_get(dborder, -1);
633     }
634 
635     dt_masks_dynbuf_add_2(dpoints, rc[0], rc[1]);
636 
637     border_init[k * 6 + 4] = dborder ? -dt_masks_dynbuf_position(dborder) : 0;
638 
639     if(dborder)
640     {
641       if(isnan(rb[0]))
642       {
643         if(isnan(dt_masks_dynbuf_get(dborder, - 2)))
644         {
645           dt_masks_dynbuf_set(dborder, -2, dt_masks_dynbuf_get(dborder, -4));
646           dt_masks_dynbuf_set(dborder, -1, dt_masks_dynbuf_get(dborder, -3));
647         }
648         rb[0] = dt_masks_dynbuf_get(dborder, -2);
649         rb[1] = dt_masks_dynbuf_get(dborder, -1);
650       }
651       dt_masks_dynbuf_add_2(dborder, rb[0], rb[1]);
652 
653       (dt_masks_dynbuf_buffer(dborder))[k * 6] = border_init[k * 6] = (dt_masks_dynbuf_buffer(dborder))[pb];
654       (dt_masks_dynbuf_buffer(dborder))[k * 6 + 1] = border_init[k * 6 + 1] = (dt_masks_dynbuf_buffer(dborder))[pb + 1];
655     }
656 
657     // we first want to be sure that there are no gaps in border
658     if(dborder && nb >= 3)
659     {
660       // we get the next point (start of the next segment)
661       _path_border_get_XY(p3[0], p3[1], p3[2], p3[3], p4[2], p4[3], p4[0], p4[1], 0, p3[4], cmin, cmin + 1,
662                           bmax, bmax + 1);
663       if(isnan(bmax[0]))
664       {
665         _path_border_get_XY(p3[0], p3[1], p3[2], p3[3], p4[2], p4[3], p4[0], p4[1], 0.0001, p3[4], cmin,
666                             cmin + 1, bmax, bmax + 1);
667       }
668       if(bmax[0] - rb[0] > 1 || bmax[0] - rb[0] < -1 || bmax[1] - rb[1] > 1 || bmax[1] - rb[1] < -1)
669       {
670         float bmin2[2] = { dt_masks_dynbuf_get(dborder, -22), dt_masks_dynbuf_get(dborder, -21) };
671         _path_points_recurs_border_gaps(rc, rb, bmin2, bmax, dpoints, dborder, _path_is_clockwise(form));
672       }
673     }
674   }
675 
676   *points_count = dt_masks_dynbuf_position(dpoints) / 2;
677   *points = dt_masks_dynbuf_harvest(dpoints);
678   dt_masks_dynbuf_free(dpoints);
679 
680   if(dborder)
681   {
682     *border_count = dt_masks_dynbuf_position(dborder) / 2;
683     *border = dt_masks_dynbuf_harvest(dborder);
684     dt_masks_dynbuf_free(dborder);
685   }
686 
687   if(darktable.unmuted & DT_DEBUG_PERF)
688   {
689     dt_print(DT_DEBUG_MASKS, "[masks %s] path_points point recurs %0.04f sec\n", form->name,
690              dt_get_wtime() - start2);
691     start2 = dt_get_wtime();
692   }
693 
694   // we don't want the border to self-intersect
695   int inter_count = 0;
696   if(border)
697   {
698     inter_count = _path_find_self_intersection(intersections, nb, *border, *border_count);
699 
700     if(darktable.unmuted & DT_DEBUG_PERF)
701     {
702       dt_print(DT_DEBUG_MASKS, "[masks %s] path_points self-intersect took %0.04f sec\n", form->name,
703                dt_get_wtime() - start2);
704       start2 = dt_get_wtime();
705     }
706   }
707 
708   // and we transform them with all distorted modules
709   if(source && transf_direction == DT_DEV_TRANSFORM_DIR_ALL)
710   {
711     // we transform with all distortion that happen *before* the module
712     // so we have now the TARGET points in module input reference
713     if(dt_dev_distort_transform_plus(dev, pipe, iop_order, DT_DEV_TRANSFORM_DIR_BACK_EXCL, *points, *points_count))
714     {
715       // now we move all the points by the shift
716       // so we have now the SOURCE points in module input reference
717       float pts[2] = { form->source[0] * wd, form->source[1] * ht };
718       if(!dt_dev_distort_transform_plus(dev, pipe, iop_order, DT_DEV_TRANSFORM_DIR_BACK_EXCL, pts, 1)) goto fail;
719 
720       dx = pts[0] - (*points)[0];
721       dy = pts[1] - (*points)[1];
722 #ifdef _OPENMP
723 #pragma omp parallel for simd default(none) \
724     dt_omp_firstprivate(points_count, points, dx, dy) \
725     schedule(static) if(*points_count > 100) aligned(points:64)
726 #endif
727       for(int i = 0; i < *points_count; i++)
728       {
729         (*points)[i * 2] += dx;
730         (*points)[i * 2 + 1] += dy;
731       }
732 
733       // we apply the rest of the distortions (those after the module)
734       // so we have now the SOURCE points in final image reference
735       if(!dt_dev_distort_transform_plus(dev, pipe, iop_order, DT_DEV_TRANSFORM_DIR_FORW_INCL, *points,
736                                         *points_count))
737         goto fail;
738     }
739 
740     if(darktable.unmuted & DT_DEBUG_PERF)
741       dt_print(DT_DEBUG_MASKS, "[masks %s] path_points end took %0.04f sec\n", form->name, dt_get_wtime() - start2);
742 
743     dt_masks_dynbuf_free(intersections);
744     dt_free_align(border_init);
745     return 1;
746   }
747   else if(dt_dev_distort_transform_plus(dev, pipe, iop_order, transf_direction, *points, *points_count))
748   {
749     if(!border || dt_dev_distort_transform_plus(dev, pipe, iop_order, transf_direction, *border, *border_count))
750     {
751       if(darktable.unmuted & DT_DEBUG_PERF)
752       {
753         dt_print(DT_DEBUG_MASKS, "[masks %s] path_points transform took %0.04f sec\n", form->name,
754                  dt_get_wtime() - start2);
755         start2 = dt_get_wtime();
756       }
757 
758       if(border)
759       {
760         // we don't want to copy the falloff points
761         for(int k = 0; k < nb; k++)
762           for(int i = 2; i < 6; i++) (*border)[k * 6 + i] = border_init[k * 6 + i];
763 
764         // now we want to write the skipping zones
765         for(int i = 0; i < inter_count; i++)
766         {
767           int v = (dt_masks_dynbuf_buffer(intersections))[i * 2];
768           int w = (dt_masks_dynbuf_buffer(intersections))[ i * 2 + 1];
769           if(v <= w)
770           {
771             (*border)[v * 2] = NAN;
772             (*border)[v * 2 + 1] = w;
773           }
774           else
775           {
776             if(w > nb * 3)
777             {
778               if(isnan((*border)[nb * 6]) && isnan((*border)[nb * 6 + 1]))
779                 (*border)[nb * 6 + 1] = w;
780               else if(isnan((*border)[nb * 6]))
781                 (*border)[nb * 6 + 1] = MAX((*border)[nb * 6 + 1], w);
782               else
783                 (*border)[nb * 6 + 1] = w;
784               (*border)[nb * 6] = NAN;
785             }
786             (*border)[v * 2] = NAN;
787             (*border)[v * 2 + 1] = NAN;
788           }
789         }
790       }
791 
792       if(darktable.unmuted & DT_DEBUG_PERF)
793         dt_print(DT_DEBUG_MASKS, "[masks %s] path_points end took %0.04f sec\n", form->name,
794                  dt_get_wtime() - start2);
795 
796       dt_masks_dynbuf_free(intersections);
797       dt_free_align(border_init);
798       return 1;
799     }
800   }
801 
802   // if we failed, then free all and return
803 fail:
804   dt_masks_dynbuf_free(intersections);
805   dt_free_align(border_init);
806   dt_free_align(*points);
807   *points = NULL;
808   *points_count = 0;
809   if(border) dt_free_align(*border);
810   if(border) *border = NULL;
811   if(border) *border_count = 0;
812   return 0;
813 }
814 
815 /** get the distance between point (x,y) and the path */
_path_get_distance(float x,float y,float as,dt_masks_form_gui_t * gui,int index,int corner_count,int * inside,int * inside_border,int * near,int * inside_source,float * dist)816 static void _path_get_distance(float x, float y, float as, dt_masks_form_gui_t *gui, int index,
817                                int corner_count, int *inside, int *inside_border, int *near, int *inside_source, float *dist)
818 {
819   // initialise returned values
820   *inside_source = 0;
821   *inside = 0;
822   *inside_border = 0;
823   *near = -1;
824   *dist = FLT_MAX;
825 
826   if(!gui) return;
827 
828   dt_masks_form_gui_points_t *gpt = (dt_masks_form_gui_points_t *)g_list_nth_data(gui->points, index);
829   if(!gpt) return;
830 
831   // we first check if we are inside the source form
832   if(dt_masks_point_in_form_exact(x, y, gpt->source, corner_count * 6, gpt->source_count))
833   {
834     *inside_source = 1;
835     *inside = 1;
836 
837     float x_min = FLT_MAX, y_min = FLT_MAX;
838     float x_max = FLT_MIN, y_max = FLT_MIN;
839 
840     for(int i = corner_count * 3; i < gpt->source_count; i++)
841     {
842       const float xx = gpt->source[i * 2];
843       const float yy = gpt->source[i * 2 + 1];
844 
845       x_min = fminf(x_min, xx);
846       x_max = fmaxf(x_max, xx);
847       y_min = fminf(y_min, yy);
848       y_max = fmaxf(y_max, yy);
849 
850       const float dd = sqf(xx - x) + sqf(yy - y);
851       *dist = fminf(*dist, dd);
852     }
853 
854     const float cx = x - (x_min + (x_max - x_min) / 2.0f);
855     const float cy = y - (y_min + (y_max - y_min) / 2.0f);
856     const float dd = sqf(cx) + sqf(cy);
857     *dist = fminf(*dist, dd);
858 
859     return;
860   }
861 
862   // we check if it's inside borders
863   if(!dt_masks_point_in_form_exact(x, y, gpt->border, corner_count * 3, gpt->border_count))
864     return;
865 
866   *inside = 1;
867 
868   // and we check if it's inside form
869   if(gpt->points_count > 2 + corner_count * 3)
870   {
871     float as2 = as * as;
872     //float as2 = 1600.0 * as1;
873     float last = gpt->points[gpt->points_count * 2 - 1];
874     int nb = 0;
875     int near_form = 0;
876     int current_seg = 1;
877 
878     float x_min = FLT_MAX, y_min = FLT_MAX;
879     float x_max = FLT_MIN, y_max = FLT_MIN;
880 
881     for(int i = corner_count * 3; i < gpt->points_count; i++)
882     {
883       //if we need to jump to skip points (in case of deleted point, because of self-intersection)
884       if(isnan(gpt->points[i * 2]))
885       {
886         if(isnan(gpt->points[i * 2 + 1])) break;
887         i = (int)gpt->points[i * 2 + 1] - 1;
888         continue;
889       }
890       // do we change of path segment ?
891       if(gpt->points[i * 2 + 1] == gpt->points[current_seg * 6 + 3]
892          && gpt->points[i * 2] == gpt->points[current_seg * 6 + 2])
893       {
894         current_seg = (current_seg + 1) % corner_count;
895       }
896       //distance from tested point to current form point
897       const float xx = gpt->points[i * 2];
898       const float yy = gpt->points[i * 2 + 1];
899 
900       x_min = fminf(x_min, xx);
901       x_max = fmaxf(x_max, xx);
902       y_min = fminf(y_min, yy);
903       y_max = fmaxf(y_max, yy);
904 
905       const float dd = sqf(xx - x) + sqf(yy - y);
906       *dist = fminf(*dist, dd);
907 
908       if(dd < as2)
909       {
910         near_form = 1;
911         if(current_seg == 0)
912           *near = corner_count - 1;
913         else
914           *near = current_seg - 1;
915       }
916 
917       if (((y<=yy && y>last) || (y>=yy && y<last)) && (gpt->points[i * 2] > x)) nb++;
918 
919       last = yy;
920     }
921     *inside_border = !((nb & 1) || (near_form));
922 
923     const float cx = x - (x_min + (x_max - x_min) / 2.0f);
924     const float cy = y - (y_min + (y_max - y_min) / 2.0f);
925     const float dd = sqf(cx) + sqf(cy);
926     *dist = fminf(*dist, dd);
927   }
928   else *inside_border = 1;
929 }
930 
_path_get_points_border(dt_develop_t * dev,dt_masks_form_t * form,float ** points,int * points_count,float ** border,int * border_count,int source,const dt_iop_module_t * module)931 static int _path_get_points_border(dt_develop_t *dev, dt_masks_form_t *form, float **points, int *points_count,
932                                    float **border, int *border_count, int source, const dt_iop_module_t *module)
933 {
934   if(source && !module) return 0;
935   const double ioporder = (module) ? module->iop_order : 0.0f;
936   return _path_get_pts_border(dev, form, ioporder, DT_DEV_TRANSFORM_DIR_ALL, dev->preview_pipe, points,
937                               points_count, border, border_count, source);
938 }
939 
_path_get_sizes(struct dt_iop_module_t * module,dt_masks_form_t * form,dt_masks_form_gui_t * gui,int index,float * masks_size,float * feather_size)940 static void _path_get_sizes(struct dt_iop_module_t *module, dt_masks_form_t *form, dt_masks_form_gui_t *gui, int index, float *masks_size, float *feather_size)
941 {
942   const dt_masks_form_gui_points_t *gpt =
943     (dt_masks_form_gui_points_t *)g_list_nth_data(gui->points, index);
944   if(!gpt) return;
945 
946   const int nb = g_list_length(form->points);
947   const float wd = darktable.develop->preview_pipe->backbuf_width;
948   const float ht = darktable.develop->preview_pipe->backbuf_height;
949 
950   float p1[2] = { FLT_MAX, FLT_MAX };
951   float p2[2] = { FLT_MIN, FLT_MIN };
952 
953   float fp1[2] = { FLT_MAX, FLT_MAX };
954   float fp2[2] = { FLT_MIN, FLT_MIN };
955 
956   for(int i = nb * 3; i < gpt->points_count; i++)
957   {
958     // line
959     const float x = gpt->points[i * 2];
960     const float y = gpt->points[i * 2 + 1];
961 
962     p1[0] = fminf(p1[0], x);
963     p2[0] = fmaxf(p2[0], x);
964     p1[1] = fminf(p1[1], y);
965     p2[1] = fmaxf(p2[1], y);
966 
967     if(feather_size)
968     {
969       // feather
970       const float fx = gpt->border[i * 2];
971       const float fy = gpt->border[i * 2 + 1];
972 
973       // ??? looks like when x border is nan then y is a point index
974       // see draw border in _path_events_post_expose.
975       if(!isnan(fx))
976       {
977         fp1[0] = fminf(fp1[0], fx);
978         fp2[0] = fmaxf(fp2[0], fx);
979         fp1[1] = fminf(fp1[1], fy);
980         fp2[1] = fmaxf(fp2[1], fy);
981       }
982     }
983   }
984 
985   *masks_size = fmaxf((p2[0] - p1[0]) / wd, (p2[1] - p1[1]) / ht);
986   if(feather_size) *feather_size = fmaxf((fp2[0] - fp1[0]) / wd, (fp2[1] - fp1[1]) / ht);
987 }
988 
_path_events_mouse_scrolled(struct dt_iop_module_t * module,float pzx,float pzy,int up,uint32_t state,dt_masks_form_t * form,int parentid,dt_masks_form_gui_t * gui,int index)989 static int _path_events_mouse_scrolled(struct dt_iop_module_t *module, float pzx, float pzy, int up,
990                                        uint32_t state, dt_masks_form_t *form, int parentid,
991                                        dt_masks_form_gui_t *gui, int index)
992 {
993   // resize a shape even if on a node or segment
994   if(gui->form_selected || gui->point_selected >= 0 || gui->feather_selected >= 0 || gui->seg_selected >= 0
995      || gui->point_border_selected >= 0)
996   {
997     // we register the current position
998     if(gui->scrollx == 0.0f && gui->scrolly == 0.0f)
999     {
1000       gui->scrollx = pzx;
1001       gui->scrolly = pzy;
1002     }
1003     if(dt_modifier_is(state, GDK_CONTROL_MASK))
1004     {
1005       // we try to change the opacity
1006       dt_masks_form_change_opacity(form, parentid, up);
1007     }
1008     else
1009     {
1010       const float amount = up ? 0.97f : 1.03f;
1011       // resize don't care where the mouse is inside a shape
1012       if(dt_modifier_is(state, GDK_SHIFT_MASK))
1013       {
1014         float masks_size = 1.0f, feather_size = 0.0f;
1015         _path_get_sizes(module, form, gui, index, &masks_size, &feather_size);
1016 
1017         // do not exceed upper limit of 1.0
1018         for(const GList *l = form->points; l; l = g_list_next(l))
1019         {
1020           const dt_masks_point_path_t *point = (dt_masks_point_path_t *)l->data;
1021           if(amount > 1.0f && (point->border[0] > 1.0f || point->border[1] > 1.0f)) return 1;
1022         }
1023         for(const GList *l = form->points; l; l = g_list_next(l))
1024         {
1025           dt_masks_point_path_t *point = (dt_masks_point_path_t *)l->data;
1026           point->border[0] *= amount;
1027           point->border[1] *= amount;
1028         }
1029         if(form->type & (DT_MASKS_CLONE|DT_MASKS_NON_CLONE))
1030         {
1031           float masks_border = dt_conf_get_float("plugins/darkroom/spots/path_border");
1032           masks_border = MAX(0.0005f, MIN(masks_border * amount, 0.5f));
1033           dt_conf_set_float("plugins/darkroom/spots/path_border", masks_border);
1034           dt_toast_log(_("feather size: %3.2f%%"), (feather_size - masks_size) / masks_size *100.0f);
1035         }
1036         else
1037         {
1038           float masks_border = dt_conf_get_float("plugins/darkroom/masks/path/border");
1039           masks_border = MAX(0.0005f, MIN(masks_border * amount, 0.5f));
1040           dt_conf_set_float("plugins/darkroom/masks/path/border", masks_border);
1041           dt_toast_log(_("feather size: %3.2f%%"), (feather_size - masks_size) / masks_size * 100.0f);
1042         }
1043       }
1044       else if(gui->edit_mode == DT_MASKS_EDIT_FULL)
1045       {
1046         // get the center of gravity of the form (like if it was a simple polygon)
1047         float bx = 0.0f;
1048         float by = 0.0f;
1049         float surf = 0.0f;
1050 
1051         for(const GList *form_points = form->points; form_points; form_points = g_list_next(form_points))
1052         {
1053           const GList *next = g_list_next_wraparound(form_points, form->points); // next w/ wrap
1054           dt_masks_point_path_t *point1 = (dt_masks_point_path_t *)form_points->data; // kth element of form->points
1055           dt_masks_point_path_t *point2 = (dt_masks_point_path_t *)next->data;
1056           surf += point1->corner[0] * point2->corner[1] - point2->corner[0] * point1->corner[1];
1057 
1058           bx += (point1->corner[0] + point2->corner[0])
1059                 * (point1->corner[0] * point2->corner[1] - point2->corner[0] * point1->corner[1]);
1060           by += (point1->corner[1] + point2->corner[1])
1061                 * (point1->corner[0] * point2->corner[1] - point2->corner[0] * point1->corner[1]);
1062         }
1063         bx /= 3.0f * surf;
1064         by /= 3.0f * surf;
1065 
1066         if(amount < 1.0f && surf < 0.00001f && surf > -0.00001f) return 1;
1067         if(amount > 1.0f && surf > 4.0f) return 1;
1068 
1069         // now we move each point
1070         for(GList *l = form->points; l; l = g_list_next(l))
1071         {
1072           dt_masks_point_path_t *point = (dt_masks_point_path_t *)l->data;
1073           const float x = (point->corner[0] - bx) * amount;
1074           const float y = (point->corner[1] - by) * amount;
1075 
1076           // we stretch ctrl points
1077           const float ct1x = (point->ctrl1[0] - point->corner[0]) * amount;
1078           const float ct1y = (point->ctrl1[1] - point->corner[1]) * amount;
1079           const float ct2x = (point->ctrl2[0] - point->corner[0]) * amount;
1080           const float ct2y = (point->ctrl2[1] - point->corner[1]) * amount;
1081 
1082           // and we set the new points
1083           point->corner[0] = bx + x;
1084           point->corner[1] = by + y;
1085           point->ctrl1[0] = point->corner[0] + ct1x;
1086           point->ctrl1[1] = point->corner[1] + ct1y;
1087           point->ctrl2[0] = point->corner[0] + ct2x;
1088           point->ctrl2[1] = point->corner[1] + ct2y;
1089         }
1090 
1091         // now the redraw/save stuff
1092         _path_init_ctrl_points(form);
1093 
1094         float masks_size = 0.0f;
1095         _path_get_sizes(module, form, gui, index, &masks_size, NULL);
1096 
1097         dt_toast_log(_("size: %3.2f%%"), masks_size * 100.0f);
1098       }
1099       else
1100       {
1101         return 0;
1102       }
1103 
1104       dt_dev_add_masks_history_item(darktable.develop, module, TRUE);
1105 
1106       // we recreate the form points
1107       dt_masks_gui_form_remove(form, gui, index);
1108       dt_masks_gui_form_create(form, gui, index, module);
1109 
1110       // we save the move
1111       dt_masks_update_image(darktable.develop);
1112     }
1113     return 1;
1114   }
1115   return 0;
1116 }
1117 
_path_events_button_pressed(struct dt_iop_module_t * module,float pzx,float pzy,double pressure,int which,int type,uint32_t state,dt_masks_form_t * form,int parentid,dt_masks_form_gui_t * gui,int index)1118 static int _path_events_button_pressed(struct dt_iop_module_t *module, float pzx, float pzy,
1119                                        double pressure, int which, int type, uint32_t state,
1120                                        dt_masks_form_t *form, int parentid, dt_masks_form_gui_t *gui, int index)
1121 {
1122   if(type == GDK_2BUTTON_PRESS || type == GDK_3BUTTON_PRESS) return 1;
1123   if(!gui) return 0;
1124   dt_masks_form_gui_points_t *gpt = (dt_masks_form_gui_points_t *)g_list_nth_data(gui->points, index);
1125   if(!gpt) return 0;
1126 
1127   float masks_border;
1128   if(form->type & (DT_MASKS_CLONE|DT_MASKS_NON_CLONE))
1129     masks_border = MIN(dt_conf_get_float("plugins/darkroom/spots/path_border"), 0.5f);
1130   else
1131     masks_border = MIN(dt_conf_get_float("plugins/darkroom/masks/path/border"), 0.5f);
1132 
1133   if(gui->creation && which == 1 && form->points == NULL
1134      && (dt_modifier_is(state, GDK_CONTROL_MASK | GDK_SHIFT_MASK) || dt_modifier_is(state, GDK_SHIFT_MASK)))
1135   {
1136     // set some absolute or relative position for the source of the clone mask
1137     if(form->type & DT_MASKS_CLONE) dt_masks_set_source_pos_initial_state(gui, state, pzx, pzy);
1138 
1139     return 1;
1140   }
1141   else if(gui->creation && (which == 3 || gui->creation_closing_form))
1142   {
1143     // we don't want a form with less than 3 points
1144     if(g_list_shorter_than(form->points, 4))
1145     {
1146       // we don't really have a way to know if the user wants to cancel the continuous add here
1147       // or just cancelling this mask, let's assume that this is not a mistake and cancel
1148       // the continuous add
1149       gui->creation_continuous = FALSE;
1150       gui->creation_continuous_module = NULL;
1151       dt_masks_set_edit_mode(module, DT_MASKS_EDIT_FULL);
1152       dt_masks_iop_update(module);
1153       dt_control_queue_redraw_center();
1154       return 1;
1155     }
1156     else
1157     {
1158       dt_iop_module_t *crea_module = gui->creation_module;
1159       // we delete last point (the one we are currently dragging)
1160       dt_masks_point_path_t *point = (dt_masks_point_path_t *)g_list_last(form->points)->data;
1161       form->points = g_list_remove(form->points, point);
1162       free(point);
1163       point = NULL;
1164 
1165       gui->point_dragging = -1;
1166       _path_init_ctrl_points(form);
1167 
1168       // we save the form and quit creation mode
1169       dt_masks_gui_form_save_creation(darktable.develop, crea_module, form, gui);
1170       if(crea_module)
1171       {
1172         dt_dev_add_history_item(darktable.develop, crea_module, TRUE);
1173         // and we switch in edit mode to show all the forms
1174         // spots and retouch have their own handling of creation_continuous
1175         if(gui->creation_continuous && ( strcmp(crea_module->so->op, "spots") == 0 || strcmp(crea_module->so->op, "retouch") == 0))
1176           dt_masks_set_edit_mode_single_form(crea_module, form->formid, DT_MASKS_EDIT_FULL);
1177         else if(!gui->creation_continuous)
1178           dt_masks_set_edit_mode(crea_module, DT_MASKS_EDIT_FULL);
1179         dt_masks_iop_update(crea_module);
1180         gui->creation_module = NULL;
1181       }
1182       else
1183       {
1184         dt_dev_masks_selection_change(darktable.develop, form->formid, TRUE);
1185       }
1186 
1187       if(gui->creation_continuous)
1188       {
1189         //spot and retouch manage creation_continuous in their own way
1190         if(strcmp(crea_module->so->op, "spots") != 0 && strcmp(crea_module->so->op, "retouch") != 0)
1191         {
1192           dt_iop_gui_blend_data_t *bd = (dt_iop_gui_blend_data_t *)crea_module->blend_data;
1193           for(int n = 0; n < DEVELOP_MASKS_NB_SHAPES; n++)
1194             if(bd->masks_type[n] == form->type)
1195               gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(bd->masks_shapes[n]), TRUE);
1196 
1197           gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(bd->masks_edit), FALSE);
1198           dt_masks_form_t *newform = dt_masks_create(form->type);
1199           dt_masks_change_form_gui(newform);
1200           darktable.develop->form_gui->creation = TRUE;
1201           darktable.develop->form_gui->creation_module = crea_module;
1202           darktable.develop->form_gui->creation_continuous = TRUE;
1203           darktable.develop->form_gui->creation_continuous_module = crea_module;
1204         }
1205         else
1206         {
1207           dt_masks_form_t *form_new = dt_masks_create(form->type);
1208           dt_masks_change_form_gui(form_new);
1209 
1210           darktable.develop->form_gui->creation = TRUE;
1211           darktable.develop->form_gui->creation_module = gui->creation_continuous_module;
1212         }
1213       }
1214       else if(form->type & (DT_MASKS_CLONE | DT_MASKS_NON_CLONE))
1215       {
1216         dt_masks_form_t *grp = darktable.develop->form_visible;
1217         if(!grp || !(grp->type & DT_MASKS_GROUP)) return 1;
1218         int pos3 = 0, pos2 = -1;
1219         for(GList *fs = grp->points; fs; fs = g_list_next(fs))
1220         {
1221           dt_masks_point_group_t *pt = (dt_masks_point_group_t *)fs->data;
1222           if(pt->formid == form->formid)
1223           {
1224             pos2 = pos3;
1225             break;
1226           }
1227           pos3++;
1228         }
1229         if(pos2 < 0) return 1;
1230         dt_masks_form_gui_t *gui2 = darktable.develop->form_gui;
1231         if(!gui2) return 1;
1232         gui2->group_selected = pos2;
1233 
1234         dt_masks_select_form(crea_module, dt_masks_get_from_id(darktable.develop, form->formid));
1235       }
1236 
1237       dt_control_queue_redraw_center();
1238     }
1239   }
1240   else if(which == 1)
1241   {
1242     if(gui->creation)
1243     {
1244       dt_masks_point_path_t *bzpt = (dt_masks_point_path_t *)(malloc(sizeof(dt_masks_point_path_t)));
1245       int nb = g_list_length(form->points);
1246       // change the values
1247       const float wd = darktable.develop->preview_pipe->backbuf_width;
1248       const float ht = darktable.develop->preview_pipe->backbuf_height;
1249       float pts[2] = { pzx * wd, pzy * ht };
1250       dt_dev_distort_backtransform(darktable.develop, pts, 1);
1251 
1252       bzpt->corner[0] = pts[0] / darktable.develop->preview_pipe->iwidth;
1253       bzpt->corner[1] = pts[1] / darktable.develop->preview_pipe->iheight;
1254       bzpt->ctrl1[0] = bzpt->ctrl1[1] = bzpt->ctrl2[0] = bzpt->ctrl2[1] = -1.0;
1255       bzpt->state = DT_MASKS_POINT_STATE_NORMAL;
1256 
1257       bzpt->border[0] = bzpt->border[1] = MAX(0.0005f, masks_border);
1258 
1259       // if that's the first point we should had another one as base point
1260       if(nb == 0)
1261       {
1262         dt_masks_point_path_t *bzpt2 = (dt_masks_point_path_t *)(malloc(sizeof(dt_masks_point_path_t)));
1263         bzpt2->corner[0] = pts[0] / darktable.develop->preview_pipe->iwidth;
1264         bzpt2->corner[1] = pts[1] / darktable.develop->preview_pipe->iheight;
1265         bzpt2->ctrl1[0] = bzpt2->ctrl1[1] = bzpt2->ctrl2[0] = bzpt2->ctrl2[1] = -1.0;
1266         bzpt2->border[0] = bzpt2->border[1] = MAX(0.0005f, masks_border);
1267         bzpt2->state = DT_MASKS_POINT_STATE_NORMAL;
1268         form->points = g_list_append(form->points, bzpt2);
1269 
1270         if(form->type & DT_MASKS_CLONE)
1271         {
1272           dt_masks_set_source_pos_initial_value(gui, DT_MASKS_PATH, form, pzx, pzy);
1273         }
1274         else
1275         {
1276           // not used by regular masks
1277           form->source[0] = form->source[1] = 0.0f;
1278         }
1279         nb++;
1280       }
1281       form->points = g_list_append(form->points, bzpt);
1282 
1283       // if this is a ctrl click, the last created point is a sharp one
1284       if(dt_modifier_is(state, GDK_CONTROL_MASK))
1285       {
1286         dt_masks_point_path_t *bzpt3 = g_list_nth_data(form->points, nb - 1);
1287         bzpt3->ctrl1[0] = bzpt3->ctrl2[0] = bzpt3->corner[0];
1288         bzpt3->ctrl1[1] = bzpt3->ctrl2[1] = bzpt3->corner[1];
1289         bzpt3->state = DT_MASKS_POINT_STATE_USER;
1290       }
1291 
1292       gui->point_dragging = nb;
1293 
1294       _path_init_ctrl_points(form);
1295 
1296       // we recreate the form points
1297       dt_masks_gui_form_remove(form, gui, index);
1298       dt_masks_gui_form_create(form, gui, index, module);
1299 
1300       dt_control_queue_redraw_center();
1301       return 1;
1302     }
1303     else if(gui->source_selected && gui->edit_mode == DT_MASKS_EDIT_FULL)
1304     {
1305       if(!gpt) return 0;
1306       // we start the form dragging
1307       gui->source_dragging = TRUE;
1308       gui->dx = gpt->source[0] - gui->posx;
1309       gui->dy = gpt->source[1] - gui->posy;
1310       return 1;
1311     }
1312     else if(gui->form_selected && gui->edit_mode == DT_MASKS_EDIT_FULL)
1313     {
1314       gui->form_dragging = TRUE;
1315       gui->point_edited = -1;
1316       gui->dx = gpt->points[2] - gui->posx;
1317       gui->dy = gpt->points[3] - gui->posy;
1318       return 1;
1319     }
1320     else if(gui->point_selected >= 0)
1321     {
1322       // if ctrl is pressed, we change the type of point
1323       if(gui->point_edited == gui->point_selected && dt_modifier_is(state, GDK_CONTROL_MASK))
1324       {
1325         dt_masks_point_path_t *point
1326             = (dt_masks_point_path_t *)g_list_nth_data(form->points, gui->point_edited);
1327         if(point == NULL)
1328         {
1329           gui->point_selected = -1;
1330           return 1;
1331         }
1332         if(point->state != DT_MASKS_POINT_STATE_NORMAL)
1333         {
1334           point->state = DT_MASKS_POINT_STATE_NORMAL;
1335           _path_init_ctrl_points(form);
1336         }
1337         else
1338         {
1339           point->ctrl1[0] = point->ctrl2[0] = point->corner[0];
1340           point->ctrl1[1] = point->ctrl2[1] = point->corner[1];
1341           point->state = DT_MASKS_POINT_STATE_USER;
1342         }
1343         dt_dev_add_masks_history_item(darktable.develop, module, TRUE);
1344 
1345         // we recreate the form points
1346         dt_masks_gui_form_remove(form, gui, index);
1347         dt_masks_gui_form_create(form, gui, index, module);
1348         gpt->clockwise = _path_is_clockwise(form);
1349         // we save the move
1350         dt_masks_update_image(darktable.develop);
1351         return 1;
1352       }
1353       // we register the current position to avoid accidental move
1354       if(gui->point_edited < 0 && gui->scrollx == 0.0f && gui->scrolly == 0.0f)
1355       {
1356         gui->scrollx = pzx;
1357         gui->scrolly = pzy;
1358       }
1359       gui->point_edited = gui->point_dragging = gui->point_selected;
1360       gpt->clockwise = _path_is_clockwise(form);
1361       dt_control_queue_redraw_center();
1362       return 1;
1363     }
1364     else if(gui->feather_selected >= 0)
1365     {
1366       gui->feather_dragging = gui->feather_selected;
1367       dt_control_queue_redraw_center();
1368       return 1;
1369     }
1370     else if(gui->point_border_selected >= 0)
1371     {
1372       gui->point_edited = -1;
1373       gui->point_border_dragging = gui->point_border_selected;
1374       dt_control_queue_redraw_center();
1375       return 1;
1376     }
1377     else if(gui->seg_selected >= 0)
1378     {
1379       gui->point_edited = -1;
1380       if(dt_modifier_is(state, GDK_CONTROL_MASK))
1381       {
1382         // we add a new point to the path
1383         dt_masks_point_path_t *bzpt = (dt_masks_point_path_t *)(malloc(sizeof(dt_masks_point_path_t)));
1384         // change the values
1385         const float wd = darktable.develop->preview_pipe->backbuf_width;
1386         const float ht = darktable.develop->preview_pipe->backbuf_height;
1387         float pts[2] = { pzx * wd, pzy * ht };
1388         dt_dev_distort_backtransform(darktable.develop, pts, 1);
1389 
1390         bzpt->corner[0] = pts[0] / darktable.develop->preview_pipe->iwidth;
1391         bzpt->corner[1] = pts[1] / darktable.develop->preview_pipe->iheight;
1392         bzpt->ctrl1[0] = bzpt->ctrl1[1] = bzpt->ctrl2[0] = bzpt->ctrl2[1] = -1.0;
1393         bzpt->state = DT_MASKS_POINT_STATE_NORMAL;
1394 
1395         // interpolate the border width of the two neighbour points'
1396         const GList* first = g_list_nth(form->points, gui->seg_selected);
1397         const GList* second = g_list_next_wraparound(first, form->points); // next, wrapping around if on last element
1398         dt_masks_point_path_t *left = (dt_masks_point_path_t *)first->data;
1399         dt_masks_point_path_t *right = (dt_masks_point_path_t *)second->data;
1400         bzpt->border[0] = MAX(0.0005f, (left->border[0] + right->border[0]) * 0.5);
1401         bzpt->border[1] = MAX(0.0005f, (left->border[1] + right->border[1]) * 0.5);
1402 
1403         form->points = g_list_insert(form->points, bzpt, gui->seg_selected + 1);
1404         _path_init_ctrl_points(form);
1405         dt_masks_gui_form_remove(form, gui, index);
1406         dt_masks_gui_form_create(form, gui, index, module);
1407         gui->point_edited = gui->point_dragging = gui->point_selected = gui->seg_selected + 1;
1408         gui->seg_selected = -1;
1409         dt_control_queue_redraw_center();
1410       }
1411       else
1412       {
1413         // we move the entire segment
1414         gui->seg_dragging = gui->seg_selected;
1415         gui->dx = gpt->points[gui->seg_selected * 6 + 2] - gui->posx;
1416         gui->dy = gpt->points[gui->seg_selected * 6 + 3] - gui->posy;
1417       }
1418       return 1;
1419     }
1420     gui->point_edited = -1;
1421   }
1422   else if(which == 3 && gui->point_selected >= 0)
1423   {
1424     // we remove the point (and the entire form if there is too few points)
1425     if(g_list_shorter_than(form->points, 4))
1426     {
1427       // if the form doesn't belong to a group, we don't delete it
1428       if(parentid <= 0) return 1;
1429 
1430       // we hide the form
1431       if(!(darktable.develop->form_visible->type & DT_MASKS_GROUP))
1432         dt_masks_change_form_gui(NULL);
1433       else if(g_list_shorter_than(darktable.develop->form_visible->points, 2))
1434         dt_masks_change_form_gui(NULL);
1435       else
1436       {
1437         const int emode = gui->edit_mode;
1438         dt_masks_clear_form_gui(darktable.develop);
1439         for(GList *forms = darktable.develop->form_visible->points; forms; forms = g_list_next(forms))
1440         {
1441           dt_masks_point_group_t *guipt = (dt_masks_point_group_t *)forms->data;
1442           if(guipt->formid == form->formid)
1443           {
1444             darktable.develop->form_visible->points
1445                 = g_list_remove(darktable.develop->form_visible->points, guipt);
1446             free(guipt);
1447             break;
1448           }
1449         }
1450         gui->edit_mode = emode;
1451       }
1452 
1453       // we delete or remove the shape
1454       dt_masks_form_remove(module, NULL, form);
1455       dt_control_queue_redraw_center();
1456       return 1;
1457     }
1458     dt_masks_point_path_t *point
1459         = (dt_masks_point_path_t *)g_list_nth_data(form->points, gui->point_selected);
1460     if(point == NULL)
1461     {
1462       gui->point_selected = -1;
1463       return 1;
1464     }
1465     form->points = g_list_remove(form->points, point);
1466     free(point);
1467     // form->points = g_list_delete_link(form->points, g_list_nth(form->points, gui->point_selected));
1468     gui->point_selected = -1;
1469     _path_init_ctrl_points(form);
1470 
1471     dt_dev_add_masks_history_item(darktable.develop, module, TRUE);
1472 
1473     // we recreate the form points
1474     dt_masks_gui_form_remove(form, gui, index);
1475     dt_masks_gui_form_create(form, gui, index, module);
1476     gpt->clockwise = _path_is_clockwise(form);
1477     // we save the move
1478     dt_masks_update_image(darktable.develop);
1479 
1480     return 1;
1481   }
1482   else if(which == 3 && gui->feather_selected >= 0)
1483   {
1484     dt_masks_point_path_t *point
1485         = (dt_masks_point_path_t *)g_list_nth_data(form->points, gui->feather_selected);
1486     if(point != NULL && point->state != DT_MASKS_POINT_STATE_NORMAL)
1487     {
1488       point->state = DT_MASKS_POINT_STATE_NORMAL;
1489       _path_init_ctrl_points(form);
1490 
1491       dt_dev_add_masks_history_item(darktable.develop, module, TRUE);
1492 
1493       // we recreate the form points
1494       dt_masks_gui_form_remove(form, gui, index);
1495       dt_masks_gui_form_create(form, gui, index, module);
1496       gpt->clockwise = _path_is_clockwise(form);
1497       // we save the move
1498       dt_masks_update_image(darktable.develop);
1499     }
1500     return 1;
1501   }
1502   else if(which == 3 && parentid > 0 && gui->edit_mode == DT_MASKS_EDIT_FULL)
1503   {
1504     // we hide the form
1505     if(!(darktable.develop->form_visible->type & DT_MASKS_GROUP))
1506       dt_masks_change_form_gui(NULL);
1507     else if(g_list_shorter_than(darktable.develop->form_visible->points, 2))
1508       dt_masks_change_form_gui(NULL);
1509     else
1510     {
1511       dt_masks_clear_form_gui(darktable.develop);
1512       for(GList *forms = darktable.develop->form_visible->points; forms; forms = g_list_next(forms))
1513       {
1514         dt_masks_point_group_t *guipt = (dt_masks_point_group_t *)forms->data;
1515         if(guipt->formid == form->formid)
1516         {
1517           darktable.develop->form_visible->points
1518               = g_list_remove(darktable.develop->form_visible->points, guipt);
1519           free(guipt);
1520           break;
1521         }
1522       }
1523       gui->edit_mode = DT_MASKS_EDIT_FULL;
1524     }
1525 
1526     // we remove the shape
1527     dt_dev_masks_list_remove(darktable.develop, form->formid, parentid);
1528     dt_masks_form_remove(module, dt_masks_get_from_id(darktable.develop, parentid), form);
1529     return 1;
1530   }
1531 
1532   return 0;
1533 }
1534 
_path_events_button_released(struct dt_iop_module_t * module,float pzx,float pzy,int which,uint32_t state,dt_masks_form_t * form,int parentid,dt_masks_form_gui_t * gui,int index)1535 static int _path_events_button_released(struct dt_iop_module_t *module, float pzx, float pzy, int which,
1536                                         uint32_t state, dt_masks_form_t *form, int parentid,
1537                                         dt_masks_form_gui_t *gui, int index)
1538 {
1539   if(!gui) return 0;
1540   if(gui->creation) return 1;
1541   dt_masks_form_gui_points_t *gpt = (dt_masks_form_gui_points_t *)g_list_nth_data(gui->points, index);
1542   if(!gpt) return 0;
1543   if(gui->form_dragging)
1544   {
1545     // we end the form dragging
1546     gui->form_dragging = FALSE;
1547 
1548     // we get point0 new values
1549     dt_masks_point_path_t *point = (dt_masks_point_path_t *)(form->points)->data;
1550     const float wd = darktable.develop->preview_pipe->backbuf_width;
1551     const float ht = darktable.develop->preview_pipe->backbuf_height;
1552     float pts[2] = { pzx * wd + gui->dx, pzy * ht + gui->dy };
1553     dt_dev_distort_backtransform(darktable.develop, pts, 1);
1554     const float dx = pts[0] / darktable.develop->preview_pipe->iwidth - point->corner[0];
1555     const float dy = pts[1] / darktable.develop->preview_pipe->iheight - point->corner[1];
1556 
1557     // we move all points
1558     for(GList *points = form->points; points; points = g_list_next(points))
1559     {
1560       point = (dt_masks_point_path_t *)points->data;
1561       point->corner[0] += dx;
1562       point->corner[1] += dy;
1563       point->ctrl1[0] += dx;
1564       point->ctrl1[1] += dy;
1565       point->ctrl2[0] += dx;
1566       point->ctrl2[1] += dy;
1567     }
1568 
1569     dt_dev_add_masks_history_item(darktable.develop, module, TRUE);
1570 
1571     // we recreate the form points
1572     dt_masks_gui_form_remove(form, gui, index);
1573     dt_masks_gui_form_create(form, gui, index, module);
1574 
1575     // we save the move
1576     dt_masks_update_image(darktable.develop);
1577 
1578     return 1;
1579   }
1580   else if(gui->source_dragging)
1581   {
1582     // we end the form dragging
1583     gui->source_dragging = FALSE;
1584 
1585     // we change the source value
1586     const float wd = darktable.develop->preview_pipe->backbuf_width;
1587     const float ht = darktable.develop->preview_pipe->backbuf_height;
1588     float pts[2] = { pzx * wd + gui->dx, pzy * ht + gui->dy };
1589     dt_dev_distort_backtransform(darktable.develop, pts, 1);
1590     form->source[0] = pts[0] / darktable.develop->preview_pipe->iwidth;
1591     form->source[1] = pts[1] / darktable.develop->preview_pipe->iheight;
1592     dt_dev_add_masks_history_item(darktable.develop, module, TRUE);
1593 
1594     // we recreate the form points
1595     dt_masks_gui_form_remove(form, gui, index);
1596     dt_masks_gui_form_create(form, gui, index, module);
1597 
1598     // we save the move
1599     dt_masks_update_image(darktable.develop);
1600 
1601     return 1;
1602   }
1603   else if(gui->seg_dragging >= 0)
1604   {
1605     gui->seg_dragging = -1;
1606     gpt->clockwise = _path_is_clockwise(form);
1607     dt_dev_add_masks_history_item(darktable.develop, module, TRUE);
1608     dt_masks_update_image(darktable.develop);
1609     return 1;
1610   }
1611   else if(gui->point_dragging >= 0)
1612   {
1613     dt_masks_point_path_t *point
1614         = (dt_masks_point_path_t *)g_list_nth_data(form->points, gui->point_dragging);
1615     gui->point_dragging = -1;
1616     if(gui->scrollx != 0.0f || gui->scrolly != 0.0f)
1617     {
1618       gui->scrollx = gui->scrolly = 0;
1619       return 1;
1620     }
1621     gui->scrollx = gui->scrolly = 0;
1622     const float wd = darktable.develop->preview_pipe->backbuf_width;
1623     const float ht = darktable.develop->preview_pipe->backbuf_height;
1624     float pts[2] = { pzx * wd, pzy * ht };
1625     dt_dev_distort_backtransform(darktable.develop, pts, 1);
1626     const float dx = pts[0] / darktable.develop->preview_pipe->iwidth - point->corner[0];
1627     const float dy = pts[1] / darktable.develop->preview_pipe->iheight - point->corner[1];
1628 
1629     point->corner[0] += dx;
1630     point->corner[1] += dy;
1631     point->ctrl1[0] += dx;
1632     point->ctrl1[1] += dy;
1633     point->ctrl2[0] += dx;
1634     point->ctrl2[1] += dy;
1635 
1636     _path_init_ctrl_points(form);
1637 
1638     dt_dev_add_masks_history_item(darktable.develop, module, TRUE);
1639 
1640     // we recreate the form points
1641     dt_masks_gui_form_remove(form, gui, index);
1642     dt_masks_gui_form_create(form, gui, index, module);
1643     gpt->clockwise = _path_is_clockwise(form);
1644     // we save the move
1645     dt_masks_update_image(darktable.develop);
1646 
1647     return 1;
1648   }
1649   else if(gui->feather_dragging >= 0)
1650   {
1651     dt_masks_point_path_t *point
1652         = (dt_masks_point_path_t *)g_list_nth_data(form->points, gui->feather_dragging);
1653     gui->feather_dragging = -1;
1654     const float wd = darktable.develop->preview_pipe->backbuf_width;
1655     const float ht = darktable.develop->preview_pipe->backbuf_height;
1656     float pts[2] = { pzx * wd, pzy * ht };
1657     dt_dev_distort_backtransform(darktable.develop, pts, 1);
1658 
1659     int p1x, p1y, p2x, p2y;
1660     _path_feather_to_ctrl(point->corner[0] * darktable.develop->preview_pipe->iwidth,
1661                           point->corner[1] * darktable.develop->preview_pipe->iheight, pts[0], pts[1], &p1x,
1662                           &p1y, &p2x, &p2y, gpt->clockwise);
1663     point->ctrl1[0] = (float)p1x / darktable.develop->preview_pipe->iwidth;
1664     point->ctrl1[1] = (float)p1y / darktable.develop->preview_pipe->iheight;
1665     point->ctrl2[0] = (float)p2x / darktable.develop->preview_pipe->iwidth;
1666     point->ctrl2[1] = (float)p2y / darktable.develop->preview_pipe->iheight;
1667 
1668     point->state = DT_MASKS_POINT_STATE_USER;
1669 
1670     _path_init_ctrl_points(form);
1671 
1672     dt_dev_add_masks_history_item(darktable.develop, module, TRUE);
1673 
1674     // we recreate the form points
1675     dt_masks_gui_form_remove(form, gui, index);
1676     dt_masks_gui_form_create(form, gui, index, module);
1677     gpt->clockwise = _path_is_clockwise(form);
1678     // we save the move
1679     dt_masks_update_image(darktable.develop);
1680 
1681     return 1;
1682   }
1683   else if(gui->point_border_dragging >= 0)
1684   {
1685     gui->point_border_dragging = -1;
1686 
1687     // we save the move
1688     dt_dev_add_masks_history_item(darktable.develop, module, TRUE);
1689     dt_masks_update_image(darktable.develop);
1690     dt_control_queue_redraw_center();
1691     return 1;
1692   }
1693 
1694   return 0;
1695 }
1696 
_path_events_mouse_moved(struct dt_iop_module_t * module,float pzx,float pzy,double pressure,int which,dt_masks_form_t * form,int parentid,dt_masks_form_gui_t * gui,int index)1697 static int _path_events_mouse_moved(struct dt_iop_module_t *module, float pzx, float pzy, double pressure,
1698                                     int which, dt_masks_form_t *form, int parentid,
1699                                     dt_masks_form_gui_t *gui, int index)
1700 {
1701   const dt_dev_zoom_t zoom = dt_control_get_dev_zoom();
1702   const int closeup = dt_control_get_dev_closeup();
1703   const float zoom_scale = dt_dev_get_zoom_scale(darktable.develop, zoom, 1<<closeup, 1);
1704   // centre view will have zoom_scale * backbuf_width pixels, we want the handle offset to scale with DPI:
1705   const float as = DT_PIXEL_APPLY_DPI(5) / zoom_scale;  // transformed to backbuf dimensions
1706   if(!gui) return 0;
1707   dt_masks_form_gui_points_t *gpt = (dt_masks_form_gui_points_t *)g_list_nth_data(gui->points, index);
1708   if(!gpt) return 0;
1709 
1710   if(gui->point_dragging >= 0)
1711   {
1712     const float wd = darktable.develop->preview_pipe->backbuf_width;
1713     const float ht = darktable.develop->preview_pipe->backbuf_height;
1714     float pts[2] = { pzx * wd, pzy * ht };
1715     if(gui->creation && !g_list_shorter_than(form->points, 4))
1716     {
1717       // if we are near the first point, we have to say that the form should be closed
1718       if(pts[0] - gpt->points[2] < as && pts[0] - gpt->points[2] > -as && pts[1] - gpt->points[3] < as
1719          && pts[1] - gpt->points[3] > -as)
1720       {
1721         gui->creation_closing_form = TRUE;
1722       }
1723       else
1724       {
1725         gui->creation_closing_form = FALSE;
1726       }
1727     }
1728 
1729     dt_dev_distort_backtransform(darktable.develop, pts, 1);
1730     dt_masks_point_path_t *bzpt = (dt_masks_point_path_t *)g_list_nth_data(form->points, gui->point_dragging);
1731     pzx = pts[0] / darktable.develop->preview_pipe->iwidth;
1732     pzy = pts[1] / darktable.develop->preview_pipe->iheight;
1733     bzpt->ctrl1[0] += pzx - bzpt->corner[0];
1734     bzpt->ctrl2[0] += pzx - bzpt->corner[0];
1735     bzpt->ctrl1[1] += pzy - bzpt->corner[1];
1736     bzpt->ctrl2[1] += pzy - bzpt->corner[1];
1737     bzpt->corner[0] = pzx;
1738     bzpt->corner[1] = pzy;
1739     _path_init_ctrl_points(form);
1740     // we recreate the form points
1741     dt_masks_gui_form_remove(form, gui, index);
1742     dt_masks_gui_form_create(form, gui, index, module);
1743     dt_control_queue_redraw_center();
1744     return 1;
1745   }
1746   else if(gui->seg_dragging >= 0)
1747   {
1748     // we get point0 new values
1749     const GList *const pt = g_list_nth(form->points, gui->seg_dragging);
1750     const GList *const pt2 = g_list_next_wraparound(pt, form->points);
1751     dt_masks_point_path_t *point = (dt_masks_point_path_t *)pt->data;
1752     dt_masks_point_path_t *point2 = (dt_masks_point_path_t *)pt2->data;
1753     const float wd = darktable.develop->preview_pipe->backbuf_width;
1754     const float ht = darktable.develop->preview_pipe->backbuf_height;
1755     float pts[2] = { pzx * wd + gui->dx, pzy * ht + gui->dy };
1756     dt_dev_distort_backtransform(darktable.develop, pts, 1);
1757     const float dx = pts[0] / darktable.develop->preview_pipe->iwidth - point->corner[0];
1758     const float dy = pts[1] / darktable.develop->preview_pipe->iheight - point->corner[1];
1759 
1760     // we move all points
1761     point->corner[0] += dx;
1762     point->corner[1] += dy;
1763     point->ctrl1[0] += dx;
1764     point->ctrl1[1] += dy;
1765     point->ctrl2[0] += dx;
1766     point->ctrl2[1] += dy;
1767     point2->corner[0] += dx;
1768     point2->corner[1] += dy;
1769     point2->ctrl1[0] += dx;
1770     point2->ctrl1[1] += dy;
1771     point2->ctrl2[0] += dx;
1772     point2->ctrl2[1] += dy;
1773 
1774     _path_init_ctrl_points(form);
1775 
1776     dt_dev_add_masks_history_item(darktable.develop, module, TRUE);
1777 
1778     // we recreate the form points
1779     dt_masks_gui_form_remove(form, gui, index);
1780     dt_masks_gui_form_create(form, gui, index, module);
1781 
1782     dt_control_queue_redraw_center();
1783     return 1;
1784   }
1785   else if(gui->feather_dragging >= 0)
1786   {
1787     const float wd = darktable.develop->preview_pipe->backbuf_width;
1788     const float ht = darktable.develop->preview_pipe->backbuf_height;
1789     float pts[2] = { pzx * wd, pzy * ht };
1790     dt_dev_distort_backtransform(darktable.develop, pts, 1);
1791     dt_masks_point_path_t *point
1792         = (dt_masks_point_path_t *)g_list_nth_data(form->points, gui->feather_dragging);
1793 
1794     int p1x, p1y, p2x, p2y;
1795     _path_feather_to_ctrl(point->corner[0] * darktable.develop->preview_pipe->iwidth,
1796                           point->corner[1] * darktable.develop->preview_pipe->iheight, pts[0], pts[1], &p1x,
1797                           &p1y, &p2x, &p2y, gpt->clockwise);
1798     point->ctrl1[0] = (float)p1x / darktable.develop->preview_pipe->iwidth;
1799     point->ctrl1[1] = (float)p1y / darktable.develop->preview_pipe->iheight;
1800     point->ctrl2[0] = (float)p2x / darktable.develop->preview_pipe->iwidth;
1801     point->ctrl2[1] = (float)p2y / darktable.develop->preview_pipe->iheight;
1802     point->state = DT_MASKS_POINT_STATE_USER;
1803 
1804     _path_init_ctrl_points(form);
1805     // we recreate the form points
1806     dt_masks_gui_form_remove(form, gui, index);
1807     dt_masks_gui_form_create(form, gui, index, module);
1808     dt_control_queue_redraw_center();
1809     return 1;
1810   }
1811   else if(gui->point_border_dragging >= 0)
1812   {
1813     const float wd = darktable.develop->preview_pipe->backbuf_width;
1814     const float ht = darktable.develop->preview_pipe->backbuf_height;
1815 
1816     const int k = gui->point_border_dragging;
1817 
1818     // now we want to know the position reflected on actual corner/border segment
1819     const float a = (gpt->border[k * 6 + 1] - gpt->points[k * 6 + 3])
1820                     / (float)(gpt->border[k * 6] - gpt->points[k * 6 + 2]);
1821     const float b = gpt->points[k * 6 + 3] - a * gpt->points[k * 6 + 2];
1822 
1823     float pts[2] = { (a * pzy * ht + pzx * wd - b * a) / (a * a + 1.0), a * pts[0] + b };
1824 
1825     dt_dev_distort_backtransform(darktable.develop, pts, 1);
1826 
1827     dt_masks_point_path_t *point = (dt_masks_point_path_t *)g_list_nth_data(form->points, k);
1828     const float nx = point->corner[0] * darktable.develop->preview_pipe->iwidth;
1829     const float ny = point->corner[1] * darktable.develop->preview_pipe->iheight;
1830     const float nr = sqrtf((pts[0] - nx) * (pts[0] - nx) + (pts[1] - ny) * (pts[1] - ny));
1831     const float bdr = nr / fminf(darktable.develop->preview_pipe->iwidth, darktable.develop->preview_pipe->iheight);
1832 
1833     point->border[0] = point->border[1] = bdr;
1834 
1835     // we recreate the form points
1836     dt_masks_gui_form_remove(form, gui, index);
1837     dt_masks_gui_form_create(form, gui, index, module);
1838     dt_control_queue_redraw_center();
1839     return 1;
1840   }
1841   else if(gui->form_dragging || gui->source_dragging)
1842   {
1843     const float wd = darktable.develop->preview_pipe->backbuf_width;
1844     const float ht = darktable.develop->preview_pipe->backbuf_height;
1845     float pts[2] = { pzx * wd + gui->dx, pzy * ht + gui->dy };
1846     dt_dev_distort_backtransform(darktable.develop, pts, 1);
1847 
1848     // we move all points
1849     if(gui->form_dragging)
1850     {
1851       dt_masks_point_path_t *point = (dt_masks_point_path_t *)(form->points)->data;
1852       const float dx = pts[0] / darktable.develop->preview_pipe->iwidth - point->corner[0];
1853       const float dy = pts[1] / darktable.develop->preview_pipe->iheight - point->corner[1];
1854       for(GList *points = form->points; points; points = g_list_next(points))
1855       {
1856         point = (dt_masks_point_path_t *)points->data;
1857         point->corner[0] += dx;
1858         point->corner[1] += dy;
1859         point->ctrl1[0] += dx;
1860         point->ctrl1[1] += dy;
1861         point->ctrl2[0] += dx;
1862         point->ctrl2[1] += dy;
1863       }
1864     }
1865     else
1866     {
1867       form->source[0] = pts[0] / darktable.develop->preview_pipe->iwidth;
1868       form->source[1] = pts[1] / darktable.develop->preview_pipe->iheight;
1869     }
1870 
1871     // we recreate the form points
1872     dt_masks_gui_form_remove(form, gui, index);
1873     dt_masks_gui_form_create(form, gui, index, module);
1874     dt_control_queue_redraw_center();
1875     return 1;
1876   }
1877 
1878   gui->form_selected = FALSE;
1879   gui->border_selected = FALSE;
1880   gui->source_selected = FALSE;
1881   gui->feather_selected = -1;
1882   gui->point_selected = -1;
1883   gui->seg_selected = -1;
1884   gui->point_border_selected = -1;
1885   // are we near a point or feather ?
1886   const guint nb = g_list_length(form->points);
1887 
1888   pzx *= darktable.develop->preview_pipe->backbuf_width,
1889       pzy *= darktable.develop->preview_pipe->backbuf_height;
1890 
1891   if((gui->group_selected == index) && gui->point_edited >= 0)
1892   {
1893     const int k = gui->point_edited;
1894     // we only select feather if the point is not "sharp"
1895     if(gpt->points[k * 6 + 2] != gpt->points[k * 6 + 4] && gpt->points[k * 6 + 3] != gpt->points[k * 6 + 5])
1896     {
1897       int ffx, ffy;
1898       _path_ctrl2_to_feather(gpt->points[k * 6 + 2], gpt->points[k * 6 + 3], gpt->points[k * 6 + 4],
1899                              gpt->points[k * 6 + 5], &ffx, &ffy, gpt->clockwise);
1900       if(pzx - ffx > -as && pzx - ffx < as && pzy - ffy > -as && pzy - ffy < as)
1901       {
1902         gui->feather_selected = k;
1903         dt_control_queue_redraw_center();
1904         return 1;
1905       }
1906     }
1907     // corner ??
1908     if(pzx - gpt->points[k * 6 + 2] > -as && pzx - gpt->points[k * 6 + 2] < as
1909        && pzy - gpt->points[k * 6 + 3] > -as && pzy - gpt->points[k * 6 + 3] < as)
1910     {
1911       gui->point_selected = k;
1912       dt_control_queue_redraw_center();
1913       return 1;
1914     }
1915   }
1916 
1917   for(int k = 0; k < nb; k++)
1918   {
1919     // corner ??
1920     if(pzx - gpt->points[k * 6 + 2] > -as && pzx - gpt->points[k * 6 + 2] < as
1921        && pzy - gpt->points[k * 6 + 3] > -as && pzy - gpt->points[k * 6 + 3] < as)
1922     {
1923       gui->point_selected = k;
1924       dt_control_queue_redraw_center();
1925       return 1;
1926     }
1927 
1928     // border corner ??
1929     if(pzx - gpt->border[k * 6] > -as && pzx - gpt->border[k * 6] < as && pzy - gpt->border[k * 6 + 1] > -as
1930        && pzy - gpt->border[k * 6 + 1] < as)
1931     {
1932       gui->point_border_selected = k;
1933       dt_control_queue_redraw_center();
1934       return 1;
1935     }
1936   }
1937 
1938   // are we inside the form or the borders or near a segment ???
1939   int in = 0, inb = 0, near = 0, ins = 0;
1940   float dist = 0;
1941   _path_get_distance(pzx, (int)pzy, as, gui, index, nb, &in, &inb, &near, &ins, &dist);
1942   gui->seg_selected = near;
1943   if(near < 0)
1944   {
1945     if(ins)
1946     {
1947       gui->form_selected = TRUE;
1948       gui->source_selected = TRUE;
1949     }
1950     else if(inb)
1951     {
1952       gui->form_selected = TRUE;
1953       gui->border_selected = TRUE;
1954     }
1955     else if(in)
1956     {
1957       gui->form_selected = TRUE;
1958     }
1959   }
1960   dt_control_queue_redraw_center();
1961   if(!gui->form_selected && !gui->border_selected && gui->seg_selected < 0) return 0;
1962   if(gui->edit_mode != DT_MASKS_EDIT_FULL) return 0;
1963   return 1;
1964 }
1965 
_path_events_post_expose(cairo_t * cr,float zoom_scale,dt_masks_form_gui_t * gui,int index,int nb)1966 static void _path_events_post_expose(cairo_t *cr, float zoom_scale, dt_masks_form_gui_t *gui, int index, int nb)
1967 {
1968   double dashed[] = { 4.0, 4.0 };
1969   dashed[0] /= zoom_scale;
1970   dashed[1] /= zoom_scale;
1971   const int len = sizeof(dashed) / sizeof(dashed[0]);
1972   if(!gui) return;
1973   dt_masks_form_gui_points_t *gpt = (dt_masks_form_gui_points_t *)g_list_nth_data(gui->points, index);
1974   if(!gpt) return;
1975 
1976   // draw path
1977   if(gpt->points_count > nb * 3 + 6)
1978   {
1979     cairo_set_dash(cr, dashed, 0, 0);
1980 
1981     cairo_move_to(cr, gpt->points[nb * 6], gpt->points[nb * 6 + 1]);
1982     int seg = 1, seg2 = 0;
1983     for(int i = nb * 3; i < gpt->points_count; i++)
1984     {
1985       cairo_line_to(cr, gpt->points[i * 2], gpt->points[i * 2 + 1]);
1986       // we decide to highlight the form segment by segment
1987       if(gpt->points[i * 2 + 1] == gpt->points[seg * 6 + 3] && gpt->points[i * 2] == gpt->points[seg * 6 + 2])
1988       {
1989         // this is the end of the last segment, so we have to draw it
1990         if((gui->group_selected == index)
1991            && (gui->form_selected || gui->form_dragging || gui->seg_selected == seg2))
1992           cairo_set_line_width(cr, 5.0 / zoom_scale);
1993         else
1994           cairo_set_line_width(cr, 3.0 / zoom_scale);
1995         dt_draw_set_color_overlay(cr, 0.3, 0.8);
1996         cairo_stroke_preserve(cr);
1997         if((gui->group_selected == index)
1998            && (gui->form_selected || gui->form_dragging || gui->seg_selected == seg2))
1999           cairo_set_line_width(cr, 2.0 / zoom_scale);
2000         else
2001           cairo_set_line_width(cr, 1.0 / zoom_scale);
2002         dt_draw_set_color_overlay(cr, 0.8, 0.8);
2003         cairo_stroke(cr);
2004         // and we update the segment number
2005         seg = (seg + 1) % nb;
2006         seg2++;
2007         cairo_move_to(cr, gpt->points[i * 2], gpt->points[i * 2 + 1]);
2008       }
2009     }
2010   }
2011 
2012   // draw corners
2013   float anchor_size = 0.0f;
2014   if(gui->group_selected == index && gpt->points_count > nb * 3 + 6)
2015   {
2016     for(int k = 0; k < nb; k++)
2017     {
2018       if(k == gui->point_dragging || k == gui->point_selected)
2019       {
2020         anchor_size = 7.0f / zoom_scale;
2021       }
2022       else
2023       {
2024         anchor_size = 5.0f / zoom_scale;
2025       }
2026       dt_draw_set_color_overlay(cr, 0.8, 0.8);
2027       cairo_rectangle(cr, gpt->points[k * 6 + 2] - (anchor_size * 0.5),
2028                       gpt->points[k * 6 + 3] - (anchor_size * 0.5), anchor_size, anchor_size);
2029       cairo_fill_preserve(cr);
2030 
2031       if((gui->group_selected == index) && (k == gui->point_dragging || k == gui->point_selected))
2032         cairo_set_line_width(cr, 2.0 / zoom_scale);
2033       else if((gui->group_selected == index)
2034               && ((k == 0 || k == nb) && gui->creation && gui->creation_closing_form))
2035         cairo_set_line_width(cr, 2.0 / zoom_scale);
2036       else
2037         cairo_set_line_width(cr, 1.0 / zoom_scale);
2038       dt_draw_set_color_overlay(cr, 0.3, 0.8);
2039       cairo_stroke(cr);
2040     }
2041   }
2042 
2043   // draw feathers
2044   if((gui->group_selected == index) && gui->point_edited >= 0)
2045   {
2046     const int k = gui->point_edited;
2047     // uncomment this part if you want to see "real" control points
2048     /*cairo_move_to(cr, gui->points[k*6+2],gui->points[k*6+3]);
2049     cairo_line_to(cr, gui->points[k*6],gui->points[k*6+1]);
2050     cairo_stroke(cr);
2051     cairo_move_to(cr, gui->points[k*6+2],gui->points[k*6+3]);
2052     cairo_line_to(cr, gui->points[k*6+4],gui->points[k*6+5]);
2053     cairo_stroke(cr);*/
2054     int ffx = 0, ffy = 0;
2055     _path_ctrl2_to_feather(gpt->points[k * 6 + 2], gpt->points[k * 6 + 3], gpt->points[k * 6 + 4],
2056                            gpt->points[k * 6 + 5], &ffx, &ffy, gpt->clockwise);
2057     cairo_move_to(cr, gpt->points[k * 6 + 2], gpt->points[k * 6 + 3]);
2058     cairo_line_to(cr, ffx, ffy);
2059     cairo_set_line_width(cr, 1.5 / zoom_scale);
2060     dt_draw_set_color_overlay(cr, 0.3, 0.8);
2061     cairo_stroke_preserve(cr);
2062     cairo_set_line_width(cr, 0.75 / zoom_scale);
2063     dt_draw_set_color_overlay(cr, 0.8, 0.8);
2064     cairo_stroke(cr);
2065 
2066     if((gui->group_selected == index) && (k == gui->feather_dragging || k == gui->feather_selected))
2067       cairo_arc(cr, ffx, ffy, 3.0f / zoom_scale, 0, 2.0 * M_PI);
2068     else
2069       cairo_arc(cr, ffx, ffy, 1.5f / zoom_scale, 0, 2.0 * M_PI);
2070     dt_draw_set_color_overlay(cr, 0.8, 0.8);
2071     cairo_fill_preserve(cr);
2072 
2073     cairo_set_line_width(cr, 1.0 / zoom_scale);
2074     dt_draw_set_color_overlay(cr, 0.3, 0.8);
2075     cairo_stroke(cr);
2076   }
2077 
2078   // draw border and corners
2079   if((gui->group_selected == index) && gpt->border_count > nb * 3 + 6)
2080   {
2081     int dep = 1;
2082     for(int i = nb * 3; i < gpt->border_count; i++)
2083     {
2084       if(isnan(gpt->border[i * 2]))
2085       {
2086         if(isnan(gpt->border[i * 2 + 1])) break;
2087         i = gpt->border[i * 2 + 1] - 1;
2088         continue;
2089       }
2090       if(dep)
2091       {
2092         cairo_move_to(cr, gpt->border[i * 2], gpt->border[i * 2 + 1]);
2093         dep = 0;
2094       }
2095       else
2096         cairo_line_to(cr, gpt->border[i * 2], gpt->border[i * 2 + 1]);
2097     }
2098     // we execute the drawing
2099     if(gui->border_selected)
2100       cairo_set_line_width(cr, 2.0 / zoom_scale);
2101     else
2102       cairo_set_line_width(cr, 1.0 / zoom_scale);
2103     dt_draw_set_color_overlay(cr, 0.3, 0.8);
2104     cairo_set_dash(cr, dashed, len, 0);
2105     cairo_stroke_preserve(cr);
2106     if(gui->border_selected)
2107       cairo_set_line_width(cr, 2.0 / zoom_scale);
2108     else
2109       cairo_set_line_width(cr, 1.0 / zoom_scale);
2110     dt_draw_set_color_overlay(cr, 0.8, 0.8);
2111     cairo_set_dash(cr, dashed, len, 4);
2112     cairo_stroke(cr);
2113 
2114     // we draw the path segment by segment
2115     for(int k = 0; k < nb; k++)
2116     {
2117       // draw the point
2118       if(gui->point_border_selected == k)
2119       {
2120         anchor_size = 7.0f / zoom_scale;
2121       }
2122       else
2123       {
2124         anchor_size = 5.0f / zoom_scale;
2125       }
2126       dt_draw_set_color_overlay(cr, 0.8, 0.8);
2127       cairo_rectangle(cr, gpt->border[k * 6] - (anchor_size * 0.5), gpt->border[k * 6 + 1] - (anchor_size * 0.5),
2128                       anchor_size, anchor_size);
2129       cairo_fill_preserve(cr);
2130 
2131       if(gui->point_border_selected == k)
2132         cairo_set_line_width(cr, 2.0 / zoom_scale);
2133       else
2134         cairo_set_line_width(cr, 1.0 / zoom_scale);
2135       dt_draw_set_color_overlay(cr, 0.3, 0.8);
2136       cairo_set_dash(cr, dashed, 0, 0);
2137       cairo_stroke(cr);
2138     }
2139   }
2140 
2141   // draw a cross where the source will be created
2142   if(gui->creation && darktable.develop->form_visible && (darktable.develop->form_visible->type & DT_MASKS_CLONE))
2143   {
2144     const int k = nb - 1;
2145     if((k * 6 + 2) >= 0)
2146     {
2147       float x = 0.f, y = 0.f;
2148       dt_masks_calculate_source_pos_value(gui, DT_MASKS_PATH, gpt->points[2], gpt->points[3],
2149                                           gpt->points[k * 6 + 2], gpt->points[k * 6 + 3], &x, &y, TRUE);
2150       dt_masks_draw_clone_source_pos(cr, zoom_scale, x, y);
2151     }
2152     else
2153     {
2154       float xpos, ypos;
2155       if((gui->posx == -1.f && gui->posy == -1.f) || gui->mouse_leaved_center)
2156       {
2157         xpos = (.5f + dt_control_get_dev_zoom_x()) * darktable.develop->preview_pipe->backbuf_width;
2158         ypos = (.5f + dt_control_get_dev_zoom_y()) * darktable.develop->preview_pipe->backbuf_height;
2159       }
2160       else
2161       {
2162         xpos = gui->posx;
2163         ypos = gui->posy;
2164       }
2165 
2166       float x = 0.0f, y = 0.0f;
2167       dt_masks_calculate_source_pos_value(gui, DT_MASKS_PATH, xpos, ypos, xpos, ypos, &x, &y, FALSE);
2168       dt_masks_draw_clone_source_pos(cr, zoom_scale, x, y);
2169     }
2170   }
2171 
2172   // draw the source if needed
2173   if(!gui->creation && gpt->source_count > nb * 3 + 6)
2174   {
2175     // we draw the line between source and dest
2176     cairo_move_to(cr, gpt->source[2], gpt->source[3]);
2177     cairo_line_to(cr, gpt->points[2], gpt->points[3]);
2178     cairo_set_dash(cr, dashed, 0, 0);
2179     if((gui->group_selected == index) && (gui->form_selected || gui->form_dragging))
2180       cairo_set_line_width(cr, 2.5 / zoom_scale);
2181     else
2182       cairo_set_line_width(cr, 1.5 / zoom_scale);
2183     dt_draw_set_color_overlay(cr, 0.3, 0.8);
2184     cairo_stroke_preserve(cr);
2185     if((gui->group_selected == index) && (gui->form_selected || gui->form_dragging))
2186       cairo_set_line_width(cr, 1.0 / zoom_scale);
2187     else
2188       cairo_set_line_width(cr, 0.5 / zoom_scale);
2189     dt_draw_set_color_overlay(cr, 0.8, 0.8);
2190     cairo_stroke(cr);
2191 
2192     // we draw the source
2193     cairo_set_dash(cr, dashed, 0, 0);
2194     if((gui->group_selected == index) && (gui->form_selected || gui->form_dragging))
2195       cairo_set_line_width(cr, 2.5 / zoom_scale);
2196     else
2197       cairo_set_line_width(cr, 1.5 / zoom_scale);
2198     dt_draw_set_color_overlay(cr, 0.3, 0.8);
2199     cairo_move_to(cr, gpt->source[nb * 6], gpt->source[nb * 6 + 1]);
2200     for(int i = nb * 3; i < gpt->source_count; i++) cairo_line_to(cr, gpt->source[i * 2], gpt->source[i * 2 + 1]);
2201     cairo_line_to(cr, gpt->source[nb * 6], gpt->source[nb * 6 + 1]);
2202     cairo_stroke_preserve(cr);
2203     if((gui->group_selected == index) && (gui->form_selected || gui->form_dragging))
2204       cairo_set_line_width(cr, 1.0 / zoom_scale);
2205     else
2206       cairo_set_line_width(cr, 0.5 / zoom_scale);
2207     dt_draw_set_color_overlay(cr, 0.8, 0.8);
2208     cairo_stroke(cr);
2209   }
2210 }
2211 
_path_bounding_box_raw(const float * const points,const float * border,const int nb_corner,const int num_points,int num_borders,float * x_min,float * x_max,float * y_min,float * y_max)2212 static void _path_bounding_box_raw(const float *const points, const float *border, const int nb_corner, const int num_points, int num_borders,
2213                                    float *x_min, float *x_max, float *y_min, float *y_max)
2214 {
2215   float xmin, xmax, ymin, ymax;
2216   xmin = ymin = FLT_MAX;
2217   xmax = ymax = FLT_MIN;
2218   for(int i = nb_corner * 3; i < num_borders; i++)
2219   {
2220     // we look at the borders
2221     const float xx = border[i * 2];
2222     const float yy = border[i * 2 + 1];
2223     if(isnan(xx))
2224     {
2225       if(isnan(yy)) break; // that means we have to skip the end of the border path
2226       i = yy - 1;
2227       continue;
2228     }
2229     xmin = fminf(xx, xmin);
2230     xmax = fmaxf(xx, xmax);
2231     ymin = fminf(yy, ymin);
2232     ymax = fmaxf(yy, ymax);
2233   }
2234   for(int i = nb_corner * 3; i < num_points; i++)
2235   {
2236     // we look at the path too
2237     const float xx = points[i * 2];
2238     const float yy = points[i * 2 + 1];
2239     xmin = fminf(xx, xmin);
2240     xmax = fmaxf(xx, xmax);
2241     ymin = fminf(yy, ymin);
2242     ymax = fmaxf(yy, ymax);
2243   }
2244 
2245   *x_min = xmin;
2246   *x_max = xmax;
2247   *y_min = ymin;
2248   *y_max = ymax;
2249 }
2250 
_path_bounding_box(const float * const points,const float * border,const int nb_corner,const int num_points,int num_borders,int * width,int * height,int * posx,int * posy)2251 static void _path_bounding_box(const float *const points, const float *border, const int nb_corner, const int num_points, int num_borders,
2252                                int *width, int *height, int *posx, int *posy)
2253 {
2254   // now we want to find the area, so we search min/max points
2255   float xmin, xmax, ymin, ymax;
2256   _path_bounding_box_raw(points, border, nb_corner, num_points, num_borders, &xmin, &xmax, &ymin, &ymax);
2257   *height = ymax - ymin + 4;
2258   *width = xmax - xmin + 4;
2259   *posx = xmin - 2;
2260   *posy = ymin - 2;
2261 }
2262 
_get_area(const dt_iop_module_t * const module,const dt_dev_pixelpipe_iop_t * const piece,dt_masks_form_t * const form,int * width,int * height,int * posx,int * posy,int get_source)2263 static int _get_area(const dt_iop_module_t *const module, const dt_dev_pixelpipe_iop_t *const piece,
2264                      dt_masks_form_t *const form, int *width, int *height, int *posx, int *posy, int get_source)
2265 {
2266   if(!module) return 0;
2267   // we get buffers for all points
2268   float *points = NULL, *border = NULL;
2269   int points_count = 0, border_count = 0;
2270   if(!_path_get_pts_border(module->dev, form, module->iop_order, DT_DEV_TRANSFORM_DIR_BACK_INCL, piece->pipe, &points, &points_count,
2271                            &border, &border_count, get_source))
2272   {
2273     dt_free_align(points);
2274     dt_free_align(border);
2275     return 0;
2276   }
2277 
2278   const guint nb_corner = g_list_length(form->points);
2279   _path_bounding_box(points, border, nb_corner, points_count, border_count, width, height, posx, posy);
2280 
2281   dt_free_align(points);
2282   dt_free_align(border);
2283   return 1;
2284 }
2285 
_path_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)2286 static int _path_get_source_area(dt_iop_module_t *module, dt_dev_pixelpipe_iop_t *piece,
2287                                  dt_masks_form_t *form, int *width, int *height, int *posx, int *posy)
2288 {
2289   return _get_area(module, piece, form, width, height, posx, posy, 1);
2290 }
2291 
_path_get_area(const dt_iop_module_t * const module,const dt_dev_pixelpipe_iop_t * const piece,dt_masks_form_t * const form,int * width,int * height,int * posx,int * posy)2292 static int _path_get_area(const dt_iop_module_t *const module, const dt_dev_pixelpipe_iop_t *const piece,
2293                           dt_masks_form_t *const form,
2294                           int *width, int *height, int *posx, int *posy)
2295 {
2296   return _get_area(module, piece, form,width, height, posx, posy, 0);
2297 }
2298 
2299 /** we write a falloff segment */
_path_falloff(float ** buffer,int * p0,int * p1,int posx,int posy,int bw)2300 static void _path_falloff(float **buffer, int *p0, int *p1, int posx, int posy, int bw)
2301 {
2302   // segment length
2303   int l = sqrt((p1[0] - p0[0]) * (p1[0] - p0[0]) + (p1[1] - p0[1]) * (p1[1] - p0[1])) + 1;
2304 
2305   const float lx = p1[0] - p0[0];
2306   const float ly = p1[1] - p0[1];
2307 
2308   for(int i = 0; i < l; i++)
2309   {
2310     // position
2311     const int x = (int)((float)i * lx / (float)l) + p0[0] - posx;
2312     const int y = (int)((float)i * ly / (float)l) + p0[1] - posy;
2313     const float op = 1.0 - (float)i / (float)l;
2314     (*buffer)[y * bw + x] = fmaxf((*buffer)[y * bw + x], op);
2315     if(x > 0)
2316       (*buffer)[y * bw + x - 1]
2317           = fmaxf((*buffer)[y * bw + x - 1], op); // this one is to avoid gap due to int rounding
2318     if(y > 0)
2319       (*buffer)[(y - 1) * bw + x]
2320           = fmaxf((*buffer)[(y - 1) * bw + x], op); // this one is to avoid gap due to int rounding
2321   }
2322 }
2323 
_path_get_mask(const dt_iop_module_t * const module,const dt_dev_pixelpipe_iop_t * const piece,dt_masks_form_t * const form,float ** buffer,int * width,int * height,int * posx,int * posy)2324 static int _path_get_mask(const dt_iop_module_t *const module, const dt_dev_pixelpipe_iop_t *const piece,
2325                           dt_masks_form_t *const form,
2326                           float **buffer, int *width, int *height, int *posx, int *posy)
2327 {
2328   if(!module) return 0;
2329   double start = 0.0;
2330   double start2 = 0.0;
2331 
2332   if(darktable.unmuted & DT_DEBUG_PERF) start = dt_get_wtime();
2333 
2334   // we get buffers for all points
2335   float *points = NULL, *border = NULL;
2336   int points_count, border_count;
2337   if(!_path_get_pts_border(module->dev, form, module->iop_order, DT_DEV_TRANSFORM_DIR_BACK_INCL, piece->pipe, &points, &points_count,
2338                            &border, &border_count, 0))
2339   {
2340     dt_free_align(points);
2341     dt_free_align(border);
2342     return 0;
2343   }
2344 
2345   if(darktable.unmuted & DT_DEBUG_PERF)
2346   {
2347     dt_print(DT_DEBUG_MASKS, "[masks %s] path points took %0.04f sec\n", form->name, dt_get_wtime() - start);
2348     start = start2 = dt_get_wtime();
2349   }
2350 
2351   // now we want to find the area, so we search min/max points
2352   const guint nb_corner = g_list_length(form->points);
2353   _path_bounding_box(points, border, nb_corner, points_count, border_count, width, height, posx, posy);
2354 
2355   const int hb = *height;
2356   const int wb = *width;
2357 
2358   if(darktable.unmuted & DT_DEBUG_PERF)
2359   {
2360     dt_print(DT_DEBUG_MASKS, "[masks %s] path_fill min max took %0.04f sec\n", form->name,
2361              dt_get_wtime() - start2);
2362     start2 = dt_get_wtime();
2363   }
2364 
2365   // we allocate the buffer
2366   const size_t bufsize = (size_t)(*width) * (*height);
2367   *buffer = dt_alloc_align_float(bufsize);
2368   if(*buffer == NULL)
2369   {
2370     dt_free_align(points);
2371     dt_free_align(border);
2372     return 0;
2373   }
2374   memset(*buffer, 0, sizeof(float) * bufsize);
2375 
2376   // we write all the point around the path into the buffer
2377   const int nbp = border_count;
2378   if(nbp > 2)
2379   {
2380     int lastx = (int)points[(nbp - 1) * 2];
2381     int lasty = (int)points[(nbp - 1) * 2 + 1];
2382     int lasty2 = (int)points[(nbp - 2) * 2 + 1];
2383 
2384     int just_change_dir = 0;
2385     for(int ii = nb_corner * 3; ii < 2 * nbp - nb_corner * 3; ii++)
2386     {
2387       // we are writing more than 1 loop in the case the dir in y change
2388       // exactly at start/end point
2389       int i = ii;
2390       if(ii >= nbp) i = (ii - nb_corner * 3) % (nbp - nb_corner * 3) + nb_corner * 3;
2391       const int xx = (int)points[i * 2];
2392       const int yy = (int)points[i * 2 + 1];
2393 
2394       // we don't store the point if it has the same y value as the last one
2395       if(yy == lasty) continue;
2396 
2397       // we want to be sure that there is no y jump
2398       if(yy - lasty > 1 || yy - lasty < -1)
2399       {
2400         if(yy < lasty)
2401         {
2402           for(int j = yy + 1; j < lasty; j++)
2403           {
2404             const int nx = (j - yy) * (lastx - xx) / (float)(lasty - yy) + xx;
2405             const size_t idx = (size_t)(j - (*posy)) * (*width) + nx - (*posx);
2406             assert(idx < bufsize);
2407             (*buffer)[idx] = 1.0f;
2408           }
2409           lasty2 = yy + 2;
2410           lasty = yy + 1;
2411         }
2412         else
2413         {
2414           for(int j = lasty + 1; j < yy; j++)
2415           {
2416             const int nx = (j - lasty) * (xx - lastx) / (float)(yy - lasty) + lastx;
2417             const size_t idx = (size_t)(j - (*posy)) * (*width) + nx - (*posx);
2418             assert(idx < bufsize);
2419             (*buffer)[idx] = 1.0f;
2420           }
2421           lasty2 = yy - 2;
2422           lasty = yy - 1;
2423         }
2424       }
2425       // if we change the direction of the path (in y), then we add a extra point
2426       if((lasty - lasty2) * (lasty - yy) > 0)
2427       {
2428         const size_t idx = (size_t)(lasty - (*posy)) * (*width) + lastx + 1 - (*posx);
2429         assert(idx < bufsize);
2430         (*buffer)[idx] = 1.0f;
2431         just_change_dir = 1;
2432       }
2433       // we add the point
2434       if(just_change_dir && ii == i)
2435       {
2436         // if we have changed the direction, we have to be careful that point can be at the same place
2437         // as the previous one, especially on sharp edges
2438         const size_t idx = (size_t)(yy - (*posy)) * (*width) + xx - (*posx);
2439         assert(idx < bufsize);
2440         float v = (*buffer)[idx];
2441         if(v > 0.0)
2442         {
2443           if(xx - (*posx) > 0)
2444           {
2445             const size_t idx_ = (size_t)(yy - (*posy)) * (*width) + xx - 1 - (*posx);
2446             assert(idx_ < bufsize);
2447             (*buffer)[idx_] = 1.0f;
2448           }
2449           else if(xx - (*posx) < (*width) - 1)
2450           {
2451             const size_t idx_ = (size_t)(yy - (*posy)) * (*width) + xx + 1 - (*posx);
2452             assert(idx_ < bufsize);
2453             (*buffer)[idx_] = 1.0f;
2454           }
2455         }
2456         else
2457         {
2458           const size_t idx_ = (size_t)(yy - (*posy)) * (*width) + xx - (*posx);
2459           assert(idx_ < bufsize);
2460           (*buffer)[idx_] = 1.0f;
2461           just_change_dir = 0;
2462         }
2463       }
2464       else
2465       {
2466         const size_t idx_ = (size_t)(yy - (*posy)) * (*width) + xx - (*posx);
2467         assert(idx_ < bufsize);
2468         (*buffer)[idx_] = 1.0f;
2469       }
2470       // we change last values
2471       lasty2 = lasty;
2472       lasty = yy;
2473       lastx = xx;
2474       if(ii != i) break;
2475     }
2476   }
2477   if(darktable.unmuted & DT_DEBUG_PERF)
2478   {
2479     dt_print(DT_DEBUG_MASKS, "[masks %s] path_fill draw path took %0.04f sec\n", form->name,
2480              dt_get_wtime() - start2);
2481     start2 = dt_get_wtime();
2482   }
2483 
2484   for(int yy = 0; yy < hb; yy++)
2485   {
2486     int state = 0;
2487     for(int xx = 0; xx < wb; xx++)
2488     {
2489       float v = (*buffer)[yy * wb + xx];
2490       if(v == 1.0f) state = !state;
2491       if(state) (*buffer)[yy * wb + xx] = 1.0f;
2492     }
2493   }
2494 
2495   if(darktable.unmuted & DT_DEBUG_PERF)
2496   {
2497     dt_print(DT_DEBUG_MASKS, "[masks %s] path_fill fill plain took %0.04f sec\n", form->name,
2498              dt_get_wtime() - start2);
2499     start2 = dt_get_wtime();
2500   }
2501 
2502   // now we fill the falloff
2503   int p0[2] = { 0 }, p1[2] = { 0 };
2504   float pf1[2] = { 0.0f };
2505   int last0[2] = { -100, -100 }, last1[2] = { -100, -100 };
2506   int next = 0;
2507   for(int i = nb_corner * 3; i < border_count; i++)
2508   {
2509     p0[0] = points[i * 2], p0[1] = points[i * 2 + 1];
2510     if(next > 0)
2511       p1[0] = pf1[0] = border[next * 2], p1[1] = pf1[1] = border[next * 2 + 1];
2512     else
2513       p1[0] = pf1[0] = border[i * 2], p1[1] = pf1[1] = border[i * 2 + 1];
2514 
2515     // now we check p1 value to know if we have to skip a part
2516     if(next == i) next = 0;
2517     while(isnan(pf1[0]))
2518     {
2519       if(isnan(pf1[1]))
2520         next = i - 1;
2521       else
2522         next = p1[1];
2523       p1[0] = pf1[0] = border[next * 2], p1[1] = pf1[1] = border[next * 2 + 1];
2524     }
2525 
2526     // and we draw the falloff
2527     if(last0[0] != p0[0] || last0[1] != p0[1] || last1[0] != p1[0] || last1[1] != p1[1])
2528     {
2529       _path_falloff(buffer, p0, p1, *posx, *posy, *width);
2530       last0[0] = p0[0], last0[1] = p0[1];
2531       last1[0] = p1[0], last1[1] = p1[1];
2532     }
2533   }
2534 
2535   if(darktable.unmuted & DT_DEBUG_PERF)
2536     dt_print(DT_DEBUG_MASKS, "[masks %s] path_fill fill falloff took %0.04f sec\n", form->name,
2537              dt_get_wtime() - start2);
2538 
2539   dt_free_align(points);
2540   dt_free_align(border);
2541 
2542   if(darktable.unmuted & DT_DEBUG_PERF)
2543     dt_print(DT_DEBUG_MASKS, "[masks %s] path fill buffer took %0.04f sec\n", form->name,
2544              dt_get_wtime() - start);
2545 
2546   return 1;
2547 }
2548 
2549 
2550 /** crop path to roi given by xmin, xmax, ymin, ymax. path segments outside of roi are replaced by
2551     nodes lying on roi borders. */
_path_crop_to_roi(float * path,const int point_count,float xmin,float xmax,float ymin,float ymax)2552 static int _path_crop_to_roi(float *path, const int point_count, float xmin, float xmax, float ymin,
2553                              float ymax)
2554 {
2555   int point_start = -1;
2556   int l = -1, r = -1;
2557 
2558 
2559   // first try to find a node clearly inside roi
2560   for(int k = 0; k < point_count; k++)
2561   {
2562     float x = path[2 * k];
2563     float y = path[2 * k + 1];
2564 
2565     if(x >= xmin + 1 && y >= ymin + 1 && x <= xmax - 1 && y <= ymax - 1)
2566     {
2567       point_start = k;
2568       break;
2569     }
2570   }
2571 
2572   // printf("crop to xmin %f, xmax %f, ymin %f, ymax %f - start %d (%f, %f)\n", xmin, xmax, ymin, ymax,
2573   // point_start, path[2*point_start], path[2*point_start+1]);
2574 
2575   if(point_start < 0) return 0; // no point means roi lies completely within path
2576 
2577   // find the crossing points with xmin and replace segment by nodes on border
2578   for(int k = 0; k < point_count; k++)
2579   {
2580     int kk = (k + point_start) % point_count;
2581 
2582     if(l < 0 && path[2 * kk] < xmin) l = k;       // where we leave roi
2583     if(l >= 0 && path[2 * kk] >= xmin) r = k - 1; // where we re-enter roi
2584 
2585     // replace that segment
2586     if(l >= 0 && r >= 0)
2587     {
2588       int count = r - l + 1;
2589       int ll = (l - 1 + point_start) % point_count;
2590       int rr = (r + 1 + point_start) % point_count;
2591       float delta_y = (count == 1) ? 0 : (path[2 * rr + 1] - path[2 * ll + 1]) / (count - 1);
2592       float start_y = path[2 * ll + 1];
2593 
2594       for(int n = 0; n < count; n++)
2595       {
2596         int nn = (n + l + point_start) % point_count;
2597         path[2 * nn] = xmin;
2598         path[2 * nn + 1] = start_y + n * delta_y;
2599       }
2600 
2601       l = r = -1;
2602     }
2603   }
2604 
2605   // find the crossing points with xmax and replace segment by nodes on border
2606   for(int k = 0; k < point_count; k++)
2607   {
2608     int kk = (k + point_start) % point_count;
2609 
2610     if(l < 0 && path[2 * kk] > xmax) l = k;       // where we leave roi
2611     if(l >= 0 && path[2 * kk] <= xmax) r = k - 1; // where we re-enter roi
2612 
2613     // replace that segment
2614     if(l >= 0 && r >= 0)
2615     {
2616       int count = r - l + 1;
2617       int ll = (l - 1 + point_start) % point_count;
2618       int rr = (r + 1 + point_start) % point_count;
2619       float delta_y = (count == 1) ? 0 : (path[2 * rr + 1] - path[2 * ll + 1]) / (count - 1);
2620       float start_y = path[2 * ll + 1];
2621 
2622       for(int n = 0; n < count; n++)
2623       {
2624         int nn = (n + l + point_start) % point_count;
2625         path[2 * nn] = xmax;
2626         path[2 * nn + 1] = start_y + n * delta_y;
2627       }
2628 
2629       l = r = -1;
2630     }
2631   }
2632 
2633   // find the crossing points with ymin and replace segment by nodes on border
2634   for(int k = 0; k < point_count; k++)
2635   {
2636     int kk = (k + point_start) % point_count;
2637 
2638     if(l < 0 && path[2 * kk + 1] < ymin) l = k;       // where we leave roi
2639     if(l >= 0 && path[2 * kk + 1] >= ymin) r = k - 1; // where we re-enter roi
2640 
2641     // replace that segment
2642     if(l >= 0 && r >= 0)
2643     {
2644       int count = r - l + 1;
2645       int ll = (l - 1 + point_start) % point_count;
2646       int rr = (r + 1 + point_start) % point_count;
2647       float delta_x = (count == 1) ? 0 : (path[2 * rr] - path[2 * ll]) / (count - 1);
2648       float start_x = path[2 * ll];
2649 
2650       for(int n = 0; n < count; n++)
2651       {
2652         int nn = (n + l + point_start) % point_count;
2653         path[2 * nn] = start_x + n * delta_x;
2654         path[2 * nn + 1] = ymin;
2655       }
2656 
2657       l = r = -1;
2658     }
2659   }
2660 
2661   // find the crossing points with ymax and replace segment by nodes on border
2662   for(int k = 0; k < point_count; k++)
2663   {
2664     int kk = (k + point_start) % point_count;
2665 
2666     if(l < 0 && path[2 * kk + 1] > ymax) l = k;       // where we leave roi
2667     if(l >= 0 && path[2 * kk + 1] <= ymax) r = k - 1; // where we re-enter roi
2668 
2669     // replace that segment
2670     if(l >= 0 && r >= 0)
2671     {
2672       int count = r - l + 1;
2673       int ll = (l - 1 + point_start) % point_count;
2674       int rr = (r + 1 + point_start) % point_count;
2675       float delta_x = (count == 1) ? 0 : (path[2 * rr] - path[2 * ll]) / (count - 1);
2676       float start_x = path[2 * ll];
2677 
2678       for(int n = 0; n < count; n++)
2679       {
2680         int nn = (n + l + point_start) % point_count;
2681         path[2 * nn] = start_x + n * delta_x;
2682         path[2 * nn + 1] = ymax;
2683       }
2684 
2685       l = r = -1;
2686     }
2687   }
2688   return 1;
2689 }
2690 
2691 /** we write a falloff segment respecting limits of buffer */
_path_falloff_roi(float * buffer,int * p0,int * p1,int bw,int bh)2692 static void _path_falloff_roi(float *buffer, int *p0, int *p1, int bw, int bh)
2693 {
2694   // segment length
2695   const int l = sqrt((p1[0] - p0[0]) * (p1[0] - p0[0]) + (p1[1] - p0[1]) * (p1[1] - p0[1])) + 1;
2696 
2697   const float lx = p1[0] - p0[0];
2698   const float ly = p1[1] - p0[1];
2699 
2700   const int dx = lx < 0 ? -1 : 1;
2701   const int dy = ly < 0 ? -1 : 1;
2702   const int dpy = dy * bw;
2703 
2704   for(int i = 0; i < l; i++)
2705   {
2706     // position
2707     const int x = (int)((float)i * lx / (float)l) + p0[0];
2708     const int y = (int)((float)i * ly / (float)l) + p0[1];
2709     const float op = 1.0f - (float)i / (float)l;
2710     float *buf = buffer + (size_t)y * bw + x;
2711     if(x >= 0 && x < bw && y >= 0 && y < bh) buf[0] = MAX(buf[0], op);
2712     if(x + dx >= 0 && x + dx < bw && y >= 0 && y < bh)
2713       buf[dx] = MAX(buf[dx], op); // this one is to avoid gap due to int rounding
2714     if(x >= 0 && x < bw && y + dy >= 0 && y + dy < bh)
2715       buf[dpy] = MAX(buf[dpy], op); // this one is to avoid gap due to int rounding
2716   }
2717 }
2718 
_path_get_mask_roi(const dt_iop_module_t * const module,const dt_dev_pixelpipe_iop_t * const piece,dt_masks_form_t * const form,const dt_iop_roi_t * roi,float * buffer)2719 static int _path_get_mask_roi(const dt_iop_module_t *const module, const dt_dev_pixelpipe_iop_t *const piece,
2720                               dt_masks_form_t *const form,
2721                               const dt_iop_roi_t *roi, float *buffer)
2722 {
2723   if(!module) return 0;
2724   double start = 0.0;
2725   double start2 = 0.0;
2726   if(darktable.unmuted & DT_DEBUG_PERF) start = dt_get_wtime();
2727 
2728   const int px = roi->x;
2729   const int py = roi->y;
2730   const int width = roi->width;
2731   const int height = roi->height;
2732   const float scale = roi->scale;
2733 
2734   // we need to take care of four different cases:
2735   // 1) path and feather are outside of roi
2736   // 2) path is outside of roi, feather reaches into roi
2737   // 3) roi lies completely within path
2738   // 4) all other situations :)
2739   int path_in_roi = 0;
2740   int feather_in_roi = 0;
2741   int path_encircles_roi = 0;
2742 
2743   // we get buffers for all points
2744   float *points = NULL, *border = NULL;
2745   int points_count = 0, border_count = 0;
2746   if(!_path_get_pts_border(module->dev, form, module->iop_order, DT_DEV_TRANSFORM_DIR_BACK_INCL, piece->pipe, &points, &points_count,
2747                            &border, &border_count, 0) || (points_count <= 2))
2748   {
2749     dt_free_align(points);
2750     dt_free_align(border);
2751     return 0;
2752   }
2753 
2754   if(darktable.unmuted & DT_DEBUG_PERF)
2755   {
2756     dt_print(DT_DEBUG_MASKS, "[masks %s] path points took %0.04f sec\n", form->name, dt_get_wtime() - start);
2757     start = start2 = dt_get_wtime();
2758   }
2759 
2760   // empty the output buffer
2761   dt_iop_image_fill(buffer, 0.0f, width, height, 1);
2762 
2763   const guint nb_corner = g_list_length(form->points);
2764 
2765   // we shift and scale down path and border
2766   for(int i = nb_corner * 3; i < border_count; i++)
2767   {
2768     float xx = border[2 * i];
2769     float yy = border[2 * i + 1];
2770     if(isnan(xx))
2771     {
2772       if(isnan(yy)) break; // that means we have to skip the end of the border path
2773       i = yy - 1;
2774       continue;
2775     }
2776     border[2 * i] = xx * scale - px;
2777     border[2 * i + 1] = yy * scale - py;
2778   }
2779   for(int i = nb_corner * 3; i < points_count; i++)
2780   {
2781     float xx = points[2 * i];
2782     float yy = points[2 * i + 1];
2783     points[2 * i] = xx * scale - px;
2784     points[2 * i + 1] = yy * scale - py;
2785   }
2786 
2787   // now check if path is at least partially within roi
2788   for(int i = nb_corner * 3; i < points_count; i++)
2789   {
2790     int xx = points[i * 2];
2791     int yy = points[i * 2 + 1];
2792 
2793     if(xx > 1 && yy > 1 && xx < width - 2 && yy < height - 2)
2794     {
2795       path_in_roi = 1;
2796       break;
2797     }
2798   }
2799 
2800   // if not this still might mean that path fully encircles roi -> we need to check that
2801   if(!path_in_roi)
2802   {
2803     int nb = 0;
2804     int last = -9999;
2805     int x = width / 2;
2806     int y = height / 2;
2807 
2808     for(int i = nb_corner * 3; i < points_count; i++)
2809     {
2810       int yy = (int)points[2 * i + 1];
2811       if(yy != last && yy == y)
2812       {
2813         if(points[2 * i] > x) nb++;
2814       }
2815       last = yy;
2816     }
2817     // if there is an uneven number of intersection points roi lies within path
2818     if(nb & 1)
2819     {
2820       path_in_roi = 1;
2821       path_encircles_roi = 1;
2822     }
2823   }
2824 
2825   // now check if feather is at least partially within roi
2826   for(int i = nb_corner * 3; i < border_count; i++)
2827   {
2828     float xx = border[i * 2];
2829     float yy = border[i * 2 + 1];
2830     if(isnan(xx))
2831     {
2832       if(isnan(yy)) break; // that means we have to skip the end of the border path
2833       i = yy - 1;
2834       continue;
2835     }
2836     if(xx > 1 && yy > 1 && xx < width - 2 && yy < height - 2)
2837     {
2838       feather_in_roi = 1;
2839       break;
2840     }
2841   }
2842 
2843   // if path and feather completely lie outside of roi -> we're done/mask remains empty
2844   if(!path_in_roi && !feather_in_roi)
2845   {
2846     dt_free_align(points);
2847     dt_free_align(border);
2848     return 1;
2849   }
2850 
2851   // now get min/max values
2852   float xmin, xmax, ymin, ymax;
2853   _path_bounding_box_raw(points, border, nb_corner, points_count, border_count, &xmin, &xmax, &ymin, &ymax);
2854 
2855   if(darktable.unmuted & DT_DEBUG_PERF)
2856   {
2857     dt_print(DT_DEBUG_MASKS, "[masks %s] path_fill min max took %0.04f sec\n", form->name,
2858              dt_get_wtime() - start2);
2859     start2 = dt_get_wtime();
2860   }
2861 
2862   // deal with path if it does not lie outside of roi
2863   if(path_in_roi)
2864   {
2865     // second copy of path which we can modify when cropping to roi
2866     float *cpoints = dt_alloc_align_float((size_t)2 * points_count);
2867     if(cpoints == NULL)
2868     {
2869       dt_free_align(points);
2870       dt_free_align(border);
2871       return 0;
2872     }
2873     memcpy(cpoints, points, sizeof(float) * 2 * points_count);
2874 
2875     // now we clip cpoints to roi -> catch special case when roi lies completely within path.
2876     // dirty trick: we allow path to extend one pixel beyond height-1. this avoids need of special handling
2877     // of the last roi line in the following edge-flag polygon fill algorithm.
2878     int crop_success = _path_crop_to_roi(cpoints + 2 * (nb_corner * 3), points_count - nb_corner * 3, 0,
2879                                          width - 1, 0, height);
2880     path_encircles_roi = path_encircles_roi || !crop_success;
2881 
2882     if(darktable.unmuted & DT_DEBUG_PERF)
2883     {
2884       dt_print(DT_DEBUG_MASKS, "[masks %s] path_fill crop to roi took %0.04f sec\n", form->name,
2885                dt_get_wtime() - start2);
2886       start2 = dt_get_wtime();
2887     }
2888 
2889     if(path_encircles_roi)
2890     {
2891       // roi lies completely within path
2892       for(size_t k = 0; k < (size_t)width * height; k++) buffer[k] = 1.0f;
2893     }
2894     else
2895     {
2896       // all other cases
2897 
2898       // edge-flag polygon fill: we write all the point around the path into the buffer
2899       float xlast = cpoints[(points_count - 1) * 2];
2900       float ylast = cpoints[(points_count - 1) * 2 + 1];
2901 
2902       for(int i = nb_corner * 3; i < points_count; i++)
2903       {
2904         float xstart = xlast;
2905         float ystart = ylast;
2906 
2907         float xend = xlast = cpoints[i * 2];
2908         float yend = ylast = cpoints[i * 2 + 1];
2909 
2910         if(ystart > yend)
2911         {
2912           float tmp;
2913           tmp = ystart, ystart = yend, yend = tmp;
2914           tmp = xstart, xstart = xend, xend = tmp;
2915         }
2916 
2917         const float m = (xstart - xend) / (ystart - yend); // we don't need special handling of ystart==yend
2918                                                            // as following loop will take care
2919 
2920         for(int yy = (int)ceilf(ystart); (float)yy < yend;
2921             yy++) // this would normally never touch the last roi line => see comment further above
2922         {
2923           const float xcross = xstart + m * (yy - ystart);
2924 
2925           int xx = floorf(xcross);
2926           if((float)xx + 0.5f <= xcross) xx++;
2927 
2928           if(xx < 0 || xx >= width || yy < 0 || yy >= height)
2929             continue; // sanity check just to be on the safe side
2930 
2931           const size_t index = (size_t)yy * width + xx;
2932 
2933           buffer[index] = 1.0f - buffer[index];
2934         }
2935       }
2936 
2937       if(darktable.unmuted & DT_DEBUG_PERF)
2938       {
2939         dt_print(DT_DEBUG_MASKS, "[masks %s] path_fill draw path took %0.04f sec\n", form->name,
2940                  dt_get_wtime() - start2);
2941         start2 = dt_get_wtime();
2942       }
2943 
2944       // we fill the inside plain
2945       // we don't need to deal with parts of shape outside of roi
2946       const int xxmin = MAX(xmin, 0);
2947       const int xxmax = MIN(xmax, width - 1);
2948       const int yymin = MAX(ymin, 0);
2949       const int yymax = MIN(ymax, height - 1);
2950 
2951 #ifdef _OPENMP
2952 #if !defined(__SUNOS__) && !defined(__NetBSD__)
2953 #pragma omp parallel for default(none) \
2954   dt_omp_firstprivate(xxmin, xxmax, yymin, yymax, width) \
2955   shared(buffer)
2956 #else
2957 #pragma omp parallel for shared(buffer)
2958 #endif
2959 #endif
2960       for(int yy = yymin; yy <= yymax; yy++)
2961       {
2962         int state = 0;
2963         for(int xx = xxmin; xx <= xxmax; xx++)
2964         {
2965           const size_t index = (size_t)yy * width + xx;
2966           const float v = buffer[index];
2967           if(v > 0.5f) state = !state;
2968           if(state) buffer[index] = 1.0f;
2969         }
2970       }
2971 
2972       if(darktable.unmuted & DT_DEBUG_PERF)
2973       {
2974         dt_print(DT_DEBUG_MASKS, "[masks %s] path_fill fill plain took %0.04f sec\n", form->name,
2975                  dt_get_wtime() - start2);
2976         start2 = dt_get_wtime();
2977       }
2978     }
2979     dt_free_align(cpoints);
2980   }
2981 
2982   // deal with feather if it does not lie outside of roi
2983   if(!path_encircles_roi)
2984   {
2985     int *dpoints = dt_alloc_align(64, sizeof(int) * 4 * border_count);
2986     if(dpoints == NULL)
2987     {
2988       dt_free_align(points);
2989       dt_free_align(border);
2990       return 0;
2991     }
2992 
2993     int dindex = 0;
2994     int p0[2], p1[2];
2995     float pf1[2];
2996     int last0[2] = { -100, -100 };
2997     int last1[2] = { -100, -100 };
2998     int next = 0;
2999     for(int i = nb_corner * 3; i < border_count; i++)
3000     {
3001       p0[0] = floorf(points[i * 2] + 0.5f);
3002       p0[1] = ceilf(points[i * 2 + 1]);
3003       if(next > 0)
3004       {
3005         p1[0] = pf1[0] = border[next * 2];
3006         p1[1] = pf1[1] = border[next * 2 + 1];
3007       }
3008       else
3009       {
3010         p1[0] = pf1[0] = border[i * 2];
3011         p1[1] = pf1[1] = border[i * 2 + 1];
3012       }
3013 
3014       // now we check p1 value to know if we have to skip a part
3015       if(next == i) next = 0;
3016       while(isnan(pf1[0]))
3017       {
3018         if(isnan(pf1[1]))
3019           next = i - 1;
3020         else
3021           next = p1[1];
3022         p1[0] = pf1[0] = border[next * 2];
3023         p1[1] = pf1[1] = border[next * 2 + 1];
3024       }
3025 
3026       // and we draw the falloff
3027       if(last0[0] != p0[0] || last0[1] != p0[1] || last1[0] != p1[0] || last1[1] != p1[1])
3028       {
3029         dpoints[dindex] = p0[0];
3030         dpoints[dindex + 1] = p0[1];
3031         dpoints[dindex + 2] = p1[0];
3032         dpoints[dindex + 3] = p1[1];
3033         dindex += 4;
3034 
3035         last0[0] = p0[0];
3036         last0[1] = p0[1];
3037         last1[0] = p1[0];
3038         last1[1] = p1[1];
3039       }
3040     }
3041 
3042 #ifdef _OPENMP
3043 #if !defined(__SUNOS__) && !defined(__NetBSD__)
3044 #pragma omp parallel for default(none) \
3045   dt_omp_firstprivate(width, height, dindex) \
3046   shared(buffer, dpoints)
3047 #else
3048 #pragma omp parallel for shared(buffer)
3049 #endif
3050 #endif
3051     for(int n = 0; n < dindex; n += 4)
3052       _path_falloff_roi(buffer, dpoints + n, dpoints + n + 2, width, height);
3053 
3054     dt_free_align(dpoints);
3055 
3056     if(darktable.unmuted & DT_DEBUG_PERF)
3057     {
3058       dt_print(DT_DEBUG_MASKS, "[masks %s] path_fill fill falloff took %0.04f sec\n", form->name,
3059                dt_get_wtime() - start2);
3060     }
3061   }
3062 
3063   dt_free_align(points);
3064   dt_free_align(border);
3065 
3066   if(darktable.unmuted & DT_DEBUG_PERF)
3067     dt_print(DT_DEBUG_MASKS, "[masks %s] path fill buffer took %0.04f sec\n", form->name,
3068              dt_get_wtime() - start);
3069 
3070   return 1;
3071 }
3072 
_path_setup_mouse_actions(const struct dt_masks_form_t * const form)3073 static GSList *_path_setup_mouse_actions(const struct dt_masks_form_t *const form)
3074 {
3075   GSList *lm = NULL;
3076   lm = dt_mouse_action_create_simple(lm, DT_MOUSE_ACTION_LEFT, 0, _("[PATH creation] add a smooth node"));
3077   lm = dt_mouse_action_create_simple(lm, DT_MOUSE_ACTION_LEFT, GDK_CONTROL_MASK,
3078                                      _("[PATH creation] add a sharp node"));
3079   lm = dt_mouse_action_create_simple(lm, DT_MOUSE_ACTION_RIGHT, 0, _("[PATH creation] terminate path creation"));
3080   lm = dt_mouse_action_create_simple(lm, DT_MOUSE_ACTION_SCROLL, GDK_CONTROL_MASK,
3081                                      _("[PATH on node] switch between smooth/sharp node"));
3082   lm = dt_mouse_action_create_simple(lm, DT_MOUSE_ACTION_RIGHT, 0, _("[PATH on node] remove the node"));
3083   lm = dt_mouse_action_create_simple(lm, DT_MOUSE_ACTION_RIGHT, 0, _("[PATH on feather] reset curvature"));
3084   lm = dt_mouse_action_create_simple(lm, DT_MOUSE_ACTION_LEFT, GDK_CONTROL_MASK,
3085                                      _("[PATH on segment] add node"));
3086   lm = dt_mouse_action_create_simple(lm, DT_MOUSE_ACTION_SCROLL, 0, _("[PATH] change size"));
3087   lm = dt_mouse_action_create_simple(lm, DT_MOUSE_ACTION_SCROLL, GDK_CONTROL_MASK, _("[PATH] change opacity"));
3088   lm = dt_mouse_action_create_simple(lm, DT_MOUSE_ACTION_SCROLL, GDK_SHIFT_MASK, _("[PATH] change feather size"));
3089   return lm;
3090 }
3091 
_path_sanitize_config(dt_masks_type_t type)3092 static void _path_sanitize_config(dt_masks_type_t type)
3093 {
3094   // nothing to do (yet?)
3095 }
3096 
_path_set_form_name(struct dt_masks_form_t * const form,const size_t nb)3097 static void _path_set_form_name(struct dt_masks_form_t *const form, const size_t nb)
3098 {
3099   snprintf(form->name, sizeof(form->name), _("path #%d"), (int)nb);
3100 }
3101 
_path_set_hint_message(const dt_masks_form_gui_t * const gui,const dt_masks_form_t * const form,const int opacity,char * const restrict msgbuf,const size_t msgbuf_len)3102 static void _path_set_hint_message(const dt_masks_form_gui_t *const gui, const dt_masks_form_t *const form,
3103                                      const int opacity, char *const restrict msgbuf, const size_t msgbuf_len)
3104 {
3105   if(gui->creation && g_list_length(form->points) < 4)
3106     g_strlcat(msgbuf, _("<b>add node</b>: click, <b>add sharp node</b>:ctrl+click\n"
3107                         "<b>cancel</b>: right-click"), msgbuf_len);
3108   else if(gui->creation)
3109     g_strlcat(msgbuf, _("<b>add node</b>: click, <b>add sharp node</b>:ctrl+click\n"
3110                         "<b>finish path</b>: right-click"), msgbuf_len);
3111   else if(gui->point_selected >= 0)
3112     g_strlcat(msgbuf, _("<b>move node</b>: drag, <b>remove node</b>: right-click\n"
3113                         "<b>switch smooth/sharp mode</b>: ctrl+click"), msgbuf_len);
3114   else if(gui->feather_selected >= 0)
3115     g_strlcat(msgbuf, _("<b>node curvature</b>: drag\n<b>reset curvature</b>: right-click"), msgbuf_len);
3116   else if(gui->seg_selected >= 0)
3117     g_strlcat(msgbuf, _("<b>move segment</b>: drag\n<b>add node</b>: ctrl+click"), msgbuf_len);
3118   else if(gui->form_selected)
3119     g_snprintf(msgbuf, msgbuf_len, _("<b>size</b>: scroll, <b>feather size</b>: shift+scroll\n"
3120                                      "<b>opacity</b>: ctrl+scroll (%d%%)"), opacity);
3121 }
3122 
_path_duplicate_points(dt_develop_t * const dev,dt_masks_form_t * const base,dt_masks_form_t * const dest)3123 static void _path_duplicate_points(dt_develop_t *const dev, dt_masks_form_t *const base, dt_masks_form_t *const dest)
3124 {
3125   (void)dev; // unused arg, keep compiler from complaining
3126   for(const GList *pts = base->points; pts; pts = g_list_next(pts))
3127   {
3128     dt_masks_point_path_t *pt = (dt_masks_point_path_t *)pts->data;
3129     dt_masks_point_path_t *npt = (dt_masks_point_path_t *)malloc(sizeof(dt_masks_point_path_t));
3130     memcpy(npt, pt, sizeof(dt_masks_point_path_t));
3131     dest->points = g_list_append(dest->points, npt);
3132   }
3133 }
3134 
_path_initial_source_pos(const float iwd,const float iht,float * x,float * y)3135 static void _path_initial_source_pos(const float iwd, const float iht, float *x, float *y)
3136 {
3137   *x = (0.02f * iwd);
3138   *y = (0.02f * iht);
3139 }
3140 
3141 // The function table for paths.  This must be public, i.e. no "static" keyword.
3142 const dt_masks_functions_t dt_masks_functions_path = {
3143   .point_struct_size = sizeof(struct dt_masks_point_path_t),
3144   .sanitize_config = _path_sanitize_config,
3145   .setup_mouse_actions = _path_setup_mouse_actions,
3146   .set_form_name = _path_set_form_name,
3147   .set_hint_message = _path_set_hint_message,
3148   .duplicate_points = _path_duplicate_points,
3149   .initial_source_pos = _path_initial_source_pos,
3150   .get_distance = _path_get_distance,
3151   .get_points_border = _path_get_points_border,
3152   .get_mask = _path_get_mask,
3153   .get_mask_roi = _path_get_mask_roi,
3154   .get_area = _path_get_area,
3155   .get_source_area = _path_get_source_area,
3156   .mouse_moved = _path_events_mouse_moved,
3157   .mouse_scrolled = _path_events_mouse_scrolled,
3158   .button_pressed = _path_events_button_pressed,
3159   .button_released = _path_events_button_released,
3160   .post_expose = _path_events_post_expose
3161 };
3162 
3163 
3164 // modelines: These editor modelines have been set for all relevant files by tools/update_modelines.sh
3165 // vim: shiftwidth=2 expandtab tabstop=2 cindent
3166 // kate: tab-indents: off; indent-width 2; replace-tabs on; indent-mode cstyle; remove-trailing-spaces modified;
3167