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