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*/
41#include "common/darktable.h"
42#include "gui/gdkkeys.h"
43#include "bauhaus/bauhaus.h"
44#include "common/debug.h"
45#include "common/imagebuf.h"
46#include "common/undo.h"
47#include "control/conf.h"
48#include "develop/blend.h"
49#include "develop/imageop.h"
50#include "develop/masks.h"
52#include "gui/actions/menu.h"
53#include <assert.h>
54
55#define HARDNESS_MIN 0.0005f
56#define HARDNESS_MAX 1.0f
57
58#define BORDER_MIN 0.00005f
59#define BORDER_MAX 0.5f
60
61static void _polygon_bounding_box_raw(const float *const point_buffer, const float *border_buffer,
62 const int corner_count, const int point_count, int border_count,
63 float *x_min, float *x_max, float *y_min, float *y_max);
64
70static void _polygon_get_XY(const float p0_x, const float p0_y, const float p1_x, const float p1_y,
71 const float p2_x, const float p2_y, const float p3_x, const float p3_y,
72 const float t, float *out_x, float *out_y)
73{
74 const float one_minus_t = 1.0f - t;
75 const float a = one_minus_t * one_minus_t * one_minus_t;
76 const float b = 3.0f * t * one_minus_t * one_minus_t;
77 const float c = 3.0f * t * t * one_minus_t;
78 const float d = t * t * t;
79 *out_x = p0_x * a + p1_x * b + p2_x * c + p3_x * d;
80 *out_y = p0_y * a + p1_y * b + p2_y * c + p3_y * d;
81}
82
88static void _polygon_border_get_XY(const float p0_x, const float p0_y, const float p1_x, const float p1_y,
89 const float p2_x, const float p2_y, const float p3_x, const float p3_y,
90 const float t, const float radius,
91 float *center_x, float *center_y, float *border_x, float *border_y)
92{
93 // we get the point
94 _polygon_get_XY(p0_x, p0_y, p1_x, p1_y, p2_x, p2_y, p3_x, p3_y, t, center_x, center_y);
95
96 // now we get derivative points
97 const double ti = 1.0 - (double)t;
98
99 const double t_t = (double)t * t;
100 const double ti_ti = ti * ti;
101 const double t_ti = t * ti;
102
103 const double a = 3.0 * ti_ti;
104 const double b = 3.0 * (ti_ti - 2.0 * t_ti);
105 const double c = 3.0 * (2.0 * t_ti - t_t);
106 const double d = 3.0 * t_t;
107
108 const double dx = -p0_x * a + p1_x * b + p2_x * c + p3_x * d;
109 const double dy = -p0_y * a + p1_y * b + p2_y * c + p3_y * d;
110
111 // so we can have the resulting point
112 if(dx == 0 && dy == 0)
113 {
114 *border_x = NAN;
115 *border_y = NAN;
116 return;
117 }
118 const double l = 1.0 / sqrt(dx * dx + dy * dy);
119 *border_x = (*center_x) + radius * dy * l;
120 *border_y = (*center_y) - radius * dx * l;
121}
122
128static void _polygon_ctrl2_to_handle(const float point_x, const float point_y,
129 const float ctrl_x, const float ctrl_y,
130 float *handle_x, float *handle_y, const gboolean clockwise)
131{
132 const float delta_y = ctrl_y - point_y;
133 const float delta_x = point_x - ctrl_x;
134 if(clockwise)
135 {
136 *handle_x = point_x - delta_y;
137 *handle_y = point_y - delta_x;
138 }
139 else
140 {
141 *handle_x = point_x + delta_y;
142 *handle_y = point_y + delta_x;
143 }
144}
145
151static void _polygon_handle_to_ctrl(const float point_x, const float point_y,
152 const float handle_x, const float handle_y,
153 float *ctrl1_x, float *ctrl1_y, float *ctrl2_x, float *ctrl2_y,
154 const gboolean clockwise)
155{
156 const float delta_y = handle_y - point_y;
157 const float delta_x = point_x - handle_x;
158
159 if(clockwise)
160 {
161 *ctrl1_x = point_x - delta_y;
162 *ctrl1_y = point_y - delta_x;
163 *ctrl2_x = point_x + delta_y;
164 *ctrl2_y = point_y + delta_x;
165 }
166 else
167 {
168 *ctrl1_x = point_x + delta_y;
169 *ctrl1_y = point_y + delta_x;
170 *ctrl2_x = point_x - delta_y;
171 *ctrl2_y = point_y - delta_x;
172 }
173}
174
178static void _polygon_catmull_to_bezier(const float x1, const float y1, const float x2, const float y2,
179 const float x3, const float y3, const float x4, const float y4,
180 float *bezier_x1, float *bezier_y1,
181 float *bezier_x2, float *bezier_y2)
182{
183 *bezier_x1 = (-x1 + 6 * x2 + x3) / 6;
184 *bezier_y1 = (-y1 + 6 * y2 + y3) / 6;
185 *bezier_x2 = (x2 + 6 * x3 - x4) / 6;
186 *bezier_y2 = (y2 + 6 * y3 - y4) / 6;
187}
188
195{
196 // if we have less that 3 points, what to do ??
197 const guint node_count = g_list_length(mask_form->points);
198 if(node_count < 2) return;
199
200 if(IS_NULL_PTR(mask_form) || IS_NULL_PTR(mask_form->points)) return;
201
202 dt_masks_node_polygon_t **nodes = malloc((size_t)node_count * sizeof(*nodes));
203 if(IS_NULL_PTR(nodes)) return;
204 const GList *form_points = mask_form->points;
205 for(guint node_index = 0; node_index < node_count; node_index++)
206 {
207 nodes[node_index] = (dt_masks_node_polygon_t *)form_points->data;
208 form_points = g_list_next(form_points);
209 }
210
211 for(guint node_index = 0; node_index < node_count; node_index++)
212 {
213 dt_masks_node_polygon_t *point3 = nodes[node_index];
214 if(IS_NULL_PTR(point3)) { dt_free(nodes); return; }
215 // if the point has not been set manually, we redefine it
217 {
218 dt_masks_node_polygon_t *point1 = nodes[(node_index + node_count - 2) % node_count];
219 dt_masks_node_polygon_t *point2 = nodes[(node_index + node_count - 1) % node_count];
220 dt_masks_node_polygon_t *point4 = nodes[(node_index + 1) % node_count];
221 dt_masks_node_polygon_t *point5 = nodes[(node_index + 2) % node_count];
222 if(IS_NULL_PTR(point1) || IS_NULL_PTR(point2) || IS_NULL_PTR(point4) || IS_NULL_PTR(point5)) { dt_free(nodes); return; }
223
224 float bezier1_x = 0.0f;
225 float bezier1_y = 0.0f;
226 float bezier2_x = 0.0f;
227 float bezier2_y = 0.0f;
228 _polygon_catmull_to_bezier(point1->node[0], point1->node[1], point2->node[0], point2->node[1],
229 point3->node[0], point3->node[1], point4->node[0], point4->node[1],
230 &bezier1_x, &bezier1_y, &bezier2_x, &bezier2_y);
231 if(point2->ctrl2[0] == -1.0) point2->ctrl2[0] = bezier1_x;
232 if(point2->ctrl2[1] == -1.0) point2->ctrl2[1] = bezier1_y;
233 point3->ctrl1[0] = bezier2_x;
234 point3->ctrl1[1] = bezier2_y;
235 _polygon_catmull_to_bezier(point2->node[0], point2->node[1], point3->node[0], point3->node[1],
236 point4->node[0], point4->node[1], point5->node[0], point5->node[1],
237 &bezier1_x, &bezier1_y, &bezier2_x, &bezier2_y);
238 if(point4->ctrl1[0] == -1.0) point4->ctrl1[0] = bezier2_x;
239 if(point4->ctrl1[1] == -1.0) point4->ctrl1[1] = bezier2_y;
240 point3->ctrl2[0] = bezier1_x;
241 point3->ctrl2[1] = bezier1_y;
242 }
243 }
244 dt_free(nodes);
245 return;
246}
247
253static gboolean _polygon_is_clockwise(dt_masks_form_t *mask_form)
254{
255 if(IS_NULL_PTR(mask_form) || IS_NULL_PTR(mask_form->points)) return 0;
256 if(!g_list_shorter_than(mask_form->points, 3)) // if we have at least three points...
257 {
258 float sum = 0.0f;
259 for(const GList *form_points = mask_form->points; form_points; form_points = g_list_next(form_points))
260 {
261 const GList *next = g_list_next_wraparound(form_points, mask_form->points); // next, wrapping around if on last elt
262 dt_masks_node_polygon_t *point1 = (dt_masks_node_polygon_t *)form_points->data; // kth element of mask_form->points
263 dt_masks_node_polygon_t *point2 = (dt_masks_node_polygon_t *)next->data;
264 if(IS_NULL_PTR(point1) || IS_NULL_PTR(point2)) return 0;
265 sum += (point2->node[0] - point1->node[0]) * (point2->node[1] + point1->node[1]);
266 }
267 return (sum < 0);
268 }
269 // return dummy answer
270 return TRUE;
271}
272
278static int _polygon_fill_gaps(int last_x, int last_y, int target_x, int target_y,
279 dt_masks_dynbuf_t *points)
280{
281 dt_masks_dynbuf_reset(points);
282 dt_masks_dynbuf_add_2(points, target_x, target_y);
283
284 const int delta_x = target_x - last_x;
285 const int delta_y = target_y - last_y;
286 const int abs_dx = abs(delta_x);
287 const int abs_dy = abs(delta_y);
288
289 // Only fill gaps if distance is > 1 in either axis
290 if(abs_dx <= 1 && abs_dy <= 1) return 1;
291
292 // Use Bresenham's line algorithm (integer-based)
293 int err = abs_dx > abs_dy ? (abs_dx / 2) : (abs_dy / 2);
294 int point_x = last_x;
295 int point_y = last_y;
296 const int step_x = delta_x > 0 ? 1 : -1;
297 const int step_y = delta_y > 0 ? 1 : -1;
298
299 if(abs_dx > abs_dy)
300 {
301 // Major axis is X
302 while(point_x != target_x)
303 {
304 point_x += step_x;
305 err -= abs_dy;
306 if(err < 0)
307 {
308 point_y += step_y;
309 err += abs_dx;
310 }
311 dt_masks_dynbuf_add_2(points, point_x, point_y);
312 }
313 }
314 else
315 {
316 // Major axis is Y
317 while(point_y != target_y)
318 {
319 point_y += step_y;
320 err -= abs_dx;
321 if(err < 0)
322 {
323 point_x += step_x;
324 err += abs_dy;
325 }
326 dt_masks_dynbuf_add_2(points, point_x, point_y);
327 }
328 }
329 return 1;
330}
331
337static void _polygon_points_recurs_border_gaps(float *center_max, float *border_min,
338 float *border_min2, float *border_max,
339 dt_masks_dynbuf_t *draw_points,
340 dt_masks_dynbuf_t *draw_border,
341 gboolean clockwise)
342{
343 // we want to find the start and end angles
344 double angle_start = atan2f(border_min[1] - center_max[1], border_min[0] - center_max[0]);
345 double angle_end = atan2f(border_max[1] - center_max[1], border_max[0] - center_max[0]);
346 if(angle_start == angle_end) return;
347
348 // we have to be sure that we turn in the correct direction
349 if(angle_end < angle_start && clockwise)
350 {
351 angle_end += 2 * M_PI;
352 }
353 if(angle_end > angle_start && !clockwise)
354 {
355 angle_start += 2 * M_PI;
356 }
357
358 // we determine start and end radius too
359 const float radius_start = sqrtf((border_min[1] - center_max[1]) * (border_min[1] - center_max[1])
360 + (border_min[0] - center_max[0]) * (border_min[0] - center_max[0]));
361 const float radius_end = sqrtf((border_max[1] - center_max[1]) * (border_max[1] - center_max[1])
362 + (border_max[0] - center_max[0]) * (border_max[0] - center_max[0]));
363
364 // and the max length of the circle arc
365 int step_count = 0;
366 if(angle_end > angle_start)
367 step_count = (angle_end - angle_start) * fmaxf(radius_start, radius_end);
368 else
369 step_count = (angle_start - angle_end) * fmaxf(radius_start, radius_end);
370 if(step_count < 2) return;
371
372 // and now we add the points
373 const float angle_step = (angle_end - angle_start) / step_count;
374 const float radius_step = (radius_end - radius_start) / step_count;
375 float current_radius = radius_start + radius_step;
376 float current_angle = angle_start + angle_step;
377 // allocate entries in the dynbufs
378 float *points_ptr = dt_masks_dynbuf_reserve_n(draw_points, 2 * (step_count - 1));
379 float *border_ptr = draw_border ? dt_masks_dynbuf_reserve_n(draw_border, 2 * (step_count - 1)) : NULL;
380 // and fill them in: the same center pos for each point in dpoints, and the corresponding border point at
381 // successive angular positions for dborder
382 if(!IS_NULL_PTR(points_ptr))
383 {
384 for(int step_index = 1; step_index < step_count; step_index++)
385 {
386 *points_ptr++ = center_max[0];
387 *points_ptr++ = center_max[1];
388 if(!IS_NULL_PTR(border_ptr))
389 {
390 *border_ptr++ = center_max[0] + current_radius * cosf(current_angle);
391 *border_ptr++ = center_max[1] + current_radius * sinf(current_angle);
392 }
393 current_radius += radius_step;
394 current_angle += angle_step;
395 }
396 }
397}
398
399static inline gboolean _is_within_pxl_threshold(float *min, float *max, int pixel_threshold)
400{
401 return abs((int)min[0] - (int)max[0]) < pixel_threshold &&
402 abs((int)min[1] - (int)max[1]) < pixel_threshold;
403}
404
405
411static void _polygon_points_recurs(float *segment_start, float *segment_end,
412 double t_min, double t_max,
413 float *polygon_min, float *polygon_max,
414 float *border_min, float *border_max,
415 float *result_polygon, float *result_border,
416 dt_masks_dynbuf_t *draw_points, dt_masks_dynbuf_t *draw_border,
417 int with_border, const int pixel_threshold)
418{
419 // we calculate points if needed
420 if(isnan(polygon_min[0]))
421 {
422 _polygon_border_get_XY(segment_start[0], segment_start[1], segment_start[2], segment_start[3],
423 segment_end[2], segment_end[3], segment_end[0], segment_end[1], t_min,
424 segment_start[4]
425 + (segment_end[4] - segment_start[4]) * t_min * t_min * (3.0 - 2.0 * t_min),
426 polygon_min, polygon_min + 1, border_min, border_min + 1);
427 }
428 if(isnan(polygon_max[0]))
429 {
430 _polygon_border_get_XY(segment_start[0], segment_start[1], segment_start[2], segment_start[3],
431 segment_end[2], segment_end[3], segment_end[0], segment_end[1], t_max,
432 segment_start[4]
433 + (segment_end[4] - segment_start[4]) * t_max * t_max * (3.0 - 2.0 * t_max),
434 polygon_max, polygon_max + 1, border_max, border_max + 1);
435 }
436
437 // are the points near ?
438 if((t_max - t_min < 0.0001)
439 || (_is_within_pxl_threshold(polygon_min, polygon_max, pixel_threshold)
440 && (!with_border || (_is_within_pxl_threshold(border_min, border_max, pixel_threshold)))))
441 {
442 dt_masks_dynbuf_add_2(draw_points, polygon_max[0], polygon_max[1]);
443 result_polygon[0] = polygon_max[0];
444 result_polygon[1] = polygon_max[1];
445
446 if(with_border)
447 {
448 dt_masks_dynbuf_add_2(draw_border, border_max[0], border_max[1]);
449 result_border[0] = border_max[0];
450 result_border[1] = border_max[1];
451 }
452 return;
453 }
454
455 // we split in two part
456 double t_mid = (t_min + t_max) / 2.0;
457 float polygon_mid[2] = { NAN, NAN };
458 float border_mid[2] = { NAN, NAN };
459 float polygon_result_left[2] = { 0 };
460 float border_result_left[2] = { 0 };
461 _polygon_points_recurs(segment_start, segment_end, t_min, t_mid,
462 polygon_min, polygon_mid, border_min, border_mid,
463 polygon_result_left, border_result_left,
464 draw_points, draw_border, with_border, pixel_threshold);
465 _polygon_points_recurs(segment_start, segment_end, t_mid, t_max,
466 polygon_result_left, polygon_max, border_result_left, border_max,
467 result_polygon, result_border,
468 draw_points, draw_border, with_border, pixel_threshold);
469}
470
471// Maximum number of self-intersection portions to track;
472// helps limit detection complexity
473#define POLYGON_MAX_SELF_INTERSECTIONS(nb_nodes) ((nb_nodes) * 4)
474
479 int node_count, float *border_points,
480 int border_point_count,
481 int *intersection_count_out)
482{
483 if(node_count == 0 || border_point_count == 0)
484 {
485 *intersection_count_out = 0;
486 return 0;
487 }
488
489 int intersection_count = 0;
490
491 // we search extreme points in x and y
492 float xmin_f = FLT_MAX, xmax_f = -FLT_MAX;
493 float ymin_f = FLT_MAX, ymax_f = -FLT_MAX;
494 int extrema_index[4] = { -1 };
495
496 for(int i = node_count * 3; i < border_point_count; i++)
497 {
498 if(isnan(border_points[i * 2]) || isnan(border_points[i * 2 + 1]))
499 {
500 // find nearest previous valid point; if at start, wrap to last valid point
501 int prev = i - 1;
502 while(prev >= node_count * 3
503 && (isnan(border_points[prev * 2]) || isnan(border_points[prev * 2 + 1]))) prev--;
504 if(prev < node_count * 3)
505 {
506 // wrap to last valid point in buffer
507 prev = border_point_count - 1;
508 while(prev >= node_count * 3
509 && (isnan(border_points[prev * 2]) || isnan(border_points[prev * 2 + 1]))) prev--;
510 }
511 if(prev >= node_count * 3)
512 {
513 border_points[i * 2] = border_points[prev * 2];
514 border_points[i * 2 + 1] = border_points[prev * 2 + 1];
515 }
516 else
517 {
518 continue; // skip if no valid point found
519 }
520 }
521 if(xmin_f > border_points[i * 2])
522 {
523 xmin_f = border_points[i * 2];
524 extrema_index[0] = i;
525 }
526 if(xmax_f < border_points[i * 2])
527 {
528 xmax_f = border_points[i * 2];
529 extrema_index[1] = i;
530 }
531 if(ymin_f > border_points[i * 2 + 1])
532 {
533 ymin_f = border_points[i * 2 + 1];
534 extrema_index[2] = i;
535 }
536 if(ymax_f < border_points[i * 2 + 1])
537 {
538 ymax_f = border_points[i * 2 + 1];
539 extrema_index[3] = i;
540 }
541 }
542
543 // Cast to int with explicit rounding for stable grid computation
544 int xmin = (int)floorf(xmin_f) - 1;
545 int xmax = (int)ceilf(xmax_f) + 1;
546 int ymin = (int)floorf(ymin_f) - 1;
547 int ymax = (int)ceilf(ymax_f) + 1;
548 const int grid_height = ymax - ymin;
549 const int grid_width = xmax - xmin;
550
551 // we allocate the buffer
552 const size_t grid_size = (size_t)grid_height * grid_width;
553 if(grid_size < 10 || grid_height < 0 || grid_width < 0)
554 {
555 *intersection_count_out = 0;
556 return 0;
557 }
558
559 int *intersection_grid = dt_pixelpipe_cache_alloc_align_cache(sizeof(int) * grid_size, 0);
560 if(IS_NULL_PTR(intersection_grid)) return 1;
561 memset(intersection_grid, 0, sizeof(int) * grid_size);
562
563 dt_masks_dynbuf_t *gap_points = dt_masks_dynbuf_init(100000, "polygon extra");
564 if(IS_NULL_PTR(gap_points))
565 {
566 dt_pixelpipe_cache_free_align(intersection_grid);
567 return 1;
568 }
569
570 // we'll iterate through all border points, but we can't start at point[0]
571 // because it may be in a self-intersected section
572 // so we choose a point where we are sure there's no intersection:
573 // one from border shape extrema (here x_max)
574 // start from the point immediately before the x_max extremum, with safe wrap-around
575 int start_idx = extrema_index[1] - 1;
576 if(start_idx < node_count * 3) start_idx = border_point_count - 1;
577 int last_x = border_points[start_idx * 2];
578 int last_y = border_points[start_idx * 2 + 1];
579
580 for(int ii = node_count * 3; ii < border_point_count; ii++)
581 {
582 // we want to loop from one border extremity
583 int i = ii - node_count * 3 + extrema_index[1];
584 if(i >= border_point_count) i = i - border_point_count + node_count * 3;
585
586 if(intersection_count >= POLYGON_MAX_SELF_INTERSECTIONS(node_count)) break;
587
588 // we want to be sure everything is continuous
589 _polygon_fill_gaps(last_x, last_y, border_points[i * 2], border_points[i * 2 + 1], gap_points);
590
591 // extra represent all the points between the last one and the current one
592 // for all the points in extra, we'll check for self-intersection
593 // and "register" them in binter
594 for(int j = dt_masks_dynbuf_position(gap_points) / 2 - 1; j >= 0; j--)
595 {
596 const int grid_x = (dt_masks_dynbuf_buffer(gap_points))[j * 2];
597 const int grid_y = (dt_masks_dynbuf_buffer(gap_points))[j * 2 + 1];
598
599 // we check also 2 points around to be sure catching intersection
600 int cell_values[3] = { 0 };
601 const int idx = (grid_y - ymin) * grid_width + (grid_x - xmin);
602 // ensure idx is within [0, ss)
603 if(idx < 0 || (size_t)idx >= grid_size)
604 {
605 dt_masks_dynbuf_free(gap_points);
606 dt_pixelpipe_cache_free_align(intersection_grid);
607 return 1;
608 }
609 cell_values[0] = intersection_grid[idx];
610 if(grid_x > xmin) cell_values[1] = intersection_grid[idx - 1];
611 if(grid_y > ymin) cell_values[2] = intersection_grid[idx - grid_width];
612
613 for(int k = 0; k < 3; k++)
614 {
615 if(cell_values[k] > 0)
616 {
617 // there's already a border point "registered" at this coordinate.
618 // so we've potentially found a self-intersection portion between v[k] and i
619 if((grid_x == last_x && grid_y == last_y) || cell_values[k] == i - 1)
620 {
621 // we haven't move from last point.
622 // this is not a real self-interesection, so we just update binter
623 intersection_grid[idx] = i;
624 }
625 else if((i > cell_values[k]
626 && ((extrema_index[0] < cell_values[k] || extrema_index[0] > i)
627 && (extrema_index[1] < cell_values[k] || extrema_index[1] > i)
628 && (extrema_index[2] < cell_values[k] || extrema_index[2] > i)
629 && (extrema_index[3] < cell_values[k] || extrema_index[3] > i)))
630 || (i < cell_values[k] && extrema_index[0] < cell_values[k] && extrema_index[0] > i
631 && extrema_index[1] < cell_values[k] && extrema_index[1] > i
632 && extrema_index[2] < cell_values[k] && extrema_index[2] > i
633 && extrema_index[3] < cell_values[k] && extrema_index[3] > i))
634 {
635 // we have found a self-intersection portion, between v[k] and i
636 // and we are sure that this portion doesn't include one of the shape extrema
637 if(intersection_count > 0)
638 {
639 const int inter_last0 = (int)dt_masks_dynbuf_get(intersections, -2);
640 const int inter_last1 = (int)dt_masks_dynbuf_get(intersections, -1);
641 if((cell_values[k] - i) * (inter_last0 - inter_last1) > 0
642 && inter_last0 >= cell_values[k] && inter_last1 <= i)
643 {
644 // we find an self-intersection portion which include the last one
645 // we just update it
646 dt_masks_dynbuf_set(intersections, -2, cell_values[k]);
647 dt_masks_dynbuf_set(intersections, -1, i);
648 }
649 else
650 {
651 // we find a new self-intersection portion
652 dt_masks_dynbuf_add_2(intersections, cell_values[k], i);
653 intersection_count++;
654 }
655 }
656 else
657 {
658 // we find a new self-intersection portion
659 dt_masks_dynbuf_add_2(intersections, cell_values[k], i);
660 intersection_count++;
661 }
662 }
663 }
664 else
665 {
666 // there wasn't anything "registered" at this place in binter
667 // we do it now
668 intersection_grid[idx] = i;
669 }
670 }
671 last_x = grid_x;
672 last_y = grid_y;
673 }
674 }
675
676 dt_masks_dynbuf_free(gap_points);
677 dt_pixelpipe_cache_free_align(intersection_grid);
678
679 // and we return the number of self-intersection found
680 *intersection_count_out = intersection_count;
681 return 0;
682}
683
690 const double iop_order, const int transform_direction,
691 dt_dev_pixelpipe_t *pipe, float **point_buffer, int *point_count,
692 float **border_buffer, int *border_count, gboolean source)
693{
694 *point_buffer = NULL;
695 *point_count = 0;
696 if(!IS_NULL_PTR(border_buffer)) *border_buffer = NULL;
697 if(!IS_NULL_PTR(border_buffer)) *border_count = 0;
698
699 if(IS_NULL_PTR(mask_form) || IS_NULL_PTR(mask_form->points)) return 0;
700
701 double start2 = 0.0;
703
704 const float input_width = pipe->iwidth;
705 const float input_height = pipe->iheight;
706 const int pixel_threshold = (dt_dev_pixelpipe_has_preview_output(darktable.develop, pipe, NULL)
707 || pipe->type == DT_DEV_PIXELPIPE_THUMBNAIL) ? 3 : 1;
708 const guint node_count = g_list_length(mask_form->points);
709
710 dt_masks_dynbuf_t *dpoints = NULL, *dborder = NULL, *intersections = NULL;
711
712 dpoints = dt_masks_dynbuf_init(1000000, "polygon dpoints");
713 if(IS_NULL_PTR(dpoints)) return 1;
714
715 if(!IS_NULL_PTR(border_buffer))
716 {
717 dborder = dt_masks_dynbuf_init(1000000, "polygon dborder");
718 if(IS_NULL_PTR(dborder))
719 {
720 dt_masks_dynbuf_free(dpoints);
721 return 1;
722 }
723 }
724
725 intersections = dt_masks_dynbuf_init(10 * MAX(node_count, 1), "polygon intersections");
726 if(IS_NULL_PTR(intersections))
727 {
728 dt_masks_dynbuf_free(dpoints);
729 dt_masks_dynbuf_free(dborder);
730 return 1;
731 }
732
733 // we store all points
734 float dx = 0.0f, dy = 0.0f;
735
736 //get the first node's position to use for source offset
737 if(source && node_count > 0 && transform_direction != DT_DEV_TRANSFORM_DIR_ALL)
738 {
739 dt_masks_node_polygon_t *polygon = (dt_masks_node_polygon_t *)mask_form->points->data;
740 if(IS_NULL_PTR(polygon)) return 0;
741 dx = (polygon->node[0] - mask_form->source[0]) * input_width;
742 dy = (polygon->node[1] - mask_form->source[1]) * input_height;
743 }
744 for(const GList *point_node = mask_form->points; point_node; point_node = g_list_next(point_node))
745 {
746 const dt_masks_node_polygon_t *const node = (dt_masks_node_polygon_t *)point_node->data;
747 float *const buf = dt_masks_dynbuf_reserve_n(dpoints, 6);
748 if(!IS_NULL_PTR(buf))
749 {
750 buf[0] = node->ctrl1[0] * input_width - dx;
751 buf[1] = node->ctrl1[1] * input_height - dy;
752 buf[2] = node->node[0] * input_width - dx;
753 buf[3] = node->node[1] * input_height - dy;
754 buf[4] = node->ctrl2[0] * input_width - dx;
755 buf[5] = node->ctrl2[1] * input_height - dy;
756 }
757 }
758 // for the border, we store value too
759 if(dborder)
760 {
761 dt_masks_dynbuf_add_zeros(dborder, 6 * node_count); // need six zeros for each border point
762 }
763
764 float *border_init = dt_pixelpipe_cache_alloc_align_float_cache((size_t)6 * node_count, 0);
765 if(IS_NULL_PTR(border_init))
766 {
767 dt_masks_dynbuf_free(intersections);
768 dt_masks_dynbuf_free(dpoints);
769 dt_masks_dynbuf_free(dborder);
770 return 1;
771 }
772 int cw = _polygon_is_clockwise(mask_form);
773 if(cw == 0) cw = -1;
774
776 {
777 dt_print(DT_DEBUG_MASKS, "[masks %s] polygon_points init took %0.04f sec\n", mask_form->name,
778 dt_get_wtime() - start2);
779 start2 = dt_get_wtime();
780 }
781
782 // we render all segments
783 const GList *form_points = mask_form->points;
784 for(int node_index = 0; node_index < node_count; node_index++)
785 {
786 const int pb = dborder ? dt_masks_dynbuf_position(dborder) : 0;
787 border_init[node_index * 6 + 2] = -pb;
788 const GList *pt2 = g_list_next_wraparound(form_points, mask_form->points); // next, wrapping around if on last element
789 const GList *pt3 = g_list_next_wraparound(pt2, mask_form->points);
790 dt_masks_node_polygon_t *point1 = (dt_masks_node_polygon_t *)form_points->data; // kth element of mask_form->points
793 float p1[5] = { point1->node[0] * input_width - dx, point1->node[1] * input_height - dy,
794 point1->ctrl2[0] * input_width - dx, point1->ctrl2[1] * input_height - dy,
795 cw * point1->border[1] * MIN(input_width, input_height) };
796 float p2[5] = { point2->node[0] * input_width - dx, point2->node[1] * input_height - dy,
797 point2->ctrl1[0] * input_width - dx, point2->ctrl1[1] * input_height - dy,
798 cw * point2->border[0] * MIN(input_width, input_height) };
799 float p3[5] = { point2->node[0] * input_width - dx, point2->node[1] * input_height - dy,
800 point2->ctrl2[0] * input_width - dx, point2->ctrl2[1] * input_height - dy,
801 cw * point2->border[1] * MIN(input_width, input_height) };
802 float p4[5] = { point3->node[0] * input_width - dx, point3->node[1] * input_height - dy,
803 point3->ctrl1[0] * input_width - dx, point3->ctrl1[1] * input_height - dy,
804 cw * point3->border[0] * MIN(input_width, input_height) };
805
806 // advance form_points for next iteration so that it tracks the kth element of mask_form->points
807 form_points = g_list_next(form_points);
808
809 // and we determine all points by recursion (to be sure the distance between 2 points is <=1)
810 float rc[2] = { 0 }, rb[2] = { 0 };
811 float bmin[2] = { NAN, NAN };
812 float bmax[2] = { NAN, NAN };
813 float cmin[2] = { NAN, NAN };
814 float cmax[2] = { NAN, NAN };
815
816 _polygon_points_recurs(p1, p2, 0.0, 1.0, cmin, cmax, bmin, bmax, rc, rb, dpoints, dborder,
817 border_buffer && (node_count >= 3), pixel_threshold);
818
819 // we check gaps in the border (sharp edges)
820 if(dborder)
821 {
822 const float lastb0 = dt_masks_dynbuf_get(dborder, -2);
823 const float lastb1 = dt_masks_dynbuf_get(dborder, -1);
824 if(fabs(lastb0 - rb[0]) > 1.0f || fabs(lastb1 - rb[1]) > 1.0f)
825 {
826 bmin[0] = lastb0;
827 bmin[1] = lastb1;
828 }
829 }
830
831 dt_masks_dynbuf_add_2(dpoints, rc[0], rc[1]);
832
833 border_init[node_index * 6 + 4] = dborder ? -dt_masks_dynbuf_position(dborder) : 0;
834
835 if(dborder)
836 {
837 if(isnan(rb[0]))
838 {
839 float lastb0 = dt_masks_dynbuf_get(dborder, -2);
840 float lastb1 = dt_masks_dynbuf_get(dborder, -1);
841 if(isnan(lastb0))
842 {
843 lastb0 = dt_masks_dynbuf_get(dborder, -4);
844 lastb1 = dt_masks_dynbuf_get(dborder, -3);
845 dt_masks_dynbuf_set(dborder, -2, lastb0);
846 dt_masks_dynbuf_set(dborder, -1, lastb1);
847 }
848 rb[0] = lastb0;
849 rb[1] = lastb1;
850 }
851 dt_masks_dynbuf_add_2(dborder, rb[0], rb[1]);
852
853 (dt_masks_dynbuf_buffer(dborder))[node_index * 6] = border_init[node_index * 6]
854 = (dt_masks_dynbuf_buffer(dborder))[pb];
855 (dt_masks_dynbuf_buffer(dborder))[node_index * 6 + 1] = border_init[node_index * 6 + 1]
856 = (dt_masks_dynbuf_buffer(dborder))[pb + 1];
857 }
858
859 // we first want to be sure that there are no gaps in border
860 if(dborder && node_count >= 3)
861 {
862 // we get the next point (start of the next segment)
863 // t=0.00001f to workaround rounding effects with full optimization that result in bmax[0] NOT being set to
864 // NAN when t=0 and the two points in p3 are identical (as is the case on a control node set to sharp corner)
865 _polygon_border_get_XY(p3[0], p3[1], p3[2], p3[3], p4[2], p4[3], p4[0], p4[1], 0.00001f, p3[4], cmin, cmin + 1,
866 bmax, bmax + 1);
867 if(isnan(bmax[0]))
868 {
869 _polygon_border_get_XY(p3[0], p3[1], p3[2], p3[3], p4[2], p4[3], p4[0], p4[1], 0.00001f, p3[4], cmin,
870 cmin + 1, bmax, bmax + 1);
871 }
872 if(bmax[0] - rb[0] > 1 || bmax[0] - rb[0] < -1 || bmax[1] - rb[1] > 1 || bmax[1] - rb[1] < -1)
873 {
874 float bmin2[2] = { dt_masks_dynbuf_get(dborder, -22), dt_masks_dynbuf_get(dborder, -21) };
875 _polygon_points_recurs_border_gaps(rc, rb, bmin2, bmax, dpoints, dborder,
876 _polygon_is_clockwise(mask_form));
877 }
878 }
879 }
880
881 *point_count = dt_masks_dynbuf_position(dpoints) / 2;
882 *point_buffer = dt_masks_dynbuf_harvest(dpoints);
883 dt_masks_dynbuf_free(dpoints);
884
885 if(dborder)
886 {
887 *border_count = dt_masks_dynbuf_position(dborder) / 2;
888 *border_buffer = dt_masks_dynbuf_harvest(dborder);
889 dt_masks_dynbuf_free(dborder);
890 }
891
893 {
894 dt_print(DT_DEBUG_MASKS, "[masks %s] polygon_points point recurs %0.04f sec\n", mask_form->name,
895 dt_get_wtime() - start2);
896 start2 = dt_get_wtime();
897 }
898
899 // we don't want the border to self-intersect
900 int inter_count = 0;
901 if(!IS_NULL_PTR(border_buffer))
902 {
903 if(_polygon_find_self_intersection(intersections, node_count, *border_buffer, *border_count,
904 &inter_count) != 0)
905 {
906 dt_masks_dynbuf_free(intersections);
907 dt_pixelpipe_cache_free_align(*point_buffer);
908 dt_pixelpipe_cache_free_align(*border_buffer);
909 return 1;
910 }
911
913 {
914 dt_print(DT_DEBUG_MASKS, "[masks %s] polygon_points self-intersect took %0.04f sec\n", mask_form->name,
915 dt_get_wtime() - start2);
916 start2 = dt_get_wtime();
917 }
918 }
919
920 // and we transform them with all distorted modules
921 if(source && transform_direction == DT_DEV_TRANSFORM_DIR_ALL)
922 {
923 // we transform with all distortion that happen *before* the module
924 // so we have now the TARGET points in module input reference
926 *point_buffer, *point_count))
927 {
928 // now we move all the points by the shift
929 // so we have now the SOURCE points in module input reference
930 float pts[2] = { mask_form->source[0] * input_width, mask_form->source[1] * input_height };
932 goto fail;
933
934 dx = pts[0] - (*point_buffer)[2];
935 dy = pts[1] - (*point_buffer)[3];
936 __OMP_PARALLEL_FOR_SIMD__(if(*point_count > 100) aligned(point_buffer:64))
937 for(int i = 0; i < *point_count; i++)
938 {
939 (*point_buffer)[i * 2] += dx;
940 (*point_buffer)[i * 2 + 1] += dy;
941 }
942
943 // we apply the rest of the distortions (those after the module)
944 // so we have now the SOURCE points in final image reference
946 *point_buffer, *point_count))
947 goto fail;
948 }
949
951 dt_print(DT_DEBUG_MASKS, "[masks %s] polygon_points end took %0.04f sec\n",
952 mask_form->name, dt_get_wtime() - start2);
953
954 dt_masks_dynbuf_free(intersections);
956 return 0;
957 }
958 else if(dt_dev_distort_transform_plus(pipe, iop_order, transform_direction,
959 *point_buffer, *point_count))
960 {
961 if(!border_buffer
962 || dt_dev_distort_transform_plus(pipe, iop_order, transform_direction,
963 *border_buffer, *border_count))
964 {
966 {
967 dt_print(DT_DEBUG_MASKS, "[masks %s] polygon_points transform took %0.04f sec\n", mask_form->name,
968 dt_get_wtime() - start2);
969 start2 = dt_get_wtime();
970 }
971
972 if(!IS_NULL_PTR(border_buffer))
973 {
974 // we don't want to copy the falloff points
975 for(int node_index = 0; node_index < node_count; node_index++)
976 for(int i = 2; i < 6; i++)
977 (*border_buffer)[node_index * 6 + i] = border_init[node_index * 6 + i];
978
979 // now we want to write the skipping zones
980 for(int i = 0; i < inter_count; i++)
981 {
982 const int v = (dt_masks_dynbuf_buffer(intersections))[i * 2];
983 const int w = (dt_masks_dynbuf_buffer(intersections))[ i * 2 + 1];
984 if(v <= w)
985 {
986 (*border_buffer)[v * 2] = NAN;
987 (*border_buffer)[v * 2 + 1] = w;
988 }
989 else
990 {
991 if(w > node_count * 3)
992 {
993 if(isnan((*border_buffer)[node_count * 6]) && isnan((*border_buffer)[node_count * 6 + 1]))
994 (*border_buffer)[node_count * 6 + 1] = w;
995 else if(isnan((*border_buffer)[node_count * 6]))
996 (*border_buffer)[node_count * 6 + 1]
997 = MAX((*border_buffer)[node_count * 6 + 1], w);
998 else
999 (*border_buffer)[node_count * 6 + 1] = w;
1000 (*border_buffer)[node_count * 6] = NAN;
1001 }
1002 (*border_buffer)[v * 2] = NAN;
1003 (*border_buffer)[v * 2 + 1] = NAN;
1004 }
1005 }
1006 }
1007
1009 dt_print(DT_DEBUG_MASKS, "[masks %s] polygon_points end took %0.04f sec\n", mask_form->name,
1010 dt_get_wtime() - start2);
1011
1012 dt_masks_dynbuf_free(intersections);
1013 dt_pixelpipe_cache_free_align(border_init);
1014 return 0;
1015 }
1016 }
1017
1018 // if we failed, then free all and return
1019fail:
1020 dt_masks_dynbuf_free(intersections);
1021 dt_pixelpipe_cache_free_align(border_init);
1022 dt_pixelpipe_cache_free_align(*point_buffer);
1023 *point_buffer = NULL;
1024 *point_count = 0;
1025 if(!IS_NULL_PTR(border_buffer))
1026 {
1027 dt_pixelpipe_cache_free_align(*border_buffer);
1028 *border_buffer = NULL;
1029 *border_count = 0;
1030 }
1031 return 1;
1032}
1033
1039static float _polygon_get_position_in_segment(float point_x, float point_y,
1040 dt_masks_form_t *mask_form, int segment_index)
1041{
1042 if(IS_NULL_PTR(mask_form) || IS_NULL_PTR(mask_form->points)) return 0;
1043 GList *firstpt = g_list_nth(mask_form->points, segment_index);
1044 dt_masks_node_polygon_t *point0 = (dt_masks_node_polygon_t *)firstpt->data;
1045 // advance to next node in list, if not already on the last
1046 GList *nextpt = g_list_next_bounded(firstpt);
1047 dt_masks_node_polygon_t *point1 = (dt_masks_node_polygon_t *)nextpt->data;
1048 nextpt = g_list_next_bounded(nextpt);
1049 dt_masks_node_polygon_t *point2 = (dt_masks_node_polygon_t *)nextpt->data;
1050 nextpt = g_list_next_bounded(nextpt);
1051 dt_masks_node_polygon_t *point3 = (dt_masks_node_polygon_t *)nextpt->data;
1052
1053 float min_t = 0.0f;
1054 float min_dist = FLT_MAX;
1055
1056 for(int i = 0; i <= 100; i++)
1057 {
1058 const float t = i / 100.0f;
1059 float sample_x = 0.0f;
1060 float sample_y = 0.0f;
1061 _polygon_get_XY(point0->node[0], point0->node[1], point1->node[0], point1->node[1],
1062 point2->node[0], point2->node[1], point3->node[0], point3->node[1], t,
1063 &sample_x, &sample_y);
1064
1065 const float dist = (point_x - sample_x) * (point_x - sample_x)
1066 + (point_y - sample_y) * (point_y - sample_y);
1067 if(dist < min_dist)
1068 {
1069 min_dist = dist;
1070 min_t = t;
1071 }
1072 }
1073
1074 return min_t;
1075}
1076
1077static void _add_node_to_segment(struct dt_iop_module_t *module,
1078 dt_masks_form_t *mask_form, int parent_id,
1079 dt_masks_form_gui_t *mask_gui, int form_index)
1080{
1081 if(IS_NULL_PTR(mask_form) || IS_NULL_PTR(mask_form->points) || IS_NULL_PTR(mask_gui)) return;
1082 const guint node_count = g_list_length(mask_form->points);
1083 const int selected_segment = dt_masks_gui_selected_segment_index(mask_gui);
1084 if(selected_segment < 0 || selected_segment >= (int)node_count) return;
1085
1086 // we add a new node to the polygon
1088 if(IS_NULL_PTR(new_node)) return;
1089
1090 // set coordinates
1092 new_node->ctrl1[0] = new_node->ctrl1[1] = new_node->ctrl2[0] = new_node->ctrl2[1] = -1.0;
1094
1095 // set other attributes of the new node. we interpolate the starting and the end node of that
1096 // segment
1097 const float t = _polygon_get_position_in_segment(new_node->node[0], new_node->node[1],
1098 mask_form, selected_segment);
1099 // start and end node of the segment
1100 GList *pt = g_list_nth(mask_form->points, selected_segment);
1101 if(IS_NULL_PTR(pt) || IS_NULL_PTR(pt->data))
1102 {
1103 dt_free(new_node);
1104 return;
1105 }
1107 const GList *const next_pt = g_list_next_wraparound(pt, mask_form->points);
1108 if(IS_NULL_PTR(next_pt) || IS_NULL_PTR(next_pt->data))
1109 {
1110 dt_free(new_node);
1111 return;
1112 }
1113 dt_masks_node_polygon_t *point1 = (dt_masks_node_polygon_t *)next_pt->data;
1114 new_node->border[0] = point0->border[0] * (1.0f - t) + point1->border[0] * t;
1115 new_node->border[1] = point0->border[1] * (1.0f - t) + point1->border[1] * t;
1116
1117 mask_form->points = g_list_insert(mask_form->points, new_node, selected_segment + 1);
1118 _polygon_init_ctrl_points(mask_form);
1119
1120 dt_masks_gui_form_create(mask_form, mask_gui, form_index, module);
1121
1122 mask_gui->node_hovered = selected_segment + 1;
1123 mask_gui->node_selected = TRUE;
1124 mask_gui->node_selected_idx = selected_segment + 1;
1125 mask_gui->seg_hovered = -1;
1126 mask_gui->seg_selected = FALSE;
1127}
1128
1129static inline void _polygon_translate_node(dt_masks_node_polygon_t *node, const float delta_x, const float delta_y)
1130{
1131 dt_masks_translate_ctrl_node(node->node, node->ctrl1, node->ctrl2, delta_x, delta_y);
1132}
1133
1134static void _polygon_translate_all_nodes(dt_masks_form_t *mask_form, const float delta_x, const float delta_y)
1135{
1136 for(GList *node_entry = mask_form->points; node_entry; node_entry = g_list_next(node_entry))
1137 _polygon_translate_node((dt_masks_node_polygon_t *)node_entry->data, delta_x, delta_y);
1138}
1139
1141 float **point_buffer, int *point_count,
1142 float **border_buffer, int *border_count,
1143 int source, const dt_iop_module_t *module)
1144{
1145 if(source && IS_NULL_PTR(module)) return 1;
1146 const double ioporder = (module) ? module->iop_order : 0.0f;
1147 return _polygon_get_pts_border(develop, mask_form, ioporder, DT_DEV_TRANSFORM_DIR_ALL,
1148 develop->virtual_pipe, point_buffer, point_count,
1149 border_buffer, border_count, source);
1150}
1151
1152static void _polygon_get_sizes(struct dt_iop_module_t *module, dt_masks_form_t *mask_form,
1153 dt_masks_form_gui_t *mask_gui, int form_index,
1154 float *mask_size, float *border_size)
1155{
1156 const dt_masks_form_gui_points_t *gui_points
1157 = (dt_masks_form_gui_points_t *)g_list_nth_data(mask_gui->points, form_index);
1158 if(IS_NULL_PTR(gui_points)) return;
1159
1160 const int node_count = g_list_length(mask_form->points);
1161 float p1[2] = { FLT_MAX, FLT_MAX };
1162 float p2[2] = { FLT_MIN, FLT_MIN };
1163
1164 float fp1[2] = { FLT_MAX, FLT_MAX };
1165 float fp2[2] = { FLT_MIN, FLT_MIN };
1166
1167 for(int i = node_count * 3; i < gui_points->points_count; i++)
1168 {
1169 // line
1170 const float x = gui_points->points[i * 2];
1171 const float y = gui_points->points[i * 2 + 1];
1172
1173 p1[0] = fminf(p1[0], x);
1174 p2[0] = fmaxf(p2[0], x);
1175 p1[1] = fminf(p1[1], y);
1176 p2[1] = fmaxf(p2[1], y);
1177
1178 if(!IS_NULL_PTR(border_size))
1179 {
1180 // border
1181 const float fx = gui_points->border[i * 2];
1182 const float fy = gui_points->border[i * 2 + 1];
1183
1184 // ??? looks like when x border is nan then y is a point index
1185 // see draw border in _polygon_events_post_expose.
1186 if(!isnan(fx))
1187 {
1188 fp1[0] = fminf(fp1[0], fx);
1189 fp2[0] = fmaxf(fp2[0], fx);
1190 fp1[1] = fminf(fp1[1], fy);
1191 fp2[1] = fmaxf(fp2[1], fy);
1192 }
1193 }
1194 }
1195
1196 float mask_span[2] = { p2[0] - p1[0], p2[1] - p1[1] };
1198 *mask_size = fmaxf(mask_span[0], mask_span[1]);
1199
1200 if(!IS_NULL_PTR(border_size))
1201 {
1202 float border_span[2] = { fp2[0] - fp1[0], fp2[1] - fp1[1] };
1204 *border_size = fmaxf(border_span[0], border_span[1]);
1205 }
1206}
1207
1208static gboolean _polygon_form_gravity_center(const dt_masks_form_t *mask_form, float *center_x,
1209 float *center_y, float *surface);
1210
1212 dt_masks_interaction_t interaction)
1213{
1214 if(IS_NULL_PTR(mask_form) || IS_NULL_PTR(mask_form->points)) return NAN;
1215
1216 switch(interaction)
1217 {
1219 {
1220 const float size = dt_masks_get_form_size_from_nodes(mask_form->points);
1221 if(size <= 0.0f) return NAN;
1222 return size;
1223 }
1225 {
1226 float hardness_sum = 0.0f;
1227 int hardness_count = 0;
1228
1229 for(const GList *point_node = mask_form->points; point_node; point_node = g_list_next(point_node))
1230 {
1231 const dt_masks_node_polygon_t *node = (const dt_masks_node_polygon_t *)point_node->data;
1232 if(IS_NULL_PTR(node)) continue;
1233 hardness_sum += node->border[0] + node->border[1];
1234 hardness_count += 2;
1235 }
1236
1237 return hardness_count > 0 ? hardness_sum / (float)hardness_count : NAN;
1238 }
1239 default:
1240 return NAN;
1241 }
1242}
1243
1244static gboolean _polygon_get_gravity_center(const dt_masks_form_t *mask_form,
1245 float center[2], float *area)
1246{
1247 if(IS_NULL_PTR(mask_form) || IS_NULL_PTR(mask_form->points) || IS_NULL_PTR(center)) return FALSE;
1248
1249 const int points_count = g_list_length(mask_form->points);
1250 if(points_count <= 0) return FALSE;
1251
1252 float *point_buffer = dt_alloc_align_float((size_t)points_count * 2);
1253 if(IS_NULL_PTR(point_buffer)) return FALSE;
1254
1255 int i = 0;
1256 for(const GList *point_node = mask_form->points; point_node; point_node = g_list_next(point_node))
1257 {
1258 const dt_masks_node_polygon_t *node = (const dt_masks_node_polygon_t *)point_node->data;
1259 if(IS_NULL_PTR(node)) continue;
1260 point_buffer[2 * i] = node->node[0];
1261 point_buffer[2 * i + 1] = node->node[1];
1262 i++;
1263 }
1264
1265 const gboolean ok = dt_masks_center_of_gravity_from_points(point_buffer, i, center, area);
1266 dt_free_align(point_buffer);
1267 return ok;
1268}
1269
1270static int _change_size(dt_masks_form_t *mask_form, int parent_id, dt_masks_form_gui_t *mask_gui,
1271 struct dt_iop_module_t *module, int form_index, const float amount,
1272 const dt_masks_increment_t increment, const int flow);
1273static int _change_hardness(dt_masks_form_t *mask_form, int parent_id, dt_masks_form_gui_t *mask_gui,
1274 struct dt_iop_module_t *module, int form_index, const float amount,
1275 const dt_masks_increment_t increment, int flow);
1276
1278 dt_masks_interaction_t interaction, float value,
1279 dt_masks_increment_t increment, int flow,
1280 dt_masks_form_gui_t *mask_gui, struct dt_iop_module_t *module)
1281{
1282 if(IS_NULL_PTR(mask_form)) return NAN;
1283 const int index = 0;
1284
1285 switch(interaction)
1286 {
1288 if(!_change_size(mask_form, 0, mask_gui, module, index, value, increment, flow)) return NAN;
1289 return _polygon_get_interaction_value(mask_form, interaction);
1291 if(!_change_hardness(mask_form, 0, mask_gui, module, index, value, increment, flow)) return NAN;
1292 return _polygon_get_interaction_value(mask_form, interaction);
1293 default:
1294 return NAN;
1295 }
1296}
1297
1301static void _polygon_get_distance(float point_x, float point_y, float radius,
1302 dt_masks_form_gui_t *mask_gui, int form_index,
1303 int node_count, int *inside, int *inside_border,
1304 int *near, int *inside_source, float *dist)
1305{
1306 // initialise returned values
1307 *inside_source = 0;
1308 *inside = 0;
1309 *inside_border = 0;
1310 *near = -1;
1311 *dist = FLT_MAX;
1312
1313 if(IS_NULL_PTR(mask_gui)) return;
1314 dt_masks_form_gui_points_t *gui_points
1315 = (dt_masks_form_gui_points_t *)g_list_nth_data(mask_gui->points, form_index);
1316 if(IS_NULL_PTR(gui_points)) return;
1317
1318 float min_dist_pixel = FLT_MAX;
1319
1320 const float radius2 = radius * radius;
1321 const float pt[2] = { point_x, point_y };
1322
1323 // we first check if we are inside the source form
1324 if(gui_points->source && gui_points->points
1325 && gui_points->source_count > node_count * 3 && gui_points->points_count > node_count * 3
1326 && dt_masks_point_in_form_exact(pt, 1, gui_points->source, node_count * 3,
1327 gui_points->source_count) >= 0)
1328 {
1329 *inside_source = 1;
1330 *inside = 1;
1331
1332 // offset between form origin and source origin
1333 const float offset_x = -gui_points->points[2] + gui_points->source[2];
1334 const float offset_y = -gui_points->points[3] + gui_points->source[3];
1335 int current_seg = 1;
1336
1337 // distance from source border
1338 for(int i = node_count * 3; i < gui_points->points_count; i++)
1339 {
1340 // check if we advance to next polygon segment
1341 if(gui_points->points[i * 2] == gui_points->points[current_seg * 6 + 2]
1342 && gui_points->points[i * 2 + 1] == gui_points->points[current_seg * 6 + 3])
1343 {
1344 current_seg = (current_seg + 1) % node_count;
1345 }
1346
1347 // calculate source position for current point
1348 const float source_x = gui_points->points[i * 2] + offset_x;
1349 const float source_y = gui_points->points[i * 2 + 1] + offset_y;
1350
1351 // distance from tested point to current source point
1352 const float sdx = point_x - source_x;
1353 const float sdy = point_y - source_y;
1354 const float sdd = sqf(sdx) + sqf(sdy);
1355 if(sdd < min_dist_pixel)
1356 min_dist_pixel = sdd;
1357 }
1358 *dist = min_dist_pixel;
1359 return;
1360 }
1361
1362 // we check if we are near a segment
1363 if(gui_points->points && gui_points->points_count > 2 + node_count * 3)
1364 {
1365 int current_seg = 1;
1366 for(int i = node_count * 3; i < gui_points->points_count; i++)
1367 {
1368 // do we change of polygon segment ?
1369 if(gui_points->points[i * 2 + 1] == gui_points->points[current_seg * 6 + 3]
1370 && gui_points->points[i * 2] == gui_points->points[current_seg * 6 + 2])
1371 {
1372 current_seg = (current_seg + 1) % node_count;
1373 }
1374 //distance from tested point to current form point
1375 const float yy = gui_points->points[i * 2 + 1];
1376 const float xx = gui_points->points[i * 2];
1377
1378 const float dx = point_x - xx;
1379 const float dy = point_y - yy;
1380 const float dd = sqf(dx) + sqf(dy);
1381 if(dd < min_dist_pixel)
1382 {
1383 min_dist_pixel = dd;
1384
1385 if(current_seg >= 0 && dd < radius2)
1386 {
1387 if(current_seg == 0)
1388 *near = node_count - 1;
1389 else
1390 *near = current_seg - 1;
1391 }
1392 }
1393 }
1394 }
1395
1396 *dist = min_dist_pixel;
1397
1398 // we check if it's not inside borders, meaning we are not inside at all
1399 if(!gui_points->border || gui_points->border_count <= node_count * 3
1400 || dt_masks_point_in_form_exact(pt, 1, gui_points->border, node_count * 3,
1401 gui_points->border_count) < 0)
1402 return;
1403
1404 // we are at least inside the border
1405 *inside = 1;
1406
1407 // and we check if it's not inside form, meaning we are inside border only
1408 if(IS_NULL_PTR(gui_points->points) || gui_points->points_count <= node_count * 3) return;
1409 *inside_border = (dt_masks_point_in_form_exact(pt, 1, gui_points->points,
1410 node_count * 3, gui_points->points_count) < 0);
1411}
1412
1418static gboolean _polygon_border_handle_cb(const dt_masks_form_gui_points_t *gui_points, int node_count,
1419 int node_index, float *handle_x, float *handle_y, void *user_data)
1420{
1421 if(IS_NULL_PTR(gui_points) || node_index < 0 || node_index >= node_count) return FALSE;
1422 *handle_x = gui_points->border[node_index * 6];
1423 *handle_y = gui_points->border[node_index * 6 + 1];
1424 return !(isnan(*handle_x) || isnan(*handle_y));
1425}
1426
1430static void _polygon_curve_handle_cb(const dt_masks_form_gui_points_t *gui_points, int node_index,
1431 float *handle_x, float *handle_y, void *user_data)
1432{
1433
1434 _polygon_ctrl2_to_handle(gui_points->points[node_index * 6 + 2], gui_points->points[node_index * 6 + 3],
1435 gui_points->points[node_index * 6 + 4], gui_points->points[node_index * 6 + 5],
1436 handle_x, handle_y, gui_points->clockwise);
1437}
1438
1442static void _polygon_distance_cb(float pointer_x, float pointer_y, float cursor_radius,
1443 dt_masks_form_gui_t *mask_gui, int form_index, int node_count, int *inside,
1444 int *inside_border, int *near, int *inside_source, float *dist, void *user_data)
1445{
1446
1447 _polygon_get_distance(pointer_x, pointer_y, cursor_radius, mask_gui, form_index, node_count,
1448 inside, inside_border, near, inside_source, dist);
1449}
1450
1451static int _find_closest_handle(dt_masks_form_t *mask_form, dt_masks_form_gui_t *mask_gui, int form_index)
1452{
1453 return dt_masks_find_closest_handle_common(mask_form, mask_gui, form_index, -1,
1455 _polygon_distance_cb, NULL, NULL);
1456}
1457
1464static void _polygon_gui_gravity_center(const float *point_buffer, int point_count,
1465 float *center_x, float *center_y, float *area)
1466{
1467 if(IS_NULL_PTR(point_buffer) || point_count < 3) return;
1468
1469 float centroid_x = 0.0f;
1470 float centroid_y = 0.0f;
1471 float signed_area = 0.0f;
1472
1473 for(int node_index = 0; node_index < point_count; node_index++)
1474 {
1475 const int next_index = (node_index + 1) % point_count;
1476 const float x0 = point_buffer[node_index * 2];
1477 const float y0 = point_buffer[node_index * 2 + 1];
1478 const float x1 = point_buffer[next_index * 2];
1479 const float y1 = point_buffer[next_index * 2 + 1];
1480 const float cross = x0 * y1 - x1 * y0;
1481
1482 signed_area += cross;
1483 centroid_x += (x0 + x1) * cross;
1484 centroid_y += (y0 + y1) * cross;
1485 }
1486
1487 if(fabsf(signed_area) > 1e-8f)
1488 {
1489 const float inv_divisor = 1.0f / (3.0f * signed_area);
1490 if(!IS_NULL_PTR(center_x)) *center_x = centroid_x * inv_divisor;
1491 if(!IS_NULL_PTR(center_y)) *center_y = centroid_y * inv_divisor;
1492 }
1493 if(!IS_NULL_PTR(area)) *area = signed_area;
1494}
1495
1499static gboolean _polygon_form_gravity_center(const dt_masks_form_t *mask_form,
1500 float *center_x, float *center_y, float *area)
1501{
1502 if(IS_NULL_PTR(mask_form) || IS_NULL_PTR(mask_form->points) || g_list_shorter_than(mask_form->points, 3)) return FALSE;
1503
1504 float centroid_x = 0.0f;
1505 float centroid_y = 0.0f;
1506 float signed_area = 0.0f;
1507
1508 for(const GList *node_iter = mask_form->points; node_iter; node_iter = g_list_next(node_iter))
1509 {
1510 const GList *next_iter = g_list_next_wraparound(node_iter, mask_form->points);
1511 const dt_masks_node_polygon_t *node0 = (const dt_masks_node_polygon_t *)node_iter->data;
1512 const dt_masks_node_polygon_t *node1 = (const dt_masks_node_polygon_t *)next_iter->data;
1513 if(!node0 || !node1) return FALSE;
1514
1515 const float cross = node0->node[0] * node1->node[1] - node1->node[0] * node0->node[1];
1516 signed_area += cross;
1517 centroid_x += (node0->node[0] + node1->node[0]) * cross;
1518 centroid_y += (node0->node[1] + node1->node[1]) * cross;
1519 }
1520
1521 if(!IS_NULL_PTR(area)) *area = signed_area;
1522 if(fabsf(signed_area) <= 1e-8f) return FALSE;
1523
1524 const float inv_divisor = 1.0f / (3.0f * signed_area);
1525 if(!IS_NULL_PTR(center_x)) *center_x = centroid_x * inv_divisor;
1526 if(!IS_NULL_PTR(center_y)) *center_y = centroid_y * inv_divisor;
1527 return TRUE;
1528}
1529
1533static int _init_hardness(dt_masks_form_t *mask_form, const float amount,
1534 const dt_masks_increment_t increment, const int flow,
1535 const float mask_size, const float border_size)
1536{
1537 const float mask_hardness = dt_masks_get_set_conf_value(mask_form, "hardness", amount,
1538 HARDNESS_MIN, HARDNESS_MAX, increment, flow);
1539 dt_toast_log(_("Hardness: %3.2f%%"), (border_size * mask_hardness) / mask_size * 100.0f);
1540 return 1;
1541}
1542
1549static int _change_size(dt_masks_form_t *mask_form, int parent_id, dt_masks_form_gui_t *mask_gui,
1550 struct dt_iop_module_t *module, int form_index, const float amount,
1551 const dt_masks_increment_t increment, const int flow)
1552{
1553 if(IS_NULL_PTR(mask_form) || IS_NULL_PTR(mask_form->points)) return 0;
1554
1555 float center_x = 0.0f;
1556 float center_y = 0.0f;
1557 float signed_area = 0.0f;
1558 if(!_polygon_form_gravity_center(mask_form, &center_x, &center_y, &signed_area)) return 1;
1559
1560 // Avoid expanding degenerate polygons or overly large shapes.
1561 if(amount < 1.0f && signed_area < 0.00001f && signed_area > -0.00001f) return 1;
1562 if(amount > 1.0f && signed_area > 4.0f) return 1;
1563
1564 float scale_delta = amount;
1565 switch(increment)
1566 {
1568 scale_delta = powf(amount, (float)flow);
1569 break;
1571 // For polygon global scaling, interpret offset as multiplicative offset around 1.0.
1572 scale_delta = 1.0f + amount * (float)flow;
1573 break;
1575 default:
1576 scale_delta = amount;
1577 break;
1578 }
1579
1580 int node_index = 0;
1581 for(GList *node_iter = mask_form->points; node_iter; node_iter = g_list_next(node_iter), node_index++)
1582 {
1583 if(dt_masks_gui_change_affects_selected_node_or_all(mask_gui, node_index))
1584 {
1586 if(!node) continue;
1587
1588 const float new_node_x = center_x + (node->node[0] - center_x) * scale_delta;
1589 const float new_node_y = center_y + (node->node[1] - center_y) * scale_delta;
1590 const float ctrl1_offset_x = (node->ctrl1[0] - node->node[0]) * scale_delta;
1591 const float ctrl1_offset_y = (node->ctrl1[1] - node->node[1]) * scale_delta;
1592 const float ctrl2_offset_x = (node->ctrl2[0] - node->node[0]) * scale_delta;
1593 const float ctrl2_offset_y = (node->ctrl2[1] - node->node[1]) * scale_delta;
1594
1595 // Update all coordinates while keeping local offsets consistent.
1596 node->node[0] = new_node_x;
1597 node->node[1] = new_node_y;
1598 node->ctrl1[0] = new_node_x + ctrl1_offset_x;
1599 node->ctrl1[1] = new_node_y + ctrl1_offset_y;
1600 node->ctrl2[0] = new_node_x + ctrl2_offset_x;
1601 node->ctrl2[1] = new_node_y + ctrl2_offset_y;
1602 }
1603 }
1604
1605 float mask_size = 0.0f;
1606 _polygon_get_sizes(module, mask_form, mask_gui, form_index, &mask_size, NULL);
1607
1608 dt_toast_log(_("Size: %3.2f%%"), mask_size * 100.0f);
1609
1610 // Rebuild the cached GUI geometry.
1611 dt_masks_gui_form_create(mask_form, mask_gui, form_index, module);
1612 return 1;
1613}
1614
1618static int _change_hardness(dt_masks_form_t *mask_form, int parent_id, dt_masks_form_gui_t *mask_gui,
1619 struct dt_iop_module_t *module, int form_index, const float amount,
1620 const dt_masks_increment_t increment, int flow)
1621{
1622 int node_index = 0;
1623 const float scale_amount = powf(amount, (float)flow);
1624 const float offset_amount = amount * (float)flow;
1625
1626 for(GList *node_iter = mask_form->points; node_iter; node_iter = g_list_next(node_iter), node_index++)
1627 {
1628 if(dt_masks_gui_change_affects_selected_node_or_all(mask_gui, node_index))
1629 {
1631 if(!node) continue;
1632
1633 node->border[0] = CLAMPF(dt_masks_apply_increment_precomputed(node->border[0], amount, scale_amount,
1634 offset_amount, increment),
1636 node->border[1] = CLAMPF(dt_masks_apply_increment_precomputed(node->border[1], amount, scale_amount,
1637 offset_amount, increment),
1639 }
1640 }
1641
1642 float mask_size = 1.0f;
1643 float border_size = 0.0f;
1644 _polygon_get_sizes(module, mask_form, mask_gui, form_index, &mask_size, &border_size);
1645
1646 _init_hardness(mask_form, amount, increment, flow, mask_size, border_size);
1647
1648 // Rebuild the cached GUI geometry.
1649 dt_masks_gui_form_create(mask_form, mask_gui, form_index, module);
1650
1651 return 1;
1652}
1653
1657/* Shape handlers receive widget-space coordinates, while normalized output-image
1658 * coordinates come from `mask_gui->rel_pos` and absolute output-image
1659 * coordinates come from `mask_gui->pos`. */
1660static int _polygon_events_mouse_scrolled(struct dt_iop_module_t *module, double x, double y, int up, int flow,
1661 uint32_t state, dt_masks_form_t *mask_form, int parent_id,
1662 dt_masks_form_gui_t *mask_gui, int form_index,
1663 dt_masks_interaction_t interaction)
1664{
1665
1666
1667
1668 if(mask_gui->creation)
1669 {
1670 // no change during creation
1671 return 0;
1672 }
1673
1674 if(mask_gui->edit_mode == DT_MASKS_EDIT_FULL && dt_masks_is_anything_selected(mask_gui))
1675 {
1676 if(dt_modifier_is(state, GDK_CONTROL_MASK))
1677 return dt_masks_form_change_opacity(mask_form, parent_id, up, flow);
1678 if(dt_modifier_is(state, GDK_SHIFT_MASK) || mask_gui->node_selected)
1679 return _change_hardness(mask_form, parent_id, mask_gui, module, form_index, up ? +0.01f : -0.01f,
1681 else
1682 return _change_size(mask_form, parent_id, mask_gui, module, form_index, up ? 1.02f : 0.98f,
1684 }
1685 return 0;
1686}
1687
1692{
1693 // we don't want a form with less than 3 points
1694 if(g_list_shorter_than(mask_form->points, 4))
1695 {
1696 dt_toast_log(_("Polygon mask requires at least 3 nodes."));
1697 return 1;
1698 }
1699
1700 dt_iop_module_t *creation_module = mask_gui->creation_module;
1701 // we delete last point (the one we are currently dragging)
1702 dt_masks_node_polygon_t *last_node = (dt_masks_node_polygon_t *)g_list_last(mask_form->points)->data;
1703 mask_form->points = g_list_remove(mask_form->points, last_node);
1704 dt_free(last_node);
1705
1706 mask_gui->node_dragging = -1;
1707 _polygon_init_ctrl_points(mask_form);
1708
1709 dt_masks_gui_form_save_creation(darktable.develop, creation_module, mask_form, mask_gui);
1710
1711 return 1;
1712}
1713
1714static int _polygon_events_button_pressed(struct dt_iop_module_t *module, double x, double y,
1715 double pressure, int which, int type, uint32_t state,
1716 dt_masks_form_t *mask_form, int parent_id,
1717 dt_masks_form_gui_t *mask_gui, int form_index)
1718{
1719 if(type == GDK_2BUTTON_PRESS || type == GDK_3BUTTON_PRESS) return 1;
1720
1721 if(which == 1)
1722 {
1723 if(mask_gui->creation)
1724 {
1725 if(mask_gui->creation_closing_form)
1726 return _polygon_creation_closing_form(mask_form, mask_gui);
1727
1728 if(dt_modifier_is(state, GDK_CONTROL_MASK | GDK_SHIFT_MASK) || dt_modifier_is(state, GDK_SHIFT_MASK))
1729 {
1730 // set some absolute or relative position for the source of the clone mask
1731 if(mask_form->type & DT_MASKS_CLONE)
1732 {
1734 return 1;
1735 }
1736 }
1737
1738 else // we create a node
1739 {
1740 float masks_border = MIN(dt_conf_get_float("plugins/darkroom/masks/polygon/hardness"), HARDNESS_MAX);
1741
1742 int node_count = g_list_length(mask_form->points);
1743 // change the values
1744 dt_masks_node_polygon_t *polygon_node = (dt_masks_node_polygon_t *)(malloc(sizeof(dt_masks_node_polygon_t)));
1745 if(IS_NULL_PTR(polygon_node)) return 0;
1746
1747 dt_masks_gui_cursor_to_raw_norm(darktable.develop, mask_gui, polygon_node->node);
1748
1749 polygon_node->ctrl1[0] = polygon_node->ctrl1[1] = polygon_node->ctrl2[0] = polygon_node->ctrl2[1] = -1.0;
1750 polygon_node->border[0] = polygon_node->border[1] = MAX(HARDNESS_MIN, masks_border);
1751 polygon_node->state = DT_MASKS_POINT_STATE_NORMAL;
1752
1753 if(node_count == 0)
1754 {
1755 // create the first node
1756 dt_masks_node_polygon_t *polygon_first_node = (dt_masks_node_polygon_t *)(malloc(sizeof(dt_masks_node_polygon_t)));
1757 polygon_first_node->node[0] = polygon_node->node[0];
1758 polygon_first_node->node[1] = polygon_node->node[1];
1759 polygon_first_node->ctrl1[0] = polygon_first_node->ctrl1[1] = polygon_first_node->ctrl2[0] = polygon_first_node->ctrl2[1] = -1.0;
1760 polygon_first_node->border[0] = polygon_first_node->border[1] = MAX(HARDNESS_MIN, masks_border);
1761 polygon_first_node->state = DT_MASKS_POINT_STATE_NORMAL;
1762 mask_form->points = g_list_append(mask_form->points, polygon_first_node);
1763
1764 if(mask_form->type & DT_MASKS_CLONE)
1765 {
1766 dt_masks_set_source_pos_initial_value(mask_gui, mask_form);
1767 }
1768 else
1769 {
1770 // not used by regular masks
1771 mask_form->source[0] = mask_form->source[1] = 0.0f;
1772 }
1773 node_count++;
1774 }
1775 mask_form->points = g_list_append(mask_form->points, polygon_node);
1776
1777 // if this is a ctrl click, the last created point is a sharp one
1778 if(dt_modifier_is(state, GDK_CONTROL_MASK))
1779 {
1780 dt_masks_node_polygon_t *polygon_last_node = g_list_nth_data(mask_form->points, node_count - 1);
1781 polygon_last_node->ctrl1[0] = polygon_last_node->ctrl2[0] = polygon_last_node->node[0];
1782 polygon_last_node->ctrl1[1] = polygon_last_node->ctrl2[1] = polygon_last_node->node[1];
1783 polygon_last_node->state = DT_MASKS_POINT_STATE_USER;
1784 }
1785
1786 mask_gui->node_hovered = node_count;
1787 mask_gui->node_selected = TRUE;
1788 mask_gui->node_selected_idx = node_count;
1789 mask_gui->node_dragging = node_count;
1790 _polygon_init_ctrl_points(mask_form);
1791 }
1792
1793 // we recreate the form points in all case
1794 dt_masks_gui_form_create(mask_form, mask_gui, form_index, module);
1795
1796 return 1;
1797 }// end of creation mode
1798
1799 dt_masks_form_gui_points_t *gui_points
1800 = (dt_masks_form_gui_points_t *)g_list_nth_data(mask_gui->points, form_index);
1801 if(IS_NULL_PTR(gui_points)) return 0;
1802
1803 // The shape handler runs before the shared press-state selection update,
1804 // so concrete hovered targets must win over stale form/source selection.
1805 else if(mask_gui->node_hovered >= 0)
1806 {
1807 // if ctrl is pressed, we change the type of point
1808 if(mask_gui->node_selected && dt_modifier_is(state, GDK_CONTROL_MASK))
1809 {
1811 = (dt_masks_node_polygon_t *)g_list_nth_data(mask_form->points, mask_gui->node_hovered);
1812 if(IS_NULL_PTR(node)) return 0;
1813 dt_masks_toggle_bezier_node_type(module, mask_form, mask_gui, form_index, gui_points,
1814 mask_gui->node_hovered, node->node, node->ctrl1, node->ctrl2,
1815 &node->state);
1816 return 1;
1817 }
1818 /*// we register the current position to avoid accidental move
1819 if(mask_gui->node_selected < 0 && mask_gui->scrollx == 0.0f && mask_gui->scrolly == 0.0f)
1820 {
1821 mask_gui->scrollx = pzx;
1822 mask_gui->scrolly = pzy;
1823 }*/
1824 mask_gui->delta[0] = gui_points->points[mask_gui->node_hovered * 6 + 2] - mask_gui->pos[0];
1825 mask_gui->delta[1] = gui_points->points[mask_gui->node_hovered * 6 + 3] - mask_gui->pos[1];
1826
1827 return 1;
1828 }
1829 else if(mask_gui->handle_hovered >= 0)
1830 {
1831 if(!dt_masks_node_is_cusp(gui_points, mask_gui->handle_hovered))
1832 {
1833 // we need to find the handle position
1834 float handle_x, handle_y;
1835 const int handle_index = mask_gui->handle_hovered;
1836 _polygon_ctrl2_to_handle(gui_points->points[handle_index * 6 + 2],
1837 gui_points->points[handle_index * 6 + 3],
1838 gui_points->points[handle_index * 6 + 4],
1839 gui_points->points[handle_index * 6 + 5],
1840 &handle_x, &handle_y, gui_points->clockwise);
1841 // compute offsets
1842 mask_gui->delta[0] = handle_x - mask_gui->pos[0];
1843 mask_gui->delta[1] = handle_y - mask_gui->pos[1];
1844
1845 return 1;
1846 }
1847 }
1848 else if(mask_gui->handle_border_hovered >= 0)
1849 {
1850 const float handle_x = gui_points->border[mask_gui->handle_border_hovered * 6];
1851 const float handle_y = gui_points->border[mask_gui->handle_border_hovered * 6 + 1];
1852 mask_gui->delta[0] = handle_x - mask_gui->pos[0];
1853 mask_gui->delta[1] = handle_y - mask_gui->pos[1];
1854
1855 return 1;
1856 }
1857 else if(mask_gui->seg_hovered >= 0)
1858 {
1859 mask_gui->node_hovered = -1;
1860
1861 if(dt_modifier_is(state, GDK_CONTROL_MASK))
1862 {
1863 _add_node_to_segment(module, mask_form, parent_id, mask_gui, form_index);
1864 }
1865 else
1866 {
1867 // we move the entire segment
1868 mask_gui->delta[0] = gui_points->points[mask_gui->seg_hovered * 6 + 2] - mask_gui->pos[0];
1869 mask_gui->delta[1] = gui_points->points[mask_gui->seg_hovered * 6 + 3] - mask_gui->pos[1];
1870 }
1871 return 1;
1872 }
1873 else if(mask_gui->source_selected && mask_gui->edit_mode == DT_MASKS_EDIT_FULL)
1874 {
1875 // we start the source dragging
1876 mask_gui->delta[0] = gui_points->source[2] - mask_gui->pos[0];
1877 mask_gui->delta[1] = gui_points->source[3] - mask_gui->pos[1];
1878 return 1;
1879 }
1880 else if(mask_gui->form_selected && mask_gui->edit_mode == DT_MASKS_EDIT_FULL)
1881 {
1882 // we start the form dragging
1883 mask_gui->delta[0] = gui_points->points[2] - mask_gui->pos[0];
1884 mask_gui->delta[1] = gui_points->points[3] - mask_gui->pos[1];
1885 return 1;
1886 }
1887 }
1888
1889 return 0;
1890}
1891
1892static int _polygon_events_button_released(struct dt_iop_module_t *module, double x, double y, int which,
1893 uint32_t state, dt_masks_form_t *mask_form, int parent_id,
1894 dt_masks_form_gui_t *mask_gui, int form_index)
1895{
1896 if(IS_NULL_PTR(mask_gui)) return 0;
1897 if(mask_gui->creation) return 1;
1898
1899 if(which == 1)
1900 {
1901 if(dt_masks_gui_is_dragging(mask_gui))
1902 return 1;
1903 }
1904 return 0;
1905}
1906
1907static int _polygon_events_key_pressed(struct dt_iop_module_t *module, GdkEventKey *event,
1908 dt_masks_form_t *mask_form, int parent_id,
1909 dt_masks_form_gui_t *mask_gui, int form_index)
1910{
1911 if(IS_NULL_PTR(mask_gui) || IS_NULL_PTR(mask_form)) return 0;
1912
1913 guint key = dt_keys_mainpad_alternatives(event->keyval);
1914
1915
1916 if(mask_gui->creation)
1917 {
1918 switch(key)
1919 {
1920 case GDK_KEY_BackSpace:
1921 {
1922 // Minimum points to create a polygon
1923 if(mask_gui->node_dragging < 1)
1924 {
1925 dt_masks_form_exit_creation(module, mask_gui);
1926 return 1;
1927 }
1928 // switch previous node coords to the current one
1929 dt_masks_node_polygon_t *previous_node
1930 = (dt_masks_node_polygon_t *)g_list_nth_data(mask_form->points, mask_gui->node_dragging - 1);
1931 dt_masks_node_polygon_t *current_node
1932 = (dt_masks_node_polygon_t *)g_list_nth_data(mask_form->points, mask_gui->node_dragging);
1933 if(!previous_node || !current_node) return 0;
1934 previous_node->node[0] = current_node->node[0];
1935 previous_node->node[1] = current_node->node[1];
1936
1937 dt_masks_remove_node(module, mask_form, 0, mask_gui, 0, mask_gui->node_dragging);
1938 // Decrease the current dragging node index
1939 mask_gui->node_dragging -= 1;
1940
1942 return 1;
1943 }
1944 case GDK_KEY_Return:
1945 return _polygon_creation_closing_form(mask_form, mask_gui);
1946 }
1947 }
1948 return 0;
1949}
1950
1959static int _polygon_events_mouse_moved(struct dt_iop_module_t *module, double x, double y, double pressure,
1960 int which, dt_masks_form_t *mask_form, int parent_id,
1961 dt_masks_form_gui_t *mask_gui, int form_index)
1962{
1963 // centre view will have zoom_scale * backbuf_width pixels, we want the handle offset to scale with DPI:
1965 dt_masks_form_gui_points_t *gui_points
1966 = (dt_masks_form_gui_points_t *)g_list_nth_data(mask_gui->points, form_index);
1967 if(IS_NULL_PTR(gui_points)) return 0;
1968
1969 const int iwidth = darktable.develop->roi.raw_width;
1970 const int iheight = darktable.develop->roi.raw_height;
1971
1972 if(mask_gui->node_dragging >= 0)
1973 {
1974 if(IS_NULL_PTR(mask_form->points)) return 0;
1975 if(mask_gui->creation && !g_list_shorter_than(mask_form->points, 4))
1976 {
1977 // check if we are near the first point to close the polygon on creation
1978 const float dist_curs = DT_GUI_MOUSE_EFFECT_RADIUS;
1979 const float dx = mask_gui->pos[0] - gui_points->points[2];
1980 const float dy = mask_gui->pos[1] - gui_points->points[3];
1981 const float dist2 = dx * dx + dy * dy;
1982 mask_gui->creation_closing_form = dist2 <= dist_curs * dist_curs;
1983 }
1984
1985 dt_masks_node_polygon_t *dragged_node
1986 = (dt_masks_node_polygon_t *)g_list_nth_data(mask_form->points, mask_gui->node_dragging);
1987 if(IS_NULL_PTR(dragged_node)) return 0;
1988
1989 float dx = 0.0f;
1990 float dy = 0.0f;
1991 dt_masks_gui_delta_from_raw_anchor(dev, mask_gui, dragged_node->node, &dx, &dy);
1992 _polygon_translate_node(dragged_node, dx, dy);
1993
1994 // if first point, adjust the source position accordingly
1995 if((mask_form->type & DT_MASKS_CLONE) && mask_gui->node_dragging == 0)
1996 dt_masks_translate_source(mask_form, dx, dy);
1997
1998 if(mask_gui->creation)
1999 _polygon_init_ctrl_points(mask_form);
2000
2001 // we recreate the form points
2002 if(dt_masks_gui_form_create_throttled(mask_form, mask_gui, form_index, module,
2003 mask_gui->pos[0], mask_gui->pos[1]))
2004 gui_points->clockwise = _polygon_is_clockwise(mask_form);
2005
2006 return 1;
2007 }
2008 else if(mask_gui->creation)
2009 {
2010 // Let the cursor motion be redrawn as it moves in GUI
2011 return 1;
2012 }
2013
2014 if(IS_NULL_PTR(mask_form->points)) return 0;
2015 const guint node_count = g_list_length(mask_form->points);
2016
2017 if(mask_gui->seg_dragging >= 0)
2018 {
2019 const GList *const pt = g_list_nth(mask_form->points, mask_gui->seg_dragging);
2020 const GList *const next_pt = g_list_next_wraparound(pt, mask_form->points);
2022 dt_masks_node_polygon_t *next_point = (dt_masks_node_polygon_t *)next_pt->data;
2023 if(IS_NULL_PTR(point) || IS_NULL_PTR(next_point)) return 0;
2024
2025 float dx = 0.0f;
2026 float dy = 0.0f;
2027 dt_masks_gui_delta_from_raw_anchor(dev, mask_gui, point->node, &dx, &dy);
2028
2029 // if first or last segment, update the source accordingly
2030 // (the source point follows the first/last segment when moved)
2031 if((mask_form->type & DT_MASKS_CLONE)
2032 && (mask_gui->seg_dragging == 0 || mask_gui->seg_dragging == (int)node_count - 1))
2033 dt_masks_translate_source(mask_form, dx, dy);
2034
2036 _polygon_translate_node(next_point, dx, dy);
2037
2038 // we recreate the form points
2039 dt_masks_gui_form_create_throttled(mask_form, mask_gui, form_index, module,
2040 mask_gui->pos[0], mask_gui->pos[1]);
2041 gui_points->clockwise = _polygon_is_clockwise(mask_form);
2042
2043 return 1;
2044 }
2045 else if(mask_gui->handle_dragging >= 0)
2046 {
2048 = (dt_masks_node_polygon_t *)g_list_nth_data(mask_form->points, mask_gui->handle_dragging);
2049 if(IS_NULL_PTR(node)) return 0;
2050
2051 float pts[2];
2052 dt_masks_gui_delta_to_image_abs(mask_gui, pts);
2053
2054 // compute ctrl points directly from new handle position
2055 float p[4];
2056 _polygon_handle_to_ctrl(gui_points->points[mask_gui->handle_dragging * 6 + 2],
2057 gui_points->points[mask_gui->handle_dragging * 6 + 3],
2058 pts[0], pts[1], &p[0], &p[1], &p[2], &p[3], gui_points->clockwise);
2059
2061
2062 // set new ctrl points
2063 dt_masks_set_ctrl_points(node->ctrl1, node->ctrl2, p);
2065
2066 _polygon_init_ctrl_points(mask_form);
2067 // we recreate the form points
2068 dt_masks_gui_form_create_throttled(mask_form, mask_gui, form_index, module,
2069 mask_gui->pos[0], mask_gui->pos[1]);
2070
2071 return 1;
2072 }
2073 else if(mask_gui->handle_border_dragging >= 0)
2074 {
2075 const int node_index = mask_gui->handle_border_dragging;
2077 = (dt_masks_node_polygon_t *)g_list_nth_data(mask_form->points, node_index);
2078 if(IS_NULL_PTR(node)) return 0;
2079
2080 const int base = node_index * 6;
2081 const int node_point_index = base + 2;
2082
2083 // Get delta between the node and its border handle
2084 float pts[2];
2085 float cursor_pos[2];
2086 const float node_pos_gui[2] = { gui_points->points[node_point_index],
2087 gui_points->points[node_point_index + 1] };
2088 const float handle_pos[2] = { gui_points->border[base], gui_points->border[base + 1] };
2089 dt_masks_gui_delta_to_image_abs(mask_gui, cursor_pos);
2090 dt_masks_project_on_line(cursor_pos, node_pos_gui, handle_pos, pts);
2091
2092 const float border = dt_masks_border_from_projected_handle(dev, node->node, pts, fminf(iwidth, iheight));
2093
2094 node->border[0] = node->border[1] = border;
2095 // we recreate the form points
2096 dt_masks_gui_form_create_throttled(mask_form, mask_gui, form_index, module,
2097 mask_gui->pos[0], mask_gui->pos[1]);
2098
2099 return 1;
2100 }
2101 else if(mask_gui->form_dragging || mask_gui->source_dragging)
2102 {
2103 if(mask_gui->form_dragging)
2104 {
2105 dt_masks_node_polygon_t *dragging_shape = (dt_masks_node_polygon_t *)(mask_form->points)->data;
2106 if(IS_NULL_PTR(dragging_shape)) return 0;
2107 float dx = 0.0f;
2108 float dy = 0.0f;
2109 dt_masks_gui_delta_from_raw_anchor(dev, mask_gui, dragging_shape->node, &dx, &dy);
2110 _polygon_translate_all_nodes(mask_form, dx, dy);
2111 }
2112 else
2113 {
2114 float raw_point[2];
2115 dt_masks_gui_delta_to_raw_norm(dev, mask_gui, raw_point);
2116 mask_form->source[0] = raw_point[0];
2117 mask_form->source[1] = raw_point[1];
2118 }
2119
2120 // we recreate the form points
2121 dt_masks_gui_form_create(mask_form, mask_gui, form_index, module);
2122 return 1;
2123 }
2124 return 0;
2125}
2126
2130static void _polygon_draw_shape(cairo_t *cr, const float *point_buffer, const int point_count,
2131 const int node_count, const gboolean draw_border, const gboolean draw_source)
2132{
2133
2134 // Find the first valid non-NaN point to start drawing
2135 // FIXME: Why not just avoid having NaN points in the array?
2136 int start_idx = -1;
2137 for(int point_index = node_count * 3 + draw_border; point_index < point_count; point_index++)
2138 {
2139 if(!isnan(point_buffer[point_index * 2]) && !isnan(point_buffer[point_index * 2 + 1]))
2140 {
2141 start_idx = point_index;
2142 break;
2143 }
2144 }
2145
2146 // Only draw if we have at least one valid point
2147 if(start_idx >= 0)
2148 {
2149 cairo_move_to(cr, point_buffer[start_idx * 2], point_buffer[start_idx * 2 + 1]);
2150 for(int point_index = start_idx + 1; point_index < point_count; point_index++)
2151 {
2152 if(!isnan(point_buffer[point_index * 2]) && !isnan(point_buffer[point_index * 2 + 1]))
2153 cairo_line_to(cr, point_buffer[point_index * 2], point_buffer[point_index * 2 + 1]);
2154 }
2155 }
2156}
2157
2161static void _polygon_events_post_expose(cairo_t *cr, float zoom_scale, dt_masks_form_gui_t *mask_gui,
2162 int form_index, int node_count)
2163{
2164 dt_masks_form_gui_points_t *gui_points
2165 = (dt_masks_form_gui_points_t *)g_list_nth_data(mask_gui->points, form_index);
2166 if(IS_NULL_PTR(gui_points)) return;
2167 const int selected_node = dt_masks_gui_selected_node_index(mask_gui);
2168 const int selected_handle = dt_masks_gui_selected_handle_index(mask_gui);
2169 const int selected_handle_border = dt_masks_gui_selected_handle_border_index(mask_gui);
2170
2171 if(mask_gui->creation)
2172 {
2173 // draw a cross where the source will be created
2175 if(visible_form && (visible_form->type & DT_MASKS_CLONE))
2176 {
2177 const gboolean have_first_node = node_count && gui_points->points && gui_points->points_count > 1;
2178 float node_posx = have_first_node ? gui_points->points[2] : mask_gui->pos[0];
2179 float node_posy = have_first_node ? gui_points->points[3] : mask_gui->pos[1];
2180
2181 dt_masks_draw_source_preview(cr, zoom_scale, mask_gui, node_posx, node_posy, node_posx, node_posy, FALSE);
2182 }
2183 }
2184
2185 // update clockwise info for the handles
2186 else if((mask_gui->type & DT_MASKS_IS_RETOUCHE) != 0 || mask_gui->node_selected || mask_gui->node_dragging >= 0
2187 || mask_gui->handle_selected)
2188 {
2190 if(!IS_NULL_PTR(group_form) && (group_form->type & DT_MASKS_GROUP))
2191 {
2192 dt_masks_form_group_t *group_entry = g_list_nth_data(group_form->points, form_index);
2193 dt_masks_form_t *polygon_form = group_entry
2195 : NULL;
2196 if(!IS_NULL_PTR(polygon_form)) gui_points->clockwise = _polygon_is_clockwise(polygon_form);
2197 }
2198 }
2199
2200 // draw polygon
2201 if(gui_points->points && node_count > 0 && gui_points->points_count > node_count * 3 + 6) // there must be something to draw
2202 {
2203 dt_masks_draw_path_seg_by_seg(cr, mask_gui, form_index, gui_points->points, gui_points->points_count,
2204 node_count, zoom_scale);
2205 }
2206
2207 if(mask_gui->group_selected == form_index)
2208 {
2209 // draw borders
2210 if(gui_points->border_count > node_count * 3 + 2)
2211 {
2212 dt_draw_shape_lines(DT_MASKS_DASH_STICK, FALSE, cr, node_count, (mask_gui->border_selected), zoom_scale,
2213 gui_points->border, gui_points->border_count, &dt_masks_functions_polygon.draw_shape,
2214 CAIRO_LINE_CAP_ROUND);
2215 }
2216
2217 // draw the current node's handle if it's a curve node
2218 if(mask_gui->node_selected && selected_node >= 0 && selected_node < node_count
2219 && !dt_masks_node_is_cusp(gui_points, selected_node))
2220 {
2221 const int node_index = selected_node;
2222 float handle[2];
2223 _polygon_ctrl2_to_handle(gui_points->points[node_index * 6 + 2], gui_points->points[node_index * 6 + 3],
2224 gui_points->points[node_index * 6 + 4], gui_points->points[node_index * 6 + 5],
2225 &handle[0], &handle[1], gui_points->clockwise);
2226 const float pt[2] = { gui_points->points[node_index * 6 + 2], gui_points->points[node_index * 6 + 3] };
2227 const gboolean selected = (mask_gui->node_hovered == node_index
2228 || (selected_handle == node_index)
2229 || (mask_gui->handle_hovered == node_index));
2230 dt_draw_handle(cr, pt, zoom_scale, handle, selected, FALSE);
2231 }
2232 }
2233
2234 // draw nodes
2235 if(mask_gui->group_selected == form_index || mask_gui->creation)
2236 {
2237 for(int node_index = 0; node_index < node_count; node_index++)
2238 {
2239 // don't draw the last node while creating
2240 if(mask_gui->creation && node_index == node_count - 1) break;
2241 if(IS_NULL_PTR(gui_points->points) || gui_points->points_count <= node_index * 3 + 1) break;
2242
2243 const gboolean squared = dt_masks_node_is_cusp(gui_points, node_index);
2244 const gboolean selected = (node_index == mask_gui->node_hovered || node_index == mask_gui->node_dragging);
2245 const gboolean action = (node_index == selected_node);
2246 const float x = gui_points->points[node_index * 6 + 2];
2247 const float y = gui_points->points[node_index * 6 + 3];
2248
2249 // draw the first node as big circle while creating the polygon
2250 if(mask_gui->creation && node_index == 0)
2251 dt_draw_node(cr, FALSE, TRUE, TRUE, zoom_scale, x, y);
2252 else
2253 dt_draw_node(cr, squared, action, selected, zoom_scale, x, y);
2254 }
2255
2256 // Draw the current node's border handle, if needed
2257 if(mask_gui->node_selected && selected_node >= 0 && selected_node < node_count
2258 && gui_points->border && gui_points->border_count > selected_node * 3 && !mask_gui->creation)
2259 {
2260 const int edited = selected_node;
2261 const gboolean selected = (mask_gui->node_hovered == edited
2262 || (selected_handle_border == edited)
2263 || mask_gui->handle_border_hovered == edited);
2264 const int curr_node = edited * 6;
2265 const float handle[2] = { gui_points->border[curr_node], gui_points->border[curr_node + 1] };
2266
2267 dt_draw_handle(cr, NULL, zoom_scale, handle, selected, TRUE);
2268 }
2269 }
2270
2271 // draw the source if needed
2272 if(gui_points->source && gui_points->source_count > node_count * 3 + 2
2273 && gui_points->points && gui_points->points_count > 0)
2274 {
2275 dt_masks_gui_center_point_t center_pt = { .main = { gui_points->points[0], gui_points->points[1] },
2276 .source = { gui_points->source[0], gui_points->source[1] } };
2277 _polygon_gui_gravity_center(gui_points->points, gui_points->points_count,
2278 &center_pt.main.x, &center_pt.main.y, NULL);
2279 // project the source's center point from the center of gravity
2280 float offset_x = gui_points->source[0] - gui_points->points[0];
2281 float offset_y = gui_points->source[1] - gui_points->points[1];
2282 center_pt.source.x = center_pt.main.x + offset_x;
2283 center_pt.source.y = center_pt.main.y + offset_y;
2284 dt_masks_draw_source(cr, mask_gui, form_index, node_count, zoom_scale,
2286
2287 //draw the current node projection
2288 for(int node_index = 0; node_index < node_count; node_index++)
2289 {
2290 if(mask_gui->group_selected == form_index
2291 && (node_index == mask_gui->node_hovered || node_index == selected_node
2292 || (mask_gui->creation && node_index == node_count - 1)))
2293 {
2294 const int proj_index = node_index * 6 + 2;
2295 if(gui_points->source_count <= node_index * 3 + 1) break;
2296 const float proj[2] = { gui_points->source[proj_index], gui_points->source[proj_index + 1] };
2297 const gboolean selected = mask_gui->node_hovered == node_index;
2298 const gboolean squared = dt_masks_node_is_cusp(gui_points, node_index);
2299
2300 dt_draw_handle(cr, NULL, zoom_scale, proj, selected, squared);
2301 }
2302 }
2303 }
2304}
2305
2309static void _polygon_bounding_box_raw(const float *const point_buffer, const float *border_buffer,
2310 const int corner_count, const int point_count, int border_count,
2311 float *x_min, float *x_max, float *y_min, float *y_max)
2312{
2313 float xmin, xmax, ymin, ymax;
2314 xmin = ymin = FLT_MAX;
2315 xmax = ymax = FLT_MIN;
2316 for(int border_index = corner_count * 3; border_index < border_count; border_index++)
2317 {
2318 // we look at the borders
2319 const float xx = border_buffer[border_index * 2];
2320 const float yy = border_buffer[border_index * 2 + 1];
2321 if(isnan(xx))
2322 {
2323 if(isnan(yy)) break; // that means we have to skip the end of the border polygon
2324 border_index = yy - 1;
2325 continue;
2326 }
2327 xmin = MIN(xx, xmin);
2328 xmax = MAX(xx, xmax);
2329 ymin = MIN(yy, ymin);
2330 ymax = MAX(yy, ymax);
2331 }
2332 for(int point_index = corner_count * 3; point_index < point_count; point_index++)
2333 {
2334 // we look at the polygon too
2335 const float xx = point_buffer[point_index * 2];
2336 const float yy = point_buffer[point_index * 2 + 1];
2337 xmin = MIN(xx, xmin);
2338 xmax = MAX(xx, xmax);
2339 ymin = MIN(yy, ymin);
2340 ymax = MAX(yy, ymax);
2341 }
2342
2343 *x_min = xmin;
2344 *x_max = xmax;
2345 *y_min = ymin;
2346 *y_max = ymax;
2347}
2348
2352static void _polygon_bounding_box(const float *const point_buffer, const float *border_buffer,
2353 const int corner_count, const int point_count, int border_count,
2354 int *width, int *height, int *posx, int *posy)
2355{
2356 // now we want to find the area, so we search min/max points
2357 float xmin, xmax, ymin, ymax;
2358 _polygon_bounding_box_raw(point_buffer, border_buffer, corner_count, point_count, border_count,
2359 &xmin, &xmax, &ymin, &ymax);
2360 *height = ymax - ymin + 4;
2361 *width = xmax - xmin + 4;
2362 *posx = xmin - 2;
2363 *posy = ymin - 2;
2364}
2365
2366static int _get_area(const dt_iop_module_t *const module, dt_dev_pixelpipe_t *pipe,
2367 const dt_dev_pixelpipe_iop_t *const piece,
2368 dt_masks_form_t *const mask_form, int *width, int *height, int *posx, int *posy,
2369 gboolean get_source)
2370{
2371 if(IS_NULL_PTR(module)) return 1;
2372
2373 // we get buffers for all points
2374 float *point_buffer = NULL;
2375 float *border_buffer = NULL;
2376 int point_count = 0;
2377 int border_count = 0;
2378
2379 if(_polygon_get_pts_border(module->dev, mask_form, module->iop_order, DT_DEV_TRANSFORM_DIR_BACK_INCL, pipe,
2380 &point_buffer, &point_count, &border_buffer, &border_count, get_source) != 0)
2381 {
2382 dt_pixelpipe_cache_free_align(point_buffer);
2383 dt_pixelpipe_cache_free_align(border_buffer);
2384 return 1;
2385 }
2386
2387 const guint corner_count = g_list_length(mask_form->points);
2388 _polygon_bounding_box(point_buffer, border_buffer, corner_count, point_count, border_count,
2389 width, height, posx, posy);
2390
2391 dt_pixelpipe_cache_free_align(point_buffer);
2392 dt_pixelpipe_cache_free_align(border_buffer);
2393 return 0;
2394}
2395
2398 dt_masks_form_t *mask_form, int *width, int *height, int *posx, int *posy)
2399{
2400 return _get_area(module, pipe, piece, mask_form, width, height, posx, posy, TRUE);
2401}
2402
2403static int _polygon_get_area(const dt_iop_module_t *const module, dt_dev_pixelpipe_t *pipe,
2404 const dt_dev_pixelpipe_iop_t *const piece,
2405 dt_masks_form_t *const mask_form,
2406 int *width, int *height, int *posx, int *posy)
2407{
2408 return _get_area(module, pipe, piece, mask_form, width, height, posx, posy, FALSE);
2409}
2410
2414/*static*/ void _polygon_falloff(float *const restrict buffer, int *p0, int *p1,
2415 int posx, int posy, int buffer_width)
2416{
2417 // segment length
2418 int l = sqrtf(sqf(p1[0] - p0[0]) + sqf(p1[1] - p0[1])) + 1;
2419
2420 const float lx = p1[0] - p0[0];
2421 const float ly = p1[1] - p0[1];
2422 const float inv_l = 1.0f / (float)l;
2423
2424 for(int i = 0; i < l; i++)
2425 {
2426 // position
2427 const int x = (int)((float)i * lx * inv_l) + p0[0] - posx;
2428 const int y = (int)((float)i * ly * inv_l) + p0[1] - posy;
2429 const float op = 1.0f - (float)i * inv_l;
2430 const size_t idx = y * buffer_width + x;
2431 buffer[idx] = fmaxf(buffer[idx], op);
2432 if(x > 0)
2433 buffer[idx - 1] = fmaxf(buffer[idx - 1], op); // this one is to avoid gap due to int rounding
2434 if(y > 0)
2435 buffer[idx - buffer_width] = fmaxf(buffer[idx - buffer_width], op); // this one is to avoid gap due to int rounding
2436 }
2437}
2438
2439static int _polygon_get_mask(const dt_iop_module_t *const module, dt_dev_pixelpipe_t *pipe,
2440 const dt_dev_pixelpipe_iop_t *const piece,
2441 dt_masks_form_t *const mask_form,
2442 float **buffer, int *width, int *height, int *posx, int *posy)
2443{
2444 if(IS_NULL_PTR(module)) return 1;
2445 double start = 0.0;
2446 double start2 = 0.0;
2447
2449
2450 // we get buffers for all points
2451 float *point_buffer = NULL;
2452 float *border_buffer = NULL;
2453 int point_count = 0;
2454 int border_count = 0;
2455 if(_polygon_get_pts_border(module->dev, mask_form, module->iop_order,
2456 DT_DEV_TRANSFORM_DIR_BACK_INCL, pipe, &point_buffer, &point_count,
2457 &border_buffer, &border_count, FALSE) != 0)
2458 {
2459 dt_pixelpipe_cache_free_align(point_buffer);
2460 dt_pixelpipe_cache_free_align(border_buffer);
2461 return 1;
2462 }
2463
2465 {
2466 dt_print(DT_DEBUG_MASKS, "[masks %s] polygon points took %0.04f sec\n",
2467 mask_form->name, dt_get_wtime() - start);
2468 start = start2 = dt_get_wtime();
2469 }
2470
2471 // now we want to find the area, so we search min/max points
2472 const guint corner_count = g_list_length(mask_form->points);
2473 _polygon_bounding_box(point_buffer, border_buffer, corner_count, point_count, border_count,
2474 width, height, posx, posy);
2475
2476 const int hb = *height;
2477 const int wb = *width;
2478 const gboolean sparse = (dt_dev_pixelpipe_has_preview_output(piece->module->dev, pipe, NULL)
2479 || pipe->type == DT_DEV_PIXELPIPE_THUMBNAIL);
2480 const int sparse_factor = sparse ? 4 : 1;
2481
2483 {
2484 dt_print(DT_DEBUG_MASKS, "[masks %s] polygon_fill min max took %0.04f sec\n", mask_form->name,
2485 dt_get_wtime() - start2);
2486 start2 = dt_get_wtime();
2487 }
2488
2489 // we allocate the buffer
2490 const size_t bufsize = (size_t)(*width) * (*height);
2491 // ensure that the buffer is zeroed, as the following code only actually sets the polygon+falloff pixels
2492 float *const restrict bufptr = *buffer = dt_pixelpipe_cache_alloc_align_float_cache(bufsize, 0);
2493 if(!IS_NULL_PTR(bufptr)) memset(bufptr, 0, sizeof(float) * bufsize);
2494 if(IS_NULL_PTR(*buffer))
2495 {
2496 dt_pixelpipe_cache_free_align(point_buffer);
2497 dt_pixelpipe_cache_free_align(border_buffer);
2498 return 1;
2499 }
2500
2501 // we write all the point around the polygon into the buffer
2502 const int border_point_count = border_count;
2503 if(border_point_count > 2)
2504 {
2505 int lastx = (int)point_buffer[(border_point_count - 1) * 2];
2506 int lasty = (int)point_buffer[(border_point_count - 1) * 2 + 1];
2507 int lasty2 = (int)point_buffer[(border_point_count - 2) * 2 + 1];
2508
2509 int just_change_dir = 0;
2510 for(int ii = corner_count * 3; ii < 2 * border_point_count - corner_count * 3; ii++)
2511 {
2512 // we are writing more than 1 loop in the case the dir in y change
2513 // exactly at start/end point
2514 int i = ii;
2515 if(ii >= border_point_count)
2516 i = (ii - corner_count * 3) % (border_point_count - corner_count * 3) + corner_count * 3;
2517 const int xx = (int)point_buffer[i * 2];
2518 const int yy = (int)point_buffer[i * 2 + 1];
2519
2520 // we don't store the point if it has the same y value as the last one
2521 if(yy == lasty) continue;
2522
2523 // we want to be sure that there is no y jump
2524 if(yy - lasty > 1 || yy - lasty < -1)
2525 {
2526 if(yy < lasty)
2527 {
2528 for(int j = yy + 1; j < lasty; j++)
2529 {
2530 const int nx = (j - yy) * (lastx - xx) / (float)(lasty - yy) + xx;
2531 const size_t idx = (size_t)(j - (*posy)) * (*width) + nx - (*posx);
2532 assert(idx < bufsize);
2533 bufptr[idx] = 1.0f;
2534 }
2535 lasty2 = yy + 2;
2536 lasty = yy + 1;
2537 }
2538 else
2539 {
2540 for(int j = lasty + 1; j < yy; j++)
2541 {
2542 const int nx = (j - lasty) * (xx - lastx) / (float)(yy - lasty) + lastx;
2543 const size_t idx = (size_t)(j - (*posy)) * (*width) + nx - (*posx);
2544 assert(idx < bufsize);
2545 bufptr[idx] = 1.0f;
2546 }
2547 lasty2 = yy - 2;
2548 lasty = yy - 1;
2549 }
2550 }
2551 // if we change the direction of the polygon (in y), then we add a extra point
2552 if((lasty - lasty2) * (lasty - yy) > 0)
2553 {
2554 const size_t idx = (size_t)(lasty - (*posy)) * (*width) + lastx + 1 - (*posx);
2555 assert(idx < bufsize);
2556 bufptr[idx] = 1.0f;
2557 just_change_dir = 1;
2558 }
2559 // we add the point
2560 if(just_change_dir && ii == i)
2561 {
2562 // if we have changed the direction, we have to be careful that point can be at the same place
2563 // as the previous one, especially on sharp edges
2564 const size_t idx = (size_t)(yy - (*posy)) * (*width) + xx - (*posx);
2565 assert(idx < bufsize);
2566 float v = bufptr[idx];
2567 if(v > 0.0)
2568 {
2569 if(xx - (*posx) > 0)
2570 {
2571 const size_t idx_ = (size_t)(yy - (*posy)) * (*width) + xx - 1 - (*posx);
2572 assert(idx_ < bufsize);
2573 bufptr[idx_] = 1.0f;
2574 }
2575 else if(xx - (*posx) < (*width) - 1)
2576 {
2577 const size_t idx_ = (size_t)(yy - (*posy)) * (*width) + xx + 1 - (*posx);
2578 assert(idx_ < bufsize);
2579 bufptr[idx_] = 1.0f;
2580 }
2581 }
2582 else
2583 {
2584 const size_t idx_ = (size_t)(yy - (*posy)) * (*width) + xx - (*posx);
2585 assert(idx_ < bufsize);
2586 bufptr[idx_] = 1.0f;
2587 just_change_dir = 0;
2588 }
2589 }
2590 else
2591 {
2592 const size_t idx_ = (size_t)(yy - (*posy)) * (*width) + xx - (*posx);
2593 assert(idx_ < bufsize);
2594 bufptr[idx_] = 1.0f;
2595 }
2596 // we change last values
2597 lasty2 = lasty;
2598 lasty = yy;
2599 lastx = xx;
2600 if(ii != i) break;
2601 }
2602 }
2604 {
2605 dt_print(DT_DEBUG_MASKS, "[masks %s] polygon_fill draw polygon took %0.04f sec\n", mask_form->name,
2606 dt_get_wtime() - start2);
2607 start2 = dt_get_wtime();
2608 }
2609 __OMP_PARALLEL_FOR__(if((size_t)hb * wb > 50000))
2610 for(int yy = 0; yy < hb; yy++)
2611 {
2612 float *const restrict row = bufptr + (size_t)yy * wb;
2613 int state = 0;
2614 for(int xx = 0; xx < wb; xx++)
2615 {
2616 const float v = row[xx];
2617 if(v == 1.0f) state = !state;
2618 if(state) row[xx] = 1.0f;
2619 }
2620 }
2621
2623 {
2624 dt_print(DT_DEBUG_MASKS, "[masks %s] polygon_fill fill plain took %0.04f sec\n", mask_form->name,
2625 dt_get_wtime() - start2);
2626 start2 = dt_get_wtime();
2627 }
2628
2629 // now we fill the falloff
2630 int p0[2] = { 0 }, p1[2] = { 0 };
2631 float pf1[2] = { 0.0f };
2632 int prev0[2] = { 0 }, prev1[2] = { 0 };
2633 gboolean have_prev = FALSE;
2634 int last0[2] = { -100, -100 }, last1[2] = { -100, -100 };
2635 int next = 0;
2636 for(int i = corner_count * 3; i < border_count; i++)
2637 {
2638 p0[0] = point_buffer[i * 2];
2639 p0[1] = point_buffer[i * 2 + 1];
2640 if(next > 0)
2641 p1[0] = pf1[0] = border_buffer[next * 2], p1[1] = pf1[1] = border_buffer[next * 2 + 1];
2642 else
2643 p1[0] = pf1[0] = border_buffer[i * 2], p1[1] = pf1[1] = border_buffer[i * 2 + 1];
2644
2645 // now we check p1 value to know if we have to skip a part
2646 if(next == i) next = 0;
2647 while(isnan(pf1[0]))
2648 {
2649 if(isnan(pf1[1]))
2650 next = i - 1;
2651 else
2652 next = p1[1];
2653 p1[0] = pf1[0] = border_buffer[next * 2];
2654 p1[1] = pf1[1] = border_buffer[next * 2 + 1];
2655 }
2656
2657 const gboolean used_next = (next > 0);
2658
2659 if(sparse && have_prev && !used_next
2660 && (prev0[0] != p0[0] || prev0[1] != p0[1] || prev1[0] != p1[0] || prev1[1] != p1[1]))
2661 {
2662 for(int k = 1; k < sparse_factor; k++)
2663 {
2664 const float t = (float)k / (float)sparse_factor;
2665 int mp0[2] = { (int)floorf(prev0[0] + t * (p0[0] - prev0[0]) + 0.5f),
2666 (int)floorf(prev0[1] + t * (p0[1] - prev0[1]) + 0.5f) };
2667 int mp1[2] = { (int)floorf(prev1[0] + t * (p1[0] - prev1[0]) + 0.5f),
2668 (int)floorf(prev1[1] + t * (p1[1] - prev1[1]) + 0.5f) };
2669 _polygon_falloff(bufptr, mp0, mp1, *posx, *posy, *width);
2670 }
2671 }
2672
2673 // and we draw the falloff
2674 if(last0[0] != p0[0] || last0[1] != p0[1] || last1[0] != p1[0] || last1[1] != p1[1])
2675 {
2676 _polygon_falloff(bufptr, p0, p1, *posx, *posy, *width);
2677 last0[0] = p0[0];
2678 last0[1] = p0[1];
2679 last1[0] = p1[0];
2680 last1[1] = p1[1];
2681 }
2682
2683 if(!used_next)
2684 {
2685 prev0[0] = p0[0];
2686 prev0[1] = p0[1];
2687 prev1[0] = p1[0];
2688 prev1[1] = p1[1];
2689 have_prev = TRUE;
2690 }
2691 else
2692 {
2693 have_prev = FALSE;
2694 }
2695 }
2696
2698 dt_print(DT_DEBUG_MASKS, "[masks %s] polygon_fill fill falloff took %0.04f sec\n", mask_form->name,
2699 dt_get_wtime() - start2);
2700
2701 dt_pixelpipe_cache_free_align(point_buffer);
2702 dt_pixelpipe_cache_free_align(border_buffer);
2703
2705 dt_print(DT_DEBUG_MASKS, "[masks %s] polygon fill buffer took %0.04f sec\n", mask_form->name,
2706 dt_get_wtime() - start);
2707
2708 return 0;
2709}
2710
2711
2714static int _polygon_crop_to_roi(float *polygon, const int point_count, float xmin, float xmax, float ymin,
2715 float ymax)
2716{
2717 int point_start = -1;
2718 int l = -1, r = -1;
2719
2720
2721 // first try to find a node clearly inside roi
2722 for(int k = 0; k < point_count; k++)
2723 {
2724 float x = polygon[2 * k];
2725 float y = polygon[2 * k + 1];
2726
2727 if(x >= xmin + 1 && y >= ymin + 1
2728 && x <= xmax - 1 && y <= ymax - 1)
2729 {
2730 point_start = k;
2731 break;
2732 }
2733 }
2734
2735 // printf("crop to xmin %f, xmax %f, ymin %f, ymax %f - start %d (%f, %f)\n", xmin, xmax, ymin, ymax,
2736 // point_start, polygon[2*point_start], polygon[2*point_start+1]);
2737
2738 if(point_start < 0) return 0; // no point means roi lies completely within polygon
2739
2740 typedef struct
2741 {
2742 int l;
2743 int r;
2744 float start;
2745 float delta;
2746 } roi_crop_segment_t;
2747
2748 roi_crop_segment_t *xmax_segs = malloc(sizeof(*xmax_segs) * point_count);
2749 roi_crop_segment_t *ymax_segs = malloc(sizeof(*ymax_segs) * point_count);
2750 if(IS_NULL_PTR(xmax_segs) || IS_NULL_PTR(ymax_segs))
2751 {
2752 dt_free(xmax_segs);
2753 dt_free(ymax_segs);
2754 goto fallback_passes;
2755 }
2756
2757 int xmin_l = -1, xmin_r = -1;
2758 int xmax_l = -1, xmax_r = -1;
2759 int xmax_count = 0;
2760
2761 // find the crossing points with xmin/xmax in a single pass
2762 for(int k = 0; k < point_count; k++)
2763 {
2764 const int kk = (k + point_start) % point_count;
2765 const float x = polygon[2 * kk];
2766
2767 if(xmin_l < 0 && x < xmin) xmin_l = k; // where we leave roi (xmin)
2768 if(xmin_l >= 0 && x >= xmin) xmin_r = k - 1; // where we re-enter roi (xmin)
2769
2770 if(xmin_l >= 0 && xmin_r >= 0)
2771 {
2772 const int count = xmin_r - xmin_l + 1;
2773 const int ll = (xmin_l - 1 + point_start) % point_count;
2774 const int rr = (xmin_r + 1 + point_start) % point_count;
2775 const float delta_y = (count == 1) ? 0 : (polygon[2 * rr + 1] - polygon[2 * ll + 1]) / (count - 1);
2776 const float start_y = polygon[2 * ll + 1];
2777
2778 for(int n = 0; n < count; n++)
2779 {
2780 const int nn = (n + xmin_l + point_start) % point_count;
2781 polygon[2 * nn] = xmin;
2782 polygon[2 * nn + 1] = start_y + n * delta_y;
2783 }
2784
2785 xmin_l = xmin_r = -1;
2786 }
2787
2788 if(xmax_l < 0 && x > xmax) xmax_l = k; // where we leave roi (xmax)
2789 if(xmax_l >= 0 && x <= xmax) xmax_r = k - 1; // where we re-enter roi (xmax)
2790
2791 if(xmax_l >= 0 && xmax_r >= 0)
2792 {
2793 const int count = xmax_r - xmax_l + 1;
2794 const int ll = (xmax_l - 1 + point_start) % point_count;
2795 const int rr = (xmax_r + 1 + point_start) % point_count;
2796 const float delta_y = (count == 1) ? 0 : (polygon[2 * rr + 1] - polygon[2 * ll + 1]) / (count - 1);
2797 const float start_y = polygon[2 * ll + 1];
2798
2799 xmax_segs[xmax_count].l = xmax_l;
2800 xmax_segs[xmax_count].r = xmax_r;
2801 xmax_segs[xmax_count].start = start_y;
2802 xmax_segs[xmax_count].delta = delta_y;
2803 xmax_count++;
2804
2805 xmax_l = xmax_r = -1;
2806 }
2807 }
2808
2809 for(int s = 0; s < xmax_count; s++)
2810 {
2811 const int count = xmax_segs[s].r - xmax_segs[s].l + 1;
2812 const float start_y = xmax_segs[s].start;
2813 const float delta_y = xmax_segs[s].delta;
2814 for(int n = 0; n < count; n++)
2815 {
2816 const int nn = (n + xmax_segs[s].l + point_start) % point_count;
2817 polygon[2 * nn] = xmax;
2818 polygon[2 * nn + 1] = start_y + n * delta_y;
2819 }
2820 }
2821
2822 dt_free(xmax_segs);
2823
2824 int ymin_l = -1, ymin_r = -1;
2825 int ymax_l = -1, ymax_r = -1;
2826 int ymax_count = 0;
2827
2828 // find the crossing points with ymin/ymax in a single pass
2829 for(int k = 0; k < point_count; k++)
2830 {
2831 const int kk = (k + point_start) % point_count;
2832 const float y = polygon[2 * kk + 1];
2833
2834 if(ymin_l < 0 && y < ymin) ymin_l = k; // where we leave roi (ymin)
2835 if(ymin_l >= 0 && y >= ymin) ymin_r = k - 1; // where we re-enter roi (ymin)
2836
2837 if(ymin_l >= 0 && ymin_r >= 0)
2838 {
2839 const int count = ymin_r - ymin_l + 1;
2840 const int ll = (ymin_l - 1 + point_start) % point_count;
2841 const int rr = (ymin_r + 1 + point_start) % point_count;
2842 const float delta_x = (count == 1) ? 0 : (polygon[2 * rr] - polygon[2 * ll]) / (count - 1);
2843 const float start_x = polygon[2 * ll];
2844
2845 for(int n = 0; n < count; n++)
2846 {
2847 const int nn = (n + ymin_l + point_start) % point_count;
2848 polygon[2 * nn] = start_x + n * delta_x;
2849 polygon[2 * nn + 1] = ymin;
2850 }
2851
2852 ymin_l = ymin_r = -1;
2853 }
2854
2855 if(ymax_l < 0 && y > ymax) ymax_l = k; // where we leave roi (ymax)
2856 if(ymax_l >= 0 && y <= ymax) ymax_r = k - 1; // where we re-enter roi (ymax)
2857
2858 if(ymax_l >= 0 && ymax_r >= 0)
2859 {
2860 const int count = ymax_r - ymax_l + 1;
2861 const int ll = (ymax_l - 1 + point_start) % point_count;
2862 const int rr = (ymax_r + 1 + point_start) % point_count;
2863 const float delta_x = (count == 1) ? 0 : (polygon[2 * rr] - polygon[2 * ll]) / (count - 1);
2864 const float start_x = polygon[2 * ll];
2865
2866 ymax_segs[ymax_count].l = ymax_l;
2867 ymax_segs[ymax_count].r = ymax_r;
2868 ymax_segs[ymax_count].start = start_x;
2869 ymax_segs[ymax_count].delta = delta_x;
2870 ymax_count++;
2871
2872 ymax_l = ymax_r = -1;
2873 }
2874 }
2875
2876 for(int s = 0; s < ymax_count; s++)
2877 {
2878 const int count = ymax_segs[s].r - ymax_segs[s].l + 1;
2879 const float start_x = ymax_segs[s].start;
2880 const float delta_x = ymax_segs[s].delta;
2881 for(int n = 0; n < count; n++)
2882 {
2883 const int nn = (n + ymax_segs[s].l + point_start) % point_count;
2884 polygon[2 * nn] = start_x + n * delta_x;
2885 polygon[2 * nn + 1] = ymax;
2886 }
2887 }
2888
2889 dt_free(ymax_segs);
2890 return 1;
2891
2892fallback_passes:
2893 l = r = -1;
2894 // find the crossing points with xmin and replace segment by nodes on border
2895 for(int k = 0; k < point_count; k++)
2896 {
2897 const int kk = (k + point_start) % point_count;
2898
2899 if(l < 0 && polygon[2 * kk] < xmin) l = k; // where we leave roi
2900 if(l >= 0 && polygon[2 * kk] >= xmin) r = k - 1; // where we re-enter roi
2901
2902 // replace that segment
2903 if(l >= 0 && r >= 0)
2904 {
2905 const int count = r - l + 1;
2906 const int ll = (l - 1 + point_start) % point_count;
2907 const int rr = (r + 1 + point_start) % point_count;
2908 const float delta_y = (count == 1) ? 0 : (polygon[2 * rr + 1] - polygon[2 * ll + 1]) / (count - 1);
2909 const float start_y = polygon[2 * ll + 1];
2910
2911 for(int n = 0; n < count; n++)
2912 {
2913 const int nn = (n + l + point_start) % point_count;
2914 polygon[2 * nn] = xmin;
2915 polygon[2 * nn + 1] = start_y + n * delta_y;
2916 }
2917
2918 l = r = -1;
2919 }
2920 }
2921
2922 // find the crossing points with xmax and replace segment by nodes on border
2923 for(int k = 0; k < point_count; k++)
2924 {
2925 const int kk = (k + point_start) % point_count;
2926
2927 if(l < 0 && polygon[2 * kk] > xmax) l = k; // where we leave roi
2928 if(l >= 0 && polygon[2 * kk] <= xmax) r = k - 1; // where we re-enter roi
2929
2930 // replace that segment
2931 if(l >= 0 && r >= 0)
2932 {
2933 const int count = r - l + 1;
2934 const int ll = (l - 1 + point_start) % point_count;
2935 const int rr = (r + 1 + point_start) % point_count;
2936 const float delta_y = (count == 1) ? 0 : (polygon[2 * rr + 1] - polygon[2 * ll + 1]) / (count - 1);
2937 const float start_y = polygon[2 * ll + 1];
2938
2939 for(int n = 0; n < count; n++)
2940 {
2941 const int nn = (n + l + point_start) % point_count;
2942 polygon[2 * nn] = xmax;
2943 polygon[2 * nn + 1] = start_y + n * delta_y;
2944 }
2945
2946 l = r = -1;
2947 }
2948 }
2949
2950 // find the crossing points with ymin and replace segment by nodes on border
2951 for(int k = 0; k < point_count; k++)
2952 {
2953 const int kk = (k + point_start) % point_count;
2954
2955 if(l < 0 && polygon[2 * kk + 1] < ymin) l = k; // where we leave roi
2956 if(l >= 0 && polygon[2 * kk + 1] >= ymin) r = k - 1; // where we re-enter roi
2957
2958 // replace that segment
2959 if(l >= 0 && r >= 0)
2960 {
2961 const int count = r - l + 1;
2962 const int ll = (l - 1 + point_start) % point_count;
2963 const int rr = (r + 1 + point_start) % point_count;
2964 const float delta_x = (count == 1) ? 0 : (polygon[2 * rr] - polygon[2 * ll]) / (count - 1);
2965 const float start_x = polygon[2 * ll];
2966
2967 for(int n = 0; n < count; n++)
2968 {
2969 const int nn = (n + l + point_start) % point_count;
2970 polygon[2 * nn] = start_x + n * delta_x;
2971 polygon[2 * nn + 1] = ymin;
2972 }
2973
2974 l = r = -1;
2975 }
2976 }
2977
2978 // find the crossing points with ymax and replace segment by nodes on border
2979 for(int k = 0; k < point_count; k++)
2980 {
2981 const int kk = (k + point_start) % point_count;
2982
2983 if(l < 0 && polygon[2 * kk + 1] > ymax) l = k; // where we leave roi
2984 if(l >= 0 && polygon[2 * kk + 1] <= ymax) r = k - 1; // where we re-enter roi
2985
2986 // replace that segment
2987 if(l >= 0 && r >= 0)
2988 {
2989 const int count = r - l + 1;
2990 const int ll = (l - 1 + point_start) % point_count;
2991 const int rr = (r + 1 + point_start) % point_count;
2992 const float delta_x = (count == 1) ? 0 : (polygon[2 * rr] - polygon[2 * ll]) / (count - 1);
2993 const float start_x = polygon[2 * ll];
2994
2995 for(int n = 0; n < count; n++)
2996 {
2997 const int nn = (n + l + point_start) % point_count;
2998 polygon[2 * nn] = start_x + n * delta_x;
2999 polygon[2 * nn + 1] = ymax;
3000 }
3001
3002 l = r = -1;
3003 }
3004 }
3005 return 1;
3006}
3007
3009static inline void _polygon_falloff_roi(float *buffer, int *p0, int *p1, int bw, int bh)
3010{
3011 // segment length
3012 const int l = sqrt((p1[0] - p0[0]) * (p1[0] - p0[0]) + (p1[1] - p0[1]) * (p1[1] - p0[1])) + 1;
3013
3014 const float lx = p1[0] - p0[0];
3015 const float ly = p1[1] - p0[1];
3016 const float inv_l = 1.0f / (float)l;
3017
3018 const int dx = lx < 0 ? -1 : 1;
3019 const int dy = ly < 0 ? -1 : 1;
3020 const int dpy = dy * bw;
3021
3022 const int x0 = p0[0], y0 = p0[1];
3023 const int x1 = p1[0], y1 = p1[1];
3024 if((x0 < 0 && x1 < 0) || (x0 >= bw && x1 >= bw) || (y0 < 0 && y1 < 0) || (y0 >= bh && y1 >= bh)) return;
3025 const int inside = (x0 >= 0 && x0 < bw && x1 >= 0 && x1 < bw && y0 >= 0 && y0 < bh && y1 >= 0 && y1 < bh);
3026
3027 for(int i = 0; i < l; i++)
3028 {
3029 // position
3030 const int x = (int)((float)i * lx * inv_l) + p0[0];
3031 const int y = (int)((float)i * ly * inv_l) + p0[1];
3032 const float op = 1.0f - (float)i * inv_l;
3033 if(!inside && (x < 0 || x >= bw || y < 0 || y >= bh)) continue;
3034 float *buf = buffer + (size_t)y * bw + x;
3035 if(inside)
3036 buf[0] = MAX(buf[0], op);
3037 else if(x >= 0 && x < bw && y >= 0 && y < bh)
3038 buf[0] = MAX(buf[0], op);
3039 if(x + dx >= 0 && x + dx < bw && y >= 0 && y < bh)
3040 buf[dx] = MAX(buf[dx], op); // this one is to avoid gap due to int rounding
3041 if(x >= 0 && x < bw && y + dy >= 0 && y + dy < bh)
3042 buf[dpy] = MAX(buf[dpy], op); // this one is to avoid gap due to int rounding
3043 }
3044}
3045
3046// build a stamp which can be combined with other shapes in the same group
3047// prerequisite: 'buffer' is all zeros
3048static int _polygon_get_mask_roi(const dt_iop_module_t *const module, dt_dev_pixelpipe_t *pipe,
3049 const dt_dev_pixelpipe_iop_t *const piece,
3050 dt_masks_form_t *const mask_form,
3051 const dt_iop_roi_t *roi, float *buffer)
3052{
3053 if(IS_NULL_PTR(module)) return 1;
3054 double start = 0.0;
3055 double start2 = 0.0;
3057
3058 const int px = roi->x;
3059 const int py = roi->y;
3060 const int width = roi->width;
3061 const int height = roi->height;
3062 const float scale = roi->scale;
3063 const gboolean sparse = (dt_dev_pixelpipe_has_preview_output(piece->module->dev, pipe, roi)
3064 || pipe->type == DT_DEV_PIXELPIPE_THUMBNAIL);
3065 const int sparse_factor = sparse ? 4 : 1;
3066
3067 // we need to take care of four different cases:
3068 // 1) polygon and feather are outside of roi
3069 // 2) polygon is outside of roi, feather reaches into roi
3070 // 3) roi lies completely within polygon
3071 // 4) all other situations :)
3072 int polygon_in_roi = 0;
3073 int feather_in_roi = 0;
3074 int polygon_encircles_roi = 0;
3075
3076 // we get buffers for all points
3077 float *points = NULL, *border = NULL;
3078 int points_count = 0, border_count = 0;
3079 if(_polygon_get_pts_border(module->dev, mask_form, module->iop_order,
3081 &points, &points_count, &border, &border_count, FALSE) != 0)
3082 {
3085 return 1;
3086 }
3087 if(points_count <= 2)
3088 {
3091 return 0;
3092 }
3093
3095 {
3096 dt_print(DT_DEBUG_MASKS, "[masks %s] polygon points took %0.04f sec\n",
3097 mask_form->name, dt_get_wtime() - start);
3098 start = start2 = dt_get_wtime();
3099 }
3100
3101 const guint corner_count = g_list_length(mask_form->points);
3102
3103 // we shift and scale down polygon and border
3104 for(int i = corner_count * 3; i < border_count; i++)
3105 {
3106 const float xx = border[2 * i];
3107 const float yy = border[2 * i + 1];
3108 if(isnan(xx))
3109 {
3110 if(isnan(yy)) break; // that means we have to skip the end of the border polygon
3111 i = yy - 1;
3112 continue;
3113 }
3114 border[2 * i] = xx * scale - px;
3115 border[2 * i + 1] = yy * scale - py;
3116 }
3117 for(int i = corner_count * 3; i < points_count; i++)
3118 {
3119 const float xx = points[2 * i];
3120 const float yy = points[2 * i + 1];
3121 points[2 * i] = xx * scale - px;
3122 points[2 * i + 1] = yy * scale - py;
3123 }
3124
3125 // now check if polygon is at least partially within roi
3126 for(int i = corner_count * 3; i < points_count; i++)
3127 {
3128 const int xx = points[i * 2];
3129 const int yy = points[i * 2 + 1];
3130
3131 if(xx > 1 && yy > 1 && xx < width - 2 && yy < height - 2)
3132 {
3133 polygon_in_roi = 1;
3134 break;
3135 }
3136 }
3137
3138 // if not this still might mean that polygon fully encircles roi -> we need to check that
3139 if(!polygon_in_roi)
3140 {
3141 int crossing_count = 0;
3142 int last_y = -9999;
3143 const int x = width / 2;
3144 const int y = height / 2;
3145
3146 for(int i = corner_count * 3; i < points_count; i++)
3147 {
3148 const int yy = (int)points[2 * i + 1];
3149 if(yy != last_y && yy == y)
3150 {
3151 if(points[2 * i] > x) crossing_count++;
3152 }
3153 last_y = yy;
3154 }
3155 // if there is an uneven number of intersection points roi lies within polygon
3156 if(crossing_count & 1)
3157 {
3158 polygon_in_roi = 1;
3159 polygon_encircles_roi = 1;
3160 }
3161 }
3162
3163 // now check if feather is at least partially within roi
3164 for(int i = corner_count * 3; i < border_count; i++)
3165 {
3166 const float xx = border[i * 2];
3167 const float yy = border[i * 2 + 1];
3168 if(isnan(xx))
3169 {
3170 if(isnan(yy)) break; // that means we have to skip the end of the border polygon
3171 i = yy - 1;
3172 continue;
3173 }
3174 if(xx > 1 && yy > 1 && xx < width - 2 && yy < height - 2)
3175 {
3176 feather_in_roi = 1;
3177 break;
3178 }
3179 }
3180
3181 // if polygon and feather completely lie outside of roi -> we're done/mask remains empty
3182 if(!polygon_in_roi && !feather_in_roi)
3183 {
3186 return 0;
3187 }
3188
3189 // now get min/max values
3190 float xmin, xmax, ymin, ymax;
3191 _polygon_bounding_box_raw(points, border, corner_count, points_count, border_count,
3192 &xmin, &xmax, &ymin, &ymax);
3193
3195 {
3196 dt_print(DT_DEBUG_MASKS, "[masks %s] polygon_fill min max took %0.04f sec\n", mask_form->name,
3197 dt_get_wtime() - start2);
3198 start2 = dt_get_wtime();
3199 }
3200
3202 {
3203 dt_print(DT_DEBUG_MASKS, "[masks %s] polygon_fill clear mask took %0.04f sec\n", mask_form->name,
3204 dt_get_wtime() - start2);
3205 start2 = dt_get_wtime();
3206 }
3207
3208 // deal with polygon if it does not lie outside of roi
3209 if(polygon_in_roi)
3210 {
3211 // second copy of polygon which we can modify when cropping to roi
3212 float *cpoints = dt_pixelpipe_cache_alloc_align_float_cache((size_t)2 * points_count, 0);
3213 if(IS_NULL_PTR(cpoints))
3214 {
3217 return 1;
3218 }
3219 memcpy(cpoints, points, sizeof(float) * 2 * points_count);
3220
3221 // now we clip cpoints to roi -> catch special case when roi lies completely within polygon.
3222 // dirty trick: we allow polygon to extend one pixel beyond height-1. this avoids need of special handling
3223 // of the last roi line in the following edge-flag polygon fill algorithm.
3224 const int crop_success = _polygon_crop_to_roi(cpoints + 2 * (corner_count * 3),
3225 points_count - corner_count * 3, 0,
3226 width - 1, 0, height);
3227 polygon_encircles_roi = polygon_encircles_roi || !crop_success;
3228
3230 {
3231 dt_print(DT_DEBUG_MASKS, "[masks %s] polygon_fill crop to roi took %0.04f sec\n", mask_form->name,
3232 dt_get_wtime() - start2);
3233 start2 = dt_get_wtime();
3234 }
3235
3236 if(polygon_encircles_roi)
3237 {
3238 // roi lies completely within polygon
3239 for(size_t k = 0; k < (size_t)width * height; k++) buffer[k] = 1.0f;
3240 }
3241 else
3242 {
3243 // all other cases
3244
3245 // edge-flag polygon fill: we write all the point around the polygon into the buffer
3246 float xlast = cpoints[(points_count - 1) * 2];
3247 float ylast = cpoints[(points_count - 1) * 2 + 1];
3248
3249 for(int i = corner_count * 3; i < points_count; i++)
3250 {
3251 float xstart = xlast;
3252 float ystart = ylast;
3253
3254 float xend = xlast = cpoints[i * 2];
3255 float yend = ylast = cpoints[i * 2 + 1];
3256
3257 if(ystart > yend)
3258 {
3259 float tmp;
3260 tmp = ystart, ystart = yend, yend = tmp;
3261 tmp = xstart, xstart = xend, xend = tmp;
3262 }
3263
3264 const float m = (xstart - xend) / (ystart - yend); // we don't need special handling of ystart==yend
3265 // as following loop will take care
3266
3267 for(int yy = (int)ceilf(ystart); (float)yy < yend;
3268 yy++) // this would normally never touch the last roi line => see comment further above
3269 {
3270 const float xcross = xstart + m * (yy - ystart);
3271
3272 int xx = floorf(xcross);
3273 if((float)xx + 0.5f <= xcross) xx++;
3274
3275 if(xx < 0 || xx >= width || yy < 0 || yy >= height)
3276 continue; // sanity check just to be on the safe side
3277
3278 const size_t index = (size_t)yy * width + xx;
3279
3280 buffer[index] = 1.0f - buffer[index];
3281 }
3282 }
3283
3285 {
3286 dt_print(DT_DEBUG_MASKS, "[masks %s] polygon_fill draw polygon took %0.04f sec\n", mask_form->name,
3287 dt_get_wtime() - start2);
3288 start2 = dt_get_wtime();
3289 }
3290
3291 // we fill the inside plain
3292 // we don't need to deal with parts of shape outside of roi
3293 const int xxmin = MAX(xmin, 0);
3294 const int xxmax = MIN(xmax, width - 1);
3295 const int yymin = MAX(ymin, 0);
3296 const int yymax = MIN(ymax, height - 1);
3297 __OMP_PARALLEL_FOR__(if((size_t)(yymax - yymin + 1) * (size_t)(xxmax - xxmin + 1) > 50000))
3298 for(int yy = yymin; yy <= yymax; yy++)
3299 {
3300 float *const restrict row = buffer + (size_t)yy * width;
3301 int state = 0;
3302 for(int xx = xxmin; xx <= xxmax; xx++)
3303 {
3304 const float v = row[xx];
3305 if(v > 0.5f) state = !state;
3306 if(state) row[xx] = 1.0f;
3307 }
3308 }
3309
3311 {
3312 dt_print(DT_DEBUG_MASKS, "[masks %s] polygon_fill fill plain took %0.04f sec\n", mask_form->name,
3313 dt_get_wtime() - start2);
3314 start2 = dt_get_wtime();
3315 }
3316 }
3318 }
3319
3320 // deal with feather if it does not lie outside of roi
3321 if(!polygon_encircles_roi)
3322 {
3323 const int dpoints_capacity = 4 * border_count * (sparse ? sparse_factor : 1);
3324 int *dpoints = dt_pixelpipe_cache_alloc_align_cache(sizeof(int) * dpoints_capacity, 0);
3325 if(IS_NULL_PTR(dpoints))
3326 {
3329 return 1;
3330 }
3331
3332 int dindex = 0;
3333 int p0[2], p1[2];
3334 float pf1[2];
3335 int prev0[2] = { 0, 0 };
3336 int prev1[2] = { 0, 0 };
3337 gboolean have_prev = FALSE;
3338 int last0[2] = { -100, -100 };
3339 int last1[2] = { -100, -100 };
3340 int next_index = 0;
3341 for(int i = corner_count * 3; i < border_count; i++)
3342 {
3343 p0[0] = floorf(points[i * 2] + 0.5f);
3344 p0[1] = ceilf(points[i * 2 + 1]);
3345 if(next_index > 0)
3346 {
3347 p1[0] = pf1[0] = border[next_index * 2];
3348 p1[1] = pf1[1] = border[next_index * 2 + 1];
3349 }
3350 else
3351 {
3352 p1[0] = pf1[0] = border[i * 2];
3353 p1[1] = pf1[1] = border[i * 2 + 1];
3354 }
3355
3356 // now we check p1 value to know if we have to skip a part
3357 if(next_index == i) next_index = 0;
3358 while(isnan(pf1[0]))
3359 {
3360 if(isnan(pf1[1]))
3361 next_index = i - 1;
3362 else
3363 next_index = p1[1];
3364 p1[0] = pf1[0] = border[next_index * 2];
3365 p1[1] = pf1[1] = border[next_index * 2 + 1];
3366 }
3367
3368 const gboolean used_next = (next_index > 0);
3369
3370 if(sparse && have_prev && !used_next
3371 && (prev0[0] != p0[0] || prev0[1] != p0[1] || prev1[0] != p1[0] || prev1[1] != p1[1]))
3372 {
3373 for(int k = 1; k < sparse_factor; k++)
3374 {
3375 const float t = (float)k / (float)sparse_factor;
3376 const int mp0[2] = { (int)floorf(prev0[0] + t * (p0[0] - prev0[0]) + 0.5f),
3377 (int)floorf(prev0[1] + t * (p0[1] - prev0[1]) + 0.5f) };
3378 const int mp1[2] = { (int)floorf(prev1[0] + t * (p1[0] - prev1[0]) + 0.5f),
3379 (int)floorf(prev1[1] + t * (p1[1] - prev1[1]) + 0.5f) };
3380 if(dindex + 4 <= dpoints_capacity)
3381 {
3382 dpoints[dindex] = mp0[0];
3383 dpoints[dindex + 1] = mp0[1];
3384 dpoints[dindex + 2] = mp1[0];
3385 dpoints[dindex + 3] = mp1[1];
3386 dindex += 4;
3387 }
3388 }
3389 }
3390
3391 // and we draw the falloff
3392 if(last0[0] != p0[0] || last0[1] != p0[1] || last1[0] != p1[0] || last1[1] != p1[1])
3393 {
3394 dpoints[dindex] = p0[0];
3395 dpoints[dindex + 1] = p0[1];
3396 dpoints[dindex + 2] = p1[0];
3397 dpoints[dindex + 3] = p1[1];
3398 dindex += 4;
3399
3400 last0[0] = p0[0];
3401 last0[1] = p0[1];
3402 last1[0] = p1[0];
3403 last1[1] = p1[1];
3404 }
3405
3406 if(!used_next)
3407 {
3408 prev0[0] = p0[0];
3409 prev0[1] = p0[1];
3410 prev1[0] = p1[0];
3411 prev1[1] = p1[1];
3412 have_prev = TRUE;
3413 }
3414 else
3415 {
3416 have_prev = FALSE;
3417 }
3418 }
3419 __OMP_PARALLEL_FOR__(if(dindex > 4096))
3420 for(int n = 0; n < dindex; n += 4)
3421 _polygon_falloff_roi(buffer, dpoints + n, dpoints + n + 2, width, height);
3422
3424
3426 {
3427 dt_print(DT_DEBUG_MASKS, "[masks %s] polygon_fill fill falloff took %0.04f sec\n",
3428 mask_form->name,
3429 dt_get_wtime() - start2);
3430 }
3431 }
3432
3435
3437 dt_print(DT_DEBUG_MASKS, "[masks %s] polygon fill buffer took %0.04f sec\n",
3438 mask_form->name,
3439 dt_get_wtime() - start);
3440
3441 return 0;
3442}
3443
3445{
3446 // nothing to do (yet?)
3447}
3448
3452static void _polygon_set_form_name(struct dt_masks_form_t *const mask_form, const size_t form_number)
3453{
3454 snprintf(mask_form->name, sizeof(mask_form->name), _("polygon #%d"), (int)form_number);
3455}
3456
3457static void _polygon_set_hint_message(const dt_masks_form_gui_t *const mask_gui,
3458 const dt_masks_form_t *const mask_form,
3459 const int opacity, char *const restrict msgbuf,
3460 const size_t msgbuf_len)
3461{
3462 const guint node_count = mask_form->points ? g_list_length(mask_form->points) : 0;
3463 if(mask_gui->creation && node_count < 4)
3464 g_strlcat(msgbuf, _("<b>Add node</b>: click, <b>Add sharp node</b>:ctrl+click\n"
3465 "<b>Cancel</b>: right-click or Esc"), msgbuf_len);
3466 else if(mask_gui->creation)
3467 g_strlcat(msgbuf, _("<b>Add node</b>: click, <b>Add sharp node</b>:ctrl+click\n"
3468 "<b>Finish polygon</b>: Enter or click on first node"), msgbuf_len);
3469 else if(mask_gui->handle_selected)
3470 g_strlcat(msgbuf, _("<b>Node curvature</b>: drag\n<b>Reset curvature</b>: right-click"), msgbuf_len);
3471 else if(mask_gui->node_selected)
3472 g_strlcat(msgbuf, _("<b>NODE:</b> <b>Move</b>: drag, <b>Delete</b>: right-click or Del\n"
3473 "<b>Hardness</b>: scroll, <b>Switch smooth/sharp</b>: ctrl+click"), msgbuf_len);
3474 else if(mask_gui->node_hovered >= 0)
3475 g_strlcat(msgbuf, _("<b>Move node</b>: drag\n<b>Delete node</b>: right-click\n"
3476 "<b>Hardness</b>: scroll, <b>Switch smooth/sharp</b>: ctrl+click"), msgbuf_len);
3477 else if(mask_gui->seg_selected)
3478 g_strlcat(msgbuf, _("<b>Move segment</b>: drag\n<b>Add node</b>: ctrl+click"), msgbuf_len);
3479 else if(mask_gui->form_selected)
3480 g_snprintf(msgbuf, msgbuf_len, _("<b>Size</b>: scroll, <b>Hardness</b>: shift+scroll\n"
3481 "<b>Opacity</b>: ctrl+scroll (%d%%)"), opacity);
3482}
3483
3484static void _polygon_duplicate_points(dt_develop_t *const dev, dt_masks_form_t *const base, dt_masks_form_t *const dest)
3485{
3486 // unused arg, keep compiler from complaining
3488}
3489
3490static void _polygon_initial_source_pos(const float iwd, const float iht, float *x, float *y)
3491{
3492
3493
3494 float offset[2] = { 0.1f, 0.1f };
3496 *x = offset[0];
3497 *y = offset[1];
3498}
3499
3500static void _polygon_creation_closing_form_callback(GtkWidget *widget, gpointer user_data)
3501{
3502 dt_masks_form_gui_t *mask_gui = (dt_masks_form_gui_t *)user_data;
3503 // This is a temp form on creation mode
3505 if(IS_NULL_PTR(mask_form)) return;
3506
3507 _polygon_creation_closing_form(mask_form, mask_gui);
3508}
3509
3510static void _polygon_switch_node_callback(GtkWidget *widget, gpointer user_data)
3511{
3512 dt_masks_form_gui_t *mask_gui = (dt_masks_form_gui_t *)user_data;
3513 if(IS_NULL_PTR(mask_gui)) return;
3514 dt_iop_module_t *module = darktable.develop->gui_module;
3515 if(IS_NULL_PTR(module)) return;
3516 const int form_id = mask_gui->formid;
3517 dt_masks_form_t *selected_form = dt_masks_get_from_id(darktable.develop, form_id);
3518 if(IS_NULL_PTR(selected_form)) return;
3519
3520 mask_gui->node_selected = TRUE;
3521 mask_gui->node_selected_idx = mask_gui->node_hovered;
3522 dt_masks_form_gui_points_t *gui_points
3523 = (dt_masks_form_gui_points_t *)g_list_nth_data(mask_gui->points, mask_gui->group_selected);
3524 const int node_index = dt_masks_gui_selected_node_index(mask_gui);
3526 = (dt_masks_node_polygon_t *)g_list_nth_data(selected_form->points, node_index);
3527 if(IS_NULL_PTR(gui_points) || IS_NULL_PTR(node)) return;
3528 dt_masks_toggle_bezier_node_type(module, selected_form, mask_gui, mask_gui->group_selected, gui_points,
3529 node_index, node->node, node->ctrl1, node->ctrl2, &node->state);
3530}
3531
3532static void _polygon_reset_round_node_callback(GtkWidget *widget, gpointer user_data)
3533{
3534 dt_masks_form_gui_t *mask_gui = (dt_masks_form_gui_t *)user_data;
3535 if(IS_NULL_PTR(mask_gui)) return;
3536 dt_iop_module_t *module = darktable.develop->gui_module;
3537 if(IS_NULL_PTR(module)) return;
3538 const int form_id = mask_gui->formid;
3539 dt_masks_form_t *selected_form = dt_masks_get_from_id(darktable.develop, form_id);
3540 if(IS_NULL_PTR(selected_form)) return;
3541
3542 mask_gui->node_selected = TRUE;
3543 mask_gui->node_selected_idx = mask_gui->node_hovered;
3544 dt_masks_form_gui_points_t *gui_points
3545 = (dt_masks_form_gui_points_t *)g_list_nth_data(mask_gui->points, mask_gui->group_selected);
3546 const int selected_handle = dt_masks_gui_selected_handle_index(mask_gui);
3547 const int node_index = MAX(mask_gui->node_hovered, selected_handle);
3549 = (dt_masks_node_polygon_t *)g_list_nth_data(selected_form->points, node_index);
3550 if(IS_NULL_PTR(gui_points) || IS_NULL_PTR(node)) return;
3551 if(dt_masks_reset_bezier_ctrl_points(module, selected_form, mask_gui, mask_gui->group_selected, gui_points,
3552 node_index, &node->state))
3553 gui_points->clockwise = _polygon_is_clockwise(selected_form);
3554}
3555
3556static void _polygon_add_node_callback(GtkWidget *menu, gpointer user_data)
3557{
3558 dt_masks_form_gui_t *mask_gui = (dt_masks_form_gui_t *)user_data;
3559 if(IS_NULL_PTR(mask_gui)) return;
3561 if(IS_NULL_PTR(visible_forms)) return;
3562
3563 dt_iop_module_t *module = darktable.develop->gui_module;
3564 if(IS_NULL_PTR(module)) return;
3565
3566 dt_masks_form_group_t *group_entry = dt_masks_form_get_selected_group(visible_forms, mask_gui);
3567 if(IS_NULL_PTR(group_entry)) return;
3568 dt_masks_form_t *selected_form = dt_masks_get_from_id(darktable.develop, group_entry->formid);
3569
3570 if(selected_form)
3571 {
3572 _add_node_to_segment(module, selected_form, group_entry->parentid, mask_gui, mask_gui->group_selected);
3573 }
3574
3575 //dt_dev_add_history_item(darktable.develop, module, TRUE, TRUE);
3576}
3577
3579 struct dt_masks_form_gui_t *mask_gui,
3580 const float pzx, const float pzy)
3581{
3582
3583
3584 GtkWidget *menu_item = NULL;
3585 gchar *accel = g_strdup_printf(_("%s+Click"), gtk_accelerator_get_label(0, GDK_CONTROL_MASK));
3586
3587 gboolean ret = FALSE;
3588
3589 if(mask_gui->creation)
3590 {
3591 menu_item = ctx_gtk_menu_item_new_with_markup(_("Close path"), menu,
3593 gtk_widget_set_sensitive(menu_item, mask_form->points && !g_list_shorter_than(mask_form->points, 4));
3594 menu_item_set_fake_accel(menu_item, GDK_KEY_Return, 0);
3595
3596 menu_item = ctx_gtk_menu_item_new_with_markup(_("Remove last point"), menu,
3598 menu_item_set_fake_accel(menu_item, GDK_KEY_BackSpace, 0);
3599
3600 ret = TRUE;
3601 }
3602
3603 else if(mask_gui->node_hovered >= 0)
3604 {
3605 dt_masks_form_gui_points_t *gui_points
3606 = (dt_masks_form_gui_points_t *)g_list_nth_data(mask_gui->points, mask_gui->group_selected);
3607 if(IS_NULL_PTR(gui_points)) goto end;
3608 dt_masks_node_polygon_t *node = (dt_masks_node_polygon_t *)g_list_nth_data(mask_form->points, mask_gui->node_hovered);
3609 if(IS_NULL_PTR(node)) goto end;
3610 const gboolean is_corner = dt_masks_node_is_cusp(gui_points, mask_gui->node_hovered);
3611
3612 {
3613 gchar *to_change_type = g_strdup_printf(_("Switch to %s node"), (is_corner) ? _("round") : _("cusp"));
3614 const dt_menu_icon_t icon = is_corner ? DT_MENU_ICON_CIRCLE : DT_MENU_ICON_SQUARE;
3615 menu_item = ctx_gtk_menu_item_new_with_icon_and_shortcut(to_change_type, accel, menu,
3616 _polygon_switch_node_callback, mask_gui, icon);
3617
3618 dt_free(to_change_type);
3619 }
3620
3621 {
3622 menu_item = ctx_gtk_menu_item_new_with_markup(_("Reset round node"), menu,
3624 gtk_widget_set_sensitive(menu_item, !is_corner);
3625 }
3626
3627 ret = TRUE;
3628 }
3629
3630 if(mask_gui->seg_selected)
3631 {
3632 menu_item = ctx_gtk_menu_item_new_with_markup_and_shortcut(_("Add a node here"), accel,
3633 menu, _polygon_add_node_callback, mask_gui);
3634 ret = TRUE;
3635 }
3636
3637 end:
3638 dt_free(accel);
3639 return ret;
3640}
3641
3642// The function table for polygons. This must be public, i.e. no "static" keyword.
3645 .sanitize_config = _polygon_sanitize_config,
3646 .set_form_name = _polygon_set_form_name,
3647 .set_hint_message = _polygon_set_hint_message,
3648 .duplicate_points = _polygon_duplicate_points,
3649 .initial_source_pos = _polygon_initial_source_pos,
3650 .get_distance = _polygon_get_distance,
3651 .get_points_border = _polygon_get_points_border,
3652 .get_mask = _polygon_get_mask,
3653 .get_mask_roi = _polygon_get_mask_roi,
3654 .get_area = _polygon_get_area,
3655 .get_source_area = _polygon_get_source_area,
3656 .get_gravity_center = _polygon_get_gravity_center,
3657 .get_interaction_value = _polygon_get_interaction_value,
3658 .set_interaction_value = _polygon_set_interaction_value,
3659 .update_hover = _find_closest_handle,
3660 .mouse_moved = _polygon_events_mouse_moved,
3661 .mouse_scrolled = _polygon_events_mouse_scrolled,
3662 .button_pressed = _polygon_events_button_pressed,
3663 .button_released = _polygon_events_button_released,
3664 .key_pressed = _polygon_events_key_pressed,
3665 .post_expose = _polygon_events_post_expose,
3666 .draw_shape = _polygon_draw_shape,
3667 .init_ctrl_points = _polygon_init_ctrl_points,
3668 .populate_context_menu = _polygon_populate_context_menu
3669};
3670
3671
3672// clang-format off
3673// modelines: These editor modelines have been set for all relevant files by tools/update_modelines.py
3674// vim: shiftwidth=2 expandtab tabstop=2 cindent
3675// kate: tab-indents: off; indent-width 2; replace-tabs on; indent-mode cstyle; remove-trailing-spaces modified;
3676// clang-format on
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:278
int width
Definition bilateral.h:1
int height
Definition bilateral.h:1
static const dt_aligned_pixel_simd_t const dt_adaptation_t const float p
static const float const float const float min
const float max
static const int row
const float delta
char * key
int type
float dt_conf_get_float(const char *name)
void dt_toast_log(const char *msg,...)
Definition control.c:808
darktable_t darktable
Definition darktable.c:181
void dt_print(dt_debug_thread_t thread, const char *msg,...)
Definition darktable.c:1542
#define dt_free_align(ptr)
Definition darktable.h:481
@ DT_DEBUG_PERF
Definition darktable.h:719
@ DT_DEBUG_MASKS
Definition darktable.h:727
#define dt_pixelpipe_cache_alloc_align_float_cache(pixels, id)
Definition darktable.h:447
static float * dt_alloc_align_float(size_t pixels)
Definition darktable.h:494
#define dt_pixelpipe_cache_alloc_align_cache(size, id)
Definition darktable.h:433
static const GList * g_list_next_wraparound(const GList *list, const GList *head)
Definition darktable.h:957
#define dt_free(ptr)
Definition darktable.h:456
#define dt_pixelpipe_cache_free_align(mem)
Definition darktable.h:453
static GList * g_list_next_bounded(GList *list)
Definition darktable.h:952
#define __OMP_PARALLEL_FOR__(...)
Definition darktable.h:258
static const dt_aligned_pixel_simd_t value
Definition darktable.h:577
static double dt_get_wtime(void)
Definition darktable.h:914
#define __OMP_PARALLEL_FOR_SIMD__(...)
Definition darktable.h:259
static gboolean dt_modifier_is(const GdkModifierType state, const GdkModifierType desired_modifier_mask)
Definition darktable.h:893
#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 darktable.h:281
static gboolean g_list_shorter_than(const GList *list, unsigned len)
Definition darktable.h:939
#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:1159
void dt_dev_coordinates_raw_norm_to_raw_abs(dt_develop_t *dev, float *points, size_t num_points)
Definition develop.c:1109
int dt_dev_distort_transform_plus(const dt_dev_pixelpipe_t *pipe, const double iop_order, const int transf_direction, float *points, size_t points_count)
Definition develop.c:1557
gboolean dt_dev_pixelpipe_has_preview_output(const dt_develop_t *dev, const dt_dev_pixelpipe_t *pipe, const dt_iop_roi_t *roi)
Definition develop.c:367
void dt_dev_coordinates_image_abs_to_raw_norm(dt_develop_t *dev, float *points, size_t num_points)
Definition develop.c:1138
@ DT_DEV_TRANSFORM_DIR_BACK_EXCL
Definition develop.h:106
@ DT_DEV_TRANSFORM_DIR_BACK_INCL
Definition develop.h:105
@ DT_DEV_TRANSFORM_DIR_FORW_INCL
Definition develop.h:103
@ DT_DEV_TRANSFORM_DIR_ALL
Definition develop.h:102
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:648
@ DT_MASKS_DASH_STICK
Definition draw.h:94
static void dt_draw_shape_lines(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)
Draw the lines of a mask shape.
Definition draw.h:734
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:597
static guint dt_keys_mainpad_alternatives(const guint key_val)
Remap keypad keys to usual mainpad ones.
Definition gdkkeys.h:113
#define DT_GUI_MOUSE_EFFECT_RADIUS
Definition gtk.h:70
static const float x
const int t
const float v
float *const restrict const size_t k
static void dt_masks_gui_delta_to_image_abs(const dt_masks_form_gui_t *gui, float point[2])
Definition masks.h:618
static int dt_masks_gui_selected_segment_index(const dt_masks_form_gui_t *gui)
Definition masks.h:549
static int dt_masks_gui_selected_node_index(const dt_masks_form_gui_t *gui)
Definition masks.h:534
static int dt_masks_gui_selected_handle_index(const dt_masks_form_gui_t *gui)
Definition masks.h:539
static float * dt_masks_dynbuf_buffer(dt_masks_dynbuf_t *a)
Definition masks.h:1333
static 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.h:682
void dt_masks_gui_form_create(dt_masks_form_t *form, dt_masks_form_gui_t *gui, int index, struct dt_iop_module_t *module)
static void dt_masks_dynbuf_add_2(dt_masks_dynbuf_t *a, float value1, float value2)
Definition masks.h:1260
@ DT_MASKS_EDIT_FULL
Definition masks.h:202
gboolean dt_masks_node_is_cusp(const dt_masks_form_gui_points_t *gpt, const int index)
returns wether a node is a corner or not. A node is a corner if its 2 control handles are at the same...
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.h:1111
int dt_masks_form_change_opacity(dt_masks_form_t *form, int parentid, int up, const int flow)
int dt_masks_point_in_form_exact(const float *pts, int num_pts, const float *points, int points_start, int points_count)
Check whether any 2D point in pts[] lies inside the form points[].
static dt_masks_dynbuf_t * dt_masks_dynbuf_init(size_t size, const char *tag)
Definition masks.h:1240
static void dt_masks_translate_source(dt_masks_form_t *form, const float delta_x, const float delta_y)
Definition masks.h:652
static float dt_masks_dynbuf_get(dt_masks_dynbuf_t *a, int offset)
Definition masks.h:1315
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:658
gboolean dt_masks_is_anything_selected(const dt_masks_form_gui_t *mask_gui)
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.
static float dt_masks_get_form_size_from_nodes(const GList *points)
Definition masks.h:563
gboolean dt_masks_gui_form_create_throttled(dt_masks_form_t *form, dt_masks_form_gui_t *gui, int index, struct dt_iop_module_t *module, float posx, float posy)
static float * dt_masks_dynbuf_reserve_n(dt_masks_dynbuf_t *a, const int n)
Definition masks.h:1275
void dt_masks_gui_form_save_creation(dt_develop_t *dev, struct dt_iop_module_t *module, dt_masks_form_t *form, dt_masks_form_gui_t *gui)
Save the form creation right after a shape has been finished drawing.
static int dt_masks_gui_selected_handle_border_index(const dt_masks_form_gui_t *gui)
Definition masks.h:544
float dt_masks_get_set_conf_value(dt_masks_form_t *form, char *feature, float new_value, float v_min, float v_max, dt_masks_increment_t increment, const int flow)
Change a numerical property of a mask shape, either by in/de-crementing the current value or setting ...
gboolean dt_masks_form_exit_creation(dt_iop_module_t *module, dt_masks_form_gui_t *gui)
void dt_masks_draw_source(cairo_t *cr, dt_masks_form_gui_t *gui, const int index, const int nb, 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.
dt_masks_type_t
Definition masks.h:129
@ DT_MASKS_CLONE
Definition masks.h:134
@ DT_MASKS_GROUP
Definition masks.h:133
@ DT_MASKS_IS_RETOUCHE
Definition masks.h:146
dt_masks_interaction_t
Definition masks.h:302
@ DT_MASKS_INTERACTION_HARDNESS
Definition masks.h:305
@ DT_MASKS_INTERACTION_SIZE
Definition masks.h:304
#define menu_item_set_fake_accel(menu_item, keyval, mods)
Definition masks.h:1522
void _masks_gui_delete_node_callback(GtkWidget *menu, gpointer user_data)
Definition masks_gui.c:488
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.h:603
dt_masks_form_group_t * dt_masks_form_get_selected_group(const struct dt_masks_form_t *form, const struct dt_masks_form_gui_t *gui)
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.h:757
static gboolean dt_masks_center_of_gravity_from_points(const float *points, const int points_count, float center[2], float *area)
Definition masks.h:1339
static size_t dt_masks_dynbuf_position(dt_masks_dynbuf_t *a)
Definition masks.h:1410
gboolean dt_masks_gui_is_dragging(const dt_masks_form_gui_t *gui)
static gboolean dt_masks_gui_change_affects_selected_node_or_all(const dt_masks_form_gui_t *gui, const int index)
Definition masks.h:554
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.h:626
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.
static void dt_masks_dynbuf_add_zeros(dt_masks_dynbuf_t *a, const int n)
Definition masks.h:1295
dt_masks_increment_t
Definition masks.h:193
@ DT_MASKS_INCREMENT_SCALE
Definition masks.h:195
@ DT_MASKS_INCREMENT_OFFSET
Definition masks.h:196
@ DT_MASKS_INCREMENT_ABSOLUTE
Definition masks.h:194
dt_masks_form_t * dt_masks_get_from_id(dt_develop_t *dev, int id)
@ DT_MASKS_POINT_STATE_NORMAL
Definition masks.h:182
@ DT_MASKS_POINT_STATE_USER
Definition masks.h:183
void dt_masks_set_source_pos_initial_value(dt_masks_form_gui_t *gui, dt_masks_form_t *form)
Initialize the clone source position based on current GUI state.
static void dt_masks_project_on_line(const float cursor[2], const float node[2], const float handle[2], float point[2])
Definition masks.h:732
static float * dt_masks_dynbuf_harvest(dt_masks_dynbuf_t *a)
Definition masks.h:1422
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)
Shared selection logic for node/handle/segment hit testing.
dt_masks_form_t * dt_masks_get_visible_form(const struct dt_develop_t *dev)
void dt_masks_remove_node(struct dt_iop_module_t *module, dt_masks_form_t *form, int parentid, dt_masks_form_gui_t *gui, int index, int node_index)
static void dt_masks_dynbuf_set(dt_masks_dynbuf_t *a, int offset, float value)
Definition masks.h:1324
static 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.h:710
void dt_masks_draw_path_seg_by_seg(cairo_t *cr, dt_masks_form_gui_t *gui, const int index, const float *points, const int points_count, const int node_count, const float zoom_scale)
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.h:611
void dt_masks_set_source_pos_initial_state(dt_masks_form_gui_t *gui, const uint32_t state)
Decide initial source positioning mode for clone masks.
static void dt_masks_dynbuf_reset(dt_masks_dynbuf_t *a)
Definition masks.h:1416
static void dt_masks_dynbuf_free(dt_masks_dynbuf_t *a)
Definition masks.h:1432
static void dt_masks_set_ctrl_points(float ctrl1[2], float ctrl2[2], const float control_points[4])
Definition masks.h:669
#define CLAMPF(a, mn, mx)
Definition math.h:89
#define M_PI
Definition math.h:45
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:130
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:173
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:188
dt_menu_icon_t
Definition menu.h:30
@ DT_MENU_ICON_CIRCLE
Definition menu.h:32
@ DT_MENU_ICON_SQUARE
Definition menu.h:33
size_t size
Definition mipmap_cache.c:3
@ DT_DEV_PIXELPIPE_THUMBNAIL
Definition pixelpipe.h:41
static gboolean _polygon_get_gravity_center(const dt_masks_form_t *mask_form, float center[2], float *area)
Definition polygon.c:1244
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:3578
static int _find_closest_handle(dt_masks_form_t *mask_form, dt_masks_form_gui_t *mask_gui, int form_index)
Definition polygon.c:1451
#define HARDNESS_MIN
Definition polygon.c:55
#define POLYGON_MAX_SELF_INTERSECTIONS(nb_nodes)
Definition polygon.c:473
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:151
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:2352
static float _polygon_get_interaction_value(const dt_masks_form_t *mask_form, dt_masks_interaction_t interaction)
Definition polygon.c:1211
static int _polygon_fill_gaps(int last_x, int last_y, int target_x, int target_y, dt_masks_dynbuf_t *points)
Fill gaps between two points with an integer Bresenham line.
Definition polygon.c:278
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:128
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:2309
static void _polygon_points_recurs_border_gaps(float *center_max, float *border_min, float *border_min2, float *border_max, dt_masks_dynbuf_t *draw_points, dt_masks_dynbuf_t *draw_border, gboolean clockwise)
Fill gaps between border points with a circular arc.
Definition polygon.c:337
static int _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)
Definition polygon.c:3048
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:178
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:1077
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:1691
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:1418
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:2161
static void _polygon_switch_node_callback(GtkWidget *widget, gpointer user_data)
Definition polygon.c:3510
static gboolean _is_within_pxl_threshold(float *min, float *max, int pixel_threshold)
Definition polygon.c:399
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:1549
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:1277
#define HARDNESS_MAX
Definition polygon.c:56
static int _polygon_get_pts_border(dt_develop_t *develop, dt_masks_form_t *mask_form, const double iop_order, const int transform_direction, dt_dev_pixelpipe_t *pipe, float **point_buffer, int *point_count, float **border_buffer, int *border_count, gboolean source)
Build point and border buffers for a polygon mask.
Definition polygon.c:689
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:1499
static void _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 *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:88
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:1039
static void _polygon_falloff_roi(float *buffer, int *p0, int *p1, int bw, int bh)
Definition polygon.c:3009
static int _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:2403
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:3452
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:1959
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:1714
static int _change_hardness(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 hardness for the active node scope or the full shape.
Definition polygon.c:1618
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)
Recursive subdivision to sample polygon and border points.
Definition polygon.c:411
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/hardness/opacity.
Definition polygon.c:1660
static void _polygon_initial_source_pos(const float iwd, const float iht, float *x, float *y)
Definition polygon.c:3490
const dt_masks_functions_t dt_masks_functions_polygon
Definition polygon.c:3643
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:1152
static void _polygon_add_node_callback(GtkWidget *menu, gpointer user_data)
Definition polygon.c:3556
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:1430
static int _polygon_crop_to_roi(float *polygon, const int point_count, float xmin, float xmax, float ymin, float ymax)
Definition polygon.c:2714
static void _polygon_translate_node(dt_masks_node_polygon_t *node, const float delta_x, const float delta_y)
Definition polygon.c:1129
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:1892
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:2414
static void _polygon_creation_closing_form_callback(GtkWidget *widget, gpointer user_data)
Definition polygon.c:3500
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, int *inside_source, float *dist, void *user_data)
Polygon-specific inside/border/segment hit testing.
Definition polygon.c:1442
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:1907
static void _polygon_translate_all_nodes(dt_masks_form_t *mask_form, const float delta_x, const float delta_y)
Definition polygon.c:1134
static int _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:2396
static int _polygon_find_self_intersection(dt_masks_dynbuf_t *intersections, int node_count, float *border_points, int border_point_count, int *intersection_count_out)
Find all self-intersection segments in a polygon border.
Definition polygon.c:478
static void _polygon_set_hint_message(const dt_masks_form_gui_t *const mask_gui, const dt_masks_form_t *const mask_form, const int opacity, char *const restrict msgbuf, const size_t msgbuf_len)
Definition polygon.c:3457
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:3484
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:194
static int _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:2439
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, int *inside_source, float *dist)
Compute proximity between a point and the polygon GUI shape.
Definition polygon.c:1301
static void _polygon_reset_round_node_callback(GtkWidget *widget, gpointer user_data)
Definition polygon.c:3532
static int _init_hardness(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 hardness from config and emit the toast with a size-normalized percentage.
Definition polygon.c:1533
static void _polygon_draw_shape(cairo_t *cr, const float *point_buffer, const int point_count, const int node_count, const gboolean draw_border, const gboolean draw_source)
Draw a polygon or border polyline, skipping NaN points.
Definition polygon.c:2130
static int _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, int source, const dt_iop_module_t *module)
Definition polygon.c:1140
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:2366
static void _polygon_sanitize_config(dt_masks_type_t type)
Definition polygon.c:3444
static gboolean _polygon_is_clockwise(dt_masks_form_t *mask_form)
Determine polygon winding order.
Definition polygon.c:253
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:70
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:1464
struct _GtkWidget GtkWidget
Definition splash.h:29
const float uint32_t state[4]
const float r
int32_t unmuted
Definition darktable.h:760
struct dt_develop_t * develop
Definition darktable.h:770
dt_dev_pixelpipe_type_t type
int32_t raw_height
Definition develop.h:228
int32_t raw_width
Definition develop.h:228
struct dt_dev_pixelpipe_t * virtual_pipe
Definition develop.h:251
struct dt_develop_t::@17 roi
struct dt_develop_t * dev
Definition imageop.h:296
Region of interest passed through the pixelpipe.
Definition imageop.h:72
double scale
Definition imageop.h:74
gboolean node_selected
Definition masks.h:470
gboolean source_selected
Definition masks.h:478
int handle_border_dragging
Definition masks.h:493
int handle_border_hovered
Definition masks.h:468
gboolean source_dragging
Definition masks.h:486
dt_masks_edit_mode_t edit_mode
Definition masks.h:463
dt_iop_module_t * creation_module
Definition masks.h:505
gboolean creation_closing_form
Definition masks.h:504
gboolean seg_selected
Definition masks.h:472
gboolean form_dragging
Definition masks.h:485
gboolean creation
Definition masks.h:503
dt_masks_type_t type
Definition masks.h:428
gboolean form_selected
Definition masks.h:476
gboolean handle_selected
Definition masks.h:471
gboolean border_selected
Definition masks.h:477
float delta[2]
Definition masks.h:455
dt_masks_type_t type
Definition masks.h:378
float source[2]
Definition masks.h:385
char name[128]
Definition masks.h:396
GList * points
Definition masks.h:377
void(* draw_shape)(cairo_t *cr, const float *points, const int points_count, const int nb, const gboolean border, const gboolean source)
Definition masks.h:368
struct dt_masks_gui_center_point_t::@34 main
struct dt_masks_gui_center_point_t::@35 source
dt_masks_points_states_t state
Definition masks.h:254
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