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 // sanity check: both indices must be valid border_point indices
638 if(cell_values[k] < node_count * 3 || cell_values[k] >= border_point_count
639 || i < node_count * 3 || i >= border_point_count)
640 continue;
641 if(intersection_count > 0)
642 {
643 const int inter_last0 = (int)dt_masks_dynbuf_get(intersections, -2);
644 const int inter_last1 = (int)dt_masks_dynbuf_get(intersections, -1);
645 if((cell_values[k] - i) * (inter_last0 - inter_last1) > 0
646 && inter_last0 >= cell_values[k] && inter_last1 <= i)
647 {
648 // we find an self-intersection portion which include the last one
649 // we just update it
650 dt_masks_dynbuf_set(intersections, -2, cell_values[k]);
651 dt_masks_dynbuf_set(intersections, -1, i);
652 }
653 else
654 {
655 // we find a new self-intersection portion
656 dt_masks_dynbuf_add_2(intersections, cell_values[k], i);
657 intersection_count++;
658 }
659 }
660 else
661 {
662 // we find a new self-intersection portion
663 dt_masks_dynbuf_add_2(intersections, cell_values[k], i);
664 intersection_count++;
665 }
666 }
667 }
668 else
669 {
670 // there wasn't anything "registered" at this place in binter
671 // we do it now
672 intersection_grid[idx] = i;
673 }
674 }
675 last_x = grid_x;
676 last_y = grid_y;
677 }
678 }
679
680 dt_masks_dynbuf_free(gap_points);
681 dt_pixelpipe_cache_free_align(intersection_grid);
682
683 // and we return the number of self-intersection found
684 *intersection_count_out = intersection_count;
685 return 0;
686}
687
694 const double iop_order, const int transform_direction,
695 dt_dev_pixelpipe_t *pipe, float **point_buffer, int *point_count,
696 float **border_buffer, int *border_count, gboolean source)
697{
698 *point_buffer = NULL;
699 *point_count = 0;
700 if(!IS_NULL_PTR(border_buffer)) *border_buffer = NULL;
701 if(!IS_NULL_PTR(border_buffer)) *border_count = 0;
702
703 if(IS_NULL_PTR(mask_form) || IS_NULL_PTR(mask_form->points)) return 0;
704
705 double start2 = 0.0;
707
708 const float input_width = pipe->iwidth;
709 const float input_height = pipe->iheight;
710 const int pixel_threshold = (dt_dev_pixelpipe_has_preview_output(darktable.develop, pipe, NULL)
711 || pipe->type == DT_DEV_PIXELPIPE_THUMBNAIL) ? 3 : 1;
712 const guint node_count = g_list_length(mask_form->points);
713
714 dt_masks_dynbuf_t *dpoints = NULL, *dborder = NULL, *intersections = NULL;
715
716 dpoints = dt_masks_dynbuf_init(1000000, "polygon dpoints");
717 if(IS_NULL_PTR(dpoints)) return 1;
718
719 if(!IS_NULL_PTR(border_buffer))
720 {
721 dborder = dt_masks_dynbuf_init(1000000, "polygon dborder");
722 if(IS_NULL_PTR(dborder))
723 {
724 dt_masks_dynbuf_free(dpoints);
725 return 1;
726 }
727 }
728
729 intersections = dt_masks_dynbuf_init(10 * MAX(node_count, 1), "polygon intersections");
730 if(IS_NULL_PTR(intersections))
731 {
732 dt_masks_dynbuf_free(dpoints);
733 dt_masks_dynbuf_free(dborder);
734 return 1;
735 }
736
737 // we store all points
738 float dx = 0.0f, dy = 0.0f;
739
740 //get the first node's position to use for source offset
741 if(source && node_count > 0 && transform_direction != DT_DEV_TRANSFORM_DIR_ALL)
742 {
743 dt_masks_node_polygon_t *polygon = (dt_masks_node_polygon_t *)mask_form->points->data;
744 if(IS_NULL_PTR(polygon)) return 0;
745 dx = (polygon->node[0] - mask_form->source[0]) * input_width;
746 dy = (polygon->node[1] - mask_form->source[1]) * input_height;
747 }
748 for(const GList *point_node = mask_form->points; point_node; point_node = g_list_next(point_node))
749 {
750 const dt_masks_node_polygon_t *const node = (dt_masks_node_polygon_t *)point_node->data;
751 float *const buf = dt_masks_dynbuf_reserve_n(dpoints, 6);
752 if(!IS_NULL_PTR(buf))
753 {
754 buf[0] = node->ctrl1[0] * input_width - dx;
755 buf[1] = node->ctrl1[1] * input_height - dy;
756 buf[2] = node->node[0] * input_width - dx;
757 buf[3] = node->node[1] * input_height - dy;
758 buf[4] = node->ctrl2[0] * input_width - dx;
759 buf[5] = node->ctrl2[1] * input_height - dy;
760 }
761 }
762 // for the border, we store value too
763 if(dborder)
764 {
765 dt_masks_dynbuf_add_zeros(dborder, 6 * node_count); // need six zeros for each border point
766 }
767
768 float *border_init = dt_pixelpipe_cache_alloc_align_float_cache((size_t)6 * node_count, 0);
769 if(!IS_NULL_PTR(border_init)) memset(border_init, 0, sizeof(float) * 6 * node_count);
770 if(IS_NULL_PTR(border_init))
771 {
772 dt_masks_dynbuf_free(intersections);
773 dt_masks_dynbuf_free(dpoints);
774 dt_masks_dynbuf_free(dborder);
775 return 1;
776 }
777 int cw = _polygon_is_clockwise(mask_form);
778 if(cw == 0) cw = -1;
779
781 {
782 dt_print(DT_DEBUG_MASKS, "[masks %s] polygon_points init took %0.04f sec\n", mask_form->name,
783 dt_get_wtime() - start2);
784 start2 = dt_get_wtime();
785 }
786
787 // we render all segments
788 const GList *form_points = mask_form->points;
789 for(int node_index = 0; node_index < node_count; node_index++)
790 {
791 const int pb = dborder ? dt_masks_dynbuf_position(dborder) : 0;
792 border_init[node_index * 6 + 2] = -pb;
793 const GList *pt2 = g_list_next_wraparound(form_points, mask_form->points); // next, wrapping around if on last element
794 const GList *pt3 = g_list_next_wraparound(pt2, mask_form->points);
795 dt_masks_node_polygon_t *point1 = (dt_masks_node_polygon_t *)form_points->data; // kth element of mask_form->points
798 float p1[5] = { point1->node[0] * input_width - dx, point1->node[1] * input_height - dy,
799 point1->ctrl2[0] * input_width - dx, point1->ctrl2[1] * input_height - dy,
800 cw * point1->border[1] * MIN(input_width, input_height) };
801 float p2[5] = { point2->node[0] * input_width - dx, point2->node[1] * input_height - dy,
802 point2->ctrl1[0] * input_width - dx, point2->ctrl1[1] * input_height - dy,
803 cw * point2->border[0] * MIN(input_width, input_height) };
804 float p3[5] = { point2->node[0] * input_width - dx, point2->node[1] * input_height - dy,
805 point2->ctrl2[0] * input_width - dx, point2->ctrl2[1] * input_height - dy,
806 cw * point2->border[1] * MIN(input_width, input_height) };
807 float p4[5] = { point3->node[0] * input_width - dx, point3->node[1] * input_height - dy,
808 point3->ctrl1[0] * input_width - dx, point3->ctrl1[1] * input_height - dy,
809 cw * point3->border[0] * MIN(input_width, input_height) };
810
811 // advance form_points for next iteration so that it tracks the kth element of mask_form->points
812 form_points = g_list_next(form_points);
813
814 // and we determine all points by recursion (to be sure the distance between 2 points is <=1)
815 float rc[2] = { 0 }, rb[2] = { 0 };
816 float bmin[2] = { NAN, NAN };
817 float bmax[2] = { NAN, NAN };
818 float cmin[2] = { NAN, NAN };
819 float cmax[2] = { NAN, NAN };
820
821 _polygon_points_recurs(p1, p2, 0.0, 1.0, cmin, cmax, bmin, bmax, rc, rb, dpoints, dborder,
822 border_buffer && (node_count >= 3), pixel_threshold);
823
824 // we check gaps in the border (sharp edges)
825 if(dborder)
826 {
827 const float lastb0 = dt_masks_dynbuf_get(dborder, -2);
828 const float lastb1 = dt_masks_dynbuf_get(dborder, -1);
829 if(fabs(lastb0 - rb[0]) > 1.0f || fabs(lastb1 - rb[1]) > 1.0f)
830 {
831 bmin[0] = lastb0;
832 bmin[1] = lastb1;
833 }
834 }
835
836 dt_masks_dynbuf_add_2(dpoints, rc[0], rc[1]);
837
838 border_init[node_index * 6 + 4] = dborder ? -dt_masks_dynbuf_position(dborder) : 0;
839
840 if(dborder)
841 {
842 if(isnan(rb[0]))
843 {
844 float lastb0 = dt_masks_dynbuf_get(dborder, -2);
845 float lastb1 = dt_masks_dynbuf_get(dborder, -1);
846 if(isnan(lastb0))
847 {
848 lastb0 = dt_masks_dynbuf_get(dborder, -4);
849 lastb1 = dt_masks_dynbuf_get(dborder, -3);
850 dt_masks_dynbuf_set(dborder, -2, lastb0);
851 dt_masks_dynbuf_set(dborder, -1, lastb1);
852 }
853 rb[0] = lastb0;
854 rb[1] = lastb1;
855 }
856 dt_masks_dynbuf_add_2(dborder, rb[0], rb[1]);
857
858 (dt_masks_dynbuf_buffer(dborder))[node_index * 6] = border_init[node_index * 6]
859 = (dt_masks_dynbuf_buffer(dborder))[pb];
860 (dt_masks_dynbuf_buffer(dborder))[node_index * 6 + 1] = border_init[node_index * 6 + 1]
861 = (dt_masks_dynbuf_buffer(dborder))[pb + 1];
862 }
863
864 // we first want to be sure that there are no gaps in border
865 if(dborder && node_count >= 3)
866 {
867 // we get the next point (start of the next segment)
868 // t=0.00001f to workaround rounding effects with full optimization that result in bmax[0] NOT being set to
869 // NAN when t=0 and the two points in p3 are identical (as is the case on a control node set to sharp corner)
870 _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,
871 bmax, bmax + 1);
872 if(isnan(bmax[0]))
873 {
874 _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,
875 cmin + 1, bmax, bmax + 1);
876 }
877 if(bmax[0] - rb[0] > 1 || bmax[0] - rb[0] < -1 || bmax[1] - rb[1] > 1 || bmax[1] - rb[1] < -1)
878 {
879 float bmin2[2] = { dt_masks_dynbuf_get(dborder, -22), dt_masks_dynbuf_get(dborder, -21) };
880 _polygon_points_recurs_border_gaps(rc, rb, bmin2, bmax, dpoints, dborder,
881 _polygon_is_clockwise(mask_form));
882 }
883 }
884 }
885
886 *point_count = dt_masks_dynbuf_position(dpoints) / 2;
887 *point_buffer = dt_masks_dynbuf_harvest(dpoints);
888 dt_masks_dynbuf_free(dpoints);
889
890 if(dborder)
891 {
892 *border_count = dt_masks_dynbuf_position(dborder) / 2;
893 *border_buffer = dt_masks_dynbuf_harvest(dborder);
894 dt_masks_dynbuf_free(dborder);
895 }
896
898 {
899 dt_print(DT_DEBUG_MASKS, "[masks %s] polygon_points point recurs %0.04f sec\n", mask_form->name,
900 dt_get_wtime() - start2);
901 start2 = dt_get_wtime();
902 }
903
904 // we don't want the border to self-intersect
905 int inter_count = 0;
906 if(!IS_NULL_PTR(border_buffer))
907 {
908 if(_polygon_find_self_intersection(intersections, node_count, *border_buffer, *border_count,
909 &inter_count) != 0)
910 {
911 dt_masks_dynbuf_free(intersections);
912 dt_pixelpipe_cache_free_align(*point_buffer);
913 dt_pixelpipe_cache_free_align(*border_buffer);
914 return 1;
915 }
916
918 {
919 dt_print(DT_DEBUG_MASKS, "[masks %s] polygon_points self-intersect took %0.04f sec\n", mask_form->name,
920 dt_get_wtime() - start2);
921 start2 = dt_get_wtime();
922 }
923 }
924
925 // and we transform them with all distorted modules
926 if(source && transform_direction == DT_DEV_TRANSFORM_DIR_ALL)
927 {
928 // we transform with all distortion that happen *before* the module
929 // so we have now the TARGET points in module input reference
931 *point_buffer, *point_count))
932 {
933 // now we move all the points by the shift
934 // so we have now the SOURCE points in module input reference
935 float pts[2] = { mask_form->source[0] * input_width, mask_form->source[1] * input_height };
937 goto fail;
938
939 dx = pts[0] - (*point_buffer)[2];
940 dy = pts[1] - (*point_buffer)[3];
941 __OMP_PARALLEL_FOR_SIMD__(if(*point_count > 100) aligned(point_buffer:64))
942 for(int i = 0; i < *point_count; i++)
943 {
944 (*point_buffer)[i * 2] += dx;
945 (*point_buffer)[i * 2 + 1] += dy;
946 }
947
948 // we apply the rest of the distortions (those after the module)
949 // so we have now the SOURCE points in final image reference
951 *point_buffer, *point_count))
952 goto fail;
953 }
954
956 dt_print(DT_DEBUG_MASKS, "[masks %s] polygon_points end took %0.04f sec\n",
957 mask_form->name, dt_get_wtime() - start2);
958
959 dt_masks_dynbuf_free(intersections);
961 return 0;
962 }
963 else if(dt_dev_distort_transform_plus(pipe, iop_order, transform_direction,
964 *point_buffer, *point_count))
965 {
966 if(!border_buffer
967 || dt_dev_distort_transform_plus(pipe, iop_order, transform_direction,
968 *border_buffer, *border_count))
969 {
971 {
972 dt_print(DT_DEBUG_MASKS, "[masks %s] polygon_points transform took %0.04f sec\n", mask_form->name,
973 dt_get_wtime() - start2);
974 start2 = dt_get_wtime();
975 }
976
977 if(!IS_NULL_PTR(border_buffer))
978 {
979 // we don't want to copy the falloff points
980 for(int node_index = 0; node_index < node_count; node_index++)
981 for(int i = 2; i < 6; i++)
982 (*border_buffer)[node_index * 6 + i] = border_init[node_index * 6 + i];
983
984 // now we want to write the skipping zones
985 // guard: buffer must be large enough to hold node falloff data
986 const int buf_count = *border_count;
987 for(int i = 0; i < inter_count; i++)
988 {
989 const int v = (int)(dt_masks_dynbuf_buffer(intersections))[i * 2];
990 const int w = (int)(dt_masks_dynbuf_buffer(intersections))[i * 2 + 1];
991 // bounds-check v and w against the allocated buffer size
992 if(v < 0 || v >= buf_count || w < 0 || w >= buf_count) continue;
993 if(v <= w)
994 {
995 (*border_buffer)[v * 2] = NAN;
996 (*border_buffer)[v * 2 + 1] = w;
997 }
998 else
999 {
1000 if(w > (int)(node_count * 3) && (int)(node_count * 3) < buf_count)
1001 {
1002 if(isnan((*border_buffer)[node_count * 6]) && isnan((*border_buffer)[node_count * 6 + 1]))
1003 (*border_buffer)[node_count * 6 + 1] = w;
1004 else if(isnan((*border_buffer)[node_count * 6]))
1005 (*border_buffer)[node_count * 6 + 1]
1006 = MAX((*border_buffer)[node_count * 6 + 1], w);
1007 else
1008 (*border_buffer)[node_count * 6 + 1] = w;
1009 (*border_buffer)[node_count * 6] = NAN;
1010 }
1011 (*border_buffer)[v * 2] = NAN;
1012 (*border_buffer)[v * 2 + 1] = NAN;
1013 }
1014 }
1015 }
1016
1018 dt_print(DT_DEBUG_MASKS, "[masks %s] polygon_points end took %0.04f sec\n", mask_form->name,
1019 dt_get_wtime() - start2);
1020
1021 dt_masks_dynbuf_free(intersections);
1022 dt_pixelpipe_cache_free_align(border_init);
1023 return 0;
1024 }
1025 }
1026
1027 // if we failed, then free all and return
1028fail:
1029 dt_masks_dynbuf_free(intersections);
1030 dt_pixelpipe_cache_free_align(border_init);
1031 dt_pixelpipe_cache_free_align(*point_buffer);
1032 *point_buffer = NULL;
1033 *point_count = 0;
1034 if(!IS_NULL_PTR(border_buffer))
1035 {
1036 dt_pixelpipe_cache_free_align(*border_buffer);
1037 *border_buffer = NULL;
1038 *border_count = 0;
1039 }
1040 return 1;
1041}
1042
1048static float _polygon_get_position_in_segment(float point_x, float point_y,
1049 dt_masks_form_t *mask_form, int segment_index)
1050{
1051 if(IS_NULL_PTR(mask_form) || IS_NULL_PTR(mask_form->points)) return 0;
1052 GList *firstpt = g_list_nth(mask_form->points, segment_index);
1053 dt_masks_node_polygon_t *point0 = (dt_masks_node_polygon_t *)firstpt->data;
1054 // advance to next node in list, if not already on the last
1055 GList *nextpt = g_list_next_bounded(firstpt);
1056 dt_masks_node_polygon_t *point1 = (dt_masks_node_polygon_t *)nextpt->data;
1057 nextpt = g_list_next_bounded(nextpt);
1058 dt_masks_node_polygon_t *point2 = (dt_masks_node_polygon_t *)nextpt->data;
1059 nextpt = g_list_next_bounded(nextpt);
1060 dt_masks_node_polygon_t *point3 = (dt_masks_node_polygon_t *)nextpt->data;
1061
1062 float min_t = 0.0f;
1063 float min_dist = FLT_MAX;
1064
1065 for(int i = 0; i <= 100; i++)
1066 {
1067 const float t = i / 100.0f;
1068 float sample_x = 0.0f;
1069 float sample_y = 0.0f;
1070 _polygon_get_XY(point0->node[0], point0->node[1], point1->node[0], point1->node[1],
1071 point2->node[0], point2->node[1], point3->node[0], point3->node[1], t,
1072 &sample_x, &sample_y);
1073
1074 const float dist = (point_x - sample_x) * (point_x - sample_x)
1075 + (point_y - sample_y) * (point_y - sample_y);
1076 if(dist < min_dist)
1077 {
1078 min_dist = dist;
1079 min_t = t;
1080 }
1081 }
1082
1083 return min_t;
1084}
1085
1086static void _add_node_to_segment(struct dt_iop_module_t *module,
1087 dt_masks_form_t *mask_form, int parent_id,
1088 dt_masks_form_gui_t *mask_gui, int form_index)
1089{
1090 if(IS_NULL_PTR(mask_form) || IS_NULL_PTR(mask_form->points) || IS_NULL_PTR(mask_gui)) return;
1091 const guint node_count = g_list_length(mask_form->points);
1092 const int selected_segment = dt_masks_gui_selected_segment_index(mask_gui);
1093 if(selected_segment < 0 || selected_segment >= (int)node_count) return;
1094
1095 // we add a new node to the polygon
1097 if(IS_NULL_PTR(new_node)) return;
1098
1099 // set coordinates
1101 new_node->ctrl1[0] = new_node->ctrl1[1] = new_node->ctrl2[0] = new_node->ctrl2[1] = -1.0;
1103
1104 // set other attributes of the new node. we interpolate the starting and the end node of that
1105 // segment
1106 const float t = _polygon_get_position_in_segment(new_node->node[0], new_node->node[1],
1107 mask_form, selected_segment);
1108 // start and end node of the segment
1109 GList *pt = g_list_nth(mask_form->points, selected_segment);
1110 if(IS_NULL_PTR(pt) || IS_NULL_PTR(pt->data))
1111 {
1112 dt_free(new_node);
1113 return;
1114 }
1116 const GList *const next_pt = g_list_next_wraparound(pt, mask_form->points);
1117 if(IS_NULL_PTR(next_pt) || IS_NULL_PTR(next_pt->data))
1118 {
1119 dt_free(new_node);
1120 return;
1121 }
1122 dt_masks_node_polygon_t *point1 = (dt_masks_node_polygon_t *)next_pt->data;
1123 new_node->border[0] = point0->border[0] * (1.0f - t) + point1->border[0] * t;
1124 new_node->border[1] = point0->border[1] * (1.0f - t) + point1->border[1] * t;
1125
1126 mask_form->points = g_list_insert(mask_form->points, new_node, selected_segment + 1);
1127 _polygon_init_ctrl_points(mask_form);
1128
1129 dt_masks_gui_form_create(mask_form, mask_gui, form_index, module);
1130
1131 mask_gui->node_hovered = selected_segment + 1;
1132 mask_gui->node_selected = TRUE;
1133 mask_gui->node_selected_idx = selected_segment + 1;
1134 mask_gui->seg_hovered = -1;
1135 mask_gui->seg_selected = FALSE;
1136}
1137
1138static inline void _polygon_translate_node(dt_masks_node_polygon_t *node, const float delta_x, const float delta_y)
1139{
1140 dt_masks_translate_ctrl_node(node->node, node->ctrl1, node->ctrl2, delta_x, delta_y);
1141}
1142
1143static void _polygon_translate_all_nodes(dt_masks_form_t *mask_form, const float delta_x, const float delta_y)
1144{
1145 for(GList *node_entry = mask_form->points; node_entry; node_entry = g_list_next(node_entry))
1146 _polygon_translate_node((dt_masks_node_polygon_t *)node_entry->data, delta_x, delta_y);
1147}
1148
1150 float **point_buffer, int *point_count,
1151 float **border_buffer, int *border_count,
1152 int source, const dt_iop_module_t *module)
1153{
1154 if(source && IS_NULL_PTR(module)) return 1;
1155 const double ioporder = (module) ? module->iop_order : 0.0f;
1156 return _polygon_get_pts_border(develop, mask_form, ioporder, DT_DEV_TRANSFORM_DIR_ALL,
1157 develop->virtual_pipe, point_buffer, point_count,
1158 border_buffer, border_count, source);
1159}
1160
1161static void _polygon_get_sizes(struct dt_iop_module_t *module, dt_masks_form_t *mask_form,
1162 dt_masks_form_gui_t *mask_gui, int form_index,
1163 float *mask_size, float *border_size)
1164{
1165 const dt_masks_form_gui_points_t *gui_points
1166 = (dt_masks_form_gui_points_t *)g_list_nth_data(mask_gui->points, form_index);
1167 if(IS_NULL_PTR(gui_points)) return;
1168
1169 const int node_count = g_list_length(mask_form->points);
1170 float p1[2] = { FLT_MAX, FLT_MAX };
1171 float p2[2] = { FLT_MIN, FLT_MIN };
1172
1173 float fp1[2] = { FLT_MAX, FLT_MAX };
1174 float fp2[2] = { FLT_MIN, FLT_MIN };
1175
1176 for(int i = node_count * 3; i < gui_points->points_count; i++)
1177 {
1178 // line
1179 const float x = gui_points->points[i * 2];
1180 const float y = gui_points->points[i * 2 + 1];
1181
1182 p1[0] = fminf(p1[0], x);
1183 p2[0] = fmaxf(p2[0], x);
1184 p1[1] = fminf(p1[1], y);
1185 p2[1] = fmaxf(p2[1], y);
1186
1187 if(!IS_NULL_PTR(border_size))
1188 {
1189 // border
1190 const float fx = gui_points->border[i * 2];
1191 const float fy = gui_points->border[i * 2 + 1];
1192
1193 // ??? looks like when x border is nan then y is a point index
1194 // see draw border in _polygon_events_post_expose.
1195 if(!isnan(fx))
1196 {
1197 fp1[0] = fminf(fp1[0], fx);
1198 fp2[0] = fmaxf(fp2[0], fx);
1199 fp1[1] = fminf(fp1[1], fy);
1200 fp2[1] = fmaxf(fp2[1], fy);
1201 }
1202 }
1203 }
1204
1205 float mask_span[2] = { p2[0] - p1[0], p2[1] - p1[1] };
1207 *mask_size = fmaxf(mask_span[0], mask_span[1]);
1208
1209 if(!IS_NULL_PTR(border_size))
1210 {
1211 float border_span[2] = { fp2[0] - fp1[0], fp2[1] - fp1[1] };
1213 *border_size = fmaxf(border_span[0], border_span[1]);
1214 }
1215}
1216
1217static gboolean _polygon_form_gravity_center(const dt_masks_form_t *mask_form, float *center_x,
1218 float *center_y, float *surface);
1219
1221 dt_masks_interaction_t interaction)
1222{
1223 if(IS_NULL_PTR(mask_form) || IS_NULL_PTR(mask_form->points)) return NAN;
1224
1225 switch(interaction)
1226 {
1228 {
1229 const float size = dt_masks_get_form_size_from_nodes(mask_form->points);
1230 if(size <= 0.0f) return NAN;
1231 return size;
1232 }
1234 {
1235 float hardness_sum = 0.0f;
1236 int hardness_count = 0;
1237
1238 for(const GList *point_node = mask_form->points; point_node; point_node = g_list_next(point_node))
1239 {
1240 const dt_masks_node_polygon_t *node = (const dt_masks_node_polygon_t *)point_node->data;
1241 if(IS_NULL_PTR(node)) continue;
1242 hardness_sum += node->border[0] + node->border[1];
1243 hardness_count += 2;
1244 }
1245
1246 return hardness_count > 0 ? hardness_sum / (float)hardness_count : NAN;
1247 }
1248 default:
1249 return NAN;
1250 }
1251}
1252
1253static gboolean _polygon_get_gravity_center(const dt_masks_form_t *mask_form,
1254 float center[2], float *area)
1255{
1256 if(IS_NULL_PTR(mask_form) || IS_NULL_PTR(mask_form->points) || IS_NULL_PTR(center)) return FALSE;
1257
1258 const int points_count = g_list_length(mask_form->points);
1259 if(points_count <= 0) return FALSE;
1260
1261 float *point_buffer = dt_alloc_align_float((size_t)points_count * 2);
1262 if(IS_NULL_PTR(point_buffer)) return FALSE;
1263
1264 int i = 0;
1265 for(const GList *point_node = mask_form->points; point_node; point_node = g_list_next(point_node))
1266 {
1267 const dt_masks_node_polygon_t *node = (const dt_masks_node_polygon_t *)point_node->data;
1268 if(IS_NULL_PTR(node)) continue;
1269 point_buffer[2 * i] = node->node[0];
1270 point_buffer[2 * i + 1] = node->node[1];
1271 i++;
1272 }
1273
1274 const gboolean ok = dt_masks_center_of_gravity_from_points(point_buffer, i, center, area);
1275 dt_free_align(point_buffer);
1276 return ok;
1277}
1278
1279static int _change_size(dt_masks_form_t *mask_form, int parent_id, dt_masks_form_gui_t *mask_gui,
1280 struct dt_iop_module_t *module, int form_index, const float amount,
1281 const dt_masks_increment_t increment, const int flow);
1282static int _change_hardness(dt_masks_form_t *mask_form, int parent_id, dt_masks_form_gui_t *mask_gui,
1283 struct dt_iop_module_t *module, int form_index, const float amount,
1284 const dt_masks_increment_t increment, int flow);
1285
1287 dt_masks_interaction_t interaction, float value,
1288 dt_masks_increment_t increment, int flow,
1289 dt_masks_form_gui_t *mask_gui, struct dt_iop_module_t *module)
1290{
1291 if(IS_NULL_PTR(mask_form)) return NAN;
1292 // Mirrors _dt_masks_events_get_dispatch_form()'s form_index: this shape's position in the
1293 // currently displayed group, so dt_masks_gui_form_create() below refreshes the right
1294 // mask_gui->points slot instead of clobbering whatever shape sits at index 0.
1295 const int index = (!IS_NULL_PTR(mask_gui) && mask_gui->group_selected >= 0) ? mask_gui->group_selected : 0;
1296
1297 switch(interaction)
1298 {
1300 if(!_change_size(mask_form, 0, mask_gui, module, index, value, increment, flow)) return NAN;
1301 return _polygon_get_interaction_value(mask_form, interaction);
1303 if(!_change_hardness(mask_form, 0, mask_gui, module, index, value, increment, flow)) return NAN;
1304 return _polygon_get_interaction_value(mask_form, interaction);
1305 default:
1306 return NAN;
1307 }
1308}
1309
1313static void _polygon_get_distance(float point_x, float point_y, float radius,
1314 dt_masks_form_gui_t *mask_gui, int form_index,
1315 int node_count, int *inside, int *inside_border,
1316 int *near, int *inside_source, float *dist)
1317{
1318 // initialise returned values
1319 *inside_source = 0;
1320 *inside = 0;
1321 *inside_border = 0;
1322 *near = -1;
1323 *dist = FLT_MAX;
1324
1325 if(IS_NULL_PTR(mask_gui)) return;
1326 dt_masks_form_gui_points_t *gui_points
1327 = (dt_masks_form_gui_points_t *)g_list_nth_data(mask_gui->points, form_index);
1328 if(IS_NULL_PTR(gui_points)) return;
1329
1330 float min_dist_pixel = FLT_MAX;
1331
1332 const float radius2 = radius * radius;
1333 const float pt[2] = { point_x, point_y };
1334
1335 // we first check if we are inside the source form
1336 if(gui_points->source && gui_points->points
1337 && gui_points->source_count > node_count * 3 && gui_points->points_count > node_count * 3
1338 && dt_masks_point_in_form_exact(pt, 1, gui_points->source, node_count * 3,
1339 gui_points->source_count) >= 0)
1340 {
1341 *inside_source = 1;
1342 *inside = 1;
1343
1344 // offset between form origin and source origin
1345 const float offset_x = -gui_points->points[2] + gui_points->source[2];
1346 const float offset_y = -gui_points->points[3] + gui_points->source[3];
1347 int current_seg = 1;
1348
1349 // distance from source border
1350 for(int i = node_count * 3; i < gui_points->points_count; i++)
1351 {
1352 // check if we advance to next polygon segment
1353 if(gui_points->points[i * 2] == gui_points->points[current_seg * 6 + 2]
1354 && gui_points->points[i * 2 + 1] == gui_points->points[current_seg * 6 + 3])
1355 {
1356 current_seg = (current_seg + 1) % node_count;
1357 }
1358
1359 // calculate source position for current point
1360 const float source_x = gui_points->points[i * 2] + offset_x;
1361 const float source_y = gui_points->points[i * 2 + 1] + offset_y;
1362
1363 // distance from tested point to current source point
1364 const float sdx = point_x - source_x;
1365 const float sdy = point_y - source_y;
1366 const float sdd = sqf(sdx) + sqf(sdy);
1367 if(sdd < min_dist_pixel)
1368 min_dist_pixel = sdd;
1369 }
1370 *dist = min_dist_pixel;
1371 return;
1372 }
1373
1374 // we check if we are near a segment
1375 if(gui_points->points && gui_points->points_count > 2 + node_count * 3)
1376 {
1377 int current_seg = 1;
1378 for(int i = node_count * 3; i < gui_points->points_count; i++)
1379 {
1380 // do we change of polygon segment ?
1381 if(gui_points->points[i * 2 + 1] == gui_points->points[current_seg * 6 + 3]
1382 && gui_points->points[i * 2] == gui_points->points[current_seg * 6 + 2])
1383 {
1384 current_seg = (current_seg + 1) % node_count;
1385 }
1386 //distance from tested point to current form point
1387 const float yy = gui_points->points[i * 2 + 1];
1388 const float xx = gui_points->points[i * 2];
1389
1390 const float dx = point_x - xx;
1391 const float dy = point_y - yy;
1392 const float dd = sqf(dx) + sqf(dy);
1393 if(dd < min_dist_pixel)
1394 {
1395 min_dist_pixel = dd;
1396
1397 if(current_seg >= 0 && dd < radius2)
1398 {
1399 if(current_seg == 0)
1400 *near = node_count - 1;
1401 else
1402 *near = current_seg - 1;
1403 }
1404 }
1405 }
1406 }
1407
1408 *dist = min_dist_pixel;
1409
1410 // we check if it's not inside borders, meaning we are not inside at all
1411 if(!gui_points->border || gui_points->border_count <= node_count * 3
1412 || dt_masks_point_in_form_exact(pt, 1, gui_points->border, node_count * 3,
1413 gui_points->border_count) < 0)
1414 return;
1415
1416 // we are at least inside the border
1417 *inside = 1;
1418
1419 // and we check if it's not inside form, meaning we are inside border only
1420 if(IS_NULL_PTR(gui_points->points) || gui_points->points_count <= node_count * 3) return;
1421 *inside_border = (dt_masks_point_in_form_exact(pt, 1, gui_points->points,
1422 node_count * 3, gui_points->points_count) < 0);
1423}
1424
1430static gboolean _polygon_border_handle_cb(const dt_masks_form_gui_points_t *gui_points, int node_count,
1431 int node_index, float *handle_x, float *handle_y, void *user_data)
1432{
1433 if(IS_NULL_PTR(gui_points) || node_index < 0 || node_index >= node_count) return FALSE;
1434 *handle_x = gui_points->border[node_index * 6];
1435 *handle_y = gui_points->border[node_index * 6 + 1];
1436 return !(isnan(*handle_x) || isnan(*handle_y));
1437}
1438
1442static void _polygon_curve_handle_cb(const dt_masks_form_gui_points_t *gui_points, int node_index,
1443 float *handle_x, float *handle_y, void *user_data)
1444{
1445
1446 _polygon_ctrl2_to_handle(gui_points->points[node_index * 6 + 2], gui_points->points[node_index * 6 + 3],
1447 gui_points->points[node_index * 6 + 4], gui_points->points[node_index * 6 + 5],
1448 handle_x, handle_y, gui_points->clockwise);
1449}
1450
1454static void _polygon_distance_cb(float pointer_x, float pointer_y, float cursor_radius,
1455 dt_masks_form_gui_t *mask_gui, int form_index, int node_count, int *inside,
1456 int *inside_border, int *near, int *inside_source, float *dist, void *user_data)
1457{
1458
1459 _polygon_get_distance(pointer_x, pointer_y, cursor_radius, mask_gui, form_index, node_count,
1460 inside, inside_border, near, inside_source, dist);
1461}
1462
1463static int _find_closest_handle(dt_masks_form_t *mask_form, dt_masks_form_gui_t *mask_gui, int form_index)
1464{
1465 return dt_masks_find_closest_handle_common(mask_form, mask_gui, form_index, -1,
1467 _polygon_distance_cb, NULL, NULL);
1468}
1469
1476static void _polygon_gui_gravity_center(const float *point_buffer, int point_count,
1477 float *center_x, float *center_y, float *area)
1478{
1479 if(IS_NULL_PTR(point_buffer) || point_count < 3) return;
1480
1481 float centroid_x = 0.0f;
1482 float centroid_y = 0.0f;
1483 float signed_area = 0.0f;
1484
1485 for(int node_index = 0; node_index < point_count; node_index++)
1486 {
1487 const int next_index = (node_index + 1) % point_count;
1488 const float x0 = point_buffer[node_index * 2];
1489 const float y0 = point_buffer[node_index * 2 + 1];
1490 const float x1 = point_buffer[next_index * 2];
1491 const float y1 = point_buffer[next_index * 2 + 1];
1492 const float cross = x0 * y1 - x1 * y0;
1493
1494 signed_area += cross;
1495 centroid_x += (x0 + x1) * cross;
1496 centroid_y += (y0 + y1) * cross;
1497 }
1498
1499 if(fabsf(signed_area) > 1e-8f)
1500 {
1501 const float inv_divisor = 1.0f / (3.0f * signed_area);
1502 if(!IS_NULL_PTR(center_x)) *center_x = centroid_x * inv_divisor;
1503 if(!IS_NULL_PTR(center_y)) *center_y = centroid_y * inv_divisor;
1504 }
1505 if(!IS_NULL_PTR(area)) *area = signed_area;
1506}
1507
1511static gboolean _polygon_form_gravity_center(const dt_masks_form_t *mask_form,
1512 float *center_x, float *center_y, float *area)
1513{
1514 if(IS_NULL_PTR(mask_form) || IS_NULL_PTR(mask_form->points) || g_list_shorter_than(mask_form->points, 3)) return FALSE;
1515
1516 float centroid_x = 0.0f;
1517 float centroid_y = 0.0f;
1518 float signed_area = 0.0f;
1519
1520 for(const GList *node_iter = mask_form->points; node_iter; node_iter = g_list_next(node_iter))
1521 {
1522 const GList *next_iter = g_list_next_wraparound(node_iter, mask_form->points);
1523 const dt_masks_node_polygon_t *node0 = (const dt_masks_node_polygon_t *)node_iter->data;
1524 const dt_masks_node_polygon_t *node1 = (const dt_masks_node_polygon_t *)next_iter->data;
1525 if(!node0 || !node1) return FALSE;
1526
1527 const float cross = node0->node[0] * node1->node[1] - node1->node[0] * node0->node[1];
1528 signed_area += cross;
1529 centroid_x += (node0->node[0] + node1->node[0]) * cross;
1530 centroid_y += (node0->node[1] + node1->node[1]) * cross;
1531 }
1532
1533 if(!IS_NULL_PTR(area)) *area = signed_area;
1534 if(fabsf(signed_area) <= 1e-8f) return FALSE;
1535
1536 const float inv_divisor = 1.0f / (3.0f * signed_area);
1537 if(!IS_NULL_PTR(center_x)) *center_x = centroid_x * inv_divisor;
1538 if(!IS_NULL_PTR(center_y)) *center_y = centroid_y * inv_divisor;
1539 return TRUE;
1540}
1541
1545static int _init_hardness(dt_masks_form_t *mask_form, const float amount,
1546 const dt_masks_increment_t increment, const int flow,
1547 const float mask_size, const float border_size)
1548{
1549 const float mask_hardness = dt_masks_get_set_conf_value(mask_form, "hardness", amount,
1550 HARDNESS_MIN, HARDNESS_MAX, increment, flow);
1551 dt_toast_log(_("Hardness: %3.2f%%"), (border_size * mask_hardness) / mask_size * 100.0f);
1552 return 1;
1553}
1554
1561static int _change_size(dt_masks_form_t *mask_form, int parent_id, dt_masks_form_gui_t *mask_gui,
1562 struct dt_iop_module_t *module, int form_index, const float amount,
1563 const dt_masks_increment_t increment, const int flow)
1564{
1565 if(IS_NULL_PTR(mask_form) || IS_NULL_PTR(mask_form->points)) return 0;
1566
1567 float center_x = 0.0f;
1568 float center_y = 0.0f;
1569 float signed_area = 0.0f;
1570 if(!_polygon_form_gravity_center(mask_form, &center_x, &center_y, &signed_area)) return 1;
1571
1572 // Avoid expanding degenerate polygons or overly large shapes.
1573 if(amount < 1.0f && signed_area < 0.00001f && signed_area > -0.00001f) return 1;
1574 if(amount > 1.0f && signed_area > 4.0f) return 1;
1575
1576 float scale_delta = amount;
1577 switch(increment)
1578 {
1580 scale_delta = powf(amount, (float)flow);
1581 break;
1583 // For polygon global scaling, interpret offset as multiplicative offset around 1.0.
1584 scale_delta = 1.0f + amount * (float)flow;
1585 break;
1587 default:
1588 scale_delta = amount;
1589 break;
1590 }
1591
1592 int node_index = 0;
1593 for(GList *node_iter = mask_form->points; node_iter; node_iter = g_list_next(node_iter), node_index++)
1594 {
1595 if(dt_masks_gui_change_affects_selected_node_or_all(mask_gui, node_index))
1596 {
1598 if(!node) continue;
1599
1600 const float new_node_x = center_x + (node->node[0] - center_x) * scale_delta;
1601 const float new_node_y = center_y + (node->node[1] - center_y) * scale_delta;
1602 const float ctrl1_offset_x = (node->ctrl1[0] - node->node[0]) * scale_delta;
1603 const float ctrl1_offset_y = (node->ctrl1[1] - node->node[1]) * scale_delta;
1604 const float ctrl2_offset_x = (node->ctrl2[0] - node->node[0]) * scale_delta;
1605 const float ctrl2_offset_y = (node->ctrl2[1] - node->node[1]) * scale_delta;
1606
1607 // Update all coordinates while keeping local offsets consistent.
1608 node->node[0] = new_node_x;
1609 node->node[1] = new_node_y;
1610 node->ctrl1[0] = new_node_x + ctrl1_offset_x;
1611 node->ctrl1[1] = new_node_y + ctrl1_offset_y;
1612 node->ctrl2[0] = new_node_x + ctrl2_offset_x;
1613 node->ctrl2[1] = new_node_y + ctrl2_offset_y;
1614 }
1615 }
1616
1617 float mask_size = 0.0f;
1618 _polygon_get_sizes(module, mask_form, mask_gui, form_index, &mask_size, NULL);
1619
1620 dt_toast_log(_("Size: %3.2f%%"), mask_size * 100.0f);
1621
1622 // Rebuild the cached GUI geometry.
1623 dt_masks_gui_form_create(mask_form, mask_gui, form_index, module);
1624 return 1;
1625}
1626
1630static int _change_hardness(dt_masks_form_t *mask_form, int parent_id, dt_masks_form_gui_t *mask_gui,
1631 struct dt_iop_module_t *module, int form_index, const float amount,
1632 const dt_masks_increment_t increment, int flow)
1633{
1634 int node_index = 0;
1635 const float scale_amount = powf(amount, (float)flow);
1636 const float offset_amount = amount * (float)flow;
1637
1638 for(GList *node_iter = mask_form->points; node_iter; node_iter = g_list_next(node_iter), node_index++)
1639 {
1640 if(dt_masks_gui_change_affects_selected_node_or_all(mask_gui, node_index))
1641 {
1643 if(!node) continue;
1644
1645 node->border[0] = CLAMPF(dt_masks_apply_increment_precomputed(node->border[0], amount, scale_amount,
1646 offset_amount, increment),
1648 node->border[1] = CLAMPF(dt_masks_apply_increment_precomputed(node->border[1], amount, scale_amount,
1649 offset_amount, increment),
1651 }
1652 }
1653
1654 float mask_size = 1.0f;
1655 float border_size = 0.0f;
1656 _polygon_get_sizes(module, mask_form, mask_gui, form_index, &mask_size, &border_size);
1657
1658 _init_hardness(mask_form, amount, increment, flow, mask_size, border_size);
1659
1660 // Rebuild the cached GUI geometry.
1661 dt_masks_gui_form_create(mask_form, mask_gui, form_index, module);
1662
1663 return 1;
1664}
1665
1669/* Shape handlers receive widget-space coordinates, while normalized output-image
1670 * coordinates come from `mask_gui->rel_pos` and absolute output-image
1671 * coordinates come from `mask_gui->pos`. */
1672static int _polygon_events_mouse_scrolled(struct dt_iop_module_t *module, double x, double y, int up, int flow,
1673 uint32_t state, dt_masks_form_t *mask_form, int parent_id,
1674 dt_masks_form_gui_t *mask_gui, int form_index,
1675 dt_masks_interaction_t interaction)
1676{
1677
1678
1679
1680 if(mask_gui->creation)
1681 {
1682 // no change during creation
1683 return 0;
1684 }
1685
1686 if(mask_gui->edit_mode == DT_MASKS_EDIT_FULL && dt_masks_is_anything_selected(mask_gui))
1687 {
1688 if(dt_modifier_is(state, GDK_CONTROL_MASK))
1689 return dt_masks_form_change_opacity(mask_form, parent_id, up, flow);
1690 if(dt_modifier_is(state, GDK_SHIFT_MASK) || mask_gui->node_selected)
1691 return _change_hardness(mask_form, parent_id, mask_gui, module, form_index, up ? +0.01f : -0.01f,
1693 else
1694 return _change_size(mask_form, parent_id, mask_gui, module, form_index, up ? 1.02f : 0.98f,
1696 }
1697 return 0;
1698}
1699
1704{
1705 // we don't want a form with less than 3 points
1706 if(g_list_shorter_than(mask_form->points, 4))
1707 {
1708 dt_toast_log(_("Polygon mask requires at least 3 nodes."));
1709 return 1;
1710 }
1711
1712 dt_iop_module_t *creation_module = mask_gui->creation_module;
1713 // we delete last point (the one we are currently dragging)
1714 dt_masks_node_polygon_t *last_node = (dt_masks_node_polygon_t *)g_list_last(mask_form->points)->data;
1715 mask_form->points = g_list_remove(mask_form->points, last_node);
1716 dt_free(last_node);
1717
1718 mask_gui->node_dragging = -1;
1719 _polygon_init_ctrl_points(mask_form);
1720
1721 dt_masks_gui_form_save_creation(darktable.develop, creation_module, mask_form, mask_gui);
1722
1723 return 1;
1724}
1725
1726static int _polygon_events_button_pressed(struct dt_iop_module_t *module, double x, double y,
1727 double pressure, int which, int type, uint32_t state,
1728 dt_masks_form_t *mask_form, int parent_id,
1729 dt_masks_form_gui_t *mask_gui, int form_index)
1730{
1731 if(type == GDK_2BUTTON_PRESS || type == GDK_3BUTTON_PRESS) return 1;
1732
1733 if(which == 1)
1734 {
1735 if(mask_gui->creation)
1736 {
1737 if(mask_gui->creation_closing_form)
1738 return _polygon_creation_closing_form(mask_form, mask_gui);
1739
1740 if(dt_modifier_is(state, GDK_CONTROL_MASK | GDK_SHIFT_MASK) || dt_modifier_is(state, GDK_SHIFT_MASK))
1741 {
1742 // set some absolute or relative position for the source of the clone mask
1743 if(mask_form->type & DT_MASKS_CLONE)
1744 {
1746 return 1;
1747 }
1748 }
1749
1750 else // we create a node
1751 {
1752 float masks_border = MIN(dt_conf_get_float("plugins/darkroom/masks/polygon/hardness"), HARDNESS_MAX);
1753
1754 int node_count = g_list_length(mask_form->points);
1755 // change the values
1756 dt_masks_node_polygon_t *polygon_node = (dt_masks_node_polygon_t *)(malloc(sizeof(dt_masks_node_polygon_t)));
1757 if(IS_NULL_PTR(polygon_node)) return 0;
1758
1759 dt_masks_gui_cursor_to_raw_norm(darktable.develop, mask_gui, polygon_node->node);
1760
1761 polygon_node->ctrl1[0] = polygon_node->ctrl1[1] = polygon_node->ctrl2[0] = polygon_node->ctrl2[1] = -1.0;
1762 polygon_node->border[0] = polygon_node->border[1] = MAX(HARDNESS_MIN, masks_border);
1763 polygon_node->state = DT_MASKS_POINT_STATE_NORMAL;
1764
1765 if(node_count == 0)
1766 {
1767 // create the first node
1768 dt_masks_node_polygon_t *polygon_first_node = (dt_masks_node_polygon_t *)(malloc(sizeof(dt_masks_node_polygon_t)));
1769 polygon_first_node->node[0] = polygon_node->node[0];
1770 polygon_first_node->node[1] = polygon_node->node[1];
1771 polygon_first_node->ctrl1[0] = polygon_first_node->ctrl1[1] = polygon_first_node->ctrl2[0] = polygon_first_node->ctrl2[1] = -1.0;
1772 polygon_first_node->border[0] = polygon_first_node->border[1] = MAX(HARDNESS_MIN, masks_border);
1773 polygon_first_node->state = DT_MASKS_POINT_STATE_NORMAL;
1774 mask_form->points = g_list_append(mask_form->points, polygon_first_node);
1775
1776 if(mask_form->type & DT_MASKS_CLONE)
1777 {
1778 dt_masks_set_source_pos_initial_value(mask_gui, mask_form);
1779 }
1780 else
1781 {
1782 // not used by regular masks
1783 mask_form->source[0] = mask_form->source[1] = 0.0f;
1784 }
1785 node_count++;
1786 }
1787 mask_form->points = g_list_append(mask_form->points, polygon_node);
1788
1789 // if this is a ctrl click, the last created point is a sharp one
1790 if(dt_modifier_is(state, GDK_CONTROL_MASK))
1791 {
1792 dt_masks_node_polygon_t *polygon_last_node = g_list_nth_data(mask_form->points, node_count - 1);
1793 polygon_last_node->ctrl1[0] = polygon_last_node->ctrl2[0] = polygon_last_node->node[0];
1794 polygon_last_node->ctrl1[1] = polygon_last_node->ctrl2[1] = polygon_last_node->node[1];
1795 polygon_last_node->state = DT_MASKS_POINT_STATE_USER;
1796 }
1797
1798 mask_gui->node_hovered = node_count;
1799 mask_gui->node_selected = TRUE;
1800 mask_gui->node_selected_idx = node_count;
1801 mask_gui->node_dragging = node_count;
1802 _polygon_init_ctrl_points(mask_form);
1803 }
1804
1805 // we recreate the form points in all case
1806 dt_masks_gui_form_create(mask_form, mask_gui, form_index, module);
1807
1808 return 1;
1809 }// end of creation mode
1810
1811 dt_masks_form_gui_points_t *gui_points
1812 = (dt_masks_form_gui_points_t *)g_list_nth_data(mask_gui->points, form_index);
1813 if(IS_NULL_PTR(gui_points)) return 0;
1814
1815 // The shape handler runs before the shared press-state selection update,
1816 // so concrete hovered targets must win over stale form/source selection.
1817 else if(mask_gui->node_hovered >= 0)
1818 {
1819 // if ctrl is pressed, we change the type of point
1820 if(mask_gui->node_selected && dt_modifier_is(state, GDK_CONTROL_MASK))
1821 {
1823 = (dt_masks_node_polygon_t *)g_list_nth_data(mask_form->points, mask_gui->node_hovered);
1824 if(IS_NULL_PTR(node)) return 0;
1825 dt_masks_toggle_bezier_node_type(module, mask_form, mask_gui, form_index, gui_points,
1826 mask_gui->node_hovered, node->node, node->ctrl1, node->ctrl2,
1827 &node->state);
1828 return 1;
1829 }
1830 /*// we register the current position to avoid accidental move
1831 if(mask_gui->node_selected < 0 && mask_gui->scrollx == 0.0f && mask_gui->scrolly == 0.0f)
1832 {
1833 mask_gui->scrollx = pzx;
1834 mask_gui->scrolly = pzy;
1835 }*/
1836 mask_gui->delta[0] = gui_points->points[mask_gui->node_hovered * 6 + 2] - mask_gui->pos[0];
1837 mask_gui->delta[1] = gui_points->points[mask_gui->node_hovered * 6 + 3] - mask_gui->pos[1];
1838
1839 return 1;
1840 }
1841 else if(mask_gui->handle_hovered >= 0)
1842 {
1843 if(!dt_masks_node_is_cusp(gui_points, mask_gui->handle_hovered))
1844 {
1845 // we need to find the handle position
1846 float handle_x, handle_y;
1847 const int handle_index = mask_gui->handle_hovered;
1848 _polygon_ctrl2_to_handle(gui_points->points[handle_index * 6 + 2],
1849 gui_points->points[handle_index * 6 + 3],
1850 gui_points->points[handle_index * 6 + 4],
1851 gui_points->points[handle_index * 6 + 5],
1852 &handle_x, &handle_y, gui_points->clockwise);
1853 // compute offsets
1854 mask_gui->delta[0] = handle_x - mask_gui->pos[0];
1855 mask_gui->delta[1] = handle_y - mask_gui->pos[1];
1856
1857 return 1;
1858 }
1859 }
1860 else if(mask_gui->handle_border_hovered >= 0)
1861 {
1862 const float handle_x = gui_points->border[mask_gui->handle_border_hovered * 6];
1863 const float handle_y = gui_points->border[mask_gui->handle_border_hovered * 6 + 1];
1864 mask_gui->delta[0] = handle_x - mask_gui->pos[0];
1865 mask_gui->delta[1] = handle_y - mask_gui->pos[1];
1866
1867 return 1;
1868 }
1869 else if(mask_gui->seg_hovered >= 0)
1870 {
1871 mask_gui->node_hovered = -1;
1872
1873 if(dt_modifier_is(state, GDK_CONTROL_MASK))
1874 {
1875 _add_node_to_segment(module, mask_form, parent_id, mask_gui, form_index);
1876 }
1877 else
1878 {
1879 // we move the entire segment
1880 mask_gui->delta[0] = gui_points->points[mask_gui->seg_hovered * 6 + 2] - mask_gui->pos[0];
1881 mask_gui->delta[1] = gui_points->points[mask_gui->seg_hovered * 6 + 3] - mask_gui->pos[1];
1882 }
1883 return 1;
1884 }
1885 else if(mask_gui->source_selected && mask_gui->edit_mode == DT_MASKS_EDIT_FULL)
1886 {
1887 // we start the source dragging
1888 mask_gui->delta[0] = gui_points->source[2] - mask_gui->pos[0];
1889 mask_gui->delta[1] = gui_points->source[3] - mask_gui->pos[1];
1890 return 1;
1891 }
1892 else if(mask_gui->form_selected && mask_gui->edit_mode == DT_MASKS_EDIT_FULL)
1893 {
1894 // we start the form dragging
1895 mask_gui->delta[0] = gui_points->points[2] - mask_gui->pos[0];
1896 mask_gui->delta[1] = gui_points->points[3] - mask_gui->pos[1];
1897 return 1;
1898 }
1899 }
1900
1901 return 0;
1902}
1903
1904static int _polygon_events_button_released(struct dt_iop_module_t *module, double x, double y, int which,
1905 uint32_t state, dt_masks_form_t *mask_form, int parent_id,
1906 dt_masks_form_gui_t *mask_gui, int form_index)
1907{
1908 if(IS_NULL_PTR(mask_gui)) return 0;
1909 if(mask_gui->creation) return 1;
1910
1911 if(which == 1)
1912 {
1913 if(dt_masks_gui_is_dragging(mask_gui))
1914 return 1;
1915 }
1916 return 0;
1917}
1918
1919static int _polygon_events_key_pressed(struct dt_iop_module_t *module, GdkEventKey *event,
1920 dt_masks_form_t *mask_form, int parent_id,
1921 dt_masks_form_gui_t *mask_gui, int form_index)
1922{
1923 if(IS_NULL_PTR(mask_gui) || IS_NULL_PTR(mask_form)) return 0;
1924
1925 guint key = dt_keys_mainpad_alternatives(event->keyval);
1926
1927
1928 if(mask_gui->creation)
1929 {
1930 switch(key)
1931 {
1932 case GDK_KEY_BackSpace:
1933 {
1934 // Minimum points to create a polygon
1935 if(mask_gui->node_dragging < 1)
1936 {
1937 dt_masks_form_exit_creation(module, mask_gui);
1938 return 1;
1939 }
1940 // switch previous node coords to the current one
1941 dt_masks_node_polygon_t *previous_node
1942 = (dt_masks_node_polygon_t *)g_list_nth_data(mask_form->points, mask_gui->node_dragging - 1);
1943 dt_masks_node_polygon_t *current_node
1944 = (dt_masks_node_polygon_t *)g_list_nth_data(mask_form->points, mask_gui->node_dragging);
1945 if(!previous_node || !current_node) return 0;
1946 previous_node->node[0] = current_node->node[0];
1947 previous_node->node[1] = current_node->node[1];
1948
1949 dt_masks_remove_node(module, mask_form, 0, mask_gui, 0, mask_gui->node_dragging);
1950 // Decrease the current dragging node index
1951 mask_gui->node_dragging -= 1;
1952
1954 return 1;
1955 }
1956 case GDK_KEY_Return:
1957 return _polygon_creation_closing_form(mask_form, mask_gui);
1958 }
1959 }
1960 return 0;
1961}
1962
1971static int _polygon_events_mouse_moved(struct dt_iop_module_t *module, double x, double y, double pressure,
1972 int which, dt_masks_form_t *mask_form, int parent_id,
1973 dt_masks_form_gui_t *mask_gui, int form_index)
1974{
1975 // centre view will have zoom_scale * backbuf_width pixels, we want the handle offset to scale with DPI:
1977 dt_masks_form_gui_points_t *gui_points
1978 = (dt_masks_form_gui_points_t *)g_list_nth_data(mask_gui->points, form_index);
1979 if(IS_NULL_PTR(gui_points)) return 0;
1980
1981 const int iwidth = darktable.develop->roi.raw_width;
1982 const int iheight = darktable.develop->roi.raw_height;
1983
1984 if(mask_gui->node_dragging >= 0)
1985 {
1986 if(IS_NULL_PTR(mask_form->points)) return 0;
1987 if(mask_gui->creation && !g_list_shorter_than(mask_form->points, 4))
1988 {
1989 // check if we are near the first point to close the polygon on creation
1990 const float dist_curs = DT_GUI_MOUSE_EFFECT_RADIUS;
1991 const float dx = mask_gui->pos[0] - gui_points->points[2];
1992 const float dy = mask_gui->pos[1] - gui_points->points[3];
1993 const float dist2 = dx * dx + dy * dy;
1994 mask_gui->creation_closing_form = dist2 <= dist_curs * dist_curs;
1995 }
1996
1997 dt_masks_node_polygon_t *dragged_node
1998 = (dt_masks_node_polygon_t *)g_list_nth_data(mask_form->points, mask_gui->node_dragging);
1999 if(IS_NULL_PTR(dragged_node)) return 0;
2000
2001 float dx = 0.0f;
2002 float dy = 0.0f;
2003 dt_masks_gui_delta_from_raw_anchor(dev, mask_gui, dragged_node->node, &dx, &dy);
2004 _polygon_translate_node(dragged_node, dx, dy);
2005
2006 // if first point, adjust the source position accordingly
2007 if((mask_form->type & DT_MASKS_CLONE) && mask_gui->node_dragging == 0)
2008 dt_masks_translate_source(mask_form, dx, dy);
2009
2010 if(mask_gui->creation)
2011 _polygon_init_ctrl_points(mask_form);
2012
2013 // we recreate the form points
2014 if(dt_masks_gui_form_create_throttled(mask_form, mask_gui, form_index, module,
2015 mask_gui->pos[0], mask_gui->pos[1]))
2016 gui_points->clockwise = _polygon_is_clockwise(mask_form);
2017
2018 return 1;
2019 }
2020 else if(mask_gui->creation)
2021 {
2022 // Let the cursor motion be redrawn as it moves in GUI
2023 return 1;
2024 }
2025
2026 if(IS_NULL_PTR(mask_form->points)) return 0;
2027 const guint node_count = g_list_length(mask_form->points);
2028
2029 if(mask_gui->seg_dragging >= 0)
2030 {
2031 const GList *const pt = g_list_nth(mask_form->points, mask_gui->seg_dragging);
2032 const GList *const next_pt = g_list_next_wraparound(pt, mask_form->points);
2034 dt_masks_node_polygon_t *next_point = (dt_masks_node_polygon_t *)next_pt->data;
2035 if(IS_NULL_PTR(point) || IS_NULL_PTR(next_point)) return 0;
2036
2037 float dx = 0.0f;
2038 float dy = 0.0f;
2039 dt_masks_gui_delta_from_raw_anchor(dev, mask_gui, point->node, &dx, &dy);
2040
2041 // if first or last segment, update the source accordingly
2042 // (the source point follows the first/last segment when moved)
2043 if((mask_form->type & DT_MASKS_CLONE)
2044 && (mask_gui->seg_dragging == 0 || mask_gui->seg_dragging == (int)node_count - 1))
2045 dt_masks_translate_source(mask_form, dx, dy);
2046
2048 _polygon_translate_node(next_point, dx, dy);
2049
2050 // we recreate the form points
2051 dt_masks_gui_form_create_throttled(mask_form, mask_gui, form_index, module,
2052 mask_gui->pos[0], mask_gui->pos[1]);
2053 gui_points->clockwise = _polygon_is_clockwise(mask_form);
2054
2055 return 1;
2056 }
2057 else if(mask_gui->handle_dragging >= 0)
2058 {
2060 = (dt_masks_node_polygon_t *)g_list_nth_data(mask_form->points, mask_gui->handle_dragging);
2061 if(IS_NULL_PTR(node)) return 0;
2062
2063 float pts[2];
2064 dt_masks_gui_delta_to_image_abs(mask_gui, pts);
2065
2066 // compute ctrl points directly from new handle position
2067 float p[4];
2068 _polygon_handle_to_ctrl(gui_points->points[mask_gui->handle_dragging * 6 + 2],
2069 gui_points->points[mask_gui->handle_dragging * 6 + 3],
2070 pts[0], pts[1], &p[0], &p[1], &p[2], &p[3], gui_points->clockwise);
2071
2073
2074 // set new ctrl points
2075 dt_masks_set_ctrl_points(node->ctrl1, node->ctrl2, p);
2077
2078 _polygon_init_ctrl_points(mask_form);
2079 // we recreate the form points
2080 dt_masks_gui_form_create_throttled(mask_form, mask_gui, form_index, module,
2081 mask_gui->pos[0], mask_gui->pos[1]);
2082
2083 return 1;
2084 }
2085 else if(mask_gui->handle_border_dragging >= 0)
2086 {
2087 const int node_index = mask_gui->handle_border_dragging;
2089 = (dt_masks_node_polygon_t *)g_list_nth_data(mask_form->points, node_index);
2090 if(IS_NULL_PTR(node)) return 0;
2091
2092 const int base = node_index * 6;
2093 const int node_point_index = base + 2;
2094
2095 // Get delta between the node and its border handle
2096 float pts[2];
2097 float cursor_pos[2];
2098 const float node_pos_gui[2] = { gui_points->points[node_point_index],
2099 gui_points->points[node_point_index + 1] };
2100 const float handle_pos[2] = { gui_points->border[base], gui_points->border[base + 1] };
2101 dt_masks_gui_delta_to_image_abs(mask_gui, cursor_pos);
2102 dt_masks_project_on_line(cursor_pos, node_pos_gui, handle_pos, pts);
2103
2104 const float border = dt_masks_border_from_projected_handle(dev, node->node, pts, fminf(iwidth, iheight));
2105
2106 node->border[0] = node->border[1] = border;
2107 // we recreate the form points
2108 dt_masks_gui_form_create_throttled(mask_form, mask_gui, form_index, module,
2109 mask_gui->pos[0], mask_gui->pos[1]);
2110
2111 return 1;
2112 }
2113 else if(mask_gui->form_dragging || mask_gui->source_dragging)
2114 {
2115 if(mask_gui->form_dragging)
2116 {
2117 dt_masks_node_polygon_t *dragging_shape = (dt_masks_node_polygon_t *)(mask_form->points)->data;
2118 if(IS_NULL_PTR(dragging_shape)) return 0;
2119 float dx = 0.0f;
2120 float dy = 0.0f;
2121 dt_masks_gui_delta_from_raw_anchor(dev, mask_gui, dragging_shape->node, &dx, &dy);
2122 _polygon_translate_all_nodes(mask_form, dx, dy);
2123 }
2124 else
2125 {
2126 float raw_point[2];
2127 dt_masks_gui_delta_to_raw_norm(dev, mask_gui, raw_point);
2128 mask_form->source[0] = raw_point[0];
2129 mask_form->source[1] = raw_point[1];
2130 }
2131
2132 // we recreate the form points
2133 dt_masks_gui_form_create(mask_form, mask_gui, form_index, module);
2134 return 1;
2135 }
2136 return 0;
2137}
2138
2142static void _polygon_draw_shape(cairo_t *cr, const float *point_buffer, const int point_count,
2143 const int node_count, const gboolean draw_border, const gboolean draw_source)
2144{
2145
2146 // Find the first valid non-NaN point to start drawing
2147 // FIXME: Why not just avoid having NaN points in the array?
2148 int start_idx = -1;
2149 for(int point_index = node_count * 3 + draw_border; point_index < point_count; point_index++)
2150 {
2151 if(!isnan(point_buffer[point_index * 2]) && !isnan(point_buffer[point_index * 2 + 1]))
2152 {
2153 start_idx = point_index;
2154 break;
2155 }
2156 }
2157
2158 // Only draw if we have at least one valid point
2159 if(start_idx >= 0)
2160 {
2161 cairo_move_to(cr, point_buffer[start_idx * 2], point_buffer[start_idx * 2 + 1]);
2162 for(int point_index = start_idx + 1; point_index < point_count; point_index++)
2163 {
2164 if(!isnan(point_buffer[point_index * 2]) && !isnan(point_buffer[point_index * 2 + 1]))
2165 cairo_line_to(cr, point_buffer[point_index * 2], point_buffer[point_index * 2 + 1]);
2166 }
2167 }
2168}
2169
2173static void _polygon_events_post_expose(cairo_t *cr, float zoom_scale, dt_masks_form_gui_t *mask_gui,
2174 int form_index, int node_count)
2175{
2176 dt_masks_form_gui_points_t *gui_points
2177 = (dt_masks_form_gui_points_t *)g_list_nth_data(mask_gui->points, form_index);
2178 if(IS_NULL_PTR(gui_points)) return;
2179 const int selected_node = dt_masks_gui_selected_node_index(mask_gui);
2180 const int selected_handle = dt_masks_gui_selected_handle_index(mask_gui);
2181 const int selected_handle_border = dt_masks_gui_selected_handle_border_index(mask_gui);
2182
2183 if(mask_gui->creation)
2184 {
2185 // draw a cross where the source will be created
2187 if(visible_form && (visible_form->type & DT_MASKS_CLONE))
2188 {
2189 const gboolean have_first_node = node_count && gui_points->points && gui_points->points_count > 1;
2190 float node_posx = have_first_node ? gui_points->points[2] : mask_gui->pos[0];
2191 float node_posy = have_first_node ? gui_points->points[3] : mask_gui->pos[1];
2192
2193 dt_masks_draw_source_preview(cr, zoom_scale, mask_gui, node_posx, node_posy, node_posx, node_posy, FALSE);
2194 }
2195 }
2196
2197 // update clockwise info for the handles
2198 else if((mask_gui->type & DT_MASKS_IS_RETOUCHE) != 0 || mask_gui->node_selected || mask_gui->node_dragging >= 0
2199 || mask_gui->handle_selected)
2200 {
2202 if(!IS_NULL_PTR(group_form) && (group_form->type & DT_MASKS_GROUP))
2203 {
2204 dt_masks_form_group_t *group_entry = g_list_nth_data(group_form->points, form_index);
2205 dt_masks_form_t *polygon_form = group_entry
2207 : NULL;
2208 if(!IS_NULL_PTR(polygon_form)) gui_points->clockwise = _polygon_is_clockwise(polygon_form);
2209 }
2210 }
2211
2212 // draw polygon
2213 if(gui_points->points && node_count > 0 && gui_points->points_count > node_count * 3 + 6) // there must be something to draw
2214 {
2215 dt_masks_draw_path_seg_by_seg(cr, mask_gui, form_index, gui_points->points, gui_points->points_count,
2216 node_count, zoom_scale);
2217 }
2218
2219 if(mask_gui->group_selected == form_index)
2220 {
2221 // draw borders
2222 if(gui_points->border_count > node_count * 3 + 2)
2223 {
2224 dt_draw_shape_lines(DT_MASKS_DASH_STICK, FALSE, cr, node_count, (mask_gui->border_selected), zoom_scale,
2225 gui_points->border, gui_points->border_count, &dt_masks_functions_polygon.draw_shape,
2226 CAIRO_LINE_CAP_ROUND);
2227 }
2228
2229 // draw the current node's handle if it's a curve node
2230 if(mask_gui->node_selected && selected_node >= 0 && selected_node < node_count
2231 && !dt_masks_node_is_cusp(gui_points, selected_node))
2232 {
2233 const int node_index = selected_node;
2234 float handle[2];
2235 _polygon_ctrl2_to_handle(gui_points->points[node_index * 6 + 2], gui_points->points[node_index * 6 + 3],
2236 gui_points->points[node_index * 6 + 4], gui_points->points[node_index * 6 + 5],
2237 &handle[0], &handle[1], gui_points->clockwise);
2238 const float pt[2] = { gui_points->points[node_index * 6 + 2], gui_points->points[node_index * 6 + 3] };
2239 const gboolean selected = (mask_gui->node_hovered == node_index
2240 || (selected_handle == node_index)
2241 || (mask_gui->handle_hovered == node_index));
2242 dt_draw_handle(cr, pt, zoom_scale, handle, selected, FALSE);
2243 }
2244 }
2245
2246 // draw nodes
2247 if(mask_gui->group_selected == form_index || mask_gui->creation)
2248 {
2249 for(int node_index = 0; node_index < node_count; node_index++)
2250 {
2251 // don't draw the last node while creating
2252 if(mask_gui->creation && node_index == node_count - 1) break;
2253 if(IS_NULL_PTR(gui_points->points) || gui_points->points_count <= node_index * 3 + 1) break;
2254
2255 const gboolean squared = dt_masks_node_is_cusp(gui_points, node_index);
2256 const gboolean selected = (node_index == mask_gui->node_hovered || node_index == mask_gui->node_dragging);
2257 const gboolean action = (node_index == selected_node);
2258 const float x = gui_points->points[node_index * 6 + 2];
2259 const float y = gui_points->points[node_index * 6 + 3];
2260
2261 // draw the first node as big circle while creating the polygon
2262 if(mask_gui->creation && node_index == 0)
2263 dt_draw_node(cr, FALSE, TRUE, TRUE, zoom_scale, x, y);
2264 else
2265 dt_draw_node(cr, squared, action, selected, zoom_scale, x, y);
2266 }
2267
2268 // Draw the current node's border handle, if needed
2269 if(mask_gui->node_selected && selected_node >= 0 && selected_node < node_count
2270 && gui_points->border && gui_points->border_count > selected_node * 3 && !mask_gui->creation)
2271 {
2272 const int edited = selected_node;
2273 const gboolean selected = (mask_gui->node_hovered == edited
2274 || (selected_handle_border == edited)
2275 || mask_gui->handle_border_hovered == edited);
2276 const int curr_node = edited * 6;
2277 const float handle[2] = { gui_points->border[curr_node], gui_points->border[curr_node + 1] };
2278
2279 dt_draw_handle(cr, NULL, zoom_scale, handle, selected, TRUE);
2280 }
2281 }
2282
2283 // draw the source if needed
2284 if(gui_points->source && gui_points->source_count > node_count * 3 + 2
2285 && gui_points->points && gui_points->points_count > 0)
2286 {
2287 dt_masks_gui_center_point_t center_pt = { .main = { gui_points->points[0], gui_points->points[1] },
2288 .source = { gui_points->source[0], gui_points->source[1] } };
2289 _polygon_gui_gravity_center(gui_points->points, gui_points->points_count,
2290 &center_pt.main.x, &center_pt.main.y, NULL);
2291 // project the source's center point from the center of gravity
2292 float offset_x = gui_points->source[0] - gui_points->points[0];
2293 float offset_y = gui_points->source[1] - gui_points->points[1];
2294 center_pt.source.x = center_pt.main.x + offset_x;
2295 center_pt.source.y = center_pt.main.y + offset_y;
2296 dt_masks_draw_source(cr, mask_gui, form_index, node_count, zoom_scale,
2298
2299 //draw the current node projection
2300 for(int node_index = 0; node_index < node_count; node_index++)
2301 {
2302 if(mask_gui->group_selected == form_index
2303 && (node_index == mask_gui->node_hovered || node_index == selected_node
2304 || (mask_gui->creation && node_index == node_count - 1)))
2305 {
2306 const int proj_index = node_index * 6 + 2;
2307 if(gui_points->source_count <= node_index * 3 + 1) break;
2308 const float proj[2] = { gui_points->source[proj_index], gui_points->source[proj_index + 1] };
2309 const gboolean selected = mask_gui->node_hovered == node_index;
2310 const gboolean squared = dt_masks_node_is_cusp(gui_points, node_index);
2311
2312 dt_draw_handle(cr, NULL, zoom_scale, proj, selected, squared);
2313 }
2314 }
2315 }
2316}
2317
2321static void _polygon_bounding_box_raw(const float *const point_buffer, const float *border_buffer,
2322 const int corner_count, const int point_count, int border_count,
2323 float *x_min, float *x_max, float *y_min, float *y_max)
2324{
2325 float xmin, xmax, ymin, ymax;
2326 xmin = ymin = FLT_MAX;
2327 xmax = ymax = FLT_MIN;
2328 for(int border_index = corner_count * 3; border_index < border_count; border_index++)
2329 {
2330 // we look at the borders
2331 const float xx = border_buffer[border_index * 2];
2332 const float yy = border_buffer[border_index * 2 + 1];
2333 if(isnan(xx))
2334 {
2335 if(isnan(yy)) break; // that means we have to skip the end of the border polygon
2336 border_index = yy - 1;
2337 continue;
2338 }
2339 xmin = MIN(xx, xmin);
2340 xmax = MAX(xx, xmax);
2341 ymin = MIN(yy, ymin);
2342 ymax = MAX(yy, ymax);
2343 }
2344 for(int point_index = corner_count * 3; point_index < point_count; point_index++)
2345 {
2346 // we look at the polygon too
2347 const float xx = point_buffer[point_index * 2];
2348 const float yy = point_buffer[point_index * 2 + 1];
2349 xmin = MIN(xx, xmin);
2350 xmax = MAX(xx, xmax);
2351 ymin = MIN(yy, ymin);
2352 ymax = MAX(yy, ymax);
2353 }
2354
2355 *x_min = xmin;
2356 *x_max = xmax;
2357 *y_min = ymin;
2358 *y_max = ymax;
2359}
2360
2364static void _polygon_bounding_box(const float *const point_buffer, const float *border_buffer,
2365 const int corner_count, const int point_count, int border_count,
2366 int *width, int *height, int *posx, int *posy)
2367{
2368 // now we want to find the area, so we search min/max points
2369 float xmin, xmax, ymin, ymax;
2370 _polygon_bounding_box_raw(point_buffer, border_buffer, corner_count, point_count, border_count,
2371 &xmin, &xmax, &ymin, &ymax);
2372 *height = ymax - ymin + 4;
2373 *width = xmax - xmin + 4;
2374 *posx = xmin - 2;
2375 *posy = ymin - 2;
2376}
2377
2378static int _get_area(const dt_iop_module_t *const module, dt_dev_pixelpipe_t *pipe,
2379 const dt_dev_pixelpipe_iop_t *const piece,
2380 dt_masks_form_t *const mask_form, int *width, int *height, int *posx, int *posy,
2381 gboolean get_source)
2382{
2383 if(IS_NULL_PTR(module)) return 1;
2384
2385 // we get buffers for all points
2386 float *point_buffer = NULL;
2387 float *border_buffer = NULL;
2388 int point_count = 0;
2389 int border_count = 0;
2390
2391 if(_polygon_get_pts_border(module->dev, mask_form, module->iop_order, DT_DEV_TRANSFORM_DIR_BACK_INCL, pipe,
2392 &point_buffer, &point_count, &border_buffer, &border_count, get_source) != 0)
2393 {
2394 dt_pixelpipe_cache_free_align(point_buffer);
2395 dt_pixelpipe_cache_free_align(border_buffer);
2396 return 1;
2397 }
2398
2399 const guint corner_count = g_list_length(mask_form->points);
2400 _polygon_bounding_box(point_buffer, border_buffer, corner_count, point_count, border_count,
2401 width, height, posx, posy);
2402
2403 dt_pixelpipe_cache_free_align(point_buffer);
2404 dt_pixelpipe_cache_free_align(border_buffer);
2405 return 0;
2406}
2407
2410 dt_masks_form_t *mask_form, int *width, int *height, int *posx, int *posy)
2411{
2412 return _get_area(module, pipe, piece, mask_form, width, height, posx, posy, TRUE);
2413}
2414
2415static int _polygon_get_area(const dt_iop_module_t *const module, dt_dev_pixelpipe_t *pipe,
2416 const dt_dev_pixelpipe_iop_t *const piece,
2417 dt_masks_form_t *const mask_form,
2418 int *width, int *height, int *posx, int *posy)
2419{
2420 return _get_area(module, pipe, piece, mask_form, width, height, posx, posy, FALSE);
2421}
2422
2426/*static*/ void _polygon_falloff(float *const restrict buffer, int *p0, int *p1,
2427 int posx, int posy, int buffer_width)
2428{
2429 // segment length
2430 int l = sqrtf(sqf(p1[0] - p0[0]) + sqf(p1[1] - p0[1])) + 1;
2431
2432 const float lx = p1[0] - p0[0];
2433 const float ly = p1[1] - p0[1];
2434 const float inv_l = 1.0f / (float)l;
2435
2436 for(int i = 0; i < l; i++)
2437 {
2438 // position
2439 const int x = (int)((float)i * lx * inv_l) + p0[0] - posx;
2440 const int y = (int)((float)i * ly * inv_l) + p0[1] - posy;
2441 const float op = 1.0f - (float)i * inv_l;
2442 const size_t idx = y * buffer_width + x;
2443 buffer[idx] = fmaxf(buffer[idx], op);
2444 if(x > 0)
2445 buffer[idx - 1] = fmaxf(buffer[idx - 1], op); // this one is to avoid gap due to int rounding
2446 if(y > 0)
2447 buffer[idx - buffer_width] = fmaxf(buffer[idx - buffer_width], op); // this one is to avoid gap due to int rounding
2448 }
2449}
2450
2451static int _polygon_get_mask(const dt_iop_module_t *const module, dt_dev_pixelpipe_t *pipe,
2452 const dt_dev_pixelpipe_iop_t *const piece,
2453 dt_masks_form_t *const mask_form,
2454 float **buffer, int *width, int *height, int *posx, int *posy)
2455{
2456 if(IS_NULL_PTR(module)) return 1;
2457 double start = 0.0;
2458 double start2 = 0.0;
2459
2461
2462 // we get buffers for all points
2463 float *point_buffer = NULL;
2464 float *border_buffer = NULL;
2465 int point_count = 0;
2466 int border_count = 0;
2467 if(_polygon_get_pts_border(module->dev, mask_form, module->iop_order,
2468 DT_DEV_TRANSFORM_DIR_BACK_INCL, pipe, &point_buffer, &point_count,
2469 &border_buffer, &border_count, FALSE) != 0)
2470 {
2471 dt_pixelpipe_cache_free_align(point_buffer);
2472 dt_pixelpipe_cache_free_align(border_buffer);
2473 return 1;
2474 }
2475
2477 {
2478 dt_print(DT_DEBUG_MASKS, "[masks %s] polygon points took %0.04f sec\n",
2479 mask_form->name, dt_get_wtime() - start);
2480 start = start2 = dt_get_wtime();
2481 }
2482
2483 // now we want to find the area, so we search min/max points
2484 const guint corner_count = g_list_length(mask_form->points);
2485 _polygon_bounding_box(point_buffer, border_buffer, corner_count, point_count, border_count,
2486 width, height, posx, posy);
2487
2488 const int hb = *height;
2489 const int wb = *width;
2490 const gboolean sparse = (dt_dev_pixelpipe_has_preview_output(piece->module->dev, pipe, NULL)
2491 || pipe->type == DT_DEV_PIXELPIPE_THUMBNAIL);
2492 const int sparse_factor = sparse ? 4 : 1;
2493
2495 {
2496 dt_print(DT_DEBUG_MASKS, "[masks %s] polygon_fill min max took %0.04f sec\n", mask_form->name,
2497 dt_get_wtime() - start2);
2498 start2 = dt_get_wtime();
2499 }
2500
2501 // we allocate the buffer
2502 const size_t bufsize = (size_t)(*width) * (*height);
2503 // ensure that the buffer is zeroed, as the following code only actually sets the polygon+falloff pixels
2504 float *const restrict bufptr = *buffer = dt_pixelpipe_cache_alloc_align_float_cache(bufsize, 0);
2505 if(!IS_NULL_PTR(bufptr)) memset(bufptr, 0, sizeof(float) * bufsize);
2506 if(IS_NULL_PTR(*buffer))
2507 {
2508 dt_pixelpipe_cache_free_align(point_buffer);
2509 dt_pixelpipe_cache_free_align(border_buffer);
2510 return 1;
2511 }
2512
2513 // we write all the point around the polygon into the buffer
2514 const int border_point_count = border_count;
2515 if(border_point_count > 2)
2516 {
2517 int lastx = (int)point_buffer[(border_point_count - 1) * 2];
2518 int lasty = (int)point_buffer[(border_point_count - 1) * 2 + 1];
2519 int lasty2 = (int)point_buffer[(border_point_count - 2) * 2 + 1];
2520
2521 int just_change_dir = 0;
2522 for(int ii = corner_count * 3; ii < 2 * border_point_count - corner_count * 3; ii++)
2523 {
2524 // we are writing more than 1 loop in the case the dir in y change
2525 // exactly at start/end point
2526 int i = ii;
2527 if(ii >= border_point_count)
2528 i = (ii - corner_count * 3) % (border_point_count - corner_count * 3) + corner_count * 3;
2529 const int xx = (int)point_buffer[i * 2];
2530 const int yy = (int)point_buffer[i * 2 + 1];
2531
2532 // we don't store the point if it has the same y value as the last one
2533 if(yy == lasty) continue;
2534
2535 // we want to be sure that there is no y jump
2536 if(yy - lasty > 1 || yy - lasty < -1)
2537 {
2538 if(yy < lasty)
2539 {
2540 for(int j = yy + 1; j < lasty; j++)
2541 {
2542 const int nx = (j - yy) * (lastx - xx) / (float)(lasty - yy) + xx;
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 else
2551 {
2552 for(int j = lasty + 1; j < yy; j++)
2553 {
2554 const int nx = (j - lasty) * (xx - lastx) / (float)(yy - lasty) + lastx;
2555 const size_t idx = (size_t)(j - (*posy)) * (*width) + nx - (*posx);
2556 assert(idx < bufsize);
2557 bufptr[idx] = 1.0f;
2558 }
2559 lasty2 = yy - 2;
2560 lasty = yy - 1;
2561 }
2562 }
2563 // if we change the direction of the polygon (in y), then we add a extra point
2564 if((lasty - lasty2) * (lasty - yy) > 0)
2565 {
2566 const size_t idx = (size_t)(lasty - (*posy)) * (*width) + lastx + 1 - (*posx);
2567 assert(idx < bufsize);
2568 bufptr[idx] = 1.0f;
2569 just_change_dir = 1;
2570 }
2571 // we add the point
2572 if(just_change_dir && ii == i)
2573 {
2574 // if we have changed the direction, we have to be careful that point can be at the same place
2575 // as the previous one, especially on sharp edges
2576 const size_t idx = (size_t)(yy - (*posy)) * (*width) + xx - (*posx);
2577 assert(idx < bufsize);
2578 float v = bufptr[idx];
2579 if(v > 0.0)
2580 {
2581 if(xx - (*posx) > 0)
2582 {
2583 const size_t idx_ = (size_t)(yy - (*posy)) * (*width) + xx - 1 - (*posx);
2584 assert(idx_ < bufsize);
2585 bufptr[idx_] = 1.0f;
2586 }
2587 else if(xx - (*posx) < (*width) - 1)
2588 {
2589 const size_t idx_ = (size_t)(yy - (*posy)) * (*width) + xx + 1 - (*posx);
2590 assert(idx_ < bufsize);
2591 bufptr[idx_] = 1.0f;
2592 }
2593 }
2594 else
2595 {
2596 const size_t idx_ = (size_t)(yy - (*posy)) * (*width) + xx - (*posx);
2597 assert(idx_ < bufsize);
2598 bufptr[idx_] = 1.0f;
2599 just_change_dir = 0;
2600 }
2601 }
2602 else
2603 {
2604 const size_t idx_ = (size_t)(yy - (*posy)) * (*width) + xx - (*posx);
2605 assert(idx_ < bufsize);
2606 bufptr[idx_] = 1.0f;
2607 }
2608 // we change last values
2609 lasty2 = lasty;
2610 lasty = yy;
2611 lastx = xx;
2612 if(ii != i) break;
2613 }
2614 }
2616 {
2617 dt_print(DT_DEBUG_MASKS, "[masks %s] polygon_fill draw polygon took %0.04f sec\n", mask_form->name,
2618 dt_get_wtime() - start2);
2619 start2 = dt_get_wtime();
2620 }
2621 __OMP_PARALLEL_FOR__(if((size_t)hb * wb > 50000))
2622 for(int yy = 0; yy < hb; yy++)
2623 {
2624 float *const restrict row = bufptr + (size_t)yy * wb;
2625 int state = 0;
2626 for(int xx = 0; xx < wb; xx++)
2627 {
2628 const float v = row[xx];
2629 if(v == 1.0f) state = !state;
2630 if(state) row[xx] = 1.0f;
2631 }
2632 }
2633
2635 {
2636 dt_print(DT_DEBUG_MASKS, "[masks %s] polygon_fill fill plain took %0.04f sec\n", mask_form->name,
2637 dt_get_wtime() - start2);
2638 start2 = dt_get_wtime();
2639 }
2640
2641 // now we fill the falloff
2642 int p0[2] = { 0 }, p1[2] = { 0 };
2643 float pf1[2] = { 0.0f };
2644 int prev0[2] = { 0 }, prev1[2] = { 0 };
2645 gboolean have_prev = FALSE;
2646 int last0[2] = { -100, -100 }, last1[2] = { -100, -100 };
2647 int next = 0;
2648 for(int i = corner_count * 3; i < border_count; i++)
2649 {
2650 p0[0] = point_buffer[i * 2];
2651 p0[1] = point_buffer[i * 2 + 1];
2652 if(next > 0)
2653 p1[0] = pf1[0] = border_buffer[next * 2], p1[1] = pf1[1] = border_buffer[next * 2 + 1];
2654 else
2655 p1[0] = pf1[0] = border_buffer[i * 2], p1[1] = pf1[1] = border_buffer[i * 2 + 1];
2656
2657 // now we check p1 value to know if we have to skip a part
2658 if(next == i) next = 0;
2659 while(isnan(pf1[0]))
2660 {
2661 if(isnan(pf1[1]))
2662 next = i - 1;
2663 else
2664 next = p1[1];
2665 p1[0] = pf1[0] = border_buffer[next * 2];
2666 p1[1] = pf1[1] = border_buffer[next * 2 + 1];
2667 }
2668
2669 const gboolean used_next = (next > 0);
2670
2671 if(sparse && have_prev && !used_next
2672 && (prev0[0] != p0[0] || prev0[1] != p0[1] || prev1[0] != p1[0] || prev1[1] != p1[1]))
2673 {
2674 for(int k = 1; k < sparse_factor; k++)
2675 {
2676 const float t = (float)k / (float)sparse_factor;
2677 int mp0[2] = { (int)floorf(prev0[0] + t * (p0[0] - prev0[0]) + 0.5f),
2678 (int)floorf(prev0[1] + t * (p0[1] - prev0[1]) + 0.5f) };
2679 int mp1[2] = { (int)floorf(prev1[0] + t * (p1[0] - prev1[0]) + 0.5f),
2680 (int)floorf(prev1[1] + t * (p1[1] - prev1[1]) + 0.5f) };
2681 _polygon_falloff(bufptr, mp0, mp1, *posx, *posy, *width);
2682 }
2683 }
2684
2685 // and we draw the falloff
2686 if(last0[0] != p0[0] || last0[1] != p0[1] || last1[0] != p1[0] || last1[1] != p1[1])
2687 {
2688 _polygon_falloff(bufptr, p0, p1, *posx, *posy, *width);
2689 last0[0] = p0[0];
2690 last0[1] = p0[1];
2691 last1[0] = p1[0];
2692 last1[1] = p1[1];
2693 }
2694
2695 if(!used_next)
2696 {
2697 prev0[0] = p0[0];
2698 prev0[1] = p0[1];
2699 prev1[0] = p1[0];
2700 prev1[1] = p1[1];
2701 have_prev = TRUE;
2702 }
2703 else
2704 {
2705 have_prev = FALSE;
2706 }
2707 }
2708
2710 dt_print(DT_DEBUG_MASKS, "[masks %s] polygon_fill fill falloff took %0.04f sec\n", mask_form->name,
2711 dt_get_wtime() - start2);
2712
2713 dt_pixelpipe_cache_free_align(point_buffer);
2714 dt_pixelpipe_cache_free_align(border_buffer);
2715
2717 dt_print(DT_DEBUG_MASKS, "[masks %s] polygon fill buffer took %0.04f sec\n", mask_form->name,
2718 dt_get_wtime() - start);
2719
2720 return 0;
2721}
2722
2723
2726static int _polygon_crop_to_roi(float *polygon, const int point_count, float xmin, float xmax, float ymin,
2727 float ymax)
2728{
2729 int point_start = -1;
2730 int l = -1, r = -1;
2731
2732
2733 // first try to find a node clearly inside roi
2734 for(int k = 0; k < point_count; k++)
2735 {
2736 float x = polygon[2 * k];
2737 float y = polygon[2 * k + 1];
2738
2739 if(x >= xmin + 1 && y >= ymin + 1
2740 && x <= xmax - 1 && y <= ymax - 1)
2741 {
2742 point_start = k;
2743 break;
2744 }
2745 }
2746
2747 // printf("crop to xmin %f, xmax %f, ymin %f, ymax %f - start %d (%f, %f)\n", xmin, xmax, ymin, ymax,
2748 // point_start, polygon[2*point_start], polygon[2*point_start+1]);
2749
2750 if(point_start < 0) return 0; // no point means roi lies completely within polygon
2751
2752 typedef struct
2753 {
2754 int l;
2755 int r;
2756 float start;
2757 float delta;
2758 } roi_crop_segment_t;
2759
2760 roi_crop_segment_t *xmax_segs = malloc(sizeof(*xmax_segs) * point_count);
2761 roi_crop_segment_t *ymax_segs = malloc(sizeof(*ymax_segs) * point_count);
2762 if(IS_NULL_PTR(xmax_segs) || IS_NULL_PTR(ymax_segs))
2763 {
2764 dt_free(xmax_segs);
2765 dt_free(ymax_segs);
2766 goto fallback_passes;
2767 }
2768
2769 int xmin_l = -1, xmin_r = -1;
2770 int xmax_l = -1, xmax_r = -1;
2771 int xmax_count = 0;
2772
2773 // find the crossing points with xmin/xmax in a single pass
2774 for(int k = 0; k < point_count; k++)
2775 {
2776 const int kk = (k + point_start) % point_count;
2777 const float x = polygon[2 * kk];
2778
2779 if(xmin_l < 0 && x < xmin) xmin_l = k; // where we leave roi (xmin)
2780 if(xmin_l >= 0 && x >= xmin) xmin_r = k - 1; // where we re-enter roi (xmin)
2781
2782 if(xmin_l >= 0 && xmin_r >= 0)
2783 {
2784 const int count = xmin_r - xmin_l + 1;
2785 const int ll = (xmin_l - 1 + point_start) % point_count;
2786 const int rr = (xmin_r + 1 + point_start) % point_count;
2787 const float delta_y = (count == 1) ? 0 : (polygon[2 * rr + 1] - polygon[2 * ll + 1]) / (count - 1);
2788 const float start_y = polygon[2 * ll + 1];
2789
2790 for(int n = 0; n < count; n++)
2791 {
2792 const int nn = (n + xmin_l + point_start) % point_count;
2793 polygon[2 * nn] = xmin;
2794 polygon[2 * nn + 1] = start_y + n * delta_y;
2795 }
2796
2797 xmin_l = xmin_r = -1;
2798 }
2799
2800 if(xmax_l < 0 && x > xmax) xmax_l = k; // where we leave roi (xmax)
2801 if(xmax_l >= 0 && x <= xmax) xmax_r = k - 1; // where we re-enter roi (xmax)
2802
2803 if(xmax_l >= 0 && xmax_r >= 0)
2804 {
2805 const int count = xmax_r - xmax_l + 1;
2806 const int ll = (xmax_l - 1 + point_start) % point_count;
2807 const int rr = (xmax_r + 1 + point_start) % point_count;
2808 const float delta_y = (count == 1) ? 0 : (polygon[2 * rr + 1] - polygon[2 * ll + 1]) / (count - 1);
2809 const float start_y = polygon[2 * ll + 1];
2810
2811 xmax_segs[xmax_count].l = xmax_l;
2812 xmax_segs[xmax_count].r = xmax_r;
2813 xmax_segs[xmax_count].start = start_y;
2814 xmax_segs[xmax_count].delta = delta_y;
2815 xmax_count++;
2816
2817 xmax_l = xmax_r = -1;
2818 }
2819 }
2820
2821 for(int s = 0; s < xmax_count; s++)
2822 {
2823 const int count = xmax_segs[s].r - xmax_segs[s].l + 1;
2824 const float start_y = xmax_segs[s].start;
2825 const float delta_y = xmax_segs[s].delta;
2826 for(int n = 0; n < count; n++)
2827 {
2828 const int nn = (n + xmax_segs[s].l + point_start) % point_count;
2829 polygon[2 * nn] = xmax;
2830 polygon[2 * nn + 1] = start_y + n * delta_y;
2831 }
2832 }
2833
2834 dt_free(xmax_segs);
2835
2836 int ymin_l = -1, ymin_r = -1;
2837 int ymax_l = -1, ymax_r = -1;
2838 int ymax_count = 0;
2839
2840 // find the crossing points with ymin/ymax in a single pass
2841 for(int k = 0; k < point_count; k++)
2842 {
2843 const int kk = (k + point_start) % point_count;
2844 const float y = polygon[2 * kk + 1];
2845
2846 if(ymin_l < 0 && y < ymin) ymin_l = k; // where we leave roi (ymin)
2847 if(ymin_l >= 0 && y >= ymin) ymin_r = k - 1; // where we re-enter roi (ymin)
2848
2849 if(ymin_l >= 0 && ymin_r >= 0)
2850 {
2851 const int count = ymin_r - ymin_l + 1;
2852 const int ll = (ymin_l - 1 + point_start) % point_count;
2853 const int rr = (ymin_r + 1 + point_start) % point_count;
2854 const float delta_x = (count == 1) ? 0 : (polygon[2 * rr] - polygon[2 * ll]) / (count - 1);
2855 const float start_x = polygon[2 * ll];
2856
2857 for(int n = 0; n < count; n++)
2858 {
2859 const int nn = (n + ymin_l + point_start) % point_count;
2860 polygon[2 * nn] = start_x + n * delta_x;
2861 polygon[2 * nn + 1] = ymin;
2862 }
2863
2864 ymin_l = ymin_r = -1;
2865 }
2866
2867 if(ymax_l < 0 && y > ymax) ymax_l = k; // where we leave roi (ymax)
2868 if(ymax_l >= 0 && y <= ymax) ymax_r = k - 1; // where we re-enter roi (ymax)
2869
2870 if(ymax_l >= 0 && ymax_r >= 0)
2871 {
2872 const int count = ymax_r - ymax_l + 1;
2873 const int ll = (ymax_l - 1 + point_start) % point_count;
2874 const int rr = (ymax_r + 1 + point_start) % point_count;
2875 const float delta_x = (count == 1) ? 0 : (polygon[2 * rr] - polygon[2 * ll]) / (count - 1);
2876 const float start_x = polygon[2 * ll];
2877
2878 ymax_segs[ymax_count].l = ymax_l;
2879 ymax_segs[ymax_count].r = ymax_r;
2880 ymax_segs[ymax_count].start = start_x;
2881 ymax_segs[ymax_count].delta = delta_x;
2882 ymax_count++;
2883
2884 ymax_l = ymax_r = -1;
2885 }
2886 }
2887
2888 for(int s = 0; s < ymax_count; s++)
2889 {
2890 const int count = ymax_segs[s].r - ymax_segs[s].l + 1;
2891 const float start_x = ymax_segs[s].start;
2892 const float delta_x = ymax_segs[s].delta;
2893 for(int n = 0; n < count; n++)
2894 {
2895 const int nn = (n + ymax_segs[s].l + point_start) % point_count;
2896 polygon[2 * nn] = start_x + n * delta_x;
2897 polygon[2 * nn + 1] = ymax;
2898 }
2899 }
2900
2901 dt_free(ymax_segs);
2902 return 1;
2903
2904fallback_passes:
2905 l = r = -1;
2906 // find the crossing points with xmin and replace segment by nodes on border
2907 for(int k = 0; k < point_count; k++)
2908 {
2909 const int kk = (k + point_start) % point_count;
2910
2911 if(l < 0 && polygon[2 * kk] < xmin) l = k; // where we leave roi
2912 if(l >= 0 && polygon[2 * kk] >= xmin) r = k - 1; // where we re-enter roi
2913
2914 // replace that segment
2915 if(l >= 0 && r >= 0)
2916 {
2917 const int count = r - l + 1;
2918 const int ll = (l - 1 + point_start) % point_count;
2919 const int rr = (r + 1 + point_start) % point_count;
2920 const float delta_y = (count == 1) ? 0 : (polygon[2 * rr + 1] - polygon[2 * ll + 1]) / (count - 1);
2921 const float start_y = polygon[2 * ll + 1];
2922
2923 for(int n = 0; n < count; n++)
2924 {
2925 const int nn = (n + l + point_start) % point_count;
2926 polygon[2 * nn] = xmin;
2927 polygon[2 * nn + 1] = start_y + n * delta_y;
2928 }
2929
2930 l = r = -1;
2931 }
2932 }
2933
2934 // find the crossing points with xmax and replace segment by nodes on border
2935 for(int k = 0; k < point_count; k++)
2936 {
2937 const int kk = (k + point_start) % point_count;
2938
2939 if(l < 0 && polygon[2 * kk] > xmax) l = k; // where we leave roi
2940 if(l >= 0 && polygon[2 * kk] <= xmax) r = k - 1; // where we re-enter roi
2941
2942 // replace that segment
2943 if(l >= 0 && r >= 0)
2944 {
2945 const int count = r - l + 1;
2946 const int ll = (l - 1 + point_start) % point_count;
2947 const int rr = (r + 1 + point_start) % point_count;
2948 const float delta_y = (count == 1) ? 0 : (polygon[2 * rr + 1] - polygon[2 * ll + 1]) / (count - 1);
2949 const float start_y = polygon[2 * ll + 1];
2950
2951 for(int n = 0; n < count; n++)
2952 {
2953 const int nn = (n + l + point_start) % point_count;
2954 polygon[2 * nn] = xmax;
2955 polygon[2 * nn + 1] = start_y + n * delta_y;
2956 }
2957
2958 l = r = -1;
2959 }
2960 }
2961
2962 // find the crossing points with ymin and replace segment by nodes on border
2963 for(int k = 0; k < point_count; k++)
2964 {
2965 const int kk = (k + point_start) % point_count;
2966
2967 if(l < 0 && polygon[2 * kk + 1] < ymin) l = k; // where we leave roi
2968 if(l >= 0 && polygon[2 * kk + 1] >= ymin) r = k - 1; // where we re-enter roi
2969
2970 // replace that segment
2971 if(l >= 0 && r >= 0)
2972 {
2973 const int count = r - l + 1;
2974 const int ll = (l - 1 + point_start) % point_count;
2975 const int rr = (r + 1 + point_start) % point_count;
2976 const float delta_x = (count == 1) ? 0 : (polygon[2 * rr] - polygon[2 * ll]) / (count - 1);
2977 const float start_x = polygon[2 * ll];
2978
2979 for(int n = 0; n < count; n++)
2980 {
2981 const int nn = (n + l + point_start) % point_count;
2982 polygon[2 * nn] = start_x + n * delta_x;
2983 polygon[2 * nn + 1] = ymin;
2984 }
2985
2986 l = r = -1;
2987 }
2988 }
2989
2990 // find the crossing points with ymax and replace segment by nodes on border
2991 for(int k = 0; k < point_count; k++)
2992 {
2993 const int kk = (k + point_start) % point_count;
2994
2995 if(l < 0 && polygon[2 * kk + 1] > ymax) l = k; // where we leave roi
2996 if(l >= 0 && polygon[2 * kk + 1] <= ymax) r = k - 1; // where we re-enter roi
2997
2998 // replace that segment
2999 if(l >= 0 && r >= 0)
3000 {
3001 const int count = r - l + 1;
3002 const int ll = (l - 1 + point_start) % point_count;
3003 const int rr = (r + 1 + point_start) % point_count;
3004 const float delta_x = (count == 1) ? 0 : (polygon[2 * rr] - polygon[2 * ll]) / (count - 1);
3005 const float start_x = polygon[2 * ll];
3006
3007 for(int n = 0; n < count; n++)
3008 {
3009 const int nn = (n + l + point_start) % point_count;
3010 polygon[2 * nn] = start_x + n * delta_x;
3011 polygon[2 * nn + 1] = ymax;
3012 }
3013
3014 l = r = -1;
3015 }
3016 }
3017 return 1;
3018}
3019
3021static inline void _polygon_falloff_roi(float *buffer, int *p0, int *p1, int bw, int bh)
3022{
3023 // segment length
3024 const int l = sqrt((p1[0] - p0[0]) * (p1[0] - p0[0]) + (p1[1] - p0[1]) * (p1[1] - p0[1])) + 1;
3025
3026 const float lx = p1[0] - p0[0];
3027 const float ly = p1[1] - p0[1];
3028 const float inv_l = 1.0f / (float)l;
3029
3030 const int dx = lx < 0 ? -1 : 1;
3031 const int dy = ly < 0 ? -1 : 1;
3032 const int dpy = dy * bw;
3033
3034 const int x0 = p0[0], y0 = p0[1];
3035 const int x1 = p1[0], y1 = p1[1];
3036 if((x0 < 0 && x1 < 0) || (x0 >= bw && x1 >= bw) || (y0 < 0 && y1 < 0) || (y0 >= bh && y1 >= bh)) return;
3037 const int inside = (x0 >= 0 && x0 < bw && x1 >= 0 && x1 < bw && y0 >= 0 && y0 < bh && y1 >= 0 && y1 < bh);
3038
3039 for(int i = 0; i < l; i++)
3040 {
3041 // position
3042 const int x = (int)((float)i * lx * inv_l) + p0[0];
3043 const int y = (int)((float)i * ly * inv_l) + p0[1];
3044 const float op = 1.0f - (float)i * inv_l;
3045 if(!inside && (x < 0 || x >= bw || y < 0 || y >= bh)) continue;
3046 float *buf = buffer + (size_t)y * bw + x;
3047 if(inside)
3048 buf[0] = MAX(buf[0], op);
3049 else if(x >= 0 && x < bw && y >= 0 && y < bh)
3050 buf[0] = MAX(buf[0], op);
3051 if(x + dx >= 0 && x + dx < bw && y >= 0 && y < bh)
3052 buf[dx] = MAX(buf[dx], op); // this one is to avoid gap due to int rounding
3053 if(x >= 0 && x < bw && y + dy >= 0 && y + dy < bh)
3054 buf[dpy] = MAX(buf[dpy], op); // this one is to avoid gap due to int rounding
3055 }
3056}
3057
3058// build a stamp which can be combined with other shapes in the same group
3059// prerequisite: 'buffer' is all zeros
3060static int _polygon_get_mask_roi(const dt_iop_module_t *const module, dt_dev_pixelpipe_t *pipe,
3061 const dt_dev_pixelpipe_iop_t *const piece,
3062 dt_masks_form_t *const mask_form,
3063 const dt_iop_roi_t *roi, float *buffer)
3064{
3065 if(IS_NULL_PTR(module)) return 1;
3066 double start = 0.0;
3067 double start2 = 0.0;
3069
3070 const int px = roi->x;
3071 const int py = roi->y;
3072 const int width = roi->width;
3073 const int height = roi->height;
3074 const float scale = roi->scale;
3075 const gboolean sparse = (dt_dev_pixelpipe_has_preview_output(piece->module->dev, pipe, roi)
3076 || pipe->type == DT_DEV_PIXELPIPE_THUMBNAIL);
3077 const int sparse_factor = sparse ? 4 : 1;
3078
3079 // we need to take care of four different cases:
3080 // 1) polygon and feather are outside of roi
3081 // 2) polygon is outside of roi, feather reaches into roi
3082 // 3) roi lies completely within polygon
3083 // 4) all other situations :)
3084 int polygon_in_roi = 0;
3085 int feather_in_roi = 0;
3086 int polygon_encircles_roi = 0;
3087
3088 // we get buffers for all points
3089 float *points = NULL, *border = NULL;
3090 int points_count = 0, border_count = 0;
3091 if(_polygon_get_pts_border(module->dev, mask_form, module->iop_order,
3093 &points, &points_count, &border, &border_count, FALSE) != 0)
3094 {
3097 return 1;
3098 }
3099 if(points_count <= 2)
3100 {
3103 return 0;
3104 }
3105
3107 {
3108 dt_print(DT_DEBUG_MASKS, "[masks %s] polygon points took %0.04f sec\n",
3109 mask_form->name, dt_get_wtime() - start);
3110 start = start2 = dt_get_wtime();
3111 }
3112
3113 const guint corner_count = g_list_length(mask_form->points);
3114
3115 // we shift and scale down polygon and border
3116 for(int i = corner_count * 3; i < border_count; i++)
3117 {
3118 const float xx = border[2 * i];
3119 const float yy = border[2 * i + 1];
3120 if(isnan(xx))
3121 {
3122 if(isnan(yy)) break; // that means we have to skip the end of the border polygon
3123 i = yy - 1;
3124 continue;
3125 }
3126 border[2 * i] = xx * scale - px;
3127 border[2 * i + 1] = yy * scale - py;
3128 }
3129 for(int i = corner_count * 3; i < points_count; i++)
3130 {
3131 const float xx = points[2 * i];
3132 const float yy = points[2 * i + 1];
3133 points[2 * i] = xx * scale - px;
3134 points[2 * i + 1] = yy * scale - py;
3135 }
3136
3137 // now check if polygon is at least partially within roi
3138 for(int i = corner_count * 3; i < points_count; i++)
3139 {
3140 const int xx = points[i * 2];
3141 const int yy = points[i * 2 + 1];
3142
3143 if(xx > 1 && yy > 1 && xx < width - 2 && yy < height - 2)
3144 {
3145 polygon_in_roi = 1;
3146 break;
3147 }
3148 }
3149
3150 // if not this still might mean that polygon fully encircles roi -> we need to check that
3151 if(!polygon_in_roi)
3152 {
3153 int crossing_count = 0;
3154 int last_y = -9999;
3155 const int x = width / 2;
3156 const int y = height / 2;
3157
3158 for(int i = corner_count * 3; i < points_count; i++)
3159 {
3160 const int yy = (int)points[2 * i + 1];
3161 if(yy != last_y && yy == y)
3162 {
3163 if(points[2 * i] > x) crossing_count++;
3164 }
3165 last_y = yy;
3166 }
3167 // if there is an uneven number of intersection points roi lies within polygon
3168 if(crossing_count & 1)
3169 {
3170 polygon_in_roi = 1;
3171 polygon_encircles_roi = 1;
3172 }
3173 }
3174
3175 // now check if feather is at least partially within roi
3176 for(int i = corner_count * 3; i < border_count; i++)
3177 {
3178 const float xx = border[i * 2];
3179 const float yy = border[i * 2 + 1];
3180 if(isnan(xx))
3181 {
3182 if(isnan(yy)) break; // that means we have to skip the end of the border polygon
3183 i = yy - 1;
3184 continue;
3185 }
3186 if(xx > 1 && yy > 1 && xx < width - 2 && yy < height - 2)
3187 {
3188 feather_in_roi = 1;
3189 break;
3190 }
3191 }
3192
3193 // if polygon and feather completely lie outside of roi -> we're done/mask remains empty
3194 if(!polygon_in_roi && !feather_in_roi)
3195 {
3198 return 0;
3199 }
3200
3201 // now get min/max values
3202 float xmin, xmax, ymin, ymax;
3203 _polygon_bounding_box_raw(points, border, corner_count, points_count, border_count,
3204 &xmin, &xmax, &ymin, &ymax);
3205
3207 {
3208 dt_print(DT_DEBUG_MASKS, "[masks %s] polygon_fill min max took %0.04f sec\n", mask_form->name,
3209 dt_get_wtime() - start2);
3210 start2 = dt_get_wtime();
3211 }
3212
3214 {
3215 dt_print(DT_DEBUG_MASKS, "[masks %s] polygon_fill clear mask took %0.04f sec\n", mask_form->name,
3216 dt_get_wtime() - start2);
3217 start2 = dt_get_wtime();
3218 }
3219
3220 // deal with polygon if it does not lie outside of roi
3221 if(polygon_in_roi)
3222 {
3223 // second copy of polygon which we can modify when cropping to roi
3224 float *cpoints = dt_pixelpipe_cache_alloc_align_float_cache((size_t)2 * points_count, 0);
3225 if(IS_NULL_PTR(cpoints))
3226 {
3229 return 1;
3230 }
3231 memcpy(cpoints, points, sizeof(float) * 2 * points_count);
3232
3233 // now we clip cpoints to roi -> catch special case when roi lies completely within polygon.
3234 // dirty trick: we allow polygon to extend one pixel beyond height-1. this avoids need of special handling
3235 // of the last roi line in the following edge-flag polygon fill algorithm.
3236 const int crop_success = _polygon_crop_to_roi(cpoints + 2 * (corner_count * 3),
3237 points_count - corner_count * 3, 0,
3238 width - 1, 0, height);
3239 polygon_encircles_roi = polygon_encircles_roi || !crop_success;
3240
3242 {
3243 dt_print(DT_DEBUG_MASKS, "[masks %s] polygon_fill crop to roi took %0.04f sec\n", mask_form->name,
3244 dt_get_wtime() - start2);
3245 start2 = dt_get_wtime();
3246 }
3247
3248 if(polygon_encircles_roi)
3249 {
3250 // roi lies completely within polygon
3251 for(size_t k = 0; k < (size_t)width * height; k++) buffer[k] = 1.0f;
3252 }
3253 else
3254 {
3255 // all other cases
3256
3257 // edge-flag polygon fill: we write all the point around the polygon into the buffer
3258 float xlast = cpoints[(points_count - 1) * 2];
3259 float ylast = cpoints[(points_count - 1) * 2 + 1];
3260
3261 for(int i = corner_count * 3; i < points_count; i++)
3262 {
3263 float xstart = xlast;
3264 float ystart = ylast;
3265
3266 float xend = xlast = cpoints[i * 2];
3267 float yend = ylast = cpoints[i * 2 + 1];
3268
3269 if(ystart > yend)
3270 {
3271 float tmp;
3272 tmp = ystart, ystart = yend, yend = tmp;
3273 tmp = xstart, xstart = xend, xend = tmp;
3274 }
3275
3276 const float m = (xstart - xend) / (ystart - yend); // we don't need special handling of ystart==yend
3277 // as following loop will take care
3278
3279 for(int yy = (int)ceilf(ystart); (float)yy < yend;
3280 yy++) // this would normally never touch the last roi line => see comment further above
3281 {
3282 const float xcross = xstart + m * (yy - ystart);
3283
3284 int xx = floorf(xcross);
3285 if((float)xx + 0.5f <= xcross) xx++;
3286
3287 if(xx < 0 || xx >= width || yy < 0 || yy >= height)
3288 continue; // sanity check just to be on the safe side
3289
3290 const size_t index = (size_t)yy * width + xx;
3291
3292 buffer[index] = 1.0f - buffer[index];
3293 }
3294 }
3295
3297 {
3298 dt_print(DT_DEBUG_MASKS, "[masks %s] polygon_fill draw polygon took %0.04f sec\n", mask_form->name,
3299 dt_get_wtime() - start2);
3300 start2 = dt_get_wtime();
3301 }
3302
3303 // we fill the inside plain
3304 // we don't need to deal with parts of shape outside of roi
3305 const int xxmin = MAX(xmin, 0);
3306 const int xxmax = MIN(xmax, width - 1);
3307 const int yymin = MAX(ymin, 0);
3308 const int yymax = MIN(ymax, height - 1);
3309 __OMP_PARALLEL_FOR__(if((size_t)(yymax - yymin + 1) * (size_t)(xxmax - xxmin + 1) > 50000))
3310 for(int yy = yymin; yy <= yymax; yy++)
3311 {
3312 float *const restrict row = buffer + (size_t)yy * width;
3313 int state = 0;
3314 for(int xx = xxmin; xx <= xxmax; xx++)
3315 {
3316 const float v = row[xx];
3317 if(v > 0.5f) state = !state;
3318 if(state) row[xx] = 1.0f;
3319 }
3320 }
3321
3323 {
3324 dt_print(DT_DEBUG_MASKS, "[masks %s] polygon_fill fill plain took %0.04f sec\n", mask_form->name,
3325 dt_get_wtime() - start2);
3326 start2 = dt_get_wtime();
3327 }
3328 }
3330 }
3331
3332 // deal with feather if it does not lie outside of roi
3333 if(!polygon_encircles_roi)
3334 {
3335 const int dpoints_capacity = 4 * border_count * (sparse ? sparse_factor : 1);
3336 int *dpoints = dt_pixelpipe_cache_alloc_align_cache(sizeof(int) * dpoints_capacity, 0);
3337 if(IS_NULL_PTR(dpoints))
3338 {
3341 return 1;
3342 }
3343
3344 int dindex = 0;
3345 int p0[2], p1[2];
3346 float pf1[2];
3347 int prev0[2] = { 0, 0 };
3348 int prev1[2] = { 0, 0 };
3349 gboolean have_prev = FALSE;
3350 int last0[2] = { -100, -100 };
3351 int last1[2] = { -100, -100 };
3352 int next_index = 0;
3353 for(int i = corner_count * 3; i < border_count; i++)
3354 {
3355 p0[0] = floorf(points[i * 2] + 0.5f);
3356 p0[1] = ceilf(points[i * 2 + 1]);
3357 if(next_index > 0)
3358 {
3359 p1[0] = pf1[0] = border[next_index * 2];
3360 p1[1] = pf1[1] = border[next_index * 2 + 1];
3361 }
3362 else
3363 {
3364 p1[0] = pf1[0] = border[i * 2];
3365 p1[1] = pf1[1] = border[i * 2 + 1];
3366 }
3367
3368 // now we check p1 value to know if we have to skip a part
3369 if(next_index == i) next_index = 0;
3370 while(isnan(pf1[0]))
3371 {
3372 if(isnan(pf1[1]))
3373 next_index = i - 1;
3374 else
3375 next_index = p1[1];
3376 p1[0] = pf1[0] = border[next_index * 2];
3377 p1[1] = pf1[1] = border[next_index * 2 + 1];
3378 }
3379
3380 const gboolean used_next = (next_index > 0);
3381
3382 if(sparse && have_prev && !used_next
3383 && (prev0[0] != p0[0] || prev0[1] != p0[1] || prev1[0] != p1[0] || prev1[1] != p1[1]))
3384 {
3385 for(int k = 1; k < sparse_factor; k++)
3386 {
3387 const float t = (float)k / (float)sparse_factor;
3388 const int mp0[2] = { (int)floorf(prev0[0] + t * (p0[0] - prev0[0]) + 0.5f),
3389 (int)floorf(prev0[1] + t * (p0[1] - prev0[1]) + 0.5f) };
3390 const int mp1[2] = { (int)floorf(prev1[0] + t * (p1[0] - prev1[0]) + 0.5f),
3391 (int)floorf(prev1[1] + t * (p1[1] - prev1[1]) + 0.5f) };
3392 if(dindex + 4 <= dpoints_capacity)
3393 {
3394 dpoints[dindex] = mp0[0];
3395 dpoints[dindex + 1] = mp0[1];
3396 dpoints[dindex + 2] = mp1[0];
3397 dpoints[dindex + 3] = mp1[1];
3398 dindex += 4;
3399 }
3400 }
3401 }
3402
3403 // and we draw the falloff
3404 if(last0[0] != p0[0] || last0[1] != p0[1] || last1[0] != p1[0] || last1[1] != p1[1])
3405 {
3406 dpoints[dindex] = p0[0];
3407 dpoints[dindex + 1] = p0[1];
3408 dpoints[dindex + 2] = p1[0];
3409 dpoints[dindex + 3] = p1[1];
3410 dindex += 4;
3411
3412 last0[0] = p0[0];
3413 last0[1] = p0[1];
3414 last1[0] = p1[0];
3415 last1[1] = p1[1];
3416 }
3417
3418 if(!used_next)
3419 {
3420 prev0[0] = p0[0];
3421 prev0[1] = p0[1];
3422 prev1[0] = p1[0];
3423 prev1[1] = p1[1];
3424 have_prev = TRUE;
3425 }
3426 else
3427 {
3428 have_prev = FALSE;
3429 }
3430 }
3431 __OMP_PARALLEL_FOR__(if(dindex > 4096))
3432 for(int n = 0; n < dindex; n += 4)
3433 _polygon_falloff_roi(buffer, dpoints + n, dpoints + n + 2, width, height);
3434
3436
3438 {
3439 dt_print(DT_DEBUG_MASKS, "[masks %s] polygon_fill fill falloff took %0.04f sec\n",
3440 mask_form->name,
3441 dt_get_wtime() - start2);
3442 }
3443 }
3444
3447
3449 dt_print(DT_DEBUG_MASKS, "[masks %s] polygon fill buffer took %0.04f sec\n",
3450 mask_form->name,
3451 dt_get_wtime() - start);
3452
3453 return 0;
3454}
3455
3457{
3458 // nothing to do (yet?)
3459}
3460
3464static void _polygon_set_form_name(struct dt_masks_form_t *const mask_form, const size_t form_number)
3465{
3466 snprintf(mask_form->name, sizeof(mask_form->name), _("polygon #%d"), (int)form_number);
3467}
3468
3469static void _polygon_set_hint_message(const dt_masks_form_gui_t *const mask_gui,
3470 const dt_masks_form_t *const mask_form,
3471 const int opacity, char *const restrict msgbuf,
3472 const size_t msgbuf_len)
3473{
3474 const guint node_count = mask_form->points ? g_list_length(mask_form->points) : 0;
3475 if(mask_gui->creation && node_count < 4)
3476 g_strlcat(msgbuf, _("<b>Add node</b>: click, <b>Add sharp node</b>:ctrl+click\n"
3477 "<b>Cancel</b>: right-click or Esc"), msgbuf_len);
3478 else if(mask_gui->creation)
3479 g_strlcat(msgbuf, _("<b>Add node</b>: click, <b>Add sharp node</b>:ctrl+click\n"
3480 "<b>Finish polygon</b>: Enter or click on first node"), msgbuf_len);
3481 else if(mask_gui->handle_selected)
3482 g_strlcat(msgbuf, _("<b>Node curvature</b>: drag\n<b>Reset curvature</b>: right-click"), msgbuf_len);
3483 else if(mask_gui->node_selected)
3484 g_strlcat(msgbuf, _("<b>NODE:</b> <b>Move</b>: drag, <b>Delete</b>: right-click or Del\n"
3485 "<b>Hardness</b>: scroll, <b>Switch smooth/sharp</b>: ctrl+click"), msgbuf_len);
3486 else if(mask_gui->node_hovered >= 0)
3487 g_strlcat(msgbuf, _("<b>Move node</b>: drag\n<b>Delete node</b>: right-click\n"
3488 "<b>Hardness</b>: scroll, <b>Switch smooth/sharp</b>: ctrl+click"), msgbuf_len);
3489 else if(mask_gui->seg_selected)
3490 g_strlcat(msgbuf, _("<b>Move segment</b>: drag\n<b>Add node</b>: ctrl+click"), msgbuf_len);
3491 else if(mask_gui->form_selected)
3492 g_snprintf(msgbuf, msgbuf_len, _("<b>Size</b>: scroll, <b>Hardness</b>: shift+scroll\n"
3493 "<b>Opacity</b>: ctrl+scroll (%d%%)"), opacity);
3494}
3495
3496static void _polygon_duplicate_points(dt_develop_t *const dev, dt_masks_form_t *const base, dt_masks_form_t *const dest)
3497{
3498 // unused arg, keep compiler from complaining
3500}
3501
3502static void _polygon_initial_source_pos(const float iwd, const float iht, float *x, float *y)
3503{
3504
3505
3506 float offset[2] = { 0.1f, 0.1f };
3508 *x = offset[0];
3509 *y = offset[1];
3510}
3511
3512static void _polygon_creation_closing_form_callback(GtkWidget *widget, gpointer user_data)
3513{
3514 dt_masks_form_gui_t *mask_gui = (dt_masks_form_gui_t *)user_data;
3515 // This is a temp form on creation mode
3517 if(IS_NULL_PTR(mask_form)) return;
3518
3519 _polygon_creation_closing_form(mask_form, mask_gui);
3520}
3521
3522static void _polygon_switch_node_callback(GtkWidget *widget, gpointer user_data)
3523{
3524 dt_masks_form_gui_t *mask_gui = (dt_masks_form_gui_t *)user_data;
3525 if(IS_NULL_PTR(mask_gui)) return;
3526 dt_iop_module_t *module = darktable.develop->gui_module;
3527 if(IS_NULL_PTR(module)) return;
3528 const int form_id = mask_gui->formid;
3529 dt_masks_form_t *selected_form = dt_masks_get_from_id(darktable.develop, form_id);
3530 if(IS_NULL_PTR(selected_form)) return;
3531
3532 mask_gui->node_selected = TRUE;
3533 mask_gui->node_selected_idx = mask_gui->node_hovered;
3534 dt_masks_form_gui_points_t *gui_points
3535 = (dt_masks_form_gui_points_t *)g_list_nth_data(mask_gui->points, mask_gui->group_selected);
3536 const int node_index = dt_masks_gui_selected_node_index(mask_gui);
3538 = (dt_masks_node_polygon_t *)g_list_nth_data(selected_form->points, node_index);
3539 if(IS_NULL_PTR(gui_points) || IS_NULL_PTR(node)) return;
3540 dt_masks_toggle_bezier_node_type(module, selected_form, mask_gui, mask_gui->group_selected, gui_points,
3541 node_index, node->node, node->ctrl1, node->ctrl2, &node->state);
3542}
3543
3544static void _polygon_reset_round_node_callback(GtkWidget *widget, gpointer user_data)
3545{
3546 dt_masks_form_gui_t *mask_gui = (dt_masks_form_gui_t *)user_data;
3547 if(IS_NULL_PTR(mask_gui)) return;
3548 dt_iop_module_t *module = darktable.develop->gui_module;
3549 if(IS_NULL_PTR(module)) return;
3550 const int form_id = mask_gui->formid;
3551 dt_masks_form_t *selected_form = dt_masks_get_from_id(darktable.develop, form_id);
3552 if(IS_NULL_PTR(selected_form)) return;
3553
3554 mask_gui->node_selected = TRUE;
3555 mask_gui->node_selected_idx = mask_gui->node_hovered;
3556 dt_masks_form_gui_points_t *gui_points
3557 = (dt_masks_form_gui_points_t *)g_list_nth_data(mask_gui->points, mask_gui->group_selected);
3558 const int selected_handle = dt_masks_gui_selected_handle_index(mask_gui);
3559 const int node_index = MAX(mask_gui->node_hovered, selected_handle);
3561 = (dt_masks_node_polygon_t *)g_list_nth_data(selected_form->points, node_index);
3562 if(IS_NULL_PTR(gui_points) || IS_NULL_PTR(node)) return;
3563 if(dt_masks_reset_bezier_ctrl_points(module, selected_form, mask_gui, mask_gui->group_selected, gui_points,
3564 node_index, &node->state))
3565 gui_points->clockwise = _polygon_is_clockwise(selected_form);
3566}
3567
3568static void _polygon_add_node_callback(GtkWidget *menu, gpointer user_data)
3569{
3570 dt_masks_form_gui_t *mask_gui = (dt_masks_form_gui_t *)user_data;
3571 if(IS_NULL_PTR(mask_gui)) return;
3573 if(IS_NULL_PTR(visible_forms)) return;
3574
3575 dt_iop_module_t *module = darktable.develop->gui_module;
3576 if(IS_NULL_PTR(module)) return;
3577
3578 dt_masks_form_group_t *group_entry = dt_masks_form_get_selected_group(visible_forms, mask_gui);
3579 if(IS_NULL_PTR(group_entry)) return;
3580 dt_masks_form_t *selected_form = dt_masks_get_from_id(darktable.develop, group_entry->formid);
3581
3582 if(selected_form)
3583 {
3584 _add_node_to_segment(module, selected_form, group_entry->parentid, mask_gui, mask_gui->group_selected);
3585 }
3586
3587 //dt_dev_add_history_item(darktable.develop, module, TRUE, TRUE);
3588}
3589
3591 struct dt_masks_form_gui_t *mask_gui,
3592 const float pzx, const float pzy)
3593{
3594
3595
3596 GtkWidget *menu_item = NULL;
3597 gchar *accel = g_strdup_printf(_("%s+Click"), gtk_accelerator_get_label(0, GDK_CONTROL_MASK));
3598
3599 gboolean ret = FALSE;
3600
3601 if(mask_gui->creation)
3602 {
3603 menu_item = ctx_gtk_menu_item_new_with_markup(_("Close path"), menu,
3605 gtk_widget_set_sensitive(menu_item, mask_form->points && !g_list_shorter_than(mask_form->points, 4));
3606 menu_item_set_fake_accel(menu_item, GDK_KEY_Return, 0);
3607
3608 menu_item = ctx_gtk_menu_item_new_with_markup(_("Remove last point"), menu,
3610 menu_item_set_fake_accel(menu_item, GDK_KEY_BackSpace, 0);
3611
3612 ret = TRUE;
3613 }
3614
3615 else if(mask_gui->node_hovered >= 0)
3616 {
3617 dt_masks_form_gui_points_t *gui_points
3618 = (dt_masks_form_gui_points_t *)g_list_nth_data(mask_gui->points, mask_gui->group_selected);
3619 if(IS_NULL_PTR(gui_points)) goto end;
3620 dt_masks_node_polygon_t *node = (dt_masks_node_polygon_t *)g_list_nth_data(mask_form->points, mask_gui->node_hovered);
3621 if(IS_NULL_PTR(node)) goto end;
3622 const gboolean is_corner = dt_masks_node_is_cusp(gui_points, mask_gui->node_hovered);
3623
3624 {
3625 gchar *to_change_type = g_strdup_printf(_("Switch to %s node"), (is_corner) ? _("round") : _("cusp"));
3626 const dt_menu_icon_t icon = is_corner ? DT_MENU_ICON_CIRCLE : DT_MENU_ICON_SQUARE;
3627 menu_item = ctx_gtk_menu_item_new_with_icon_and_shortcut(to_change_type, accel, menu,
3628 _polygon_switch_node_callback, mask_gui, icon);
3629
3630 dt_free(to_change_type);
3631 }
3632
3633 {
3634 menu_item = ctx_gtk_menu_item_new_with_markup(_("Reset round node"), menu,
3636 gtk_widget_set_sensitive(menu_item, !is_corner);
3637 }
3638
3639 ret = TRUE;
3640 }
3641
3642 if(mask_gui->seg_selected)
3643 {
3644 menu_item = ctx_gtk_menu_item_new_with_markup_and_shortcut(_("Add a node here"), accel,
3645 menu, _polygon_add_node_callback, mask_gui);
3646 ret = TRUE;
3647 }
3648
3649 end:
3650 dt_free(accel);
3651 return ret;
3652}
3653
3654// The function table for polygons. This must be public, i.e. no "static" keyword.
3657 .sanitize_config = _polygon_sanitize_config,
3658 .set_form_name = _polygon_set_form_name,
3659 .set_hint_message = _polygon_set_hint_message,
3660 .duplicate_points = _polygon_duplicate_points,
3661 .initial_source_pos = _polygon_initial_source_pos,
3662 .get_distance = _polygon_get_distance,
3663 .get_points_border = _polygon_get_points_border,
3664 .get_mask = _polygon_get_mask,
3665 .get_mask_roi = _polygon_get_mask_roi,
3666 .get_area = _polygon_get_area,
3667 .get_source_area = _polygon_get_source_area,
3668 .get_gravity_center = _polygon_get_gravity_center,
3669 .get_interaction_value = _polygon_get_interaction_value,
3670 .set_interaction_value = _polygon_set_interaction_value,
3671 .update_hover = _find_closest_handle,
3672 .mouse_moved = _polygon_events_mouse_moved,
3673 .mouse_scrolled = _polygon_events_mouse_scrolled,
3674 .button_pressed = _polygon_events_button_pressed,
3675 .button_released = _polygon_events_button_released,
3676 .key_pressed = _polygon_events_key_pressed,
3677 .post_expose = _polygon_events_post_expose,
3678 .draw_shape = _polygon_draw_shape,
3679 .init_ctrl_points = _polygon_init_ctrl_points,
3680 .populate_context_menu = _polygon_populate_context_menu
3681};
3682
3683
3684// clang-format off
3685// modelines: These editor modelines have been set for all relevant files by tools/update_modelines.py
3686// vim: shiftwidth=2 expandtab tabstop=2 cindent
3687// kate: tab-indents: off; indent-width 2; replace-tabs on; indent-mode cstyle; remove-trailing-spaces modified;
3688// 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:824
darktable_t darktable
Definition darktable.c:183
void dt_print(dt_debug_thread_t thread, const char *msg,...)
Definition darktable.c:1600
#define dt_free_align(ptr)
Definition darktable.h:503
@ DT_DEBUG_PERF
Definition darktable.h:741
@ DT_DEBUG_MASKS
Definition darktable.h:749
#define dt_pixelpipe_cache_alloc_align_float_cache(pixels, id)
Definition darktable.h:459
static float * dt_alloc_align_float(size_t pixels)
Definition darktable.h:516
#define dt_pixelpipe_cache_alloc_align_cache(size, id)
Definition darktable.h:445
static const GList * g_list_next_wraparound(const GList *list, const GList *head)
Definition darktable.h:1019
#define dt_free(ptr)
Definition darktable.h:478
#define dt_pixelpipe_cache_free_align(mem)
Definition darktable.h:475
static GList * g_list_next_bounded(GList *list)
Definition darktable.h:1014
#define __OMP_PARALLEL_FOR__(...)
Definition darktable.h:270
static const dt_aligned_pixel_simd_t value
Definition darktable.h:599
static double dt_get_wtime(void)
Definition darktable.h:976
#define __OMP_PARALLEL_FOR_SIMD__(...)
Definition darktable.h:271
static gboolean dt_modifier_is(const GdkModifierType state, const GdkModifierType desired_modifier_mask)
Definition darktable.h:955
#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:293
static gboolean g_list_shorter_than(const GList *list, unsigned len)
Definition darktable.h:1001
#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:1212
void dt_dev_coordinates_raw_norm_to_raw_abs(dt_develop_t *dev, float *points, size_t num_points)
Definition develop.c:1162
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:1621
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:414
void dt_dev_coordinates_image_abs_to_raw_norm(dt_develop_t *dev, float *points, size_t num_points)
Definition develop.c:1191
@ 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:632
static int dt_masks_gui_selected_segment_index(const dt_masks_form_gui_t *gui)
Definition masks.h:563
static int dt_masks_gui_selected_node_index(const dt_masks_form_gui_t *gui)
Definition masks.h:548
static int dt_masks_gui_selected_handle_index(const dt_masks_form_gui_t *gui)
Definition masks.h:553
static float * dt_masks_dynbuf_buffer(dt_masks_dynbuf_t *a)
Definition masks.h:1367
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:696
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:1294
@ DT_MASKS_EDIT_FULL
Definition masks.h:203
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:1141
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:1274
static void dt_masks_translate_source(dt_masks_form_t *form, const float delta_x, const float delta_y)
Definition masks.h:666
static float dt_masks_dynbuf_get(dt_masks_dynbuf_t *a, int offset)
Definition masks.h:1349
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:672
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:577
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:1309
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:558
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:130
@ DT_MASKS_CLONE
Definition masks.h:135
@ DT_MASKS_GROUP
Definition masks.h:134
@ DT_MASKS_IS_RETOUCHE
Definition masks.h:147
dt_masks_interaction_t
Definition masks.h:303
@ DT_MASKS_INTERACTION_HARDNESS
Definition masks.h:306
@ DT_MASKS_INTERACTION_SIZE
Definition masks.h:305
#define menu_item_set_fake_accel(menu_item, keyval, mods)
Definition masks.h:1556
void _masks_gui_delete_node_callback(GtkWidget *menu, gpointer user_data)
Definition masks_gui.c:541
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:617
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:771
static gboolean dt_masks_center_of_gravity_from_points(const float *points, const int points_count, float center[2], float *area)
Definition masks.h:1373
static size_t dt_masks_dynbuf_position(dt_masks_dynbuf_t *a)
Definition masks.h:1444
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:568
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:640
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:1329
dt_masks_increment_t
Definition masks.h:194
@ DT_MASKS_INCREMENT_SCALE
Definition masks.h:196
@ DT_MASKS_INCREMENT_OFFSET
Definition masks.h:197
@ DT_MASKS_INCREMENT_ABSOLUTE
Definition masks.h:195
dt_masks_form_t * dt_masks_get_from_id(dt_develop_t *dev, int id)
@ DT_MASKS_POINT_STATE_NORMAL
Definition masks.h:183
@ DT_MASKS_POINT_STATE_USER
Definition masks.h:184
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:746
static float * dt_masks_dynbuf_harvest(dt_masks_dynbuf_t *a)
Definition masks.h:1456
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:1358
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:724
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:625
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:1450
static void dt_masks_dynbuf_free(dt_masks_dynbuf_t *a)
Definition masks.h:1466
static void dt_masks_set_ctrl_points(float ctrl1[2], float ctrl2[2], const float control_points[4])
Definition masks.h:683
#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:129
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:172
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:187
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:1253
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:3590
static int _find_closest_handle(dt_masks_form_t *mask_form, dt_masks_form_gui_t *mask_gui, int form_index)
Definition polygon.c:1463
#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:2364
static float _polygon_get_interaction_value(const dt_masks_form_t *mask_form, dt_masks_interaction_t interaction)
Definition polygon.c:1220
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:2321
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:3060
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:1086
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:1703
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:1430
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:2173
static void _polygon_switch_node_callback(GtkWidget *widget, gpointer user_data)
Definition polygon.c:3522
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:1561
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:1286
#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:693
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:1511
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:1048
static void _polygon_falloff_roi(float *buffer, int *p0, int *p1, int bw, int bh)
Definition polygon.c:3021
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:2415
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:3464
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:1971
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:1726
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:1630
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:1672
static void _polygon_initial_source_pos(const float iwd, const float iht, float *x, float *y)
Definition polygon.c:3502
const dt_masks_functions_t dt_masks_functions_polygon
Definition polygon.c:3655
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:1161
static void _polygon_add_node_callback(GtkWidget *menu, gpointer user_data)
Definition polygon.c:3568
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:1442
static int _polygon_crop_to_roi(float *polygon, const int point_count, float xmin, float xmax, float ymin, float ymax)
Definition polygon.c:2726
static void _polygon_translate_node(dt_masks_node_polygon_t *node, const float delta_x, const float delta_y)
Definition polygon.c:1138
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:1904
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:2426
static void _polygon_creation_closing_form_callback(GtkWidget *widget, gpointer user_data)
Definition polygon.c:3512
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:1454
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:1919
static void _polygon_translate_all_nodes(dt_masks_form_t *mask_form, const float delta_x, const float delta_y)
Definition polygon.c:1143
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:2408
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:3469
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:3496
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:2451
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:1313
static void _polygon_reset_round_node_callback(GtkWidget *widget, gpointer user_data)
Definition polygon.c:3544
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:1545
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:2142
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:1149
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:2378
static void _polygon_sanitize_config(dt_masks_type_t type)
Definition polygon.c:3456
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:1476
struct _GtkWidget GtkWidget
Definition splash.h:29
const float uint32_t state[4]
const float r
int32_t unmuted
Definition darktable.h:788
struct dt_develop_t * develop
Definition darktable.h:798
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:333
Region of interest passed through the pixelpipe.
Definition imageop.h:72
double scale
Definition imageop.h:74
gboolean node_selected
Definition masks.h:484
gboolean source_selected
Definition masks.h:492
int handle_border_dragging
Definition masks.h:507
int handle_border_hovered
Definition masks.h:482
gboolean source_dragging
Definition masks.h:500
dt_masks_edit_mode_t edit_mode
Definition masks.h:477
dt_iop_module_t * creation_module
Definition masks.h:519
gboolean creation_closing_form
Definition masks.h:518
gboolean seg_selected
Definition masks.h:486
gboolean form_dragging
Definition masks.h:499
gboolean creation
Definition masks.h:517
dt_masks_type_t type
Definition masks.h:442
gboolean form_selected
Definition masks.h:490
gboolean handle_selected
Definition masks.h:485
gboolean border_selected
Definition masks.h:491
float delta[2]
Definition masks.h:469
dt_masks_type_t type
Definition masks.h:380
float source[2]
Definition masks.h:387
char name[128]
Definition masks.h:403
GList * points
Definition masks.h:379
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:370
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:255
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