Ansel 0.0
A darktable fork - bloat + design vision
Loading...
Searching...
No Matches
polygon.c
Go to the documentation of this file.
1/*
2 This file is part of darktable,
3 Copyright (C) 2013-2016, 2021 Aldric Renaudin.
4 Copyright (C) 2013 Moritz Lipp.
5 Copyright (C) 2013-2014, 2020-2022 Pascal Obry.
6 Copyright (C) 2013-2016 Roman Lebedev.
7 Copyright (C) 2013 Simon Spannagel.
8 Copyright (C) 2013-2017 Tobias Ellinghaus.
9 Copyright (C) 2013-2017, 2019 Ulrich Pegelow.
10 Copyright (C) 2014 Jérémy Rosen.
11 Copyright (C) 2016 Fabio Valentini.
12 Copyright (C) 2016, 2018 johannes hanika.
13 Copyright (C) 2017-2019 Edgardo Hoszowski.
14 Copyright (C) 2017 luzpaz.
15 Copyright (C) 2020, 2022 Chris Elston.
16 Copyright (C) 2020 GrahamByrnes.
17 Copyright (C) 2020 Heiko Bauke.
18 Copyright (C) 2020-2021 Hubert Kowalski.
19 Copyright (C) 2020-2021 Ralf Brown.
20 Copyright (C) 2021 Marco Carrarini.
21 Copyright (C) 2021 Victor Forsiuk.
22 Copyright (C) 2022 Martin Bařinka.
23 Copyright (C) 2022 Miloš Komarčević.
24 Copyright (C) 2023, 2025-2026 Aurélien PIERRE.
25 Copyright (C) 2024 Alynx Zhou.
26 Copyright (C) 2025-2026 Guillaume Stutin.
27
28 darktable is free software: you can redistribute it and/or modify
29 it under the terms of the GNU General Public License as published by
30 the Free Software Foundation, either version 3 of the License, or
31 (at your option) any later version.
32
33 darktable is distributed in the hope that it will be useful,
34 but WITHOUT ANY WARRANTY; without even the implied warranty of
35 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
36 GNU General Public License for more details.
37
38 You should have received a copy of the GNU General Public License
39 along with darktable. If not, see <http://www.gnu.org/licenses/>.
40*/
42#include "math/math.h"
43#include "system/macros.h"
44#include "system/openmp.h"
45#include "system/mem_alloc.h"
46#include "common/logging.h"
47#include "common/times.h"
48#include "common/glib_utils.h"
50#include "widgets/gdkkeys.h"
53#include "common/conf.h"
54#include "develop/imageop.h"
56#include "develop/masks.h"
57#include "develop/masks_gui.h"
60#include "math/openmp_maths.h"
61#include "gui/actions/menu.h"
62#include <assert.h>
63
64#define FADING_MIN 0.0005f
65#define FADING_MAX 1.0f
66
67#define BORDER_MIN 0.00005f
68#define BORDER_MAX 0.5f
69
70static void _polygon_bounding_box_raw(const float *const point_buffer, const float *border_buffer,
71 const int corner_count, const int point_count, int border_count,
72 float *x_min, float *x_max, float *y_min, float *y_max);
73
79static void _polygon_get_XY(const float p0_x, const float p0_y, const float p1_x, const float p1_y,
80 const float p2_x, const float p2_y, const float p3_x, const float p3_y,
81 const float t, float *out_x, float *out_y)
82{
83 const float one_minus_t = 1.0f - t;
84 const float a = one_minus_t * one_minus_t * one_minus_t;
85 const float b = 3.0f * t * one_minus_t * one_minus_t;
86 const float c = 3.0f * t * t * one_minus_t;
87 const float d = t * t * t;
88 *out_x = p0_x * a + p1_x * b + p2_x * c + p3_x * d;
89 *out_y = p0_y * a + p1_y * b + p2_y * c + p3_y * d;
90}
91
97static gboolean _polygon_border_get_XY(const float p0_x, const float p0_y, const float p1_x, const float p1_y,
98 const float p2_x, const float p2_y, const float p3_x, const float p3_y,
99 const float t, const float radius, float radius_rate,
100 float *center_x, float *center_y, float *border_x, float *border_y)
101{
102 // we get the point
103 _polygon_get_XY(p0_x, p0_y, p1_x, p1_y, p2_x, p2_y, p3_x, p3_y, t, center_x, center_y);
104
105 // now we get derivative points
106 const double ti = 1.0 - (double)t;
107
108 const double t_t = (double)t * t;
109 const double ti_ti = ti * ti;
110 const double t_ti = t * ti;
111
112 const double a = 3.0 * ti_ti;
113 const double b = 3.0 * (ti_ti - 2.0 * t_ti);
114 const double c = 3.0 * (2.0 * t_ti - t_t);
115 const double d = 3.0 * t_t;
116
117 /* not const: a degenerate first-order term is replaced by the limit direction below */
118 double dx = -p0_x * a + p1_x * b + p2_x * c + p3_x * d;
119 double dy = -p0_y * a + p1_y * b + p2_y * c + p3_y * d;
120
121 /* A vanishing derivative does not mean the tangent is undefined -- it means the first-order
122 * term is degenerate and the direction is the limit, given by the first non-zero term. That
123 * is exactly how a CUSP is stored: both of the node's handles sit on the node, so the cubic's
124 * endpoint derivative 3*(p3 - p2) is zero there.
125 *
126 * Giving up instead left the offset with no direction at that one sample, and the caller then
127 * held the previous border point. A polygon's feather is painted as radial spokes from each
128 * outline sample to its border sample, so a held border point means a spoke that goes nowhere:
129 * reported on polygon #2 of issue #1313's sidecar as "a radial spoke is missing in the
130 * feathering area" at node 12, and visible in the rasterised mask as a thin dark line cutting
131 * down through the feather band.
132 *
133 * This is the same defect the brush had at its own cusp, fixed the same way, and the
134 * comparison has to be RELATIVE for the same reason: these are image pixels, so the products
135 * either side of the cancellation are rounded before they are subtracted and what should be
136 * zero arrives as a small residue. The brush measured 1.22e-4 in float. Doubles here make it
137 * smaller, not absent, and normalising a residue yields a direction made of rounding noise --
138 * which is worse than giving up, because it looks like an answer. */
139 const double span = fmax(fmax(fabs((double)p3_x - p0_x), fabs((double)p3_y - p0_y)),
140 fmax(fabs((double)p2_x - p1_x), fabs((double)p2_y - p1_y)));
141 const double degenerate = fmax(span, 1.0) * 1e-9;
142
143 if(fabs(dx) < degenerate && fabs(dy) < degenerate)
144 {
145 /* for a cubic, if the first-order term vanishes the second-order one gives the limit */
146 if(t < 0.5f) { dx = (double)p2_x - p0_x; dy = (double)p2_y - p0_y; }
147 else { dx = (double)p3_x - p1_x; dy = (double)p3_y - p1_y; }
148
149 if(fabs(dx) < degenerate && fabs(dy) < degenerate)
150 {
151 dx = (double)p3_x - p0_x;
152 dy = (double)p3_y - p0_y;
153 }
154 /* only a curve collapsed to a single point has no direction at all */
155 if(dx == 0.0 && dy == 0.0) return FALSE;
156
157 /* a limit direction has a direction and no speed, so no rate can be taken against it */
158 radius_rate = 0.0f;
159 }
160
161 /* on the envelope of the feather's discs, not on the normal: see
162 * dt_masks_outline_envelope_offset() */
163 const float centre[2] = { *center_x, *center_y };
164 float border[2];
165 dt_masks_outline_envelope_offset(centre, (float)dx, (float)dy, radius, radius_rate, border);
166 *border_x = border[0];
167 *border_y = border[1];
168 return TRUE;
169}
170
171/* The feather along a segment eases from one node's to the other's, r1 + (r2 - r1) * t^2 (3 - 2t);
172 * its rate by t, (r2 - r1) * 6 t (1 - t), is zero at both ends, so the end samples stay on the
173 * normal and the joints built from them are unchanged. */
174static inline float _polygon_radius_at(const float r1, const float r2, const double t)
175{
176 return r1 + (r2 - r1) * t * t * (3.0 - 2.0 * t);
177}
178
179static inline float _polygon_radius_rate_at(const float r1, const float r2, const double t)
180{
181 return (r2 - r1) * 6.0 * t * (1.0 - t);
182}
183
189static void _polygon_ctrl2_to_handle(const float point_x, const float point_y,
190 const float ctrl_x, const float ctrl_y,
191 float *handle_x, float *handle_y, const gboolean clockwise)
192{
193 const float delta_y = ctrl_y - point_y;
194 const float delta_x = point_x - ctrl_x;
195 if(clockwise)
196 {
197 *handle_x = point_x - delta_y;
198 *handle_y = point_y - delta_x;
199 }
200 else
201 {
202 *handle_x = point_x + delta_y;
203 *handle_y = point_y + delta_x;
204 }
205}
206
212static void _polygon_handle_to_ctrl(const float point_x, const float point_y,
213 const float handle_x, const float handle_y,
214 float *ctrl1_x, float *ctrl1_y, float *ctrl2_x, float *ctrl2_y,
215 const gboolean clockwise)
216{
217 const float delta_y = handle_y - point_y;
218 const float delta_x = point_x - handle_x;
219
220 if(clockwise)
221 {
222 *ctrl1_x = point_x - delta_y;
223 *ctrl1_y = point_y - delta_x;
224 *ctrl2_x = point_x + delta_y;
225 *ctrl2_y = point_y + delta_x;
226 }
227 else
228 {
229 *ctrl1_x = point_x + delta_y;
230 *ctrl1_y = point_y + delta_x;
231 *ctrl2_x = point_x - delta_y;
232 *ctrl2_y = point_y - delta_x;
233 }
234}
235
239static void _polygon_catmull_to_bezier(const float x1, const float y1, const float x2, const float y2,
240 const float x3, const float y3, const float x4, const float y4,
241 float *bezier_x1, float *bezier_y1,
242 float *bezier_x2, float *bezier_y2)
243{
244 *bezier_x1 = (-x1 + 6 * x2 + x3) / 6;
245 *bezier_y1 = (-y1 + 6 * y2 + y3) / 6;
246 *bezier_x2 = (x2 + 6 * x3 - x4) / 6;
247 *bezier_y2 = (y2 + 6 * y3 - y4) / 6;
248}
249
256{
257 // if we have less that 3 points, what to do ??
258 const guint node_count = g_list_length(mask_form->points);
259 if(node_count < 2) return;
260
261 if(IS_NULL_PTR(mask_form) || IS_NULL_PTR(mask_form->points)) return;
262
263 dt_masks_node_polygon_t **nodes = dt_alloc_align((size_t)node_count * sizeof(*nodes));
264 if(IS_NULL_PTR(nodes)) return;
265 const GList *form_points = mask_form->points;
266 for(guint node_index = 0; node_index < node_count; node_index++)
267 {
268 nodes[node_index] = (dt_masks_node_polygon_t *)form_points->data;
269 form_points = g_list_next(form_points);
270 }
271
272 for(guint node_index = 0; node_index < node_count; node_index++)
273 {
274 dt_masks_node_polygon_t *point3 = nodes[node_index];
275 if(IS_NULL_PTR(point3)) { dt_free_align(nodes); return; }
276 // if the point has not been set manually, we redefine it
278 {
279 dt_masks_node_polygon_t *point1 = nodes[(node_index + node_count - 2) % node_count];
280 dt_masks_node_polygon_t *point2 = nodes[(node_index + node_count - 1) % node_count];
281 dt_masks_node_polygon_t *point4 = nodes[(node_index + 1) % node_count];
282 dt_masks_node_polygon_t *point5 = nodes[(node_index + 2) % node_count];
283 if(IS_NULL_PTR(point1) || IS_NULL_PTR(point2) || IS_NULL_PTR(point4) || IS_NULL_PTR(point5)) { dt_free_align(nodes); return; }
284
285 float bezier1_x = 0.0f;
286 float bezier1_y = 0.0f;
287 float bezier2_x = 0.0f;
288 float bezier2_y = 0.0f;
289 _polygon_catmull_to_bezier(point1->node[0], point1->node[1], point2->node[0], point2->node[1],
290 point3->node[0], point3->node[1], point4->node[0], point4->node[1],
291 &bezier1_x, &bezier1_y, &bezier2_x, &bezier2_y);
292 if(point2->ctrl2[0] == -1.0) point2->ctrl2[0] = bezier1_x;
293 if(point2->ctrl2[1] == -1.0) point2->ctrl2[1] = bezier1_y;
294 point3->ctrl1[0] = bezier2_x;
295 point3->ctrl1[1] = bezier2_y;
296 _polygon_catmull_to_bezier(point2->node[0], point2->node[1], point3->node[0], point3->node[1],
297 point4->node[0], point4->node[1], point5->node[0], point5->node[1],
298 &bezier1_x, &bezier1_y, &bezier2_x, &bezier2_y);
299 if(point4->ctrl1[0] == -1.0) point4->ctrl1[0] = bezier2_x;
300 if(point4->ctrl1[1] == -1.0) point4->ctrl1[1] = bezier2_y;
301 point3->ctrl2[0] = bezier1_x;
302 point3->ctrl2[1] = bezier1_y;
303 }
304 }
305 dt_free_align(nodes);
306 return;
307}
308
314static gboolean _polygon_is_clockwise(dt_masks_form_t *mask_form)
315{
316 if(IS_NULL_PTR(mask_form) || IS_NULL_PTR(mask_form->points)) return 0;
317 if(!g_list_shorter_than(mask_form->points, 3)) // if we have at least three points...
318 {
319 float sum = 0.0f;
320 for(const GList *form_points = mask_form->points; form_points; form_points = g_list_next(form_points))
321 {
322 const GList *next = g_list_next_wraparound(form_points, mask_form->points); // next, wrapping around if on last elt
323 dt_masks_node_polygon_t *point1 = (dt_masks_node_polygon_t *)form_points->data; // kth element of mask_form->points
324 dt_masks_node_polygon_t *point2 = (dt_masks_node_polygon_t *)next->data;
325 if(IS_NULL_PTR(point1) || IS_NULL_PTR(point2)) return 0;
326 sum += (point2->node[0] - point1->node[0]) * (point2->node[1] + point1->node[1]);
327 }
328 return (sum < 0);
329 }
330 // return dummy answer
331 return TRUE;
332}
333
339static void _polygon_points_recurs_border_gaps(const float *const center_max, const float *const border_min,
340 const float *const border_max, dt_masks_dynbuf_t *draw_points,
341 dt_masks_dynbuf_t *draw_border,
342 gboolean clockwise, const int step)
343{
344 // we want to find the start and end angles
345 double angle_start = atan2f(border_min[1] - center_max[1], border_min[0] - center_max[0]);
346 double angle_end = atan2f(border_max[1] - center_max[1], border_max[0] - center_max[0]);
347 if(angle_start == angle_end) return;
348
349 // we have to be sure that we turn in the correct direction
350 if(angle_end < angle_start && clockwise)
351 {
352 angle_end += 2 * M_PI;
353 }
354 if(angle_end > angle_start && !clockwise)
355 {
356 angle_start += 2 * M_PI;
357 }
358
359 // we determine start and end radius too
360 const float radius_start = sqrtf((border_min[1] - center_max[1]) * (border_min[1] - center_max[1])
361 + (border_min[0] - center_max[0]) * (border_min[0] - center_max[0]));
362 const float radius_end = sqrtf((border_max[1] - center_max[1]) * (border_max[1] - center_max[1])
363 + (border_max[0] - center_max[0]) * (border_max[0] - center_max[0]));
364
365 // and the max length of the circle arc, in samples
366 int step_count = 0;
367 if(angle_end > angle_start)
368 step_count = (angle_end - angle_start) * fmaxf(radius_start, radius_end) / (float)step;
369 else
370 step_count = (angle_start - angle_end) * fmaxf(radius_start, radius_end) / (float)step;
371 if(step_count < 2) return;
372
373 // and now we add the points
374 const float angle_step = (angle_end - angle_start) / step_count;
375 const float radius_step = (radius_end - radius_start) / step_count;
376 float current_radius = radius_start + radius_step;
377 float current_angle = angle_start + angle_step;
378 // allocate entries in the dynbufs
379 float *points_ptr = dt_masks_dynbuf_reserve_n(draw_points, 2 * (step_count - 1));
380 float *border_ptr = draw_border ? dt_masks_dynbuf_reserve_n(draw_border, 2 * (step_count - 1)) : NULL;
381 // and fill them in: the same center pos for each point in dpoints, and the corresponding border point at
382 // successive angular positions for dborder
383 if(!IS_NULL_PTR(points_ptr))
384 {
385 for(int step_index = 1; step_index < step_count; step_index++)
386 {
387 *points_ptr++ = center_max[0];
388 *points_ptr++ = center_max[1];
389 if(!IS_NULL_PTR(border_ptr))
390 {
391 *border_ptr++ = center_max[0] + current_radius * cosf(current_angle);
392 *border_ptr++ = center_max[1] + current_radius * sinf(current_angle);
393 }
394 current_radius += radius_step;
395 current_angle += angle_step;
396 }
397 }
398}
399
400static inline gboolean _is_within_pxl_threshold(float *min, float *max, int pixel_threshold)
401{
402 return abs((int)min[0] - (int)max[0]) < pixel_threshold &&
403 abs((int)min[1] - (int)max[1]) < pixel_threshold;
404}
405
406
412static void _polygon_points_recurs(float *segment_start, float *segment_end,
413 double t_min, double t_max,
414 float *polygon_min, float *polygon_max,
415 float *border_min, float *border_max,
416 float *result_polygon, float *result_border,
417 dt_masks_dynbuf_t *draw_points, dt_masks_dynbuf_t *draw_border,
418 int with_border, const int pixel_threshold,
419 const gboolean have_min, const gboolean have_max,
420 gboolean have_border_min, gboolean have_border_max,
421 gboolean *out_have_border)
422{
423 /* have_* say whether the caller already evaluated that endpoint and whether a border point
424 * came with it. They were read out of the arrays as NaN, so "not computed", "no border" and
425 * real geometry all shared one representation in the buffers the outline is built from. */
426 if(!have_min)
427 {
428 have_border_min =
429 _polygon_border_get_XY(segment_start[0], segment_start[1], segment_start[2], segment_start[3],
430 segment_end[2], segment_end[3], segment_end[0], segment_end[1], t_min,
431 _polygon_radius_at(segment_start[4], segment_end[4], t_min),
432 _polygon_radius_rate_at(segment_start[4], segment_end[4], t_min),
433 polygon_min, polygon_min + 1, border_min, border_min + 1);
434 }
435 if(!have_max)
436 {
437 have_border_max =
438 _polygon_border_get_XY(segment_start[0], segment_start[1], segment_start[2], segment_start[3],
439 segment_end[2], segment_end[3], segment_end[0], segment_end[1], t_max,
440 _polygon_radius_at(segment_start[4], segment_end[4], t_max),
441 _polygon_radius_rate_at(segment_start[4], segment_end[4], t_max),
442 polygon_max, polygon_max + 1, border_max, border_max + 1);
443 }
444
445 // are the points near_handle ?
446 if((t_max - t_min < 0.0001)
447 || (_is_within_pxl_threshold(polygon_min, polygon_max, pixel_threshold)
448 && (!with_border || (_is_within_pxl_threshold(border_min, border_max, pixel_threshold)))))
449 {
450 dt_masks_dynbuf_add_2(draw_points, polygon_max[0], polygon_max[1]);
451 result_polygon[0] = polygon_max[0];
452 result_polygon[1] = polygon_max[1];
453
454 if(with_border)
455 {
456 /* one end of the span may have had no direction to offset along; borrow the other's */
457 if(!have_border_max && have_border_min)
458 {
459 border_max[0] = border_min[0];
460 border_max[1] = border_min[1];
461 have_border_max = TRUE;
462 }
463 else if(!have_border_max)
464 {
465 /* Neither end has a direction. The walk only hands the recursion segments that have
466 * one at an end, so this is unreachable in practice -- but what used to happen here
467 * is the brush's issue #1360 on a polygon: border_max was the caller's scratch, NaN
468 * at the top level and (0, 0) -- the image origin -- below it, written to the buffer
469 * as geometry. A spoke of the right LENGTH along the chord's normal is always a valid
470 * piece of the disc union. */
471 const float radius = _polygon_radius_at(segment_start[4], segment_end[4], t_max);
472 dt_masks_outline_offset_along(polygon_max, segment_end[1] - segment_start[1],
473 -(segment_end[0] - segment_start[0]), radius, border_max);
474 have_border_max = TRUE;
475 }
476 dt_masks_dynbuf_add_2(draw_border, border_max[0], border_max[1]);
477 result_border[0] = border_max[0];
478 result_border[1] = border_max[1];
479 if(!IS_NULL_PTR(out_have_border)) *out_have_border = have_border_max;
480 }
481 return;
482 }
483
484 // we split in two part
485 double t_mid = (t_min + t_max) / 2.0;
486 float polygon_mid[2] = { 0.0f, 0.0f };
487 float border_mid[2] = { 0.0f, 0.0f };
488 float polygon_result_left[2] = { 0 };
489 float border_result_left[2] = { 0 };
490 _polygon_points_recurs(segment_start, segment_end, t_min, t_mid,
491 polygon_min, polygon_mid, border_min, border_mid,
492 polygon_result_left, border_result_left,
493 draw_points, draw_border, with_border, pixel_threshold,
494 have_min, FALSE, have_border_min, FALSE, NULL);
495 _polygon_points_recurs(segment_start, segment_end, t_mid, t_max,
496 polygon_result_left, polygon_max, border_result_left, border_max,
497 result_polygon, result_border,
498 draw_points, draw_border, with_border, pixel_threshold,
499 TRUE, have_max, TRUE, have_border_max, out_have_border);
500}
501
502// Maximum number of self-intersection portions to track;
503// helps limit detection complexity
504
505// Self-intersection cuts are dt_masks_skip_range_t (masks_types.h), built by
506// dt_masks_skip_ranges_build() and handed to every consumer OUT-OF-BAND -- never encoded into
507// the border buffer. The in-band NaN-jump encoding this replaced hosted both bugs the
508// mechanism ever had (a reader cycle, then issue #1313's seam fold) while the geometry was
509// right both times.
510
511/* THE WALK.
512 *
513 * A polygon's feather is the union of a disc of the local radius over every point of its
514 * path, outside the path; the rasteriser paints it as spokes from every path sample out to
515 * its border sample, and fills the path's interior separately. The path is closed, so it is
516 * walked once, the border on the outside -- the winding is folded into the sign of the
517 * radius -- and every convex joint gets an arc centred on its node.
518 *
519 * Everything a joint needs is taken from the two segment END SAMPLES that meet there and from
520 * the node data. Nothing is read back out of the buffers. The previous walk took "the border
521 * sample last written", and the one ten samples before it, as the inputs of every joint arc,
522 * and wrote its zero-initialised scratch -- the image origin -- into the border wherever a
523 * segment had no direction. That is the brush's issue #1360, on a polygon: a pen resting
524 * under rising pressure, or a node dropped twice on the same spot, produces exactly such a
525 * segment. A DEGENERATE segment -- its four control points one point -- contributes nothing;
526 * the joint that closes over it is between its two live neighbours, which for the disc union
527 * is exactly right. */
528typedef struct _polygon_frame_t
529{
530 float iwd;
531 float iht;
532 float dx;
533 float dy;
534 float radius_sign; /* +1 or -1: the winding, so the offset falls outside the path */
536
537/* The two ends of one segment, in image pixels: node, the control point that faces the other
538 * end, signed radius. The start of a segment carries its node's border[1] and the end its
539 * node's border[0]; every writer sets the two together. */
540static void _polygon_segment_load(const dt_masks_node_polygon_t *const from,
541 const dt_masks_node_polygon_t *const to, const _polygon_frame_t *const f,
542 float p1[5], float p2[5])
543{
544 const float scale = f->radius_sign * MIN(f->iwd, f->iht);
545 p1[0] = from->node[0] * f->iwd - f->dx;
546 p1[1] = from->node[1] * f->iht - f->dy;
547 p1[2] = from->ctrl2[0] * f->iwd - f->dx;
548 p1[3] = from->ctrl2[1] * f->iht - f->dy;
549 p1[4] = from->border[1] * scale;
550 p2[0] = to->node[0] * f->iwd - f->dx;
551 p2[1] = to->node[1] * f->iht - f->dy;
552 p2[2] = to->ctrl1[0] * f->iwd - f->dx;
553 p2[3] = to->ctrl1[1] * f->iht - f->dy;
554 p2[4] = to->border[0] * scale;
555}
556
557typedef struct _polygon_walk_t
558{
563 gboolean with_border; /* a border is built: it was asked for, and there are enough nodes */
564 gboolean clockwise;
567 float *node_border; /* per node, the border sample at the node: the header's handle */
570
571/* The walk at the node the next segment starts from, and the first live segment's start, for
572 * the joint that closes the path. */
574{
575 gboolean have_prev;
576 float c[2];
577 float b[2];
578 gboolean have_first;
579 float first_c[2];
580 float first_b[2];
582
583/* The arc that bridges a joint, the short way round; on a tie the winding decides. The
584 * filler is the polygon's own. */
585static void _polygon_joint_arc(const _polygon_walk_t *const w, const float *const centre, const float *const from,
586 const float *const to)
587{
588 if(fabsf(to[0] - from[0]) <= 1.0f && fabsf(to[1] - from[1]) <= 1.0f) return;
589 const gboolean clockwise = dt_masks_outline_short_way(centre, from, to, w->clockwise);
590 _polygon_points_recurs_border_gaps(centre, from, to, w->dpoints, w->dborder, clockwise, w->pixel_threshold);
591}
592
593/* One segment of the walk: the joint at its start node, then every sample within a pixel of
594 * the last and its border. Returns FALSE for a degenerate segment, which contributes nothing. */
595static gboolean _polygon_walk_segment(const _polygon_walk_t *const w, _polygon_walk_state_t *const s, const int k)
596{
597 const int k1 = (k + 1) % w->node_count;
598 float p1[5];
599 float p2[5];
600 _polygon_segment_load(w->nodes[k], w->nodes[k1], &w->frame, p1, p2);
601
602 /* the segment's own end samples; a segment with a direction at neither end is a point */
603 float c0[2];
604 float b0[2];
605 float c1[2];
606 float b1[2];
607 const gboolean have_b0 = _polygon_border_get_XY(p1[0], p1[1], p1[2], p1[3], p2[2], p2[3], p2[0], p2[1], 0.0f,
608 p1[4], 0.0f, c0, c0 + 1, b0, b0 + 1);
609 const gboolean have_b1 = _polygon_border_get_XY(p1[0], p1[1], p1[2], p1[3], p2[2], p2[3], p2[0], p2[1], 1.0f,
610 p2[4], 0.0f, c1, c1 + 1, b1, b1 + 1);
611 if(!have_b0 && !have_b1) return FALSE;
612 if(!have_b0) dt_masks_outline_offset_along(c0, b1[0] - c1[0], b1[1] - c1[1], p1[4], b0);
613 if(!have_b1) dt_masks_outline_offset_along(c1, b0[0] - c0[0], b0[1] - c0[1], p2[4], b1);
614
615 if(w->with_border)
616 {
617 if(s->have_prev) _polygon_joint_arc(w, c0, s->b, b0);
618 w->node_border[k * 2] = b0[0];
619 w->node_border[k * 2 + 1] = b0[1];
620 w->node_has_border[k] = 1;
621 if(!s->have_first)
622 {
623 s->first_c[0] = c0[0];
624 s->first_c[1] = c0[1];
625 s->first_b[0] = b0[0];
626 s->first_b[1] = b0[1];
627 s->have_first = TRUE;
628 }
629 }
630
631 float rc[2];
632 float rb[2];
633 float bmin[2] = { b0[0], b0[1] };
634 float bmax[2] = { b1[0], b1[1] };
635 float cmin[2] = { c0[0], c0[1] };
636 float cmax[2] = { c1[0], c1[1] };
637 gboolean have_rb = FALSE;
638 _polygon_points_recurs(p1, p2, 0.0, 1.0, cmin, cmax, bmin, bmax, rc, rb, w->dpoints, w->dborder,
639 w->with_border, w->pixel_threshold, TRUE, TRUE, TRUE, TRUE, &have_rb);
640
641 dt_masks_dynbuf_add_2(w->dpoints, rc[0], rc[1]);
642 if(w->with_border)
643 {
644 if(!have_rb) dt_masks_outline_offset_along(rc, b1[0] - c1[0], b1[1] - c1[1], p2[4], rb);
645 dt_masks_dynbuf_add_2(w->dborder, rb[0], rb[1]);
646 }
647
648 s->c[0] = rc[0];
649 s->c[1] = rc[1];
650 s->b[0] = rb[0];
651 s->b[1] = rb[1];
652 s->have_prev = TRUE;
653 return TRUE;
654}
655
656/* The header's per-node border sample: a node whose own segment was a point takes its
657 * successor's, so a handle is drawn for every node and drawn where the border is. */
659{
660 if(!w->with_border) return;
661 float *const border = dt_masks_dynbuf_buffer(w->dborder);
662 for(int k = 0; k < w->node_count; k++)
663 {
664 int from = k;
665 for(int step = 0; step < w->node_count && !w->node_has_border[from]; step++) from = (from + 1) % w->node_count;
666 if(!w->node_has_border[from]) return; /* no segment had a direction: the header stays zero */
667 border[k * 6] = w->node_border[from * 2];
668 border[k * 6 + 1] = w->node_border[from * 2 + 1];
669 }
670}
671
675 const double iop_order, const int transform_direction,
676 const dt_masks_distort_t *const dist, float **point_buffer, int *point_count,
677 float **border_buffer, int *border_count, gboolean source)
678{
679 *point_buffer = NULL;
680 *point_count = 0;
681 if(!IS_NULL_PTR(border_buffer)) *border_buffer = NULL;
682 if(!IS_NULL_PTR(border_buffer)) *border_count = 0;
683
684 if(IS_NULL_PTR(mask_form) || IS_NULL_PTR(mask_form->points)) return 0;
685
686 double start2 = 0.0;
688
689 const float input_width = dist->iwidth;
690 const float input_height = dist->iheight;
691
692 /* The pipe's is fixed at one pixel: its interior fill is a scanline over these samples and
693 * needs every row crossed, and its mask_rasterization_step is a spoke-spacing budget, not a
694 * scanline one. The GUI fills nothing and samples at the density it shows. */
695 const int pixel_threshold = IS_NULL_PTR(dist->pipe) ? dist->rasterization_step : 1;
696 const int node_count = (int)g_list_length(mask_form->points);
697
698 dt_masks_dynbuf_t *dpoints = dt_masks_dynbuf_init(1000000, "polygon dpoints");
699 if(IS_NULL_PTR(dpoints)) return 1;
700 dt_masks_dynbuf_t *dborder = NULL;
701 if(!IS_NULL_PTR(border_buffer))
702 {
703 dborder = dt_masks_dynbuf_init(1000000, "polygon dborder");
704 if(IS_NULL_PTR(dborder))
705 {
706 dt_masks_dynbuf_free(dpoints);
707 return 1;
708 }
709 }
710
711 dt_masks_node_polygon_t **nodes = dt_alloc_align((size_t)node_count * sizeof(*nodes));
712 float *node_border = dt_alloc_align((size_t)node_count * 2 * sizeof(float));
713 uint8_t *node_has_border = dt_alloc_align((size_t)node_count);
714 if(IS_NULL_PTR(nodes) || IS_NULL_PTR(node_border) || IS_NULL_PTR(node_has_border))
715 {
716 dt_free_align(nodes);
717 dt_free_align(node_border);
718 dt_free_align(node_has_border);
719 dt_masks_dynbuf_free(dpoints);
720 dt_masks_dynbuf_free(dborder);
721 return 1;
722 }
723 memset(node_has_border, 0, (size_t)node_count);
724
725 // the source shape of a clone is walked in place, shifted from its target
726 float dx = 0.0f;
727 float dy = 0.0f;
728 if(source && transform_direction != DT_DEV_TRANSFORM_DIR_ALL)
729 {
730 const dt_masks_node_polygon_t *const first = (dt_masks_node_polygon_t *)mask_form->points->data;
731 dx = (first->node[0] - mask_form->source[0]) * input_width;
732 dy = (first->node[1] - mask_form->source[1]) * input_height;
733 }
734
735 // the header: three entries per node, ctrl1 / node / ctrl2
736 int node_index = 0;
737 for(const GList *point_node = mask_form->points; point_node; point_node = g_list_next(point_node))
738 {
739 dt_masks_node_polygon_t *const node = (dt_masks_node_polygon_t *)point_node->data;
740 nodes[node_index++] = node;
741 float *const buf = dt_masks_dynbuf_reserve_n(dpoints, 6);
742 if(!IS_NULL_PTR(buf))
743 {
744 buf[0] = node->ctrl1[0] * input_width - dx;
745 buf[1] = node->ctrl1[1] * input_height - dy;
746 buf[2] = node->node[0] * input_width - dx;
747 buf[3] = node->node[1] * input_height - dy;
748 buf[4] = node->ctrl2[0] * input_width - dx;
749 buf[5] = node->ctrl2[1] * input_height - dy;
750 }
751 }
752 if(!IS_NULL_PTR(dborder)) dt_masks_dynbuf_add_zeros(dborder, 6 * node_count);
753
754 const gboolean clockwise = _polygon_is_clockwise(mask_form);
756 {
757 dt_print(DT_DEBUG_MASKS, "[masks %s] polygon_points init took %0.04f sec\n", mask_form->name,
758 dt_get_wtime() - start2);
759 start2 = dt_get_wtime();
760 }
761
762 const _polygon_walk_t walk = { .nodes = nodes,
763 .node_count = node_count,
764 .frame = { input_width, input_height, dx, dy, clockwise ? 1.0f : -1.0f },
765 .pixel_threshold = pixel_threshold,
766 .with_border = (!IS_NULL_PTR(dborder) && node_count >= 3),
767 .clockwise = clockwise,
768 .dpoints = dpoints,
769 .dborder = dborder,
770 .node_border = node_border,
771 .node_has_border = node_has_border };
773 for(int k = 0; k < node_count; k++) _polygon_walk_segment(&walk, &state, k);
774 /* the joint that closes the path, between the last live segment and the first */
775 if(walk.with_border && state.have_prev && state.have_first)
776 _polygon_joint_arc(&walk, state.first_c, state.b, state.first_b);
778
779 dt_free_align(nodes);
780 dt_free_align(node_border);
781 dt_free_align(node_has_border);
782
783 *point_count = dt_masks_dynbuf_position(dpoints) / 2;
784 *point_buffer = dt_masks_dynbuf_harvest(dpoints);
785 dt_masks_dynbuf_free(dpoints);
786 if(!IS_NULL_PTR(dborder))
787 {
788 *border_count = dt_masks_dynbuf_position(dborder) / 2;
789 *border_buffer = dt_masks_dynbuf_harvest(dborder);
790 dt_masks_dynbuf_free(dborder);
791 }
792
794 {
795 dt_print(DT_DEBUG_MASKS, "[masks %s] polygon_points point recurs %0.04f sec\n", mask_form->name,
796 dt_get_wtime() - start2);
797 start2 = dt_get_wtime();
798 }
799
800 // and we transform them with all distorted modules
801 if(source && transform_direction == DT_DEV_TRANSFORM_DIR_ALL)
802 {
803 // we transform with all distortion that happen *before* the module
804 // so we have now the TARGET points in module input reference
806 *point_buffer, *point_count))
807 {
808 // now we move all the points by the shift
809 // so we have now the SOURCE points in module input reference
810 float pts[2] = { mask_form->source[0] * input_width, mask_form->source[1] * input_height };
812 goto fail;
813
814 dx = pts[0] - (*point_buffer)[2];
815 dy = pts[1] - (*point_buffer)[3];
816 __OMP_PARALLEL_FOR_SIMD__(if(*point_count > 100) aligned(point_buffer:64))
817 for(int i = 0; i < *point_count; i++)
818 {
819 (*point_buffer)[i * 2] += dx;
820 (*point_buffer)[i * 2 + 1] += dy;
821 }
822
823 // we apply the rest of the distortions (those after the module)
824 // so we have now the SOURCE points in final image reference
826 *point_buffer, *point_count))
827 goto fail;
828 }
829
831 dt_print(DT_DEBUG_MASKS, "[masks %s] polygon_points end took %0.04f sec\n",
832 mask_form->name, dt_get_wtime() - start2);
833 return 0;
834 }
835 else if(dt_masks_distort_transform(dist, iop_order, transform_direction, *point_buffer, *point_count)
836 && (IS_NULL_PTR(border_buffer)
837 || dt_masks_distort_transform(dist, iop_order, transform_direction, *border_buffer, *border_count)))
838 {
840 dt_print(DT_DEBUG_MASKS, "[masks %s] polygon_points transform took %0.04f sec\n", mask_form->name,
841 dt_get_wtime() - start2);
842 return 0;
843 }
844
845fail:
846 // if we failed, then free all and return
847 dt_pixelpipe_cache_free_align(*point_buffer);
848 *point_buffer = NULL;
849 *point_count = 0;
850 if(!IS_NULL_PTR(border_buffer))
851 {
852 dt_pixelpipe_cache_free_align(*border_buffer);
853 *border_buffer = NULL;
854 *border_count = 0;
855 }
856 return 1;
857}
858
864static float _polygon_get_position_in_segment(float point_x, float point_y,
865 dt_masks_form_t *mask_form, int segment_index)
866{
867 if(IS_NULL_PTR(mask_form) || IS_NULL_PTR(mask_form->points)) return 0;
868 GList *firstpt = g_list_nth(mask_form->points, segment_index);
869 dt_masks_node_polygon_t *point0 = (dt_masks_node_polygon_t *)firstpt->data;
870 // advance to next node in list, if not already on the last
871 GList *nextpt = g_list_next_bounded(firstpt);
872 dt_masks_node_polygon_t *point1 = (dt_masks_node_polygon_t *)nextpt->data;
873 nextpt = g_list_next_bounded(nextpt);
874 dt_masks_node_polygon_t *point2 = (dt_masks_node_polygon_t *)nextpt->data;
875 nextpt = g_list_next_bounded(nextpt);
876 dt_masks_node_polygon_t *point3 = (dt_masks_node_polygon_t *)nextpt->data;
877
878 float min_t = 0.0f;
879 float min_dist = FLT_MAX;
880
881 for(int i = 0; i <= 100; i++)
882 {
883 const float t = i / 100.0f;
884 float sample_x = 0.0f;
885 float sample_y = 0.0f;
886 _polygon_get_XY(point0->node[0], point0->node[1], point1->node[0], point1->node[1],
887 point2->node[0], point2->node[1], point3->node[0], point3->node[1], t,
888 &sample_x, &sample_y);
889
890 const float dist = (point_x - sample_x) * (point_x - sample_x)
891 + (point_y - sample_y) * (point_y - sample_y);
892 if(dist < min_dist)
893 {
894 min_dist = dist;
895 min_t = t;
896 }
897 }
898
899 return min_t;
900}
901
902static void _add_node_to_segment(struct dt_iop_module_t *module,
903 dt_masks_form_t *mask_form, int parent_id,
904 dt_masks_form_gui_t *mask_gui, int form_index)
905{
906 if(IS_NULL_PTR(mask_form) || IS_NULL_PTR(mask_form->points) || IS_NULL_PTR(mask_gui)) return;
907 const guint node_count = g_list_length(mask_form->points);
908 const int selected_segment = dt_masks_gui_selected_segment_index(mask_gui);
909 if(selected_segment < 0 || selected_segment >= (int)node_count) return;
910
911 // we add a new node to the polygon
913 if(IS_NULL_PTR(new_node)) return;
914
915 // set coordinates
916 dt_masks_gui_cursor_to_raw_norm(mask_gui->dev, mask_gui, new_node->node);
917 new_node->ctrl1[0] = new_node->ctrl1[1] = new_node->ctrl2[0] = new_node->ctrl2[1] = -1.0;
919
920 // set other attributes of the new node. we interpolate the starting and the end node of that
921 // segment
922 const float t = _polygon_get_position_in_segment(new_node->node[0], new_node->node[1],
923 mask_form, selected_segment);
924 // start and end node of the segment
925 GList *pt = g_list_nth(mask_form->points, selected_segment);
926 if(IS_NULL_PTR(pt) || IS_NULL_PTR(pt->data))
927 {
928 dt_free(new_node);
929 return;
930 }
932 const GList *const next_pt = g_list_next_wraparound(pt, mask_form->points);
933 if(IS_NULL_PTR(next_pt) || IS_NULL_PTR(next_pt->data))
934 {
935 dt_free(new_node);
936 return;
937 }
938 dt_masks_node_polygon_t *point1 = (dt_masks_node_polygon_t *)next_pt->data;
939 new_node->border[0] = point0->border[0] * (1.0f - t) + point1->border[0] * t;
940 new_node->border[1] = point0->border[1] * (1.0f - t) + point1->border[1] * t;
941
942 mask_form->points = g_list_insert(mask_form->points, new_node, selected_segment + 1);
943 _polygon_init_ctrl_points(mask_form);
944
945 dt_masks_gui_form_create(mask_form, mask_gui, form_index, module);
946
947 mask_gui->node_hovered = selected_segment + 1;
948 mask_gui->node_selected = TRUE;
949 mask_gui->node_selected_idx = selected_segment + 1;
950 mask_gui->seg_hovered = -1;
951 mask_gui->seg_selected = FALSE;
952}
953
954static inline void _polygon_translate_node(dt_masks_node_polygon_t *node, const float delta_x, const float delta_y)
955{
956 dt_masks_translate_ctrl_node(node->node, node->ctrl1, node->ctrl2, delta_x, delta_y);
957}
958
959static void _polygon_translate_all_nodes(dt_masks_form_t *mask_form, const float delta_x, const float delta_y)
960{
961 for(GList *node_entry = mask_form->points; node_entry; node_entry = g_list_next(node_entry))
962 _polygon_translate_node((dt_masks_node_polygon_t *)node_entry->data, delta_x, delta_y);
963}
964
966 float **point_buffer, int *point_count,
967 float **border_buffer, int *border_count,
968 dt_masks_skip_range_t **border_skips, int *border_skip_count,
969 int source, const dt_iop_module_t *module)
970{
971 // Asking for the source outline without a module is a programming error, not an empty shape.
972 if(source && IS_NULL_PTR(module)) return DT_MASKS_RASTER_ERROR;
973 const double ioporder = (module) ? module->iop_order : 0.0f;
974 const dt_masks_distort_t gui_dist = dt_masks_distort_for_gui(develop);
975 if(!IS_NULL_PTR(border_skips)) *border_skips = NULL;
976 if(!IS_NULL_PTR(border_skip_count)) *border_skip_count = 0;
977 const int status = _polygon_get_pts_border(develop, mask_form, ioporder, DT_DEV_TRANSFORM_DIR_ALL, &gui_dist,
978 point_buffer, point_count, border_buffer, border_count, source);
979
980 /* This outline feeds the GUI only -- the rasterisers build their own from the pixel path and
981 * paint every spoke. What the outline shows is the BOUNDARY of what they paint, decided per
982 * sample by dt_masks_outline_boundary_skips(): a border sample is on it iff it is not strictly
983 * inside any other sample's disc. The folds of a concave run, the inside of a joint arc and
984 * one side of the path running through the other all fail that test and travel out-of-band
985 * as skip ranges, which is what every consumer of this outline already reads. The polygon's
986 * own detector, which intersected the outline with itself and chose cuts, is gone with the
987 * brush's. */
988 if(status == 0 && !IS_NULL_PTR(border_buffer) && !IS_NULL_PTR(*border_buffer)
989 && !IS_NULL_PTR(border_skips) && !IS_NULL_PTR(border_skip_count))
990 {
991 const int header = (int)g_list_length(mask_form->points) * 3;
992 *border_skip_count = dt_masks_outline_boundary_skips(*point_buffer, *border_buffer, *border_count, header,
993 border_skips);
994 }
996}
997
998static void _polygon_get_sizes(struct dt_iop_module_t *module, dt_masks_form_t *mask_form,
999 dt_masks_form_gui_t *mask_gui, int form_index,
1000 float *mask_size, float *border_size)
1001{
1002 const dt_masks_form_gui_points_t *gui_points
1003 = (dt_masks_form_gui_points_t *)g_list_nth_data(mask_gui->points, form_index);
1004 if(IS_NULL_PTR(gui_points)) return;
1005
1006 const int node_count = g_list_length(mask_form->points);
1007 float p1[2] = { FLT_MAX, FLT_MAX };
1008 float p2[2] = { -FLT_MAX, -FLT_MAX };
1009
1010 float fp1[2] = { FLT_MAX, FLT_MAX };
1011 float fp2[2] = { -FLT_MAX, -FLT_MAX };
1012
1013 for(int i = node_count * 3; i < gui_points->points_count; i++)
1014 {
1015 // line
1016 const float x = gui_points->points[i * 2];
1017 const float y = gui_points->points[i * 2 + 1];
1018
1019 p1[0] = fminf(p1[0], x);
1020 p2[0] = fmaxf(p2[0], x);
1021 p1[1] = fminf(p1[1], y);
1022 p2[1] = fmaxf(p2[1], y);
1023
1024 // Bounded by border_count, not by the loop's points_count. points/points_count and
1025 // border/border_count are separate arrays with separate lengths -- the other border loops
1026 // in this file spell it `i < border_count` -- so indexing border[] with an index only
1027 // checked against points_count reads past its end whenever the border is the shorter of
1028 // the two. That is Sentry 142390966: EXCEPTION_ACCESS_VIOLATION here, reached from the
1029 // interaction slider (dt_masks_form_set_interaction_value -> _change_size). border can
1030 // also be NULL outright when none was rasterised.
1031 if(!IS_NULL_PTR(border_size)
1032 && !IS_NULL_PTR(gui_points->border)
1033 && i < gui_points->border_count)
1034 {
1035 // border
1036 const float fx = gui_points->border[i * 2];
1037 const float fy = gui_points->border[i * 2 + 1];
1038
1039 /* The "??? looks like when x border is nan then y is a point index" this replaces was a
1040 * true reading of the in-band jump encoding, and the question mark was the problem: a
1041 * coordinate buffer that sometimes holds an index, recognisable only by a NaN beside it.
1042 * It is gone; the border is coordinates. */
1043 fp1[0] = fminf(fp1[0], fx);
1044 fp2[0] = fmaxf(fp2[0], fx);
1045 fp1[1] = fminf(fp1[1], fy);
1046 fp2[1] = fmaxf(fp2[1], fy);
1047 }
1048 }
1049
1050 float mask_span[2] = { p2[0] - p1[0], p2[1] - p1[1] };
1051 dt_dev_coordinates_preview_abs_to_image_norm(mask_gui->dev, mask_span, 1);
1052 *mask_size = fmaxf(mask_span[0], mask_span[1]);
1053
1054 if(!IS_NULL_PTR(border_size))
1055 {
1056 float border_span[2] = { fp2[0] - fp1[0], fp2[1] - fp1[1] };
1057 dt_dev_coordinates_preview_abs_to_image_norm(mask_gui->dev, border_span, 1);
1058 *border_size = fmaxf(border_span[0], border_span[1]);
1059 }
1060}
1061
1062static gboolean _polygon_form_gravity_center(const dt_masks_form_t *mask_form, float *center_x,
1063 float *center_y, float *surface);
1064
1066 dt_masks_interaction_t interaction)
1067{
1068 if(IS_NULL_PTR(mask_form) || IS_NULL_PTR(mask_form->points)) return NAN;
1069
1070 switch(interaction)
1071 {
1073 {
1074 const float size = dt_masks_get_form_size_from_nodes(mask_form->points);
1075 if(size <= 0.0f) return NAN;
1076 return size;
1077 }
1079 {
1080 float fading_sum = 0.0f;
1081 int fading_count = 0;
1082
1083 for(const GList *point_node = mask_form->points; point_node; point_node = g_list_next(point_node))
1084 {
1085 const dt_masks_node_polygon_t *node = (const dt_masks_node_polygon_t *)point_node->data;
1086 if(IS_NULL_PTR(node)) continue;
1087 fading_sum += node->border[0] + node->border[1];
1088 fading_count += 2;
1089 }
1090
1091 return fading_count > 0 ? fading_sum / (float)fading_count : NAN;
1092 }
1093 default:
1094 return NAN;
1095 }
1096}
1097
1098static gboolean _polygon_get_gravity_center(dt_develop_t *dev, const dt_masks_form_t *mask_form,
1099 float center[2], float *area)
1100{
1101 if(IS_NULL_PTR(mask_form) || IS_NULL_PTR(mask_form->points) || IS_NULL_PTR(center)) return FALSE;
1102
1103 const int points_count = g_list_length(mask_form->points);
1104 if(points_count <= 0) return FALSE;
1105
1106 float *point_buffer = dt_alloc_align_float((size_t)points_count * 2);
1107 if(IS_NULL_PTR(point_buffer)) return FALSE;
1108
1109 int i = 0;
1110 for(const GList *point_node = mask_form->points; point_node; point_node = g_list_next(point_node))
1111 {
1112 const dt_masks_node_polygon_t *node = (const dt_masks_node_polygon_t *)point_node->data;
1113 if(IS_NULL_PTR(node)) continue;
1114 point_buffer[2 * i] = node->node[0];
1115 point_buffer[2 * i + 1] = node->node[1];
1116 i++;
1117 }
1118
1119 const gboolean ok = dt_masks_center_of_gravity_from_points(point_buffer, i, center, area);
1120 dt_free_align(point_buffer);
1121 return ok;
1122}
1123
1124static int _change_size(dt_masks_form_t *mask_form, int parent_id, dt_masks_form_gui_t *mask_gui,
1125 struct dt_iop_module_t *module, int form_index, const float amount,
1126 const dt_masks_increment_t increment, const int flow);
1127static int _change_fading(dt_masks_form_t *mask_form, int parent_id, dt_masks_form_gui_t *mask_gui,
1128 struct dt_iop_module_t *module, int form_index, const float amount,
1129 const dt_masks_increment_t increment, int flow);
1130
1132 dt_masks_interaction_t interaction, float value,
1133 dt_masks_increment_t increment, int flow,
1134 dt_masks_form_gui_t *mask_gui, struct dt_iop_module_t *module)
1135{
1136 if(IS_NULL_PTR(mask_form)) return NAN;
1137 // Mirrors _dt_masks_events_get_dispatch_form()'s form_index: this shape's position in the
1138 // currently displayed group, so dt_masks_gui_form_create() below refreshes the right
1139 // mask_gui->points slot instead of clobbering whatever shape sits at index 0.
1140 const int index = (!IS_NULL_PTR(mask_gui) && mask_gui->group_selected >= 0) ? mask_gui->group_selected : 0;
1141
1142 switch(interaction)
1143 {
1145 if(!_change_size(mask_form, 0, mask_gui, module, index, value, increment, flow)) return NAN;
1146 return _polygon_get_interaction_value(mask_form, interaction);
1148 if(!_change_fading(mask_form, 0, mask_gui, module, index, value, increment, flow)) return NAN;
1149 return _polygon_get_interaction_value(mask_form, interaction);
1150 default:
1151 return NAN;
1152 }
1153}
1154
1158static void _polygon_get_distance(float point_x, float point_y, float radius,
1159 dt_masks_form_gui_t *mask_gui, int form_index,
1160 int node_count, int *inside, int *inside_border,
1161 int *near_handle, int *inside_source, float *dist)
1162{
1163 // initialise returned values
1164 *inside_source = 0;
1165 *inside = 0;
1166 *inside_border = 0;
1167 *near_handle = -1;
1168 *dist = FLT_MAX;
1169
1170 if(IS_NULL_PTR(mask_gui)) return;
1171 dt_masks_form_gui_points_t *gui_points
1172 = (dt_masks_form_gui_points_t *)g_list_nth_data(mask_gui->points, form_index);
1173 if(IS_NULL_PTR(gui_points)) return;
1174 /* Nothing to walk when the cursor cannot reach a sample: the box every sample spans, grown by
1175 * the cursor's reach, answers for the whole shape in four comparisons, and every answer
1176 * initialised above is what the walk would have given -- nothing inside, nothing near. */
1177 if(!dt_masks_gui_points_reach(gui_points, point_x, point_y, 2.0f * radius)) return;
1178
1179 float min_dist_pixel = FLT_MAX;
1180
1181 const float radius2 = radius * radius;
1182 const float pt[2] = { point_x, point_y };
1183
1184 // we first check if we are inside the source form
1185 if(gui_points->source && gui_points->points
1186 && gui_points->source_count > node_count * 3 && gui_points->points_count > node_count * 3
1187 && dt_masks_point_in_form_exact(pt, 1, gui_points->source, node_count * 3,
1188 gui_points->source_count, NULL, 0) >= 0)
1189 {
1190 *inside_source = 1;
1191 *inside = 1;
1192
1193 // offset between form origin and source origin
1194 const float offset_x = -gui_points->points[2] + gui_points->source[2];
1195 const float offset_y = -gui_points->points[3] + gui_points->source[3];
1196 int current_seg = 1;
1197
1198 // distance from source border
1199 for(int i = node_count * 3; i < gui_points->points_count; i++)
1200 {
1201 // check if we advance to next polygon segment
1202 if(gui_points->points[i * 2] == gui_points->points[current_seg * 6 + 2]
1203 && gui_points->points[i * 2 + 1] == gui_points->points[current_seg * 6 + 3])
1204 {
1205 current_seg = (current_seg + 1) % node_count;
1206 }
1207
1208 // calculate source position for current point
1209 const float source_x = gui_points->points[i * 2] + offset_x;
1210 const float source_y = gui_points->points[i * 2 + 1] + offset_y;
1211
1212 // distance from tested point to current source point
1213 const float sdx = point_x - source_x;
1214 const float sdy = point_y - source_y;
1215 const float sdd = sqf(sdx) + sqf(sdy);
1216 if(sdd < min_dist_pixel)
1217 min_dist_pixel = sdd;
1218 }
1219 *dist = min_dist_pixel;
1220 return;
1221 }
1222
1223 // we check if we are near_handle a segment
1224 if(gui_points->points && gui_points->points_count > 2 + node_count * 3)
1225 {
1226 int current_seg = 1;
1227 for(int i = node_count * 3; i < gui_points->points_count; i++)
1228 {
1229 // do we change of polygon segment ?
1230 if(gui_points->points[i * 2 + 1] == gui_points->points[current_seg * 6 + 3]
1231 && gui_points->points[i * 2] == gui_points->points[current_seg * 6 + 2])
1232 {
1233 current_seg = (current_seg + 1) % node_count;
1234 }
1235 //distance from tested point to current form point
1236 const float yy = gui_points->points[i * 2 + 1];
1237 const float xx = gui_points->points[i * 2];
1238
1239 const float dx = point_x - xx;
1240 const float dy = point_y - yy;
1241 const float dd = sqf(dx) + sqf(dy);
1242 if(dd < min_dist_pixel)
1243 {
1244 min_dist_pixel = dd;
1245
1246 if(current_seg >= 0 && dd < radius2)
1247 {
1248 if(current_seg == 0)
1249 *near_handle = node_count - 1;
1250 else
1251 *near_handle = current_seg - 1;
1252 }
1253 }
1254 }
1255 }
1256
1257 *dist = min_dist_pixel;
1258
1259 if(!gui_points->border || gui_points->border_count <= node_count * 3) return;
1260
1261 // Proximity to the feather's OUTER line, the same measure the form line was tested with above.
1262 int near_border = 0;
1263 for(int i = node_count * 3; i < gui_points->border_count && !near_border; i++)
1264 {
1265 const float bdx = point_x - gui_points->border[i * 2];
1266 const float bdy = point_y - gui_points->border[i * 2 + 1];
1267 near_border = (sqf(bdx) + sqf(bdy)) < radius2;
1268 }
1269
1270 const int enclosed = dt_masks_point_in_form_exact(pt, 1, gui_points->border, node_count * 3,
1271 gui_points->border_count,
1272 gui_points->border_skips,
1273 gui_points->border_skip_count) >= 0;
1274
1275 /* The feathering is part of the shape and answers `inside'; being anywhere in that band is not
1276 * a hit on the border. Only proximity to its outer line is, and only where no form-line segment
1277 * is closer. The shared hit test answers on the border before the segment, so reporting the
1278 * whole band left the segment reachable from the inside of the form line alone -- the outer
1279 * half of the cursor's reach, the half that falls inside the feathering, went to the border
1280 * instead. Same definition as the brush, whose band covers the whole stroke and hit this first. */
1281 *inside = enclosed || near_border;
1282 *inside_border = near_border && (*near_handle < 0);
1283}
1284
1290static gboolean _polygon_border_handle_cb(const dt_masks_form_gui_points_t *gui_points, int node_count,
1291 int node_index, float *handle_x, float *handle_y, void *user_data)
1292{
1293 if(IS_NULL_PTR(gui_points) || node_index < 0 || node_index >= node_count) return FALSE;
1294 *handle_x = gui_points->border[node_index * 6];
1295 *handle_y = gui_points->border[node_index * 6 + 1];
1296 return TRUE;
1297}
1298
1302static void _polygon_curve_handle_cb(const dt_masks_form_gui_points_t *gui_points, int node_index,
1303 float *handle_x, float *handle_y, void *user_data)
1304{
1305
1306 _polygon_ctrl2_to_handle(gui_points->points[node_index * 6 + 2], gui_points->points[node_index * 6 + 3],
1307 gui_points->points[node_index * 6 + 4], gui_points->points[node_index * 6 + 5],
1308 handle_x, handle_y, gui_points->clockwise);
1309}
1310
1314static void _polygon_distance_cb(float pointer_x, float pointer_y, float cursor_radius,
1315 dt_masks_form_gui_t *mask_gui, int form_index, int node_count, int *inside,
1316 int *inside_border, int *near_handle, int *inside_source, float *dist, void *user_data)
1317{
1318
1319 _polygon_get_distance(pointer_x, pointer_y, cursor_radius, mask_gui, form_index, node_count,
1320 inside, inside_border, near_handle, inside_source, dist);
1321}
1322
1323static int _find_closest_handle(dt_masks_form_t *mask_form, dt_masks_form_gui_t *mask_gui, int form_index)
1324{
1325 return dt_masks_find_closest_handle_common(mask_form, mask_gui, form_index, -1,
1327 _polygon_distance_cb, NULL, NULL);
1328}
1329
1336static void _polygon_gui_gravity_center(const float *point_buffer, int point_count,
1337 float *center_x, float *center_y, float *area)
1338{
1339 if(IS_NULL_PTR(point_buffer) || point_count < 3) return;
1340
1341 float centroid_x = 0.0f;
1342 float centroid_y = 0.0f;
1343 float signed_area = 0.0f;
1344
1345 for(int node_index = 0; node_index < point_count; node_index++)
1346 {
1347 const int next_index = (node_index + 1) % point_count;
1348 const float x0 = point_buffer[node_index * 2];
1349 const float y0 = point_buffer[node_index * 2 + 1];
1350 const float x1 = point_buffer[next_index * 2];
1351 const float y1 = point_buffer[next_index * 2 + 1];
1352 const float cross = x0 * y1 - x1 * y0;
1353
1354 signed_area += cross;
1355 centroid_x += (x0 + x1) * cross;
1356 centroid_y += (y0 + y1) * cross;
1357 }
1358
1359 if(fabsf(signed_area) > 1e-8f)
1360 {
1361 const float inv_divisor = 1.0f / (3.0f * signed_area);
1362 if(!IS_NULL_PTR(center_x)) *center_x = centroid_x * inv_divisor;
1363 if(!IS_NULL_PTR(center_y)) *center_y = centroid_y * inv_divisor;
1364 }
1365 if(!IS_NULL_PTR(area)) *area = signed_area;
1366}
1367
1371static gboolean _polygon_form_gravity_center(const dt_masks_form_t *mask_form,
1372 float *center_x, float *center_y, float *area)
1373{
1374 if(IS_NULL_PTR(mask_form) || IS_NULL_PTR(mask_form->points) || g_list_shorter_than(mask_form->points, 3)) return FALSE;
1375
1376 float centroid_x = 0.0f;
1377 float centroid_y = 0.0f;
1378 float signed_area = 0.0f;
1379
1380 for(const GList *node_iter = mask_form->points; node_iter; node_iter = g_list_next(node_iter))
1381 {
1382 const GList *next_iter = g_list_next_wraparound(node_iter, mask_form->points);
1383 const dt_masks_node_polygon_t *node0 = (const dt_masks_node_polygon_t *)node_iter->data;
1384 const dt_masks_node_polygon_t *node1 = (const dt_masks_node_polygon_t *)next_iter->data;
1385 if(!node0 || !node1) return FALSE;
1386
1387 const float cross = node0->node[0] * node1->node[1] - node1->node[0] * node0->node[1];
1388 signed_area += cross;
1389 centroid_x += (node0->node[0] + node1->node[0]) * cross;
1390 centroid_y += (node0->node[1] + node1->node[1]) * cross;
1391 }
1392
1393 if(!IS_NULL_PTR(area)) *area = signed_area;
1394 if(fabsf(signed_area) <= 1e-8f) return FALSE;
1395
1396 const float inv_divisor = 1.0f / (3.0f * signed_area);
1397 if(!IS_NULL_PTR(center_x)) *center_x = centroid_x * inv_divisor;
1398 if(!IS_NULL_PTR(center_y)) *center_y = centroid_y * inv_divisor;
1399 return TRUE;
1400}
1401
1405static int _init_fading(dt_masks_form_t *mask_form, const float amount,
1406 const dt_masks_increment_t increment, const int flow,
1407 const float mask_size, const float border_size)
1408{
1409 const float mask_fading = dt_masks_get_set_conf_value(mask_form, "fading", amount,
1410 FADING_MIN, FADING_MAX, increment, flow);
1411 dt_toast_log(_("Fading: %3.2f%%"), (border_size * mask_fading) / mask_size * 100.0f);
1412 return 1;
1413}
1414
1421static int _change_size(dt_masks_form_t *mask_form, int parent_id, dt_masks_form_gui_t *mask_gui,
1422 struct dt_iop_module_t *module, int form_index, const float amount,
1423 const dt_masks_increment_t increment, const int flow)
1424{
1425 if(IS_NULL_PTR(mask_form) || IS_NULL_PTR(mask_form->points)) return 0;
1426
1427 float center_x = 0.0f;
1428 float center_y = 0.0f;
1429 float signed_area = 0.0f;
1430 if(!_polygon_form_gravity_center(mask_form, &center_x, &center_y, &signed_area)) return 1;
1431
1432 // Avoid expanding degenerate polygons or overly large shapes.
1433 if(amount < 1.0f && signed_area < 0.00001f && signed_area > -0.00001f) return 1;
1434 if(amount > 1.0f && signed_area > 4.0f) return 1;
1435
1436 float scale_delta = amount;
1437 switch(increment)
1438 {
1440 scale_delta = powf(amount, (float)flow);
1441 break;
1443 // For polygon global scaling, interpret offset as multiplicative offset around 1.0.
1444 scale_delta = 1.0f + amount * (float)flow;
1445 break;
1447 default:
1448 scale_delta = amount;
1449 break;
1450 }
1451
1452 int node_index = 0;
1453 for(GList *node_iter = mask_form->points; node_iter; node_iter = g_list_next(node_iter), node_index++)
1454 {
1455 if(dt_masks_gui_change_affects_selected_node_or_all(mask_gui, node_index))
1456 {
1457 dt_masks_node_polygon_t *node = (dt_masks_node_polygon_t *)node_iter->data;
1458 if(!node) continue;
1459
1460 const float new_node_x = center_x + (node->node[0] - center_x) * scale_delta;
1461 const float new_node_y = center_y + (node->node[1] - center_y) * scale_delta;
1462 const float ctrl1_offset_x = (node->ctrl1[0] - node->node[0]) * scale_delta;
1463 const float ctrl1_offset_y = (node->ctrl1[1] - node->node[1]) * scale_delta;
1464 const float ctrl2_offset_x = (node->ctrl2[0] - node->node[0]) * scale_delta;
1465 const float ctrl2_offset_y = (node->ctrl2[1] - node->node[1]) * scale_delta;
1466
1467 // Update all coordinates while keeping local offsets consistent.
1468 node->node[0] = new_node_x;
1469 node->node[1] = new_node_y;
1470 node->ctrl1[0] = new_node_x + ctrl1_offset_x;
1471 node->ctrl1[1] = new_node_y + ctrl1_offset_y;
1472 node->ctrl2[0] = new_node_x + ctrl2_offset_x;
1473 node->ctrl2[1] = new_node_y + ctrl2_offset_y;
1474 }
1475 }
1476
1477 float mask_size = 0.0f;
1478 _polygon_get_sizes(module, mask_form, mask_gui, form_index, &mask_size, NULL);
1479
1480 dt_toast_log(_("Size: %3.2f%%"), mask_size * 100.0f);
1481
1482 // Rebuild the cached GUI geometry.
1483 dt_masks_gui_form_create(mask_form, mask_gui, form_index, module);
1484 return 1;
1485}
1486
1490static int _change_fading(dt_masks_form_t *mask_form, int parent_id, dt_masks_form_gui_t *mask_gui,
1491 struct dt_iop_module_t *module, int form_index, const float amount,
1492 const dt_masks_increment_t increment, int flow)
1493{
1494 int node_index = 0;
1495 const float scale_amount = powf(amount, (float)flow);
1496 const float offset_amount = amount * (float)flow;
1497
1498 for(GList *node_iter = mask_form->points; node_iter; node_iter = g_list_next(node_iter), node_index++)
1499 {
1500 if(dt_masks_gui_change_affects_selected_node_or_all(mask_gui, node_index))
1501 {
1502 dt_masks_node_polygon_t *node = (dt_masks_node_polygon_t *)node_iter->data;
1503 if(!node) continue;
1504
1505 node->border[0] = CLAMPF(dt_masks_apply_increment_precomputed(node->border[0], amount, scale_amount,
1506 offset_amount, increment),
1508 node->border[1] = CLAMPF(dt_masks_apply_increment_precomputed(node->border[1], amount, scale_amount,
1509 offset_amount, increment),
1511 }
1512 }
1513
1514 float mask_size = 1.0f;
1515 float border_size = 0.0f;
1516 _polygon_get_sizes(module, mask_form, mask_gui, form_index, &mask_size, &border_size);
1517
1518 _init_fading(mask_form, amount, increment, flow, mask_size, border_size);
1519
1520 // Rebuild the cached GUI geometry.
1521 dt_masks_gui_form_create(mask_form, mask_gui, form_index, module);
1522
1523 return 1;
1524}
1525
1529/* Shape handlers receive widget-space coordinates, while normalized output-image
1530 * coordinates come from `mask_gui->rel_pos` and absolute output-image
1531 * coordinates come from `mask_gui->pos`. */
1532static int _polygon_events_mouse_scrolled(struct dt_iop_module_t *module, double x, double y, int up, int flow,
1533 uint32_t state, dt_masks_form_t *mask_form, int parent_id,
1534 dt_masks_form_gui_t *mask_gui, int form_index,
1535 dt_masks_interaction_t interaction)
1536{
1537
1538
1539
1540 if(mask_gui->creation)
1541 {
1542 // no change during creation
1543 return 0;
1544 }
1545
1546 /* `state` is the caller's raw key state, kept for the callback signature: the property to
1547 * act on was already resolved from it by dt_masks_scroll_get_interaction(). A polygon owns
1548 * no rotation, so that mapping falls through and the wheel does nothing here. Size and
1549 * fading apply to the selected node when there is one, to every node otherwise -- that
1550 * scoping is the selection's business (dt_masks_gui_change_affects_selected_node_or_all),
1551 * not the wheel's, which is why a selected node no longer forces fading. */
1552 if(mask_gui->edit_mode == DT_MASKS_EDIT_FULL && dt_masks_is_anything_selected(mask_gui))
1553 {
1554 switch(interaction)
1555 {
1557 return dt_masks_form_change_opacity(mask_gui->dev, mask_form, parent_id, up, flow);
1559 return _change_fading(mask_form, parent_id, mask_gui, module, form_index, up ? +0.01f : -0.01f,
1562 return _change_size(mask_form, parent_id, mask_gui, module, form_index, up ? 1.02f : 0.98f,
1564 default:
1565 return 0;
1566 }
1567 }
1568 return 0;
1569}
1570
1575{
1576 // we don't want a form with less than 3 points
1577 if(g_list_shorter_than(mask_form->points, 4))
1578 {
1579 dt_toast_log(_("Polygon mask requires at least 3 nodes."));
1580 return 1;
1581 }
1582
1583 dt_iop_module_t *creation_module = mask_gui->creation_module;
1584 // we delete last point (the one we are currently dragging)
1585 dt_masks_node_polygon_t *last_node = (dt_masks_node_polygon_t *)g_list_last(mask_form->points)->data;
1586 mask_form->points = g_list_remove(mask_form->points, last_node);
1587 dt_free(last_node);
1588
1589 mask_gui->node_dragging = -1;
1590 _polygon_init_ctrl_points(mask_form);
1591
1592 dt_masks_gui_form_save_creation(mask_gui->dev, creation_module, mask_form, mask_gui);
1593
1594 return 1;
1595}
1596
1597static int _polygon_events_button_pressed(struct dt_iop_module_t *module, double x, double y,
1598 double pressure, int which, int type, uint32_t state,
1599 dt_masks_form_t *mask_form, int parent_id,
1600 dt_masks_form_gui_t *mask_gui, int form_index)
1601{
1602 if(type == GDK_2BUTTON_PRESS || type == GDK_3BUTTON_PRESS) return 1;
1603
1604 if(which == 1)
1605 {
1606 if(mask_gui->creation)
1607 {
1608 if(mask_gui->creation_closing_form)
1609 return _polygon_creation_closing_form(mask_form, mask_gui);
1610
1611 if(dt_modifier_is(state, DT_PRIMARY_MASK | GDK_SHIFT_MASK) || dt_modifier_is(state, GDK_SHIFT_MASK))
1612 {
1613 // set some absolute or relative position for the source of the clone mask
1614 if(mask_form->type & DT_MASKS_CLONE)
1615 {
1617 return 1;
1618 }
1619 }
1620
1621 else // we create a node
1622 {
1623 float masks_border = MIN(dt_conf_get_float("plugins/darkroom/masks/polygon/fading"), FADING_MAX);
1624
1625 int node_count = g_list_length(mask_form->points);
1626 // change the values
1627 dt_masks_node_polygon_t *polygon_node = (dt_masks_node_polygon_t *)(malloc(sizeof(dt_masks_node_polygon_t)));
1628 if(IS_NULL_PTR(polygon_node)) return 0;
1629
1630 dt_masks_gui_cursor_to_raw_norm(mask_gui->dev, mask_gui, polygon_node->node);
1631
1632 polygon_node->ctrl1[0] = polygon_node->ctrl1[1] = polygon_node->ctrl2[0] = polygon_node->ctrl2[1] = -1.0;
1633 polygon_node->border[0] = polygon_node->border[1] = MAX(FADING_MIN, masks_border);
1634 polygon_node->state = DT_MASKS_POINT_STATE_NORMAL;
1635
1636 if(node_count == 0)
1637 {
1638 // create the first node
1639 dt_masks_node_polygon_t *polygon_first_node = (dt_masks_node_polygon_t *)(malloc(sizeof(dt_masks_node_polygon_t)));
1640 polygon_first_node->node[0] = polygon_node->node[0];
1641 polygon_first_node->node[1] = polygon_node->node[1];
1642 polygon_first_node->ctrl1[0] = polygon_first_node->ctrl1[1] = polygon_first_node->ctrl2[0] = polygon_first_node->ctrl2[1] = -1.0;
1643 polygon_first_node->border[0] = polygon_first_node->border[1] = MAX(FADING_MIN, masks_border);
1644 polygon_first_node->state = DT_MASKS_POINT_STATE_NORMAL;
1645 mask_form->points = g_list_append(mask_form->points, polygon_first_node);
1646
1647 if(mask_form->type & DT_MASKS_CLONE)
1648 {
1649 dt_masks_set_source_pos_initial_value(mask_gui, mask_form);
1650 }
1651 else
1652 {
1653 // not used by regular masks
1654 mask_form->source[0] = mask_form->source[1] = 0.0f;
1655 }
1656 node_count++;
1657 }
1658 mask_form->points = g_list_append(mask_form->points, polygon_node);
1659
1660 // if this is a ctrl click, the last created point is a sharp one
1662 {
1663 dt_masks_node_polygon_t *polygon_last_node = g_list_nth_data(mask_form->points, node_count - 1);
1664 polygon_last_node->ctrl1[0] = polygon_last_node->ctrl2[0] = polygon_last_node->node[0];
1665 polygon_last_node->ctrl1[1] = polygon_last_node->ctrl2[1] = polygon_last_node->node[1];
1666 polygon_last_node->state = DT_MASKS_POINT_STATE_USER;
1667 }
1668
1669 mask_gui->node_hovered = node_count;
1670 mask_gui->node_selected = TRUE;
1671 mask_gui->node_selected_idx = node_count;
1672 mask_gui->node_dragging = node_count;
1673 _polygon_init_ctrl_points(mask_form);
1674 }
1675
1676 // we recreate the form points in all case
1677 dt_masks_gui_form_create(mask_form, mask_gui, form_index, module);
1678
1679 return 1;
1680 }// end of creation mode
1681
1682 dt_masks_form_gui_points_t *gui_points
1683 = (dt_masks_form_gui_points_t *)g_list_nth_data(mask_gui->points, form_index);
1684 if(IS_NULL_PTR(gui_points)) return 0;
1685
1686 // The shape handler runs before the shared press-state selection update,
1687 // so concrete hovered targets must win over stale form/source selection.
1688 else if(mask_gui->node_hovered >= 0)
1689 {
1690 // if ctrl is pressed, we change the type of point
1692 {
1694 = (dt_masks_node_polygon_t *)g_list_nth_data(mask_form->points, mask_gui->node_hovered);
1695 if(IS_NULL_PTR(node)) return 0;
1696 dt_masks_toggle_bezier_node_type(module, mask_form, mask_gui, form_index, gui_points,
1697 mask_gui->node_hovered, node->node, node->ctrl1, node->ctrl2,
1698 &node->state);
1699 return 1;
1700 }
1701 /*// we register the current position to avoid accidental move
1702 if(mask_gui->node_selected < 0 && mask_gui->scrollx == 0.0f && mask_gui->scrolly == 0.0f)
1703 {
1704 mask_gui->scrollx = pzx;
1705 mask_gui->scrolly = pzy;
1706 }*/
1707 mask_gui->delta[0] = gui_points->points[mask_gui->node_hovered * 6 + 2] - mask_gui->pos[0];
1708 mask_gui->delta[1] = gui_points->points[mask_gui->node_hovered * 6 + 3] - mask_gui->pos[1];
1709
1710 return 1;
1711 }
1712 else if(mask_gui->handle_hovered >= 0)
1713 {
1714 if(!dt_masks_node_is_cusp(gui_points, mask_gui->handle_hovered))
1715 {
1716 // we need to find the handle position
1717 float handle_x, handle_y;
1718 const int handle_index = mask_gui->handle_hovered;
1719 _polygon_ctrl2_to_handle(gui_points->points[handle_index * 6 + 2],
1720 gui_points->points[handle_index * 6 + 3],
1721 gui_points->points[handle_index * 6 + 4],
1722 gui_points->points[handle_index * 6 + 5],
1723 &handle_x, &handle_y, gui_points->clockwise);
1724 // compute offsets
1725 mask_gui->delta[0] = handle_x - mask_gui->pos[0];
1726 mask_gui->delta[1] = handle_y - mask_gui->pos[1];
1727
1728 return 1;
1729 }
1730 }
1731 else if(mask_gui->handle_border_hovered >= 0)
1732 {
1733 const float handle_x = gui_points->border[mask_gui->handle_border_hovered * 6];
1734 const float handle_y = gui_points->border[mask_gui->handle_border_hovered * 6 + 1];
1735 mask_gui->delta[0] = handle_x - mask_gui->pos[0];
1736 mask_gui->delta[1] = handle_y - mask_gui->pos[1];
1737
1738 return 1;
1739 }
1740 else if(mask_gui->seg_hovered >= 0)
1741 {
1742 mask_gui->node_hovered = -1;
1743
1745 {
1746 _add_node_to_segment(module, mask_form, parent_id, mask_gui, form_index);
1747 }
1748 else
1749 {
1750 // we move the entire segment
1751 mask_gui->delta[0] = gui_points->points[mask_gui->seg_hovered * 6 + 2] - mask_gui->pos[0];
1752 mask_gui->delta[1] = gui_points->points[mask_gui->seg_hovered * 6 + 3] - mask_gui->pos[1];
1753 }
1754 return 1;
1755 }
1756 else if(mask_gui->source_selected && mask_gui->edit_mode == DT_MASKS_EDIT_FULL)
1757 {
1758 // we start the source dragging
1759 mask_gui->delta[0] = gui_points->source[2] - mask_gui->pos[0];
1760 mask_gui->delta[1] = gui_points->source[3] - mask_gui->pos[1];
1761 return 1;
1762 }
1763 else if(mask_gui->form_selected && mask_gui->edit_mode == DT_MASKS_EDIT_FULL)
1764 {
1765 // we start the form dragging
1766 mask_gui->delta[0] = gui_points->points[2] - mask_gui->pos[0];
1767 mask_gui->delta[1] = gui_points->points[3] - mask_gui->pos[1];
1768 return 1;
1769 }
1770 }
1771
1772 return 0;
1773}
1774
1775static int _polygon_events_button_released(struct dt_iop_module_t *module, double x, double y, int which,
1776 uint32_t state, dt_masks_form_t *mask_form, int parent_id,
1777 dt_masks_form_gui_t *mask_gui, int form_index)
1778{
1779 if(IS_NULL_PTR(mask_gui)) return 0;
1780 if(mask_gui->creation) return 1;
1781
1782 if(which == 1)
1783 {
1784 if(dt_masks_gui_is_dragging(mask_gui))
1785 return 1;
1786 }
1787 return 0;
1788}
1789
1790static int _polygon_events_key_pressed(struct dt_iop_module_t *module, GdkEventKey *event,
1791 dt_masks_form_t *mask_form, int parent_id,
1792 dt_masks_form_gui_t *mask_gui, int form_index)
1793{
1794 if(IS_NULL_PTR(mask_gui) || IS_NULL_PTR(mask_form)) return 0;
1795
1796 guint key = dt_keys_mainpad_alternatives(event->keyval);
1797
1798
1799 if(mask_gui->creation)
1800 {
1801 switch(key)
1802 {
1803 case GDK_KEY_BackSpace:
1804 {
1805 // Minimum points to create a polygon
1806 if(mask_gui->node_dragging < 1)
1807 {
1808 dt_masks_form_exit_creation(module, mask_gui);
1809 return 1;
1810 }
1811 // switch previous node coords to the current one
1812 dt_masks_node_polygon_t *previous_node
1813 = (dt_masks_node_polygon_t *)g_list_nth_data(mask_form->points, mask_gui->node_dragging - 1);
1814 dt_masks_node_polygon_t *current_node
1815 = (dt_masks_node_polygon_t *)g_list_nth_data(mask_form->points, mask_gui->node_dragging);
1816 if(!previous_node || !current_node) return 0;
1817 previous_node->node[0] = current_node->node[0];
1818 previous_node->node[1] = current_node->node[1];
1819
1820 dt_masks_remove_node(module, mask_form, 0, mask_gui, 0, mask_gui->node_dragging);
1821 // Decrease the current dragging node index
1822 mask_gui->node_dragging -= 1;
1823
1825 return 1;
1826 }
1827 case GDK_KEY_Return:
1828 return _polygon_creation_closing_form(mask_form, mask_gui);
1829 }
1830 }
1831 return 0;
1832}
1833
1842static int _polygon_events_mouse_moved(struct dt_iop_module_t *module, double x, double y, double pressure,
1843 int which, dt_masks_form_t *mask_form, int parent_id,
1844 dt_masks_form_gui_t *mask_gui, int form_index)
1845{
1846 // centre view will have zoom_scale * backbuf_width pixels, we want the handle offset to scale with DPI:
1847 dt_develop_t *const dev = mask_gui->dev;
1848 dt_masks_form_gui_points_t *gui_points
1849 = (dt_masks_form_gui_points_t *)g_list_nth_data(mask_gui->points, form_index);
1850 if(IS_NULL_PTR(gui_points)) return 0;
1851
1853 const int iwidth = geometry.raw_width;
1854 const int iheight = geometry.raw_height;
1855
1856 if(mask_gui->node_dragging >= 0)
1857 {
1858 if(IS_NULL_PTR(mask_form->points)) return 0;
1859 if(mask_gui->creation && !g_list_shorter_than(mask_form->points, 4))
1860 {
1861 // check if we are near_handle the first point to close the polygon on creation
1862 const float dist_curs = DT_GUI_MOUSE_EFFECT_RADIUS;
1863 const float dx = mask_gui->pos[0] - gui_points->points[2];
1864 const float dy = mask_gui->pos[1] - gui_points->points[3];
1865 const float dist2 = dx * dx + dy * dy;
1866 mask_gui->creation_closing_form = dist2 <= dist_curs * dist_curs;
1867 }
1868
1869 dt_masks_node_polygon_t *dragged_node
1870 = (dt_masks_node_polygon_t *)g_list_nth_data(mask_form->points, mask_gui->node_dragging);
1871 if(IS_NULL_PTR(dragged_node)) return 0;
1872
1873 float dx = 0.0f;
1874 float dy = 0.0f;
1875 dt_masks_gui_delta_from_raw_anchor(dev, mask_gui, dragged_node->node, &dx, &dy);
1876 _polygon_translate_node(dragged_node, dx, dy);
1877
1878 // if first point, adjust the source position accordingly
1879 if((mask_form->type & DT_MASKS_CLONE) && mask_gui->node_dragging == 0)
1880 dt_masks_translate_source(mask_form, dx, dy);
1881
1882 if(mask_gui->creation)
1883 _polygon_init_ctrl_points(mask_form);
1884
1885 // we recreate the form points
1886 if(dt_masks_gui_form_create_throttled(mask_form, mask_gui, form_index, module,
1887 mask_gui->pos[0], mask_gui->pos[1]))
1888 gui_points->clockwise = _polygon_is_clockwise(mask_form);
1889
1890 return 1;
1891 }
1892 else if(mask_gui->creation)
1893 {
1894 // Let the cursor motion be redrawn as it moves in GUI
1895 return 1;
1896 }
1897
1898 if(IS_NULL_PTR(mask_form->points)) return 0;
1899 const guint node_count = g_list_length(mask_form->points);
1900
1901 if(mask_gui->seg_dragging >= 0)
1902 {
1903 const GList *const pt = g_list_nth(mask_form->points, mask_gui->seg_dragging);
1904 const GList *const next_pt = g_list_next_wraparound(pt, mask_form->points);
1906 dt_masks_node_polygon_t *next_point = (dt_masks_node_polygon_t *)next_pt->data;
1907 if(IS_NULL_PTR(point) || IS_NULL_PTR(next_point)) return 0;
1908
1909 float dx = 0.0f;
1910 float dy = 0.0f;
1911 dt_masks_gui_delta_from_raw_anchor(dev, mask_gui, point->node, &dx, &dy);
1912
1913 // if first or last segment, update the source accordingly
1914 // (the source point follows the first/last segment when moved)
1915 if((mask_form->type & DT_MASKS_CLONE)
1916 && (mask_gui->seg_dragging == 0 || mask_gui->seg_dragging == (int)node_count - 1))
1917 dt_masks_translate_source(mask_form, dx, dy);
1918
1920 _polygon_translate_node(next_point, dx, dy);
1921
1922 // we recreate the form points
1923 dt_masks_gui_form_create_throttled(mask_form, mask_gui, form_index, module,
1924 mask_gui->pos[0], mask_gui->pos[1]);
1925 gui_points->clockwise = _polygon_is_clockwise(mask_form);
1926
1927 return 1;
1928 }
1929 else if(mask_gui->handle_dragging >= 0)
1930 {
1932 = (dt_masks_node_polygon_t *)g_list_nth_data(mask_form->points, mask_gui->handle_dragging);
1933 if(IS_NULL_PTR(node)) return 0;
1934
1935 float pts[2];
1936 dt_masks_gui_delta_to_image_abs(mask_gui, pts);
1937
1938 // compute ctrl points directly from new handle position
1939 float p[4];
1940 _polygon_handle_to_ctrl(gui_points->points[mask_gui->handle_dragging * 6 + 2],
1941 gui_points->points[mask_gui->handle_dragging * 6 + 3],
1942 pts[0], pts[1], &p[0], &p[1], &p[2], &p[3], gui_points->clockwise);
1943
1945
1946 // set new ctrl points
1947 dt_masks_set_ctrl_points(node->ctrl1, node->ctrl2, p);
1949
1950 _polygon_init_ctrl_points(mask_form);
1951 // we recreate the form points
1952 dt_masks_gui_form_create_throttled(mask_form, mask_gui, form_index, module,
1953 mask_gui->pos[0], mask_gui->pos[1]);
1954
1955 return 1;
1956 }
1957 else if(mask_gui->handle_border_dragging >= 0)
1958 {
1959 const int node_index = mask_gui->handle_border_dragging;
1961 = (dt_masks_node_polygon_t *)g_list_nth_data(mask_form->points, node_index);
1962 if(IS_NULL_PTR(node)) return 0;
1963
1964 const int base = node_index * 6;
1965 const int node_point_index = base + 2;
1966
1967 // Get delta between the node and its border handle
1968 float pts[2];
1969 float cursor_pos[2];
1970 const float node_pos_gui[2] = { gui_points->points[node_point_index],
1971 gui_points->points[node_point_index + 1] };
1972 const float handle_pos[2] = { gui_points->border[base], gui_points->border[base + 1] };
1973 dt_masks_gui_delta_to_image_abs(mask_gui, cursor_pos);
1974 dt_masks_project_on_line(cursor_pos, node_pos_gui, handle_pos, pts);
1975
1976 const float border = dt_masks_border_from_projected_handle(dev, node->node, pts, fminf(iwidth, iheight));
1977
1978 node->border[0] = node->border[1] = border;
1979 // we recreate the form points
1980 dt_masks_gui_form_create_throttled(mask_form, mask_gui, form_index, module,
1981 mask_gui->pos[0], mask_gui->pos[1]);
1982
1983 return 1;
1984 }
1985 else if(mask_gui->form_dragging || mask_gui->source_dragging)
1986 {
1987 if(mask_gui->form_dragging)
1988 {
1989 dt_masks_node_polygon_t *dragging_shape = (dt_masks_node_polygon_t *)(mask_form->points)->data;
1990 if(IS_NULL_PTR(dragging_shape)) return 0;
1991 float dx = 0.0f;
1992 float dy = 0.0f;
1993 dt_masks_gui_delta_from_raw_anchor(dev, mask_gui, dragging_shape->node, &dx, &dy);
1994 _polygon_translate_all_nodes(mask_form, dx, dy);
1995 }
1996 else
1997 {
1998 float raw_point[2];
1999 dt_masks_gui_delta_to_raw_norm(dev, mask_gui, raw_point);
2000 mask_form->source[0] = raw_point[0];
2001 mask_form->source[1] = raw_point[1];
2002 }
2003
2004 // we recreate the form points
2005 dt_masks_gui_form_create(mask_form, mask_gui, form_index, module);
2006 return 1;
2007 }
2008 return 0;
2009}
2010
2014static void _polygon_draw_shape(struct dt_develop_t *dev, cairo_t *cr, const float *point_buffer, const int point_count,
2015 const int node_count, const gboolean draw_border, const gboolean draw_source,
2016 const dt_masks_skip_range_t *skips, const int skip_count)
2017{
2018 /* dev and draw_source are the shared shape_draw_function_t signature; the source outline is
2019 * drawn by its own caller, which passes no exclusion list. */
2020 (void)dev; (void)draw_source;
2021
2022 /* This used to ignore the exclusion list and test the buffer for NaN instead -- which polygon
2023 * never writes, since its cuts have travelled out-of-band since the #1313 refactor. So the
2024 * test matched nothing and the folds were drawn. Honouring the list is the fix and costs one
2025 * call: it is the same walk the brush does, because it is the same question. */
2026 dt_masks_draw_outline_runs(cr, point_buffer, node_count * 3 + draw_border, point_count,
2027 skips, skip_count);
2028}
2029
2033static void _polygon_events_post_expose(cairo_t *cr, float zoom_scale, dt_masks_form_gui_t *mask_gui,
2034 int form_index, int node_count)
2035{
2036 dt_masks_form_gui_points_t *gui_points
2037 = (dt_masks_form_gui_points_t *)g_list_nth_data(mask_gui->points, form_index);
2038 if(IS_NULL_PTR(gui_points)) return;
2039 const int selected_node = dt_masks_gui_selected_node_index(mask_gui);
2040 const int selected_handle = dt_masks_gui_selected_handle_index(mask_gui);
2041 const int selected_handle_border = dt_masks_gui_selected_handle_border_index(mask_gui);
2042
2043 if(mask_gui->creation)
2044 {
2045 // draw a cross where the source will be created
2046 dt_masks_form_t *visible_form = dt_masks_get_visible_form(mask_gui->dev);
2047 if(visible_form && (visible_form->type & DT_MASKS_CLONE))
2048 {
2049 const gboolean have_first_node = node_count && gui_points->points && gui_points->points_count > 1;
2050 float node_posx = have_first_node ? gui_points->points[2] : mask_gui->pos[0];
2051 float node_posy = have_first_node ? gui_points->points[3] : mask_gui->pos[1];
2052
2053 dt_masks_draw_source_preview(cr, zoom_scale, mask_gui, node_posx, node_posy, node_posx, node_posy, FALSE);
2054 }
2055 }
2056
2057 // update clockwise info for the handles
2058 else if((mask_gui->type & DT_MASKS_IS_RETOUCHE) != 0 || mask_gui->node_selected || mask_gui->node_dragging >= 0
2059 || mask_gui->handle_selected)
2060 {
2061 dt_masks_form_t *group_form = dt_masks_get_visible_form(mask_gui->dev);
2062 if(!IS_NULL_PTR(group_form) && (group_form->type & DT_MASKS_GROUP))
2063 {
2064 dt_masks_form_group_t *group_entry = g_list_nth_data(group_form->points, form_index);
2065 dt_masks_form_t *polygon_form = group_entry
2066 ? dt_masks_get_from_id(mask_gui->dev, group_entry->formid)
2067 : NULL;
2068 if(!IS_NULL_PTR(polygon_form)) gui_points->clockwise = _polygon_is_clockwise(polygon_form);
2069 }
2070 }
2071
2072 // draw polygon
2073 if(gui_points->points && node_count > 0 && gui_points->points_count > node_count * 3 + 6) // there must be something to draw
2074 {
2075 dt_masks_draw_path_seg_by_seg(cr, mask_gui, form_index, gui_points->points, gui_points->points_count,
2076 node_count, zoom_scale, FALSE);
2077 }
2078
2079 if(mask_gui->group_selected == form_index)
2080 {
2081 // draw borders
2082 if(gui_points->border_count > node_count * 3 + 2)
2083 {
2084 dt_draw_shape_lines(mask_gui->dev, DT_MASKS_DASH_STICK, FALSE, cr, node_count, (mask_gui->border_selected), zoom_scale,
2085 gui_points->border, gui_points->border_count, &dt_masks_functions_polygon.draw_shape,
2086 CAIRO_LINE_CAP_ROUND, gui_points->border_skips, gui_points->border_skip_count);
2087 }
2088
2089 // draw the current node's handle if it's a curve node
2090 if(mask_gui->node_selected && selected_node >= 0 && selected_node < node_count
2091 && !dt_masks_node_is_cusp(gui_points, selected_node))
2092 {
2093 const int node_index = selected_node;
2094 float handle[2];
2095 _polygon_ctrl2_to_handle(gui_points->points[node_index * 6 + 2], gui_points->points[node_index * 6 + 3],
2096 gui_points->points[node_index * 6 + 4], gui_points->points[node_index * 6 + 5],
2097 &handle[0], &handle[1], gui_points->clockwise);
2098 const float pt[2] = { gui_points->points[node_index * 6 + 2], gui_points->points[node_index * 6 + 3] };
2099 const gboolean selected = (mask_gui->node_hovered == node_index
2100 || (selected_handle == node_index)
2101 || (mask_gui->handle_hovered == node_index));
2102 dt_draw_handle(cr, pt, zoom_scale, handle, selected, FALSE);
2103 }
2104 }
2105
2106 // draw nodes
2107 if(mask_gui->group_selected == form_index || mask_gui->creation)
2108 {
2109 for(int node_index = 0; node_index < node_count; node_index++)
2110 {
2111 // don't draw the last node while creating
2112 if(mask_gui->creation && node_index == node_count - 1) break;
2113 if(IS_NULL_PTR(gui_points->points) || gui_points->points_count <= node_index * 3 + 1) break;
2114
2115 const gboolean squared = dt_masks_node_is_cusp(gui_points, node_index);
2116 const gboolean selected = (node_index == mask_gui->node_hovered || node_index == mask_gui->node_dragging);
2117 const gboolean action = (node_index == selected_node);
2118 const float x = gui_points->points[node_index * 6 + 2];
2119 const float y = gui_points->points[node_index * 6 + 3];
2120
2121 // draw the first node as big circle while creating the polygon
2122 if(mask_gui->creation && node_index == 0)
2123 dt_draw_node(cr, FALSE, TRUE, TRUE, zoom_scale, x, y);
2124 else
2125 dt_draw_node(cr, squared, action, selected, zoom_scale, x, y);
2126 }
2127
2128 // Draw the current node's border handle, if needed
2129 if(mask_gui->node_selected && selected_node >= 0 && selected_node < node_count
2130 && gui_points->border && gui_points->border_count > selected_node * 3 && !mask_gui->creation)
2131 {
2132 const int edited = selected_node;
2133 const gboolean selected = (mask_gui->node_hovered == edited
2134 || (selected_handle_border == edited)
2135 || mask_gui->handle_border_hovered == edited);
2136 const int curr_node = edited * 6;
2137 const float handle[2] = { gui_points->border[curr_node], gui_points->border[curr_node + 1] };
2138
2139 dt_draw_handle(cr, NULL, zoom_scale, handle, selected, TRUE);
2140 }
2141 }
2142
2143 // draw the source if needed
2144 if(gui_points->source && gui_points->source_count > node_count * 3 + 2
2145 && gui_points->points && gui_points->points_count > 0)
2146 {
2147 dt_masks_gui_center_point_t center_pt = { .main = { gui_points->points[0], gui_points->points[1] },
2148 .source = { gui_points->source[0], gui_points->source[1] } };
2149 _polygon_gui_gravity_center(gui_points->points, gui_points->points_count,
2150 &center_pt.main.x, &center_pt.main.y, NULL);
2151 // project the source's center point from the center of gravity
2152 float offset_x = gui_points->source[0] - gui_points->points[0];
2153 float offset_y = gui_points->source[1] - gui_points->points[1];
2154 center_pt.source.x = center_pt.main.x + offset_x;
2155 center_pt.source.y = center_pt.main.y + offset_y;
2156 dt_masks_draw_source(cr, mask_gui, form_index, node_count, zoom_scale,
2158
2159 //draw the current node projection
2160 for(int node_index = 0; node_index < node_count; node_index++)
2161 {
2162 if(mask_gui->group_selected == form_index
2163 && (node_index == mask_gui->node_hovered || node_index == selected_node
2164 || (mask_gui->creation && node_index == node_count - 1)))
2165 {
2166 const int proj_index = node_index * 6 + 2;
2167 if(gui_points->source_count <= node_index * 3 + 1) break;
2168 const float proj[2] = { gui_points->source[proj_index], gui_points->source[proj_index + 1] };
2169 const gboolean selected = mask_gui->node_hovered == node_index;
2170 const gboolean squared = dt_masks_node_is_cusp(gui_points, node_index);
2171
2172 dt_draw_handle(cr, NULL, zoom_scale, proj, selected, squared);
2173 }
2174 }
2175 }
2176}
2177
2181static void _polygon_bounding_box_raw(const float *const point_buffer, const float *border_buffer,
2182 const int corner_count, const int point_count, int border_count,
2183 float *x_min, float *x_max, float *y_min, float *y_max)
2184{
2185 /* -FLT_MAX, not FLT_MIN: FLT_MIN is the smallest POSITIVE float, and a running maximum seeded
2186 * with it clamps the box at 0 for a shape entirely off the left or top edge */
2187 float xmin = FLT_MAX;
2188 float ymin = FLT_MAX;
2189 float xmax = -FLT_MAX;
2190 float ymax = -FLT_MAX;
2191 for(int border_index = corner_count * 3; border_index < border_count; border_index++)
2192 {
2193 // A cut span is not shape: its points are the offset curve folded over itself, and the
2194 // walks that render the mask never visit them, so the box must not grow to include them.
2195 // we look at the borders
2196 const float xx = border_buffer[border_index * 2];
2197 const float yy = border_buffer[border_index * 2 + 1];
2198 xmin = MIN(xx, xmin);
2199 xmax = MAX(xx, xmax);
2200 ymin = MIN(yy, ymin);
2201 ymax = MAX(yy, ymax);
2202 }
2203 for(int point_index = corner_count * 3; point_index < point_count; point_index++)
2204 {
2205 // we look at the polygon too
2206 const float xx = point_buffer[point_index * 2];
2207 const float yy = point_buffer[point_index * 2 + 1];
2208 xmin = MIN(xx, xmin);
2209 xmax = MAX(xx, xmax);
2210 ymin = MIN(yy, ymin);
2211 ymax = MAX(yy, ymax);
2212 }
2213
2214 *x_min = xmin;
2215 *x_max = xmax;
2216 *y_min = ymin;
2217 *y_max = ymax;
2218}
2219
2223static void _polygon_bounding_box(const float *const point_buffer, const float *border_buffer,
2224 const int corner_count, const int point_count, int border_count,
2225 int *width, int *height, int *posx, int *posy)
2226{
2227 // now we want to find the area, so we search min/max points
2228 float xmin, xmax, ymin, ymax;
2229 _polygon_bounding_box_raw(point_buffer, border_buffer, corner_count, point_count, border_count,
2230 &xmin, &xmax, &ymin, &ymax);
2231 *height = ymax - ymin + 4;
2232 *width = xmax - xmin + 4;
2233 *posx = xmin - 2;
2234 *posy = ymin - 2;
2235}
2236
2237static int _get_area(const dt_iop_module_t *const module, dt_dev_pixelpipe_t *pipe,
2238 const dt_dev_pixelpipe_iop_t *const piece,
2239 dt_masks_form_t *const mask_form, int *width, int *height, int *posx, int *posy,
2240 gboolean get_source)
2241{
2242 if(IS_NULL_PTR(module)) return 1;
2243
2244 // we get buffers for all points
2245 float *point_buffer = NULL;
2246 float *border_buffer = NULL;
2247 int point_count = 0;
2248 int border_count = 0;
2249
2250 const dt_masks_distort_t pipe_dist = dt_masks_distort_for_pipe(pipe, module->dev);
2251 if(_polygon_get_pts_border(module->dev, mask_form, module->iop_order, DT_DEV_TRANSFORM_DIR_BACK_INCL, &pipe_dist,
2252 &point_buffer, &point_count, &border_buffer, &border_count, get_source) != 0)
2253 {
2254 dt_pixelpipe_cache_free_align(point_buffer);
2255 dt_pixelpipe_cache_free_align(border_buffer);
2256 return 1;
2257 }
2258
2259 const guint corner_count = g_list_length(mask_form->points);
2260 _polygon_bounding_box(point_buffer, border_buffer, corner_count, point_count, border_count,
2261 width, height, posx, posy);
2262
2263 dt_pixelpipe_cache_free_align(point_buffer);
2264 dt_pixelpipe_cache_free_align(border_buffer);
2265 return 0;
2266}
2267
2270 dt_masks_form_t *mask_form, int *width, int *height, int *posx, int *posy)
2271{
2272 *width = 0;
2273 *height = 0;
2274 *posx = 0;
2275 *posy = 0;
2277 _get_area(module, pipe, piece, mask_form, width, height, posx, posy, TRUE));
2278}
2279
2281 const dt_dev_pixelpipe_iop_t *const piece,
2282 dt_masks_form_t *const mask_form,
2283 int *width, int *height, int *posx, int *posy)
2284{
2285 *width = 0;
2286 *height = 0;
2287 *posx = 0;
2288 *posy = 0;
2290 _get_area(module, pipe, piece, mask_form, width, height, posx, posy, FALSE));
2291}
2292
2296/*static*/ void _polygon_falloff(float *const restrict buffer, int *p0, int *p1,
2297 int posx, int posy, int buffer_width)
2298{
2299 // segment length
2300 int l = dt_fast_hypotf(p1[0] - p0[0], p1[1] - p0[1]) + 1;
2301
2302 const float lx = p1[0] - p0[0];
2303 const float ly = p1[1] - p0[1];
2304 const float inv_l = 1.0f / (float)l;
2305
2306 for(int i = 0; i < l; i++)
2307 {
2308 // position
2309 const int x = (int)((float)i * lx * inv_l) + p0[0] - posx;
2310 const int y = (int)((float)i * ly * inv_l) + p0[1] - posy;
2311 const float op = 1.0f - (float)i * inv_l;
2312 const size_t idx = y * buffer_width + x;
2313 buffer[idx] = fmaxf(buffer[idx], op);
2314 if(x > 0)
2315 buffer[idx - 1] = fmaxf(buffer[idx - 1], op); // this one is to avoid gap due to int rounding
2316 if(y > 0)
2317 buffer[idx - buffer_width] = fmaxf(buffer[idx - buffer_width], op); // this one is to avoid gap due to int rounding
2318 }
2319}
2320
2332static inline int _falloff_bridge_steps(const int *const last0, const int *const last1,
2333 const int *const p0, const int *const p1)
2334{
2335 const int jump = MAX(MAX(abs(p0[0] - last0[0]), abs(p0[1] - last0[1])),
2336 MAX(abs(p1[0] - last1[0]), abs(p1[1] - last1[1])));
2337 /* a bound: past this the two samples are not a fan, they are unrelated geometry */
2338 return MIN(jump, 64);
2339}
2340
2341static inline void _falloff_bridge_lerp(const int *const from, const int *const to, const float t,
2342 int *const out)
2343{
2344 out[0] = (int)floorf(from[0] + t * (to[0] - from[0]) + 0.5f);
2345 out[1] = (int)floorf(from[1] + t * (to[1] - from[1]) + 0.5f);
2346}
2347
2349static inline void _falloff_bridge_stamp(float *const restrict buffer, const int *const last0,
2350 const int *const last1, const int *const p0,
2351 const int *const p1, const int posx, const int posy,
2352 const int width)
2353{
2354 const int steps = _falloff_bridge_steps(last0, last1, p0, p1);
2355 for(int k = 1; k < steps; k++)
2356 {
2357 const float t = (float)k / (float)steps;
2358 int b0[2];
2359 int b1[2];
2360 _falloff_bridge_lerp(last0, p0, t, b0);
2361 _falloff_bridge_lerp(last1, p1, t, b1);
2362 _polygon_falloff(buffer, b0, b1, posx, posy, width);
2363 }
2364}
2365
2368static inline int _falloff_bridge_queue(int *const dpoints, int dindex, const int capacity,
2369 const int *const last0, const int *const last1,
2370 const int *const p0, const int *const p1)
2371{
2372 const int steps = _falloff_bridge_steps(last0, last1, p0, p1);
2373 for(int k = 1; k < steps && dindex + 4 <= capacity; k++)
2374 {
2375 const float t = (float)k / (float)steps;
2376 int b0[2];
2377 int b1[2];
2378 _falloff_bridge_lerp(last0, p0, t, b0);
2379 _falloff_bridge_lerp(last1, p1, t, b1);
2380 dpoints[dindex] = b0[0];
2381 dpoints[dindex + 1] = b0[1];
2382 dpoints[dindex + 2] = b1[0];
2383 dpoints[dindex + 3] = b1[1];
2384 dindex += 4;
2385 }
2386 return dindex;
2387}
2388
2390 const dt_dev_pixelpipe_iop_t *const piece,
2391 dt_masks_form_t *const mask_form,
2392 float **buffer, int *width, int *height, int *posx, int *posy)
2393{
2394 *buffer = NULL;
2395 *width = 0;
2396 *height = 0;
2397 *posx = 0;
2398 *posy = 0;
2399 if(IS_NULL_PTR(module)) return DT_MASKS_RASTER_ERROR;
2400 double start = 0.0;
2401 double start2 = 0.0;
2402
2404
2405 // we get buffers for all points
2406 float *point_buffer = NULL;
2407 float *border_buffer = NULL;
2408 int point_count = 0;
2409 int border_count = 0;
2410 const dt_masks_distort_t pipe_dist = dt_masks_distort_for_pipe(pipe, module->dev);
2411 if(_polygon_get_pts_border(module->dev, mask_form, module->iop_order,
2412 DT_DEV_TRANSFORM_DIR_BACK_INCL, &pipe_dist, &point_buffer, &point_count,
2413 &border_buffer, &border_count, FALSE) != 0)
2414 {
2415 dt_pixelpipe_cache_free_align(point_buffer);
2416 dt_pixelpipe_cache_free_align(border_buffer);
2417 return DT_MASKS_RASTER_ERROR;
2418 }
2419
2421 {
2422 dt_print(DT_DEBUG_MASKS, "[masks %s] polygon points took %0.04f sec\n",
2423 mask_form->name, dt_get_wtime() - start);
2424 start = start2 = dt_get_wtime();
2425 }
2426
2427 // now we want to find the area, so we search min/max points
2428 const guint corner_count = g_list_length(mask_form->points);
2429 _polygon_bounding_box(point_buffer, border_buffer, corner_count, point_count, border_count,
2430 width, height, posx, posy);
2431
2432 const int hb = *height;
2433 const int wb = *width;
2434 /* Nothing left to interpolate: the outline above is sampled at one pixel whatever the pipe's
2435 * step, because the self-intersection detector needs it that way, so consecutive falloff
2436 * segments are already adjacent. Interpolating between them would only stamp the same pixels
2437 * again. Polygon therefore buys no speed from a coarse step today -- making it do so means
2438 * decimating the PAINTING while keeping the geometry exact, which is a separate change. */
2439 const gboolean sparse = FALSE;
2440 const int sparse_factor = 1;
2441
2443 {
2444 dt_print(DT_DEBUG_MASKS, "[masks %s] polygon_fill min max took %0.04f sec\n", mask_form->name,
2445 dt_get_wtime() - start2);
2446 start2 = dt_get_wtime();
2447 }
2448
2449 // we allocate the buffer
2450 const size_t bufsize = (size_t)(*width) * (*height);
2451 // ensure that the buffer is zeroed, as the following code only actually sets the polygon+falloff pixels
2452 float *const restrict bufptr = *buffer = dt_pixelpipe_cache_alloc_align_float_cache(bufsize, 0);
2453 if(!IS_NULL_PTR(bufptr)) memset(bufptr, 0, sizeof(float) * bufsize);
2454 if(IS_NULL_PTR(*buffer))
2455 {
2456 dt_pixelpipe_cache_free_align(point_buffer);
2457 dt_pixelpipe_cache_free_align(border_buffer);
2458 return DT_MASKS_RASTER_ERROR;
2459 }
2460
2461 // we write all the point around the polygon into the buffer
2462 const int border_point_count = border_count;
2463 if(border_point_count > 2)
2464 {
2465 int lastx = (int)point_buffer[(border_point_count - 1) * 2];
2466 int lasty = (int)point_buffer[(border_point_count - 1) * 2 + 1];
2467 int lasty2 = (int)point_buffer[(border_point_count - 2) * 2 + 1];
2468
2469 int just_change_dir = 0;
2470 for(int ii = corner_count * 3; ii < 2 * border_point_count - corner_count * 3; ii++)
2471 {
2472 // we are writing more than 1 loop in the case the dir in y change
2473 // exactly at start/end point
2474 int i = ii;
2475 if(ii >= border_point_count)
2476 i = (ii - corner_count * 3) % (border_point_count - corner_count * 3) + corner_count * 3;
2477 const int xx = (int)point_buffer[i * 2];
2478 const int yy = (int)point_buffer[i * 2 + 1];
2479
2480 // we don't store the point if it has the same y value as the last one
2481 if(yy == lasty) continue;
2482
2483 // we want to be sure that there is no y jump
2484 if(yy - lasty > 1 || yy - lasty < -1)
2485 {
2486 if(yy < lasty)
2487 {
2488 for(int j = yy + 1; j < lasty; j++)
2489 {
2490 const int nx = (j - yy) * (lastx - xx) / (float)(lasty - yy) + xx;
2491 const size_t idx = (size_t)(j - (*posy)) * (*width) + nx - (*posx);
2492 assert(idx < bufsize);
2493 bufptr[idx] = 1.0f;
2494 }
2495 lasty2 = yy + 2;
2496 lasty = yy + 1;
2497 }
2498 else
2499 {
2500 for(int j = lasty + 1; j < yy; j++)
2501 {
2502 const int nx = (j - lasty) * (xx - lastx) / (float)(yy - lasty) + lastx;
2503 const size_t idx = (size_t)(j - (*posy)) * (*width) + nx - (*posx);
2504 assert(idx < bufsize);
2505 bufptr[idx] = 1.0f;
2506 }
2507 lasty2 = yy - 2;
2508 lasty = yy - 1;
2509 }
2510 }
2511 // if we change the direction of the polygon (in y), then we add a extra point
2512 if((lasty - lasty2) * (lasty - yy) > 0)
2513 {
2514 const size_t idx = (size_t)(lasty - (*posy)) * (*width) + lastx + 1 - (*posx);
2515 assert(idx < bufsize);
2516 bufptr[idx] = 1.0f;
2517 just_change_dir = 1;
2518 }
2519 // we add the point
2520 if(just_change_dir && ii == i)
2521 {
2522 // if we have changed the direction, we have to be careful that point can be at the same place
2523 // as the previous one, especially on sharp edges
2524 const size_t idx = (size_t)(yy - (*posy)) * (*width) + xx - (*posx);
2525 assert(idx < bufsize);
2526 float v = bufptr[idx];
2527 if(v > 0.0)
2528 {
2529 if(xx - (*posx) > 0)
2530 {
2531 const size_t idx_ = (size_t)(yy - (*posy)) * (*width) + xx - 1 - (*posx);
2532 assert(idx_ < bufsize);
2533 bufptr[idx_] = 1.0f;
2534 }
2535 else if(xx - (*posx) < (*width) - 1)
2536 {
2537 const size_t idx_ = (size_t)(yy - (*posy)) * (*width) + xx + 1 - (*posx);
2538 assert(idx_ < bufsize);
2539 bufptr[idx_] = 1.0f;
2540 }
2541 }
2542 else
2543 {
2544 const size_t idx_ = (size_t)(yy - (*posy)) * (*width) + xx - (*posx);
2545 assert(idx_ < bufsize);
2546 bufptr[idx_] = 1.0f;
2547 just_change_dir = 0;
2548 }
2549 }
2550 else
2551 {
2552 const size_t idx_ = (size_t)(yy - (*posy)) * (*width) + xx - (*posx);
2553 assert(idx_ < bufsize);
2554 bufptr[idx_] = 1.0f;
2555 }
2556 // we change last values
2557 lasty2 = lasty;
2558 lasty = yy;
2559 lastx = xx;
2560 if(ii != i) break;
2561 }
2562 }
2564 {
2565 dt_print(DT_DEBUG_MASKS, "[masks %s] polygon_fill draw polygon took %0.04f sec\n", mask_form->name,
2566 dt_get_wtime() - start2);
2567 start2 = dt_get_wtime();
2568 }
2569 __OMP_PARALLEL_FOR__(if((size_t)hb * wb > 50000))
2570 for(int yy = 0; yy < hb; yy++)
2571 {
2572 float *const restrict row = bufptr + (size_t)yy * wb;
2573 int state = 0;
2574 for(int xx = 0; xx < wb; xx++)
2575 {
2576 const float v = row[xx];
2577 if(v == 1.0f) state = !state;
2578 if(state) row[xx] = 1.0f;
2579 }
2580 }
2581
2583 {
2584 dt_print(DT_DEBUG_MASKS, "[masks %s] polygon_fill fill plain took %0.04f sec\n", mask_form->name,
2585 dt_get_wtime() - start2);
2586 start2 = dt_get_wtime();
2587 }
2588
2589 // now we fill the falloff
2590 int p0[2] = { 0 }, p1[2] = { 0 };
2591 int prev0[2] = { 0 }, prev1[2] = { 0 };
2592 gboolean have_prev = FALSE;
2593 int last0[2] = { -100, -100 }, last1[2] = { -100, -100 };
2594 for(int i = corner_count * 3; i < border_count; i++)
2595 {
2596 p0[0] = point_buffer[i * 2];
2597 p0[1] = point_buffer[i * 2 + 1];
2598
2599 /* Inside a cut, the border side of every falloff segment collapses to the cut's resume
2600 * point: the same segments the in-band jump used to produce, read from a range instead of
2601 * decoded out of a NaN slot. */
2602 const int border_index = i;
2603 p1[0] = border_buffer[border_index * 2];
2604 p1[1] = border_buffer[border_index * 2 + 1];
2605
2606 const gboolean used_next = FALSE;
2607
2608 if(sparse && have_prev && !used_next
2609 && (prev0[0] != p0[0] || prev0[1] != p0[1] || prev1[0] != p1[0] || prev1[1] != p1[1]))
2610 {
2611 for(int k = 1; k < sparse_factor; k++)
2612 {
2613 const float t = (float)k / (float)sparse_factor;
2614 int mp0[2] = { (int)floorf(prev0[0] + t * (p0[0] - prev0[0]) + 0.5f),
2615 (int)floorf(prev0[1] + t * (p0[1] - prev0[1]) + 0.5f) };
2616 int mp1[2] = { (int)floorf(prev1[0] + t * (p1[0] - prev1[0]) + 0.5f),
2617 (int)floorf(prev1[1] + t * (p1[1] - prev1[1]) + 0.5f) };
2618 _polygon_falloff(bufptr, mp0, mp1, *posx, *posy, *width);
2619 }
2620 }
2621
2622 /* BRIDGE A JUMP BETWEEN CONSECUTIVE SPOKES.
2623 *
2624 * The feather is the union of segments from each outline sample to its border sample, so it
2625 * is only continuous while consecutive segments stay within a pixel of each other at BOTH
2626 * ends. At a cut boundary they do not: the border side collapses to the cut's resume point,
2627 * which is somewhere else entirely, and the fan opens in one step. Measured on polygon #2 of
2628 * issue #1313's sidecar, at the sample before the cut at 23528: the outline end moves a
2629 * fraction of a pixel while the border end jumps 4.57 px, and the wedge between the two
2630 * segments is never stamped -- a thin dark radial line through the feather, reported as "a
2631 * radial spoke is missing" at node 12. A second one, 2.80 px, sits before the cut at 6532.
2632 *
2633 * Subdividing until each step is at most a pixel closes it. This is not the same thing as
2634 * the `sparse' interpolation above, which fills in for samples deliberately not visited
2635 * when the outline is walked at reduced density; this fires on adjacent samples, where the
2636 * geometry itself is discontinuous. */
2637 if(last0[0] != p0[0] || last0[1] != p0[1] || last1[0] != p1[0] || last1[1] != p1[1])
2638 {
2639 if(last0[0] > -100 || last0[1] > -100)
2640 _falloff_bridge_stamp(bufptr, last0, last1, p0, p1, *posx, *posy, *width);
2641
2642 _polygon_falloff(bufptr, p0, p1, *posx, *posy, *width);
2643 last0[0] = p0[0];
2644 last0[1] = p0[1];
2645 last1[0] = p1[0];
2646 last1[1] = p1[1];
2647 }
2648
2649 if(!used_next)
2650 {
2651 prev0[0] = p0[0];
2652 prev0[1] = p0[1];
2653 prev1[0] = p1[0];
2654 prev1[1] = p1[1];
2655 have_prev = TRUE;
2656 }
2657 else
2658 {
2659 have_prev = FALSE;
2660 }
2661 }
2662
2664 dt_print(DT_DEBUG_MASKS, "[masks %s] polygon_fill fill falloff took %0.04f sec\n", mask_form->name,
2665 dt_get_wtime() - start2);
2666
2667 dt_pixelpipe_cache_free_align(point_buffer);
2668 dt_pixelpipe_cache_free_align(border_buffer);
2670 dt_print(DT_DEBUG_MASKS, "[masks %s] polygon fill buffer took %0.04f sec\n", mask_form->name,
2671 dt_get_wtime() - start);
2672
2673 return DT_MASKS_RASTER_OK;
2674}
2675
2676
2679static int _polygon_crop_to_roi(float *polygon, const int point_count, float xmin, float xmax, float ymin,
2680 float ymax)
2681{
2682 int point_start = -1;
2683 int l = -1, r = -1;
2684
2685
2686 // first try to find a node clearly inside roi
2687 for(int k = 0; k < point_count; k++)
2688 {
2689 float x = polygon[2 * k];
2690 float y = polygon[2 * k + 1];
2691
2692 if(x >= xmin + 1 && y >= ymin + 1
2693 && x <= xmax - 1 && y <= ymax - 1)
2694 {
2695 point_start = k;
2696 break;
2697 }
2698 }
2699
2700 // printf("crop to xmin %f, xmax %f, ymin %f, ymax %f - start %d (%f, %f)\n", xmin, xmax, ymin, ymax,
2701 // point_start, polygon[2*point_start], polygon[2*point_start+1]);
2702
2703 if(point_start < 0) return 0; // no point means roi lies completely within polygon
2704
2705 typedef struct
2706 {
2707 int l;
2708 int r;
2709 float start;
2710 float delta;
2711 } roi_crop_segment_t;
2712
2713 roi_crop_segment_t *xmax_segs = dt_alloc_align(sizeof(*xmax_segs) * point_count);
2714 roi_crop_segment_t *ymax_segs = dt_alloc_align(sizeof(*ymax_segs) * point_count);
2715 if(IS_NULL_PTR(xmax_segs) || IS_NULL_PTR(ymax_segs))
2716 {
2717 dt_free_align(xmax_segs);
2718 dt_free_align(ymax_segs);
2719 goto fallback_passes;
2720 }
2721
2722 int xmin_l = -1, xmin_r = -1;
2723 int xmax_l = -1, xmax_r = -1;
2724 int xmax_count = 0;
2725
2726 // find the crossing points with xmin/xmax in a single pass
2727 for(int k = 0; k < point_count; k++)
2728 {
2729 const int kk = (k + point_start) % point_count;
2730 const float x = polygon[2 * kk];
2731
2732 if(xmin_l < 0 && x < xmin) xmin_l = k; // where we leave roi (xmin)
2733 if(xmin_l >= 0 && x >= xmin) xmin_r = k - 1; // where we re-enter roi (xmin)
2734
2735 if(xmin_l >= 0 && xmin_r >= 0)
2736 {
2737 const int count = xmin_r - xmin_l + 1;
2738 const int ll = (xmin_l - 1 + point_start) % point_count;
2739 const int rr = (xmin_r + 1 + point_start) % point_count;
2740 const float delta_y = (count == 1) ? 0 : (polygon[2 * rr + 1] - polygon[2 * ll + 1]) / (count - 1);
2741 const float start_y = polygon[2 * ll + 1];
2742
2743 for(int n = 0; n < count; n++)
2744 {
2745 const int nn = (n + xmin_l + point_start) % point_count;
2746 polygon[2 * nn] = xmin;
2747 polygon[2 * nn + 1] = start_y + n * delta_y;
2748 }
2749
2750 xmin_l = xmin_r = -1;
2751 }
2752
2753 if(xmax_l < 0 && x > xmax) xmax_l = k; // where we leave roi (xmax)
2754 if(xmax_l >= 0 && x <= xmax) xmax_r = k - 1; // where we re-enter roi (xmax)
2755
2756 if(xmax_l >= 0 && xmax_r >= 0)
2757 {
2758 const int count = xmax_r - xmax_l + 1;
2759 const int ll = (xmax_l - 1 + point_start) % point_count;
2760 const int rr = (xmax_r + 1 + point_start) % point_count;
2761 const float delta_y = (count == 1) ? 0 : (polygon[2 * rr + 1] - polygon[2 * ll + 1]) / (count - 1);
2762 const float start_y = polygon[2 * ll + 1];
2763
2764 xmax_segs[xmax_count].l = xmax_l;
2765 xmax_segs[xmax_count].r = xmax_r;
2766 xmax_segs[xmax_count].start = start_y;
2767 xmax_segs[xmax_count].delta = delta_y;
2768 xmax_count++;
2769
2770 xmax_l = xmax_r = -1;
2771 }
2772 }
2773
2774 for(int s = 0; s < xmax_count; s++)
2775 {
2776 const int count = xmax_segs[s].r - xmax_segs[s].l + 1;
2777 const float start_y = xmax_segs[s].start;
2778 const float delta_y = xmax_segs[s].delta;
2779 for(int n = 0; n < count; n++)
2780 {
2781 const int nn = (n + xmax_segs[s].l + point_start) % point_count;
2782 polygon[2 * nn] = xmax;
2783 polygon[2 * nn + 1] = start_y + n * delta_y;
2784 }
2785 }
2786
2787 dt_free_align(xmax_segs);
2788
2789 int ymin_l = -1, ymin_r = -1;
2790 int ymax_l = -1, ymax_r = -1;
2791 int ymax_count = 0;
2792
2793 // find the crossing points with ymin/ymax in a single pass
2794 for(int k = 0; k < point_count; k++)
2795 {
2796 const int kk = (k + point_start) % point_count;
2797 const float y = polygon[2 * kk + 1];
2798
2799 if(ymin_l < 0 && y < ymin) ymin_l = k; // where we leave roi (ymin)
2800 if(ymin_l >= 0 && y >= ymin) ymin_r = k - 1; // where we re-enter roi (ymin)
2801
2802 if(ymin_l >= 0 && ymin_r >= 0)
2803 {
2804 const int count = ymin_r - ymin_l + 1;
2805 const int ll = (ymin_l - 1 + point_start) % point_count;
2806 const int rr = (ymin_r + 1 + point_start) % point_count;
2807 const float delta_x = (count == 1) ? 0 : (polygon[2 * rr] - polygon[2 * ll]) / (count - 1);
2808 const float start_x = polygon[2 * ll];
2809
2810 for(int n = 0; n < count; n++)
2811 {
2812 const int nn = (n + ymin_l + point_start) % point_count;
2813 polygon[2 * nn] = start_x + n * delta_x;
2814 polygon[2 * nn + 1] = ymin;
2815 }
2816
2817 ymin_l = ymin_r = -1;
2818 }
2819
2820 if(ymax_l < 0 && y > ymax) ymax_l = k; // where we leave roi (ymax)
2821 if(ymax_l >= 0 && y <= ymax) ymax_r = k - 1; // where we re-enter roi (ymax)
2822
2823 if(ymax_l >= 0 && ymax_r >= 0)
2824 {
2825 const int count = ymax_r - ymax_l + 1;
2826 const int ll = (ymax_l - 1 + point_start) % point_count;
2827 const int rr = (ymax_r + 1 + point_start) % point_count;
2828 const float delta_x = (count == 1) ? 0 : (polygon[2 * rr] - polygon[2 * ll]) / (count - 1);
2829 const float start_x = polygon[2 * ll];
2830
2831 ymax_segs[ymax_count].l = ymax_l;
2832 ymax_segs[ymax_count].r = ymax_r;
2833 ymax_segs[ymax_count].start = start_x;
2834 ymax_segs[ymax_count].delta = delta_x;
2835 ymax_count++;
2836
2837 ymax_l = ymax_r = -1;
2838 }
2839 }
2840
2841 for(int s = 0; s < ymax_count; s++)
2842 {
2843 const int count = ymax_segs[s].r - ymax_segs[s].l + 1;
2844 const float start_x = ymax_segs[s].start;
2845 const float delta_x = ymax_segs[s].delta;
2846 for(int n = 0; n < count; n++)
2847 {
2848 const int nn = (n + ymax_segs[s].l + point_start) % point_count;
2849 polygon[2 * nn] = start_x + n * delta_x;
2850 polygon[2 * nn + 1] = ymax;
2851 }
2852 }
2853
2854 dt_free_align(ymax_segs);
2855 return 1;
2856
2857fallback_passes:
2858 l = r = -1;
2859 // find the crossing points with xmin and replace segment by nodes on border
2860 for(int k = 0; k < point_count; k++)
2861 {
2862 const int kk = (k + point_start) % point_count;
2863
2864 if(l < 0 && polygon[2 * kk] < xmin) l = k; // where we leave roi
2865 if(l >= 0 && polygon[2 * kk] >= xmin) r = k - 1; // where we re-enter roi
2866
2867 // replace that segment
2868 if(l >= 0 && r >= 0)
2869 {
2870 const int count = r - l + 1;
2871 const int ll = (l - 1 + point_start) % point_count;
2872 const int rr = (r + 1 + point_start) % point_count;
2873 const float delta_y = (count == 1) ? 0 : (polygon[2 * rr + 1] - polygon[2 * ll + 1]) / (count - 1);
2874 const float start_y = polygon[2 * ll + 1];
2875
2876 for(int n = 0; n < count; n++)
2877 {
2878 const int nn = (n + l + point_start) % point_count;
2879 polygon[2 * nn] = xmin;
2880 polygon[2 * nn + 1] = start_y + n * delta_y;
2881 }
2882
2883 l = r = -1;
2884 }
2885 }
2886
2887 // find the crossing points with xmax and replace segment by nodes on border
2888 for(int k = 0; k < point_count; k++)
2889 {
2890 const int kk = (k + point_start) % point_count;
2891
2892 if(l < 0 && polygon[2 * kk] > xmax) l = k; // where we leave roi
2893 if(l >= 0 && polygon[2 * kk] <= xmax) r = k - 1; // where we re-enter roi
2894
2895 // replace that segment
2896 if(l >= 0 && r >= 0)
2897 {
2898 const int count = r - l + 1;
2899 const int ll = (l - 1 + point_start) % point_count;
2900 const int rr = (r + 1 + point_start) % point_count;
2901 const float delta_y = (count == 1) ? 0 : (polygon[2 * rr + 1] - polygon[2 * ll + 1]) / (count - 1);
2902 const float start_y = polygon[2 * ll + 1];
2903
2904 for(int n = 0; n < count; n++)
2905 {
2906 const int nn = (n + l + point_start) % point_count;
2907 polygon[2 * nn] = xmax;
2908 polygon[2 * nn + 1] = start_y + n * delta_y;
2909 }
2910
2911 l = r = -1;
2912 }
2913 }
2914
2915 // find the crossing points with ymin and replace segment by nodes on border
2916 for(int k = 0; k < point_count; k++)
2917 {
2918 const int kk = (k + point_start) % point_count;
2919
2920 if(l < 0 && polygon[2 * kk + 1] < ymin) l = k; // where we leave roi
2921 if(l >= 0 && polygon[2 * kk + 1] >= ymin) r = k - 1; // where we re-enter roi
2922
2923 // replace that segment
2924 if(l >= 0 && r >= 0)
2925 {
2926 const int count = r - l + 1;
2927 const int ll = (l - 1 + point_start) % point_count;
2928 const int rr = (r + 1 + point_start) % point_count;
2929 const float delta_x = (count == 1) ? 0 : (polygon[2 * rr] - polygon[2 * ll]) / (count - 1);
2930 const float start_x = polygon[2 * ll];
2931
2932 for(int n = 0; n < count; n++)
2933 {
2934 const int nn = (n + l + point_start) % point_count;
2935 polygon[2 * nn] = start_x + n * delta_x;
2936 polygon[2 * nn + 1] = ymin;
2937 }
2938
2939 l = r = -1;
2940 }
2941 }
2942
2943 // find the crossing points with ymax and replace segment by nodes on border
2944 for(int k = 0; k < point_count; k++)
2945 {
2946 const int kk = (k + point_start) % point_count;
2947
2948 if(l < 0 && polygon[2 * kk + 1] > ymax) l = k; // where we leave roi
2949 if(l >= 0 && polygon[2 * kk + 1] <= ymax) r = k - 1; // where we re-enter roi
2950
2951 // replace that segment
2952 if(l >= 0 && r >= 0)
2953 {
2954 const int count = r - l + 1;
2955 const int ll = (l - 1 + point_start) % point_count;
2956 const int rr = (r + 1 + point_start) % point_count;
2957 const float delta_x = (count == 1) ? 0 : (polygon[2 * rr] - polygon[2 * ll]) / (count - 1);
2958 const float start_x = polygon[2 * ll];
2959
2960 for(int n = 0; n < count; n++)
2961 {
2962 const int nn = (n + l + point_start) % point_count;
2963 polygon[2 * nn] = start_x + n * delta_x;
2964 polygon[2 * nn + 1] = ymax;
2965 }
2966
2967 l = r = -1;
2968 }
2969 }
2970 return 1;
2971}
2972
2974static inline void _polygon_falloff_roi(float *buffer, int *p0, int *p1, int bw, int bh)
2975{
2976 // segment length
2977 const int l = sqrt((p1[0] - p0[0]) * (p1[0] - p0[0]) + (p1[1] - p0[1]) * (p1[1] - p0[1])) + 1;
2978
2979 const float lx = p1[0] - p0[0];
2980 const float ly = p1[1] - p0[1];
2981 const float inv_l = 1.0f / (float)l;
2982
2983 const int dx = lx < 0 ? -1 : 1;
2984 const int dy = ly < 0 ? -1 : 1;
2985 const int dpy = dy * bw;
2986
2987 const int x0 = p0[0], y0 = p0[1];
2988 const int x1 = p1[0], y1 = p1[1];
2989 if((x0 < 0 && x1 < 0) || (x0 >= bw && x1 >= bw) || (y0 < 0 && y1 < 0) || (y0 >= bh && y1 >= bh)) return;
2990 const int inside = (x0 >= 0 && x0 < bw && x1 >= 0 && x1 < bw && y0 >= 0 && y0 < bh && y1 >= 0 && y1 < bh);
2991
2992 for(int i = 0; i < l; i++)
2993 {
2994 // position
2995 const int x = (int)((float)i * lx * inv_l) + p0[0];
2996 const int y = (int)((float)i * ly * inv_l) + p0[1];
2997 const float op = 1.0f - (float)i * inv_l;
2998 if(!inside && (x < 0 || x >= bw || y < 0 || y >= bh)) continue;
2999 float *buf = buffer + (size_t)y * bw + x;
3000 if(inside)
3001 buf[0] = MAX(buf[0], op);
3002 else if(x >= 0 && x < bw && y >= 0 && y < bh)
3003 buf[0] = MAX(buf[0], op);
3004 if(x + dx >= 0 && x + dx < bw && y >= 0 && y < bh)
3005 buf[dx] = MAX(buf[dx], op); // this one is to avoid gap due to int rounding
3006 if(x >= 0 && x < bw && y + dy >= 0 && y + dy < bh)
3007 buf[dpy] = MAX(buf[dpy], op); // this one is to avoid gap due to int rounding
3008 }
3009}
3010
3011// build a stamp which can be combined with other shapes in the same group
3012// prerequisite: 'buffer' is all zeros
3014 const dt_dev_pixelpipe_iop_t *const piece,
3015 dt_masks_form_t *const mask_form,
3016 const dt_iop_roi_t *roi, float *buffer,
3017 dt_iop_roi_t *touched)
3018{
3019 dt_masks_touched_none(touched);
3020 if(IS_NULL_PTR(module)) return DT_MASKS_RASTER_ERROR;
3021 double start = 0.0;
3022 double start2 = 0.0;
3024
3025 const int px = roi->x;
3026 const int py = roi->y;
3027 const int width = roi->width;
3028 const int height = roi->height;
3029 const float scale = roi->scale;
3030 /* Nothing left to interpolate: the outline above is sampled at one pixel whatever the pipe's
3031 * step, because the self-intersection detector needs it that way, so consecutive falloff
3032 * segments are already adjacent. Interpolating between them would only stamp the same pixels
3033 * again. Polygon therefore buys no speed from a coarse step today -- making it do so means
3034 * decimating the PAINTING while keeping the geometry exact, which is a separate change. */
3035 const gboolean sparse = FALSE;
3036 const int sparse_factor = 1;
3037
3038 // we need to take care of four different cases:
3039 // 1) polygon and feather are outside of roi
3040 // 2) polygon is outside of roi, feather reaches into roi
3041 // 3) roi lies completely within polygon
3042 // 4) all other situations :)
3043 int polygon_in_roi = 0;
3044 int feather_in_roi = 0;
3045 int polygon_encircles_roi = 0;
3046
3047 // we get buffers for all points
3048 float *points = NULL;
3049 float *border = NULL;
3050 int points_count = 0;
3051 int border_count = 0;
3052 const dt_masks_distort_t pipe_dist = dt_masks_distort_for_pipe(pipe, module->dev);
3053 if(_polygon_get_pts_border(module->dev, mask_form, module->iop_order,
3055 &points, &points_count, &border, &border_count, FALSE) != 0)
3056 {
3059 return DT_MASKS_RASTER_ERROR;
3060 }
3061 /* nothing past the header: every segment was a point */
3062 if(points_count <= 2 || points_count <= (int)g_list_length(mask_form->points) * 3)
3063 {
3066 return DT_MASKS_RASTER_EMPTY;
3067 }
3068
3070 {
3071 dt_print(DT_DEBUG_MASKS, "[masks %s] polygon points took %0.04f sec\n",
3072 mask_form->name, dt_get_wtime() - start);
3073 start = start2 = dt_get_wtime();
3074 }
3075
3076 const guint corner_count = g_list_length(mask_form->points);
3077
3078 // we shift and scale down polygon and border. Cut spans are scaled too: their points are
3079 // valid coordinates (the cuts travel out-of-band now), and every reader below skips the
3080 // spans by range, so nothing observes them either way -- while the old skip-during-scaling
3081 // left them in IMAGE coordinates, which is what painted the feather from out-of-buffer
3082 // positions when a spurious cut appeared (issue #1116).
3083 for(int i = corner_count * 3; i < border_count; i++)
3084 {
3085 border[2 * i] = border[2 * i] * scale - px;
3086 border[2 * i + 1] = border[2 * i + 1] * scale - py;
3087 }
3088 for(int i = corner_count * 3; i < points_count; i++)
3089 {
3090 const float xx = points[2 * i];
3091 const float yy = points[2 * i + 1];
3092 points[2 * i] = xx * scale - px;
3093 points[2 * i + 1] = yy * scale - py;
3094 }
3095
3096 // now check if polygon is at least partially within roi
3097 for(int i = corner_count * 3; i < points_count; i++)
3098 {
3099 const int xx = points[i * 2];
3100 const int yy = points[i * 2 + 1];
3101
3102 if(xx > 1 && yy > 1 && xx < width - 2 && yy < height - 2)
3103 {
3104 polygon_in_roi = 1;
3105 break;
3106 }
3107 }
3108
3109 // if not this still might mean that polygon fully encircles roi -> we need to check that
3110 if(!polygon_in_roi)
3111 {
3112 int crossing_count = 0;
3113 int last_y = -9999;
3114 const int x = width / 2;
3115 const int y = height / 2;
3116
3117 for(int i = corner_count * 3; i < points_count; i++)
3118 {
3119 const int yy = (int)points[2 * i + 1];
3120 if(yy != last_y && yy == y)
3121 {
3122 if(points[2 * i] > x) crossing_count++;
3123 }
3124 last_y = yy;
3125 }
3126 // if there is an uneven number of intersection points roi lies within polygon
3127 if(crossing_count & 1)
3128 {
3129 polygon_in_roi = 1;
3130 polygon_encircles_roi = 1;
3131 }
3132 }
3133
3134 // now check if feather is at least partially within roi. Cut spans are not feather.
3135 for(int i = corner_count * 3; i < border_count; i++)
3136 {
3137 const float xx = border[i * 2];
3138 const float yy = border[i * 2 + 1];
3139 if(xx > 1 && yy > 1 && xx < width - 2 && yy < height - 2)
3140 {
3141 feather_in_roi = 1;
3142 break;
3143 }
3144 }
3145
3146 // if polygon and feather completely lie outside of roi -> we're done/mask remains empty
3147 if(!polygon_in_roi && !feather_in_roi)
3148 {
3151 return DT_MASKS_RASTER_EMPTY;
3152 }
3153
3154 // now get min/max values
3155 float xmin, xmax, ymin, ymax;
3156 _polygon_bounding_box_raw(points, border, corner_count, points_count, border_count,
3157 &xmin, &xmax, &ymin, &ymax);
3158
3160 {
3161 dt_print(DT_DEBUG_MASKS, "[masks %s] polygon_fill min max took %0.04f sec\n", mask_form->name,
3162 dt_get_wtime() - start2);
3163 start2 = dt_get_wtime();
3164 }
3165
3167 {
3168 dt_print(DT_DEBUG_MASKS, "[masks %s] polygon_fill clear mask took %0.04f sec\n", mask_form->name,
3169 dt_get_wtime() - start2);
3170 start2 = dt_get_wtime();
3171 }
3172
3173 // deal with polygon if it does not lie outside of roi
3174 if(polygon_in_roi)
3175 {
3176 // second copy of polygon which we can modify when cropping to roi
3177 float *cpoints = dt_pixelpipe_cache_alloc_align_float_cache((size_t)2 * points_count, 0);
3178 if(IS_NULL_PTR(cpoints))
3179 {
3182 return DT_MASKS_RASTER_ERROR;
3183 }
3184 memcpy(cpoints, points, sizeof(float) * 2 * points_count);
3185
3186 // now we clip cpoints to roi -> catch special case when roi lies completely within polygon.
3187 // dirty trick: we allow polygon to extend one pixel beyond height-1. this avoids need of special handling
3188 // of the last roi line in the following edge-flag polygon fill algorithm.
3189 const int crop_success = _polygon_crop_to_roi(cpoints + 2 * (corner_count * 3),
3190 points_count - corner_count * 3, 0,
3191 width - 1, 0, height);
3192 polygon_encircles_roi = polygon_encircles_roi || !crop_success;
3193
3195 {
3196 dt_print(DT_DEBUG_MASKS, "[masks %s] polygon_fill crop to roi took %0.04f sec\n", mask_form->name,
3197 dt_get_wtime() - start2);
3198 start2 = dt_get_wtime();
3199 }
3200
3201 if(polygon_encircles_roi)
3202 {
3203 // roi lies completely within polygon
3204 for(size_t k = 0; k < (size_t)width * height; k++) buffer[k] = 1.0f;
3206 }
3207 else
3208 {
3209 // all other cases
3210
3211 // edge-flag polygon fill: we write all the point around the polygon into the buffer
3212 float xlast = cpoints[(points_count - 1) * 2];
3213 float ylast = cpoints[(points_count - 1) * 2 + 1];
3214
3215 for(int i = corner_count * 3; i < points_count; i++)
3216 {
3217 float xstart = xlast;
3218 float ystart = ylast;
3219
3220 float xend = xlast = cpoints[i * 2];
3221 float yend = ylast = cpoints[i * 2 + 1];
3222
3223 if(ystart > yend)
3224 {
3225 float tmp;
3226 tmp = ystart, ystart = yend, yend = tmp;
3227 tmp = xstart, xstart = xend, xend = tmp;
3228 }
3229
3230 const float m = (xstart - xend) / (ystart - yend); // we don't need special handling of ystart==yend
3231 // as following loop will take care
3232
3233 for(int yy = (int)ceilf(ystart); (float)yy < yend;
3234 yy++) // this would normally never touch the last roi line => see comment further above
3235 {
3236 const float xcross = xstart + m * (yy - ystart);
3237
3238 int xx = floorf(xcross);
3239 if((float)xx + 0.5f <= xcross) xx++;
3240
3241 if(xx < 0 || xx >= width || yy < 0 || yy >= height)
3242 continue; // sanity check just to be on the safe side
3243
3244 const size_t index = (size_t)yy * width + xx;
3245
3246 buffer[index] = 1.0f - buffer[index];
3247 }
3248 }
3249
3251 {
3252 dt_print(DT_DEBUG_MASKS, "[masks %s] polygon_fill draw polygon took %0.04f sec\n", mask_form->name,
3253 dt_get_wtime() - start2);
3254 start2 = dt_get_wtime();
3255 }
3256
3257 // we fill the inside plain
3258 // we don't need to deal with parts of shape outside of roi
3259 const int xxmin = MAX(xmin, 0);
3260 const int xxmax = MIN(xmax, width - 1);
3261 const int yymin = MAX(ymin, 0);
3262 const int yymax = MIN(ymax, height - 1);
3263 __OMP_PARALLEL_FOR__(if((size_t)(yymax - yymin + 1) * (size_t)(xxmax - xxmin + 1) > 50000))
3264 for(int yy = yymin; yy <= yymax; yy++)
3265 {
3266 float *const restrict row = buffer + (size_t)yy * width;
3267 int state = 0;
3268 for(int xx = xxmin; xx <= xxmax; xx++)
3269 {
3270 const float v = row[xx];
3271 if(v > 0.5f) state = !state;
3272 if(state) row[xx] = 1.0f;
3273 }
3274 }
3275
3277 {
3278 dt_print(DT_DEBUG_MASKS, "[masks %s] polygon_fill fill plain took %0.04f sec\n", mask_form->name,
3279 dt_get_wtime() - start2);
3280 start2 = dt_get_wtime();
3281 }
3282 }
3284 }
3285
3286 // deal with feather if it does not lie outside of roi
3287 if(!polygon_encircles_roi)
3288 {
3289 /* the jump bridge below can add segments between two adjacent samples, so the buffer needs
3290 * headroom beyond one segment per sample; it is bounded and the writes are capacity-checked */
3291 const int dpoints_capacity = 4 * (border_count + 1024) * (sparse ? sparse_factor : 1);
3292 int *dpoints = dt_pixelpipe_cache_alloc_align_cache(sizeof(int) * dpoints_capacity, 0);
3293 if(IS_NULL_PTR(dpoints))
3294 {
3297 return DT_MASKS_RASTER_ERROR;
3298 }
3299
3300 int dindex = 0;
3301 int p0[2], p1[2];
3302 int prev0[2] = { 0, 0 };
3303 int prev1[2] = { 0, 0 };
3304 gboolean have_prev = FALSE;
3305 int last0[2] = { -100, -100 };
3306 int last1[2] = { -100, -100 };
3307 gboolean have_last = FALSE;
3308 for(int i = corner_count * 3; i < border_count; i++)
3309 {
3310 p0[0] = floorf(points[i * 2] + 0.5f);
3311 p0[1] = ceilf(points[i * 2 + 1]);
3312
3313 /* Inside a cut, the border side of every falloff segment collapses to the cut's resume
3314 * point: the same segments the in-band jump used to produce, read from a range instead
3315 * of decoded out of a NaN slot. */
3316 const int border_index = i;
3317 p1[0] = border[border_index * 2];
3318 p1[1] = border[border_index * 2 + 1];
3319
3320 const gboolean used_next = FALSE;
3321
3322 if(sparse && have_prev && !used_next
3323 && (prev0[0] != p0[0] || prev0[1] != p0[1] || prev1[0] != p1[0] || prev1[1] != p1[1]))
3324 {
3325 for(int k = 1; k < sparse_factor; k++)
3326 {
3327 const float t = (float)k / (float)sparse_factor;
3328 const int mp0[2] = { (int)floorf(prev0[0] + t * (p0[0] - prev0[0]) + 0.5f),
3329 (int)floorf(prev0[1] + t * (p0[1] - prev0[1]) + 0.5f) };
3330 const int mp1[2] = { (int)floorf(prev1[0] + t * (p1[0] - prev1[0]) + 0.5f),
3331 (int)floorf(prev1[1] + t * (p1[1] - prev1[1]) + 0.5f) };
3332 if(dindex + 4 <= dpoints_capacity)
3333 {
3334 dpoints[dindex] = mp0[0];
3335 dpoints[dindex + 1] = mp0[1];
3336 dpoints[dindex + 2] = mp1[0];
3337 dpoints[dindex + 3] = mp1[1];
3338 dindex += 4;
3339 }
3340 }
3341 }
3342
3343 /* BRIDGE A JUMP BETWEEN CONSECUTIVE SPOKES.
3344 *
3345 * The feather is the union of segments from each outline sample to its border sample, so
3346 * it stays continuous only while consecutive segments are within a pixel of each other at
3347 * BOTH ends. At a cut boundary they are not: the border side collapses to the cut's
3348 * resume point, which is somewhere else, and the fan opens in a single step. Measured on
3349 * polygon #2 of issue #1313's sidecar, at the sample before the cut at 23528 -- the
3350 * outline end does not move at all while the border end jumps 5 px -- and the wedge
3351 * between the two segments is never stamped. That is the thin dark radial line through
3352 * the feather reported as "a radial spoke is missing" at node 12; a second, 3 px, sits
3353 * before the cut at 6532.
3354 *
3355 * Subdividing until each step is at most a pixel closes it. This is NOT the `sparse'
3356 * interpolation above: that one fills in for samples deliberately not visited when the
3357 * outline is walked at reduced density, and skips itself precisely when the spoke is
3358 * inside a cut -- which is the one case that needs bridging. This fires between adjacent
3359 * samples, where the geometry itself is discontinuous. */
3360 if(last0[0] != p0[0] || last0[1] != p0[1] || last1[0] != p1[0] || last1[1] != p1[1])
3361 {
3362 if(have_last)
3363 dindex = _falloff_bridge_queue(dpoints, dindex, dpoints_capacity,
3364 last0, last1, p0, p1);
3365
3366 if(dindex + 4 > dpoints_capacity) break;
3367 dpoints[dindex] = p0[0];
3368 dpoints[dindex + 1] = p0[1];
3369 dpoints[dindex + 2] = p1[0];
3370 dpoints[dindex + 3] = p1[1];
3371 dindex += 4;
3372 have_last = TRUE;
3373
3374 last0[0] = p0[0];
3375 last0[1] = p0[1];
3376 last1[0] = p1[0];
3377 last1[1] = p1[1];
3378 }
3379
3380 if(!used_next)
3381 {
3382 prev0[0] = p0[0];
3383 prev0[1] = p0[1];
3384 prev1[0] = p1[0];
3385 prev1[1] = p1[1];
3386 have_prev = TRUE;
3387 }
3388 else
3389 {
3390 have_prev = FALSE;
3391 }
3392 }
3393 __OMP_PARALLEL_FOR__(if(dindex > 4096))
3394 for(int n = 0; n < dindex; n += 4)
3395 _polygon_falloff_roi(buffer, dpoints + n, dpoints + n + 2, width, height);
3396
3398
3400 {
3401 dt_print(DT_DEBUG_MASKS, "[masks %s] polygon_fill fill falloff took %0.04f sec\n",
3402 mask_form->name,
3403 dt_get_wtime() - start2);
3404 }
3405 }
3406
3409
3410 /* The raw bounding box already spans the border samples, so the feather falloff lies inside
3411 * it too; the margin covers the one-pixel neighbour writes of the falloff stamps. The
3412 * encircling case reported the full buffer above. */
3413 if(!polygon_encircles_roi)
3414 dt_masks_touched_set(touched, (int)floorf(xmin) - 2, (int)floorf(ymin) - 2, (int)ceilf(xmax) + 2,
3415 (int)ceilf(ymax) + 2, width, height);
3416
3418 dt_print(DT_DEBUG_MASKS, "[masks %s] polygon fill buffer took %0.04f sec\n",
3419 mask_form->name,
3420 dt_get_wtime() - start);
3421
3422 return DT_MASKS_RASTER_OK;
3423}
3424
3426{
3427 // nothing to do (yet?)
3428}
3429
3433static void _polygon_set_form_name(struct dt_masks_form_t *const mask_form, const size_t form_number)
3434{
3435 snprintf(mask_form->name, sizeof(mask_form->name), _("polygon #%d"), (int)form_number);
3436}
3437
3438static void _polygon_set_hint_message(const dt_masks_form_gui_t *const mask_gui,
3439 const dt_masks_form_t *const mask_form,
3440 char *const restrict msgbuf, const size_t msgbuf_len)
3441{
3442 // Ordered like the hit test in dt_masks_find_closest_handle_common(): the innermost target
3443 // under the cursor is the one the next click will act on, so it is the one we describe.
3444 // Only gestures that cannot be discovered any other way -- what the wheel does is the user's
3445 // own mapping now (masks_gui.h), shown in the Drawn tab, so it is not repeated here.
3446 const guint node_count = mask_form->points ? g_list_length(mask_form->points) : 0;
3447 if(mask_gui->creation && node_count < 4)
3448 g_strlcat(msgbuf, _("<b>Add sharp node</b>: Ctrl+Click\n"
3449 "<b>Remove last node</b>: Backspace, <b>Cancel</b>: Esc"), msgbuf_len);
3450 else if(mask_gui->creation)
3451 g_strlcat(msgbuf, _("<b>Add sharp node</b>: Ctrl+Click, <b>Close path</b>: Enter, or Click on the first node\n"
3452 "<b>Remove last node</b>: Backspace, <b>Cancel</b>: Esc"), msgbuf_len);
3453 else if(mask_gui->source_selected)
3454 g_strlcat(msgbuf, _("<b>Move source</b>: Drag"), msgbuf_len);
3455 else if(mask_gui->handle_border_hovered >= 0)
3456 g_strlcat(msgbuf, _("<b>Node fading</b>: Drag"), msgbuf_len);
3457 else if(mask_gui->handle_hovered >= 0)
3458 g_strlcat(msgbuf, _("<b>Node curvature</b>: Drag"), msgbuf_len);
3459 // The node operations below need the node to be selected, which the first click does.
3460 else if(mask_gui->node_hovered >= 0 && mask_gui->node_selected)
3461 g_strlcat(msgbuf, _("<b>Move node</b>: Drag, <b>Switch smooth/sharp</b>: Ctrl+Click\n"
3462 "<b>Delete node</b>: Right-click or Del"), msgbuf_len);
3463 else if(mask_gui->node_hovered >= 0)
3464 g_strlcat(msgbuf, _("<b>Move node</b>: Drag, <b>Delete node</b>: Right-click"), msgbuf_len);
3465 else if(mask_gui->seg_hovered >= 0 || mask_gui->seg_selected)
3466 g_strlcat(msgbuf, _("<b>Move segment</b>: Drag, <b>Add node</b>: Ctrl+Click"), msgbuf_len);
3467 else if(mask_gui->form_selected || mask_gui->border_selected)
3468 g_strlcat(msgbuf, _("<b>Move</b>: Drag"), msgbuf_len);
3469}
3470
3471static void _polygon_duplicate_points(dt_develop_t *const dev, dt_masks_form_t *const base, dt_masks_form_t *const dest)
3472{
3473 // unused arg, keep compiler from complaining
3475}
3476
3477static void _polygon_initial_source_pos(struct dt_develop_t *dev, const float iwd, const float iht, float *x, float *y)
3478{
3479
3480
3481 float offset[2] = { 0.1f, 0.1f };
3483 *x = offset[0];
3484 *y = offset[1];
3485}
3486
3487static void _polygon_creation_closing_form_callback(GtkWidget *widget, gpointer user_data)
3488{
3489 dt_masks_form_gui_t *mask_gui = (dt_masks_form_gui_t *)user_data;
3490 // This is a temp form on creation mode
3491 dt_masks_form_t *mask_form = dt_masks_get_visible_form(mask_gui->dev);
3492 if(IS_NULL_PTR(mask_form)) return;
3493
3494 _polygon_creation_closing_form(mask_form, mask_gui);
3495}
3496
3497static void _polygon_switch_node_callback(GtkWidget *widget, gpointer user_data)
3498{
3499 dt_masks_form_gui_t *mask_gui = (dt_masks_form_gui_t *)user_data;
3500 if(IS_NULL_PTR(mask_gui)) return;
3501 dt_iop_module_t *module = mask_gui->dev->gui_module;
3502 if(IS_NULL_PTR(module)) return;
3503 const int form_id = mask_gui->formid;
3504 dt_masks_form_t *selected_form = dt_masks_get_from_id(mask_gui->dev, form_id);
3505 if(IS_NULL_PTR(selected_form)) return;
3506
3507 mask_gui->node_selected = TRUE;
3508 mask_gui->node_selected_idx = mask_gui->node_hovered;
3509 dt_masks_form_gui_points_t *gui_points
3510 = (dt_masks_form_gui_points_t *)g_list_nth_data(mask_gui->points, mask_gui->group_selected);
3511 const int node_index = dt_masks_gui_selected_node_index(mask_gui);
3513 = (dt_masks_node_polygon_t *)g_list_nth_data(selected_form->points, node_index);
3514 if(IS_NULL_PTR(gui_points) || IS_NULL_PTR(node)) return;
3515 dt_masks_toggle_bezier_node_type(module, selected_form, mask_gui, mask_gui->group_selected, gui_points,
3516 node_index, node->node, node->ctrl1, node->ctrl2, &node->state);
3517}
3518
3519static void _polygon_reset_round_node_callback(GtkWidget *widget, gpointer user_data)
3520{
3521 dt_masks_form_gui_t *mask_gui = (dt_masks_form_gui_t *)user_data;
3522 if(IS_NULL_PTR(mask_gui)) return;
3523 dt_iop_module_t *module = mask_gui->dev->gui_module;
3524 if(IS_NULL_PTR(module)) return;
3525 const int form_id = mask_gui->formid;
3526 dt_masks_form_t *selected_form = dt_masks_get_from_id(mask_gui->dev, form_id);
3527 if(IS_NULL_PTR(selected_form)) return;
3528
3529 mask_gui->node_selected = TRUE;
3530 mask_gui->node_selected_idx = mask_gui->node_hovered;
3531 dt_masks_form_gui_points_t *gui_points
3532 = (dt_masks_form_gui_points_t *)g_list_nth_data(mask_gui->points, mask_gui->group_selected);
3533 const int selected_handle = dt_masks_gui_selected_handle_index(mask_gui);
3534 const int node_index = MAX(mask_gui->node_hovered, selected_handle);
3536 = (dt_masks_node_polygon_t *)g_list_nth_data(selected_form->points, node_index);
3537 if(IS_NULL_PTR(gui_points) || IS_NULL_PTR(node)) return;
3538 if(dt_masks_reset_bezier_ctrl_points(module, selected_form, mask_gui, mask_gui->group_selected, gui_points,
3539 node_index, &node->state))
3540 gui_points->clockwise = _polygon_is_clockwise(selected_form);
3541}
3542
3543static void _polygon_add_node_callback(GtkWidget *menu, gpointer user_data)
3544{
3545 dt_masks_form_gui_t *mask_gui = (dt_masks_form_gui_t *)user_data;
3546 if(IS_NULL_PTR(mask_gui)) return;
3547 dt_masks_form_t *visible_forms = dt_masks_get_visible_form(mask_gui->dev);
3548 if(IS_NULL_PTR(visible_forms)) return;
3549
3550 dt_iop_module_t *module = mask_gui->dev->gui_module;
3551 if(IS_NULL_PTR(module)) return;
3552
3553 dt_masks_form_group_t *group_entry = dt_masks_form_get_selected_group(visible_forms, mask_gui);
3554 if(IS_NULL_PTR(group_entry)) return;
3555 dt_masks_form_t *selected_form = dt_masks_get_from_id(mask_gui->dev, group_entry->formid);
3556
3557 if(selected_form)
3558 {
3559 _add_node_to_segment(module, selected_form, group_entry->parentid, mask_gui, mask_gui->group_selected);
3560 }
3561
3562 //dt_dev_add_history_item(mask_gui->dev, module, TRUE, TRUE);
3563}
3564
3566 struct dt_masks_form_gui_t *mask_gui,
3567 const float pzx, const float pzy)
3568{
3569
3570
3571 GtkWidget *menu_item = NULL;
3572 gchar *accel = g_strdup_printf(_("%s+Click"), gtk_accelerator_get_label(0, dt_accels_display_mods(DT_PRIMARY_MASK)));
3573
3574 gboolean ret = FALSE;
3575
3576 if(mask_gui->creation)
3577 {
3578 menu_item = ctx_gtk_menu_item_new_with_markup(_("Close path"), menu,
3580 gtk_widget_set_sensitive(menu_item, mask_form->points && !g_list_shorter_than(mask_form->points, 4));
3581 menu_item_set_fake_accel(menu_item, GDK_KEY_Return, 0);
3582
3583 menu_item = ctx_gtk_menu_item_new_with_markup(_("Remove last point"), menu,
3585 menu_item_set_fake_accel(menu_item, GDK_KEY_BackSpace, 0);
3586
3587 ret = TRUE;
3588 }
3589
3590 else if(mask_gui->node_hovered >= 0)
3591 {
3592 dt_masks_form_gui_points_t *gui_points
3593 = (dt_masks_form_gui_points_t *)g_list_nth_data(mask_gui->points, mask_gui->group_selected);
3594 if(IS_NULL_PTR(gui_points)) goto end;
3595 dt_masks_node_polygon_t *node = (dt_masks_node_polygon_t *)g_list_nth_data(mask_form->points, mask_gui->node_hovered);
3596 if(IS_NULL_PTR(node)) goto end;
3597 const gboolean is_corner = dt_masks_node_is_cusp(gui_points, mask_gui->node_hovered);
3598
3599 {
3600 gchar *to_change_type = g_strdup_printf(_("Switch to %s node"), (is_corner) ? _("round") : _("cusp"));
3601 const dt_menu_icon_t icon = is_corner ? DT_MENU_ICON_CIRCLE : DT_MENU_ICON_SQUARE;
3602 menu_item = ctx_gtk_menu_item_new_with_icon_and_shortcut(to_change_type, accel, menu,
3603 _polygon_switch_node_callback, mask_gui, icon);
3604
3605 dt_free(to_change_type);
3606 }
3607
3608 {
3609 menu_item = ctx_gtk_menu_item_new_with_markup(_("Reset round node"), menu,
3611 gtk_widget_set_sensitive(menu_item, !is_corner);
3612 }
3613
3614 ret = TRUE;
3615 }
3616
3617 if(mask_gui->seg_selected)
3618 {
3619 menu_item = ctx_gtk_menu_item_new_with_markup_and_shortcut(_("Add a node here"), accel,
3620 menu, _polygon_add_node_callback, mask_gui);
3621 ret = TRUE;
3622 }
3623
3624 end:
3625 dt_free(accel);
3626 return ret;
3627}
3628
3629// The function table for polygons. This must be public, i.e. no "static" keyword.
3632 .sanitize_config = _polygon_sanitize_config,
3633 .set_form_name = _polygon_set_form_name,
3634 .set_hint_message = _polygon_set_hint_message,
3635 .duplicate_points = _polygon_duplicate_points,
3636 .initial_source_pos = _polygon_initial_source_pos,
3637 .get_distance = _polygon_get_distance,
3638 .get_points_border = _polygon_get_points_border,
3639 .get_mask = _polygon_get_mask,
3640 .get_mask_roi = _polygon_get_mask_roi,
3641 .get_area = _polygon_get_area,
3642 .get_source_area = _polygon_get_source_area,
3643 .get_gravity_center = _polygon_get_gravity_center,
3644 .get_interaction_value = _polygon_get_interaction_value,
3645 .set_interaction_value = _polygon_set_interaction_value,
3646 .update_hover = _find_closest_handle,
3647 .mouse_moved = _polygon_events_mouse_moved,
3648 .mouse_scrolled = _polygon_events_mouse_scrolled,
3649 .button_pressed = _polygon_events_button_pressed,
3650 .button_released = _polygon_events_button_released,
3651 .key_pressed = _polygon_events_key_pressed,
3652 .post_expose = _polygon_events_post_expose,
3653 .draw_shape = _polygon_draw_shape,
3654 .init_ctrl_points = _polygon_init_ctrl_points,
3655 .populate_context_menu = _polygon_populate_context_menu
3656};
3657
3658
3659// clang-format off
3660// modelines: These editor modelines have been set for all relevant files by tools/update_modelines.py
3661// vim: shiftwidth=2 expandtab tabstop=2 cindent
3662// kate: tab-indents: off; indent-width 2; replace-tabs on; indent-mode cstyle; remove-trailing-spaces modified;
3663// clang-format on
Handle default and user-set shortcuts (accelerators)
#define DT_PRIMARY_MASK
static double dist(double x1, double y1, double x2, double y2)
Definition ashift_lsd.c:250
#define TRUE
Definition ashift_lsd.c:162
#define FALSE
Definition ashift_lsd.c:158
#define m
Definition basecurve.c:283
typedef void((*dt_cache_allocate_t)(void *userdata, dt_cache_entry_t *entry))
static const float x
const float f
const int t
const float v
struct _GtkWidget GtkWidget
GtkWidget, opaque, spelled exactly as GTK spells it.
Definition colorspaces.h:98
static const float const float const float min
const float max
const dt_colormatrix_t dt_aligned_pixel_t out
static const int row
const float delta
float dt_conf_get_float(const char *name)
Float for name, clamped to its declared bounds.
void dt_toast_log(const char *msg,...)
Definition control.c:871
void * dt_alloc_align(size_t size)
Allocate cacheline-aligned memory.
Definition darktable.c:508
dt_dev_image_geometry_t dt_dev_geometry_snapshot(const dt_develop_t *dev)
#define dt_dev_pixelpipe_update_history_preview(dev)
void dt_dev_coordinates_preview_abs_to_image_norm(dt_develop_t *dev, float *points, size_t num_points)
Definition develop.c:1306
void dt_dev_coordinates_raw_norm_to_raw_abs(dt_develop_t *dev, float *points, size_t num_points)
Definition develop.c:1255
void dt_dev_coordinates_image_abs_to_raw_norm(dt_develop_t *dev, float *points, size_t num_points)
Definition develop.c:1285
@ DT_DEV_TRANSFORM_DIR_BACK_EXCL
Definition develop.h:111
@ DT_DEV_TRANSFORM_DIR_BACK_INCL
Definition develop.h:110
@ DT_DEV_TRANSFORM_DIR_FORW_INCL
Definition develop.h:108
@ DT_DEV_TRANSFORM_DIR_ALL
Definition develop.h:107
GtkWidget * geometry
its size, under the preview
GHashTable * selected
set of checked row labels, mirrored to conf on every change
GtkWidget * status
result of the last capture
static void dt_draw_handle(cairo_t *cr, const float pt[2], const float zoom_scale, const float handle[2], const gboolean selected, const gboolean square)
Draw a control handle attached to a point with a tail between the node and the handle.
Definition draw.h:725
@ DT_MASKS_DASH_STICK
Definition draw.h:135
static void dt_draw_shape_lines(struct dt_develop_t *dev, const dt_draw_dash_type_t dash_type, const gboolean source, cairo_t *cr, const int nb, const gboolean selected, const float zoom_scale, const float *points, const int points_count, const shape_draw_function_t *draw_shape_func, const cairo_line_cap_t line_cap, const struct dt_masks_skip_range_t *skips, const int skip_count)
Draw the lines of a mask shape.
Definition draw.h:831
static void dt_draw_node(cairo_t *cr, const gboolean square, const gboolean point_action, const gboolean selected, const float zoom_scale, const float x, const float y)
Draw an node point of a mask.
Definition draw.h:669
static guint dt_keys_mainpad_alternatives(const guint key_val)
Remap keypad keys to usual mainpad ones.
Definition gdkkeys.h:118
static const GList * g_list_next_wraparound(const GList *list, const GList *head)
Definition glib_utils.h:52
static GList * g_list_next_bounded(GList *list)
Definition glib_utils.h:47
static gboolean g_list_shorter_than(const GList *list, unsigned len)
Definition glib_utils.h:34
_lib_location_type_t type
Definition location.c:1
@ DT_DEBUG_PERF
Definition logging.h:55
@ DT_DEBUG_MASKS
Definition logging.h:62
int32_t dt_get_debug_flags(void)
Definition darktable.c:2085
void dt_print(dt_debug_thread_t thread, const char *msg,...) __attribute__((format(printf
Print to stdout when thread is enabled, prefixed with seconds since startup.
float *const restrict const size_t k
#define IS_NULL_PTR(p)
C is way too permissive with !=, == and if(var) checks, which can mean too many things depending on w...
Definition macros.h:96
static float * dt_masks_dynbuf_buffer(dt_masks_dynbuf_t *a)
Definition masks.h:655
static void dt_masks_dynbuf_add_2(dt_masks_dynbuf_t *a, float value1, float value2)
Definition masks.h:582
static dt_masks_raster_result_t dt_masks_raster_from_status(const int status)
Definition masks.h:322
static dt_masks_dynbuf_t * dt_masks_dynbuf_init(size_t size, const char *tag)
Definition masks.h:562
static void dt_masks_translate_source(dt_masks_form_t *form, const float delta_x, const float delta_y)
Definition masks.h:367
dt_masks_raster_result_t
What a rasterisation attempt produced.
Definition masks.h:313
@ DT_MASKS_RASTER_ERROR
Definition masks.h:316
@ DT_MASKS_RASTER_EMPTY
Definition masks.h:315
@ DT_MASKS_RASTER_OK
Definition masks.h:314
static void dt_masks_translate_ctrl_node(float node[2], float ctrl1[2], float ctrl2[2], const float delta_x, const float delta_y)
Definition masks.h:373
void dt_masks_duplicate_points(const dt_masks_form_t *base, dt_masks_form_t *dest, size_t node_size)
Duplicate a points list for a mask using a fixed node size.
Definition masks.c:1332
static float * dt_masks_dynbuf_reserve_n(dt_masks_dynbuf_t *a, const int n)
Definition masks.h:597
static gboolean dt_masks_center_of_gravity_from_points(const float *points, const int points_count, float center[2], float *area)
Definition masks.h:661
static size_t dt_masks_dynbuf_position(dt_masks_dynbuf_t *a)
Definition masks.h:732
int dt_masks_form_change_opacity(dt_develop_t *dev, dt_masks_form_t *form, int parentid, int up, const int flow)
Definition masks_gui.c:5840
static void dt_masks_dynbuf_add_zeros(dt_masks_dynbuf_t *a, const int n)
Definition masks.h:617
dt_masks_form_t * dt_masks_get_from_id(dt_develop_t *dev, int id)
Definition masks.c:909
@ DT_MASKS_POINT_STATE_NORMAL
Definition masks.h:156
@ DT_MASKS_POINT_STATE_USER
Definition masks.h:157
static float * dt_masks_dynbuf_harvest(dt_masks_dynbuf_t *a)
Definition masks.h:744
static void dt_masks_dynbuf_free(dt_masks_dynbuf_t *a)
Definition masks.h:754
static void dt_masks_set_ctrl_points(float ctrl1[2], float ctrl2[2], const float control_points[4])
Definition masks.h:384
What a mask outline needs in order to place itself, and who supplies it.
static dt_masks_distort_t dt_masks_distort_for_pipe(dt_dev_pixelpipe_t *pipe, dt_develop_t *dev)
The rendering supplier: compose this pipe, sample as this pipe was told to.
static dt_masks_distort_t dt_masks_distort_for_gui(dt_develop_t *dev)
The GUI supplier: compose through the geometry service, at full resolution, sampled at the density th...
static int dt_masks_distort_transform(const dt_masks_distort_t *const d, const double iop_order, const int transf_direction, float *points, size_t points_count)
Compose forward, bounded exactly as dt_dev_distort_transform_plus() is.
The per-shape function table, private to the masks implementation.
int dt_masks_point_in_form_exact(const float *pts, int num_pts, const float *points, int points_start, int points_count, const dt_masks_skip_range_t *skips, int skip_count)
Ray-cast point-in-polygon over a form point stream, honouring skip ranges.
Definition masks_gui.c:5403
void dt_masks_outline_envelope_offset(const float *centre, float dx, float dy, float radius, float radius_rate, float *out)
int dt_masks_outline_boundary_skips(const float *const points, const float *const border, const int count, const int header, dt_masks_skip_range_t **skips_out)
void dt_masks_outline_offset_along(const float *const centre, float dx, float dy, const float radius, float *const border)
gboolean dt_masks_outline_short_way(const float *const centre, const float *const from, const float *const to, const gboolean default_clockwise)
void dt_masks_draw_source(cairo_t *cr, dt_masks_form_gui_t *mask_gui, const int form_index, const int node_count, const float zoom_scale, struct dt_masks_gui_center_point_t *center_point, const shape_draw_function_t *draw_shape_func)
Draw the source for a correction mask.
Definition masks_gui.c:3368
gboolean dt_masks_reset_bezier_ctrl_points(struct dt_iop_module_t *module, struct dt_masks_form_t *mask_form, struct dt_masks_form_gui_t *mask_gui, const int form_index, const struct dt_masks_form_gui_points_t *gui_points, const int node_index, dt_masks_points_states_t *state)
Definition masks_gui.c:1123
void dt_masks_remove_node(struct dt_iop_module_t *module, dt_masks_form_t *mask_form, int parent_id, dt_masks_form_gui_t *mask_gui, int form_index, int node_index)
Definition masks_gui.c:2020
float dt_masks_get_set_conf_value(dt_masks_form_t *mask_form, char *feature, float new_value, float value_min, float value_max, dt_masks_increment_t increment, int flow)
Change a numerical property of a mask shape, either by in/de-crementing the current value or setting ...
Definition masks_gui.c:5275
gboolean dt_masks_toggle_bezier_node_type(struct dt_iop_module_t *module, struct dt_masks_form_t *mask_form, struct dt_masks_form_gui_t *mask_gui, const int form_index, const struct dt_masks_form_gui_points_t *gui_points, const int node_index, float node[2], float ctrl1[2], float ctrl2[2], dt_masks_points_states_t *state)
Definition masks_gui.c:1095
gboolean dt_masks_gui_form_create_throttled(dt_masks_form_t *mask_form, dt_masks_form_gui_t *mask_gui, int form_index, dt_iop_module_t *module, float posx, float posy)
Definition masks_gui.c:1976
dt_masks_form_group_t * dt_masks_form_get_selected_group(const dt_masks_form_t *mask_form, const dt_masks_form_gui_t *mask_gui)
Get the selected group entry from the GUI selection index.
Definition masks_gui.c:1522
gboolean dt_masks_is_anything_selected(const dt_masks_form_gui_t *mask_gui)
Definition masks_gui.c:2658
void _masks_gui_delete_node_callback(GtkWidget *menu, gpointer user_data)
Definition masks_gui.c:679
gboolean dt_masks_node_is_cusp(const dt_masks_form_gui_points_t *gui_points, const int node_index)
returns wether a node is a corner or not. A node is a corner if its 2 control handles are at the same...
Definition masks_gui.c:3282
dt_masks_form_t * dt_masks_get_visible_form(const dt_develop_t *dev)
Return the currently visible form used by the masks GUI.
Definition masks_gui.c:1408
void dt_masks_draw_path_seg_by_seg(cairo_t *cr, dt_masks_form_gui_t *mask_gui, const int form_index, const float *points, const int points_count, const int node_count, const float zoom_scale, const gboolean round_ends)
Definition masks_gui.c:3483
gboolean dt_masks_gui_is_dragging(const dt_masks_form_gui_t *gui)
Definition masks_gui.c:1475
float dt_masks_apply_increment_precomputed(float current, float amount, float scale_amount, float offset_amount, dt_masks_increment_t increment)
Apply a scroll increment using precomputed scale/offset factors.
Definition masks_gui.c:5260
void dt_masks_draw_outline_runs(cairo_t *cr, const float *const points, const int first, const int last, const dt_masks_skip_range_t *skips, const int skip_count)
Definition masks_gui.c:6049
void dt_masks_set_source_pos_initial_value(dt_masks_form_gui_t *mask_gui, dt_masks_form_t *mask_form)
Initialize the clone source position based on current GUI state.
Definition masks_gui.c:5570
int dt_masks_find_closest_handle_common(dt_masks_form_t *mask_form, dt_masks_form_gui_t *mask_gui, int form_index, int node_count_override, dt_masks_border_handle_fn border_handle_cb, dt_masks_curve_handle_fn curve_handle_cb, dt_masks_node_position_fn node_position_cb, dt_masks_distance_fn distance_cb, dt_masks_post_select_fn post_select_cb, void *user_data)
Centralized hit-testing for node/handle/segment selection across shapes.
Definition masks_gui.c:1183
void dt_masks_set_source_pos_initial_state(dt_masks_form_gui_t *mask_gui, const uint32_t key_state)
Decide initial source positioning mode for clone masks.
Definition masks_gui.c:5545
void dt_masks_gui_form_create(dt_masks_form_t *mask_form, dt_masks_form_gui_t *mask_gui, int form_index, dt_iop_module_t *module)
Definition masks_gui.c:1914
gboolean dt_masks_form_exit_creation(dt_iop_module_t *module, dt_masks_form_gui_t *mask_gui)
Definition masks_gui.c:2250
void dt_masks_gui_form_save_creation(dt_develop_t *develop, dt_iop_module_t *module, dt_masks_form_t *mask_form, dt_masks_form_gui_t *mask_gui)
Save the form creation right after a shape has been finished drawing.
Definition masks_gui.c:2525
The interactive half of the masks subsystem: the editing state (dt_masks_form_gui_t),...
static void dt_masks_gui_delta_to_image_abs(const dt_masks_form_gui_t *gui, float point[2])
Definition masks_gui.h:319
static int dt_masks_gui_selected_segment_index(const dt_masks_form_gui_t *gui)
Definition masks_gui.h:250
static int dt_masks_gui_selected_node_index(const dt_masks_form_gui_t *gui)
Definition masks_gui.h:235
static int dt_masks_gui_selected_handle_index(const dt_masks_form_gui_t *gui)
Definition masks_gui.h:240
static void dt_masks_draw_source_preview(cairo_t *cr, const float zoom_scale, dt_masks_form_gui_t *gui, const float initial_xpos, const float initial_ypos, const float xpos, const float ypos, const int adding)
Definition masks_gui.h:719
static float dt_masks_get_form_size_from_nodes(const GList *points)
Definition masks_gui.h:264
static int dt_masks_gui_selected_handle_border_index(const dt_masks_form_gui_t *gui)
Definition masks_gui.h:245
#define menu_item_set_fake_accel(menu_item, keyval, mods)
Definition masks_gui.h:958
static void dt_masks_gui_cursor_to_raw_norm(dt_develop_t *dev, const dt_masks_form_gui_t *gui, float point[2])
Definition masks_gui.h:304
static float dt_masks_border_from_projected_handle(dt_develop_t *dev, const float node[2], const float projected_image_pos[2], const float scale_ref)
Definition masks_gui.h:386
static gboolean dt_masks_gui_change_affects_selected_node_or_all(const dt_masks_form_gui_t *gui, const int index)
Definition masks_gui.h:255
static void dt_masks_gui_delta_from_raw_anchor(dt_develop_t *dev, const dt_masks_form_gui_t *gui, const float anchor[2], float *delta_x, float *delta_y)
Definition masks_gui.h:327
static void dt_masks_project_on_line(const float cursor[2], const float node[2], const float handle[2], float point[2])
Definition masks_gui.h:361
static gboolean dt_masks_gui_points_reach(const dt_masks_form_gui_points_t *gp, const float x, const float y, const float reach)
Definition masks_gui.h:83
static void dt_masks_gui_delta_to_raw_norm(dt_develop_t *dev, const dt_masks_form_gui_t *gui, float point[2])
Definition masks_gui.h:312
static void dt_masks_touched_set(dt_iop_roi_t *touched, int x0, int y0, int x1, int y1, const int width, const int height)
static void dt_masks_touched_full(dt_iop_roi_t *touched, const int width, const int height)
static void dt_masks_touched_none(dt_iop_roi_t *touched)
@ DT_MASKS_EDIT_FULL
dt_masks_type_t
Definition masks_types.h:62
@ DT_MASKS_CLONE
Definition masks_types.h:67
@ DT_MASKS_GROUP
Definition masks_types.h:66
@ DT_MASKS_IS_RETOUCHE
Definition masks_types.h:79
dt_masks_interaction_t
@ DT_MASKS_INTERACTION_OPACITY
@ DT_MASKS_INTERACTION_SIZE
@ DT_MASKS_INTERACTION_FADING
dt_masks_increment_t
@ DT_MASKS_INCREMENT_SCALE
@ DT_MASKS_INCREMENT_OFFSET
@ DT_MASKS_INCREMENT_ABSOLUTE
#define CLAMPF(a, mn, mx)
Definition math.h:91
#define M_PI
Definition math.h:47
#define dt_free_align(ptr)
Release memory from dt_alloc_align() and set ptr to NULL.
Definition mem_alloc.h:214
static float * dt_alloc_align_float(size_t pixels)
Allocate pixels floats, cacheline-aligned and marked as such.
Definition mem_alloc.h:235
#define dt_free(ptr)
g_free() ptr and set it to NULL, skipping both if it is already NULL.
Definition mem_alloc.h:171
GtkWidget * ctx_gtk_menu_item_new_with_icon_and_shortcut(const char *label, const char *shortcut, GtkWidget *menu, void(*activate_callback)(GtkWidget *widget, gpointer user_data), gpointer user_data, dt_menu_icon_t icon)
Definition menu.c:127
GtkWidget * ctx_gtk_menu_item_new_with_markup(const char *label, GtkWidget *menu, void(*activate_callback)(GtkWidget *widget, gpointer user_data), gpointer user_data)
Definition menu.c:170
GtkWidget * ctx_gtk_menu_item_new_with_markup_and_shortcut(const char *label, const char *shortcut, GtkWidget *menu, void(*activate_callback)(GtkWidget *widget, gpointer user_data), gpointer user_data)
Definition menu.c:185
dt_menu_icon_t
Definition menu.h:30
@ DT_MENU_ICON_CIRCLE
Definition menu.h:32
@ DT_MENU_ICON_SQUARE
Definition menu.h:33
char * key
uint32_t width
Definition mipmap_cache.c:0
uint32_t height
Definition mipmap_cache.c:1
size_t size
Definition mipmap_cache.c:3
#define __OMP_PARALLEL_FOR__(...)
Definition openmp.h:95
#define __OMP_PARALLEL_FOR_SIMD__(...)
Definition openmp.h:96
#define dt_pixelpipe_cache_alloc_align_float_cache(pixels, id)
#define dt_pixelpipe_cache_alloc_align_cache(size, id)
#define dt_pixelpipe_cache_free_align(mem)
static void _polygon_set_hint_message(const dt_masks_form_gui_t *const mask_gui, const dt_masks_form_t *const mask_form, char *const restrict msgbuf, const size_t msgbuf_len)
Definition polygon.c:3438
static gboolean _polygon_get_gravity_center(dt_develop_t *dev, const dt_masks_form_t *mask_form, float center[2], float *area)
Definition polygon.c:1098
static dt_masks_raster_result_t _polygon_get_mask_roi(const dt_iop_module_t *const module, dt_dev_pixelpipe_t *pipe, const dt_dev_pixelpipe_iop_t *const piece, dt_masks_form_t *const mask_form, const dt_iop_roi_t *roi, float *buffer, dt_iop_roi_t *touched)
Definition polygon.c:3013
static int _polygon_populate_context_menu(GtkWidget *menu, struct dt_masks_form_t *mask_form, struct dt_masks_form_gui_t *mask_gui, const float pzx, const float pzy)
Definition polygon.c:3565
static void _polygon_joint_arc(const _polygon_walk_t *const w, const float *const centre, const float *const from, const float *const to)
Definition polygon.c:585
static int _find_closest_handle(dt_masks_form_t *mask_form, dt_masks_form_gui_t *mask_gui, int form_index)
Definition polygon.c:1323
static void _polygon_handle_to_ctrl(const float point_x, const float point_y, const float handle_x, const float handle_y, float *ctrl1_x, float *ctrl1_y, float *ctrl2_x, float *ctrl2_y, const gboolean clockwise)
Convert a handle extremity into symmetric Bezier control points.
Definition polygon.c:212
static void _polygon_bounding_box(const float *const point_buffer, const float *border_buffer, const int corner_count, const int point_count, int border_count, int *width, int *height, int *posx, int *posy)
Compute bounding box and add a small padding for rasterization safety.
Definition polygon.c:2223
static float _polygon_get_interaction_value(const dt_masks_form_t *mask_form, dt_masks_interaction_t interaction)
Definition polygon.c:1065
static void _polygon_ctrl2_to_handle(const float point_x, const float point_y, const float ctrl_x, const float ctrl_y, float *handle_x, float *handle_y, const gboolean clockwise)
Convert control point #2 into a handle extremity.
Definition polygon.c:189
static void _polygon_bounding_box_raw(const float *const point_buffer, const float *border_buffer, const int corner_count, const int point_count, int border_count, float *x_min, float *x_max, float *y_min, float *y_max)
Compute raw bounding box for polygon points and border samples.
Definition polygon.c:2181
static gboolean _polygon_walk_segment(const _polygon_walk_t *const w, _polygon_walk_state_t *const s, const int k)
Definition polygon.c:595
static void _polygon_catmull_to_bezier(const float x1, const float y1, const float x2, const float y2, const float x3, const float y3, const float x4, const float y4, float *bezier_x1, float *bezier_y1, float *bezier_x2, float *bezier_y2)
Convert a Catmull-Rom segment to Bezier control points.
Definition polygon.c:239
static void _add_node_to_segment(struct dt_iop_module_t *module, dt_masks_form_t *mask_form, int parent_id, dt_masks_form_gui_t *mask_gui, int form_index)
Definition polygon.c:902
#define FADING_MIN
Definition polygon.c:64
static int _polygon_creation_closing_form(dt_masks_form_t *mask_form, dt_masks_form_gui_t *mask_gui)
Close the polygon creation by removing the temporary last node.
Definition polygon.c:1574
static gboolean _polygon_border_handle_cb(const dt_masks_form_gui_points_t *gui_points, int node_count, int node_index, float *handle_x, float *handle_y, void *user_data)
Polygon-specific border handle lookup.
Definition polygon.c:1290
static gboolean _polygon_border_get_XY(const float p0_x, const float p0_y, const float p1_x, const float p1_y, const float p2_x, const float p2_y, const float p3_x, const float p3_y, const float t, const float radius, float radius_rate, float *center_x, float *center_y, float *border_x, float *border_y)
Evaluate a cubic Bezier and its border offset at t in [0, 1].
Definition polygon.c:97
static float _polygon_radius_rate_at(const float r1, const float r2, const double t)
Definition polygon.c:179
static void _polygon_events_post_expose(cairo_t *cr, float zoom_scale, dt_masks_form_gui_t *mask_gui, int form_index, int node_count)
Draw polygon overlays (nodes, handles, borders, source) after exposure.
Definition polygon.c:2033
static void _polygon_switch_node_callback(GtkWidget *widget, gpointer user_data)
Definition polygon.c:3497
static gboolean _is_within_pxl_threshold(float *min, float *max, int pixel_threshold)
Definition polygon.c:400
static int _change_size(dt_masks_form_t *mask_form, int parent_id, dt_masks_form_gui_t *mask_gui, struct dt_iop_module_t *module, int form_index, const float amount, const dt_masks_increment_t increment, const int flow)
Scale the polygon around its centroid.
Definition polygon.c:1421
static float _polygon_set_interaction_value(dt_masks_form_t *mask_form, dt_masks_interaction_t interaction, float value, dt_masks_increment_t increment, int flow, dt_masks_form_gui_t *mask_gui, struct dt_iop_module_t *module)
Definition polygon.c:1131
static void _polygon_distance_cb(float pointer_x, float pointer_y, float cursor_radius, dt_masks_form_gui_t *mask_gui, int form_index, int node_count, int *inside, int *inside_border, int *near_handle, int *inside_source, float *dist, void *user_data)
Polygon-specific inside/border/segment hit testing.
Definition polygon.c:1314
static gboolean _polygon_form_gravity_center(const dt_masks_form_t *mask_form, float *center_x, float *center_y, float *surface)
Compute polygon centroid from the form nodes (normalized space).
Definition polygon.c:1371
static dt_masks_raster_result_t _polygon_get_mask(const dt_iop_module_t *const module, dt_dev_pixelpipe_t *pipe, const dt_dev_pixelpipe_iop_t *const piece, dt_masks_form_t *const mask_form, float **buffer, int *width, int *height, int *posx, int *posy)
Definition polygon.c:2389
static dt_masks_raster_result_t _polygon_get_points_border(dt_develop_t *develop, dt_masks_form_t *mask_form, float **point_buffer, int *point_count, float **border_buffer, int *border_count, dt_masks_skip_range_t **border_skips, int *border_skip_count, int source, const dt_iop_module_t *module)
Definition polygon.c:965
static float _polygon_get_position_in_segment(float point_x, float point_y, dt_masks_form_t *mask_form, int segment_index)
Find the parametric position along a segment closest to a point.
Definition polygon.c:864
static float _polygon_radius_at(const float r1, const float r2, const double t)
Definition polygon.c:174
static void _polygon_falloff_roi(float *buffer, int *p0, int *p1, int bw, int bh)
Definition polygon.c:2974
static void _polygon_set_form_name(struct dt_masks_form_t *const mask_form, const size_t form_number)
Assign a default name for a polygon form.
Definition polygon.c:3433
static int _polygon_events_mouse_moved(struct dt_iop_module_t *module, double x, double y, double pressure, int which, dt_masks_form_t *mask_form, int parent_id, dt_masks_form_gui_t *mask_gui, int form_index)
Polygon mouse-move handler.
Definition polygon.c:1842
static int _polygon_events_button_pressed(struct dt_iop_module_t *module, double x, double y, double pressure, int which, int type, uint32_t state, dt_masks_form_t *mask_form, int parent_id, dt_masks_form_gui_t *mask_gui, int form_index)
Definition polygon.c:1597
static int _polygon_events_mouse_scrolled(struct dt_iop_module_t *module, double x, double y, int up, int flow, uint32_t state, dt_masks_form_t *mask_form, int parent_id, dt_masks_form_gui_t *mask_gui, int form_index, dt_masks_interaction_t interaction)
Handle mouse wheel updates for polygon size/fading/opacity.
Definition polygon.c:1532
static void _polygon_points_recurs_border_gaps(const float *const center_max, const float *const border_min, const float *const border_max, dt_masks_dynbuf_t *draw_points, dt_masks_dynbuf_t *draw_border, gboolean clockwise, const int step)
Fill gaps between border points with a circular arc.
Definition polygon.c:339
const dt_masks_functions_t dt_masks_functions_polygon
Definition polygon.c:3630
static void _polygon_get_sizes(struct dt_iop_module_t *module, dt_masks_form_t *mask_form, dt_masks_form_gui_t *mask_gui, int form_index, float *mask_size, float *border_size)
Definition polygon.c:998
static void _polygon_add_node_callback(GtkWidget *menu, gpointer user_data)
Definition polygon.c:3543
static void _polygon_curve_handle_cb(const dt_masks_form_gui_points_t *gui_points, int node_index, float *handle_x, float *handle_y, void *user_data)
Polygon-specific curve handle lookup (depends on winding direction).
Definition polygon.c:1302
static int _polygon_crop_to_roi(float *polygon, const int point_count, float xmin, float xmax, float ymin, float ymax)
Definition polygon.c:2679
static void _polygon_translate_node(dt_masks_node_polygon_t *node, const float delta_x, const float delta_y)
Definition polygon.c:954
static int _init_fading(dt_masks_form_t *mask_form, const float amount, const dt_masks_increment_t increment, const int flow, const float mask_size, const float border_size)
Initialize fading from config and emit the toast with a size-normalized percentage.
Definition polygon.c:1405
static int _polygon_events_button_released(struct dt_iop_module_t *module, double x, double y, int which, uint32_t state, dt_masks_form_t *mask_form, int parent_id, dt_masks_form_gui_t *mask_gui, int form_index)
Definition polygon.c:1775
void _polygon_falloff(float *const restrict buffer, int *p0, int *p1, int posx, int posy, int buffer_width)
Write a falloff segment into the mask buffer.
Definition polygon.c:2296
static void _polygon_creation_closing_form_callback(GtkWidget *widget, gpointer user_data)
Definition polygon.c:3487
static int _falloff_bridge_queue(int *const dpoints, int dindex, const int capacity, const int *const last0, const int *const last1, const int *const p0, const int *const p1)
Definition polygon.c:2368
#define FADING_MAX
Definition polygon.c:65
static dt_masks_raster_result_t _polygon_get_source_area(dt_iop_module_t *module, dt_dev_pixelpipe_t *pipe, dt_dev_pixelpipe_iop_t *piece, dt_masks_form_t *mask_form, int *width, int *height, int *posx, int *posy)
Definition polygon.c:2268
static int _polygon_events_key_pressed(struct dt_iop_module_t *module, GdkEventKey *event, dt_masks_form_t *mask_form, int parent_id, dt_masks_form_gui_t *mask_gui, int form_index)
Definition polygon.c:1790
static void _polygon_translate_all_nodes(dt_masks_form_t *mask_form, const float delta_x, const float delta_y)
Definition polygon.c:959
static void _polygon_segment_load(const dt_masks_node_polygon_t *const from, const dt_masks_node_polygon_t *const to, const _polygon_frame_t *const f, float p1[5], float p2[5])
Definition polygon.c:540
static int _change_fading(dt_masks_form_t *mask_form, int parent_id, dt_masks_form_gui_t *mask_gui, struct dt_iop_module_t *module, int form_index, const float amount, const dt_masks_increment_t increment, int flow)
Change polygon fading for the active node scope or the full shape.
Definition polygon.c:1490
static void _polygon_initial_source_pos(struct dt_develop_t *dev, const float iwd, const float iht, float *x, float *y)
Definition polygon.c:3477
static void _polygon_duplicate_points(dt_develop_t *const dev, dt_masks_form_t *const base, dt_masks_form_t *const dest)
Definition polygon.c:3471
static void _polygon_draw_shape(struct dt_develop_t *dev, cairo_t *cr, const float *point_buffer, const int point_count, const int node_count, const gboolean draw_border, const gboolean draw_source, const dt_masks_skip_range_t *skips, const int skip_count)
Draw a polygon or border polyline, skipping NaN points.
Definition polygon.c:2014
static void _polygon_init_ctrl_points(dt_masks_form_t *mask_form)
Initialize control points to match a Catmull-Rom-like spline.
Definition polygon.c:255
static dt_masks_raster_result_t _polygon_get_area(const dt_iop_module_t *const module, dt_dev_pixelpipe_t *pipe, const dt_dev_pixelpipe_iop_t *const piece, dt_masks_form_t *const mask_form, int *width, int *height, int *posx, int *posy)
Definition polygon.c:2280
static int _falloff_bridge_steps(const int *const last0, const int *const last1, const int *const p0, const int *const p1)
Definition polygon.c:2332
static void _polygon_points_recurs(float *segment_start, float *segment_end, double t_min, double t_max, float *polygon_min, float *polygon_max, float *border_min, float *border_max, float *result_polygon, float *result_border, dt_masks_dynbuf_t *draw_points, dt_masks_dynbuf_t *draw_border, int with_border, const int pixel_threshold, const gboolean have_min, const gboolean have_max, gboolean have_border_min, gboolean have_border_max, gboolean *out_have_border)
Recursive subdivision to sample polygon and border points.
Definition polygon.c:412
static void _polygon_get_distance(float point_x, float point_y, float radius, dt_masks_form_gui_t *mask_gui, int form_index, int node_count, int *inside, int *inside_border, int *near_handle, int *inside_source, float *dist)
Compute proximity between a point and the polygon GUI shape.
Definition polygon.c:1158
static void _polygon_reset_round_node_callback(GtkWidget *widget, gpointer user_data)
Definition polygon.c:3519
static void _polygon_walk_write_header(const _polygon_walk_t *const w)
Definition polygon.c:658
static void _falloff_bridge_stamp(float *const restrict buffer, const int *const last0, const int *const last1, const int *const p0, const int *const p1, const int posx, const int posy, const int width)
Definition polygon.c:2349
static void _falloff_bridge_lerp(const int *const from, const int *const to, const float t, int *const out)
Definition polygon.c:2341
static int _polygon_get_pts_border(dt_develop_t *develop, dt_masks_form_t *mask_form, const double iop_order, const int transform_direction, const dt_masks_distort_t *const dist, float **point_buffer, int *point_count, float **border_buffer, int *border_count, gboolean source)
Definition polygon.c:674
static int _get_area(const dt_iop_module_t *const module, dt_dev_pixelpipe_t *pipe, const dt_dev_pixelpipe_iop_t *const piece, dt_masks_form_t *const mask_form, int *width, int *height, int *posx, int *posy, gboolean get_source)
Definition polygon.c:2237
static void _polygon_sanitize_config(dt_masks_type_t type)
Definition polygon.c:3425
static gboolean _polygon_is_clockwise(dt_masks_form_t *mask_form)
Determine polygon winding order.
Definition polygon.c:314
static void _polygon_get_XY(const float p0_x, const float p0_y, const float p1_x, const float p1_y, const float p2_x, const float p2_y, const float p3_x, const float p3_y, const float t, float *out_x, float *out_y)
Evaluate a cubic Bezier at t in [0, 1].
Definition polygon.c:79
static void _polygon_gui_gravity_center(const float *point_buffer, int point_count, float *center_x, float *center_y, float *area)
Compute polygon centroid from GUI points using the shoelace formula.
Definition polygon.c:1336
static const dt_aligned_pixel_simd_t value
Definition simd.h:144
const float uint32_t state[4]
const float r
float radius_sign
Definition polygon.c:534
dt_masks_node_polygon_t ** nodes
Definition polygon.c:559
float * node_border
Definition polygon.c:567
_polygon_frame_t frame
Definition polygon.c:561
gboolean clockwise
Definition polygon.c:564
dt_masks_dynbuf_t * dpoints
Definition polygon.c:565
gboolean with_border
Definition polygon.c:563
dt_masks_dynbuf_t * dborder
Definition polygon.c:566
uint8_t * node_has_border
Definition polygon.c:568
int pixel_threshold
Definition polygon.c:562
Objective facts about the image a dev is working on.
struct dt_develop_t * dev
Definition imageop.h:311
Region of interest passed through the pixelpipe.
Definition format.h:49
double scale
Definition format.h:51
int width
Definition format.h:50
int height
Definition format.h:50
Where an outline builder gets its geometry from.
dt_masks_skip_range_t * border_skips
Definition masks_gui.h:70
gboolean node_selected
Definition masks_gui.h:145
gboolean source_selected
Definition masks_gui.h:153
gboolean source_dragging
Definition masks_gui.h:161
dt_masks_edit_mode_t edit_mode
Definition masks_gui.h:138
dt_iop_module_t * creation_module
Definition masks_gui.h:180
gboolean creation_closing_form
Definition masks_gui.h:179
gboolean seg_selected
Definition masks_gui.h:147
gboolean form_dragging
Definition masks_gui.h:160
dt_masks_type_t type
Definition masks_gui.h:103
gboolean form_selected
Definition masks_gui.h:151
gboolean handle_selected
Definition masks_gui.h:146
gboolean border_selected
Definition masks_gui.h:152
struct dt_develop_t * dev
Definition masks_gui.h:101
dt_masks_type_t type
Definition masks.h:254
float source[2]
Definition masks.h:261
char name[128]
Definition masks.h:277
GList * points
Definition masks.h:253
shape_draw_function_t draw_shape
struct dt_masks_gui_center_point_t::@27 main
struct dt_masks_gui_center_point_t::@28 source
dt_masks_points_states_t state
Definition masks.h:218
One cut in a shape's border outline: while walking the border buffer forward, on reaching index jump_...
typedef double((*spd)(unsigned long int wavelength, double TempK))
#define MIN(a, b)
Definition thinplate.c:32
#define MAX(a, b)
Definition thinplate.c:29
static double dt_get_wtime(void)
Definition times.h:43
Telling the user something happened.
static gboolean dt_modifier_is(GdkModifierType state, const GdkModifierType desired_modifier_mask)
static GdkModifierType dt_accels_display_mods(GdkModifierType mods)
#define DT_GUI_MOUSE_EFFECT_RADIUS