Ansel 0.0
A darktable fork - bloat + design vision
Loading...
Searching...
No Matches
circle.c
Go to the documentation of this file.
1/*
2 This file is part of darktable,
3 Copyright (C) 2013-2014, 2016, 2021 Aldric Renaudin.
4 Copyright (C) 2013, 2018, 2020-2022 Pascal Obry.
5 Copyright (C) 2013 Simon Spannagel.
6 Copyright (C) 2013-2017 Tobias Ellinghaus.
7 Copyright (C) 2013-2016, 2019 Ulrich Pegelow.
8 Copyright (C) 2014-2016 Roman Lebedev.
9 Copyright (C) 2017-2019 Edgardo Hoszowski.
10 Copyright (C) 2018 johannes hanika.
11 Copyright (C) 2019 Andreas Schneider.
12 Copyright (C) 2019, 2023, 2025-2026 Aurélien PIERRE.
13 Copyright (C) 2019 Matthieu Moy.
14 Copyright (C) 2020, 2022 Chris Elston.
15 Copyright (C) 2020 GrahamByrnes.
16 Copyright (C) 2020 Heiko Bauke.
17 Copyright (C) 2020-2021 Hubert Kowalski.
18 Copyright (C) 2020-2021 Ralf Brown.
19 Copyright (C) 2021 Diederik Ter Rahe.
20 Copyright (C) 2022 Martin Bařinka.
21 Copyright (C) 2025-2026 Guillaume Stutin.
22
23 darktable is free software: you can redistribute it and/or modify
24 it under the terms of the GNU General Public License as published by
25 the Free Software Foundation, either version 3 of the License, or
26 (at your option) any later version.
27
28 darktable is distributed in the hope that it will be useful,
29 but WITHOUT ANY WARRANTY; without even the implied warranty of
30 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
31 GNU General Public License for more details.
32
33 You should have received a copy of the GNU General Public License
34 along with darktable. If not, see <http://www.gnu.org/licenses/>.
35*/
36#include "bauhaus/bauhaus.h"
37#include "common/debug.h"
38#include "common/undo.h"
39#include "control/conf.h"
40#include "develop/blend.h"
41#include "develop/imageop.h"
42#include "develop/masks.h"
44
45#define HARDNESS_MIN 0.0005f
46#define HARDNESS_MAX 1.0f
47
48#define BORDER_MIN 0.00005f
49#define BORDER_MAX 0.5f
50
51static void _circle_get_distance(float x, float y, float as, dt_masks_form_gui_t *gui, int index,
52 int num_points, int *inside, int *inside_border, int *near,
53 int *inside_source, float *dist)
54{
55 // initialise returned values
56 *inside_source = 0;
57 *inside = 0;
58 *inside_border = 0;
59 *near = -1;
60 *dist = FLT_MAX;
61
62 dt_masks_form_gui_points_t *gpt = (dt_masks_form_gui_points_t *)g_list_nth_data(gui->points, index);
63 if(IS_NULL_PTR(gpt)) return;
64
65 // we first check if we are inside the source form
66 const float pt[2] = { x, y };
67
68 if(gpt->source && gpt->source_count > 0
69 && dt_masks_point_in_form_exact(pt, 1, gpt->source, 1, gpt->source_count) >= 0)
70 {
71 *inside_source = 1;
72 *inside = 1;
73
74 // distance from source center
75 const float center_dx = x - gpt->source[0];
76 const float center_dy = y - gpt->source[1];
77 *dist = sqf(center_dx) + sqf(center_dy);
78
79 return;
80 }
81
82 if(IS_NULL_PTR(gpt->points) || gpt->points_count <= 0 || !gpt->border || gpt->border_count <= 0) return;
83
84 // distance from center
85 const float center_dx = x - gpt->points[0];
86 const float center_dy = y - gpt->points[1];
87 *dist = sqf(center_dx) + sqf(center_dy);
88
89 // we check if it's inside borders
90 if(dt_masks_point_in_form_exact(pt, 1, gpt->border, 1, gpt->border_count) < 0) return;
91 *inside = 1;
92
93 // and we check if it's inside form
94 if(dt_masks_point_in_form_exact(pt, 1, gpt->points, 1, gpt->points_count) < 0)
95 *inside_border = 1;
96}
97
101static void _circle_distance_cb(float pointer_x, float pointer_y, float cursor_radius,
102 dt_masks_form_gui_t *mask_gui, int form_index, int node_count, int *inside,
103 int *inside_border, int *near, int *inside_source, float *dist, void *user_data)
104{
105 _circle_get_distance(pointer_x, pointer_y, cursor_radius, mask_gui, form_index, 0, inside,
106 inside_border, near, inside_source, dist);
107}
108
109static int _find_closest_handle(dt_masks_form_t *mask_form, dt_masks_form_gui_t *mask_gui, int form_index)
110{
111 return dt_masks_find_closest_handle_common(mask_form, mask_gui, form_index, 0,
112 NULL, NULL, NULL, _circle_distance_cb, NULL, NULL);
113}
114
115static void _circle_get_creation_values(const dt_masks_form_t *form, float *radius, float *border)
116{
117 const gboolean use_spot_defaults = dt_masks_form_uses_spot_defaults(form);
118 *radius = dt_conf_get_float(use_spot_defaults ? "plugins/darkroom/spots/circle/size"
119 : "plugins/darkroom/masks/circle/size");
120 *border = dt_conf_get_float(use_spot_defaults ? "plugins/darkroom/spots/circle/border"
121 : "plugins/darkroom/masks/circle/border");
122}
123
134
135static int _circle_get_points(dt_develop_t *dev, float x, float y, float radius, float radius2, float rotation,
136 float **points, int *points_count);
137
138// Build the temporary preview geometry for circle creation so the expose path only
139// handles drawing and buffer lifetime.
142{
143 float radius_shape = 0.0f;
144 float radius_border = 0.0f;
145 _circle_get_creation_values(form, &radius_shape, &radius_border);
146 radius_border += radius_shape;
147
148 float center[2];
150
151 *preview = (dt_masks_preview_buffers_t){ 0 };
152 int err = _circle_get_points(darktable.develop, center[0], center[1], radius_shape, 0.0f, 0.0f,
153 &preview->points, &preview->points_count);
154 if(!err && radius_shape != radius_border)
155 err = _circle_get_points(darktable.develop, center[0], center[1], radius_border, 0.0f, 0.0f,
156 &preview->border, &preview->border_count);
157
158 if(!err && dt_masks_form_is_clone(form))
159 {
160 float source_pos[2] = { 0.0f, 0.0f };
161 dt_masks_calculate_source_pos_origin(gui, gui->pos[0], gui->pos[1], gui->pos[0], gui->pos[1],
162 &source_pos[0], &source_pos[1], FALSE);
163 const float center_source[2] = { source_pos[0] - gui->pos[0], source_pos[1] - gui->pos[1] };
164
166 if(IS_NULL_PTR(preview->source_points))
167 err = 1;
168
169 for(int i = 0; !err && i < preview->points_count; i++)
170 {
171 preview->source_points[i * 2] = preview->points[i * 2] + center_source[0];
172 preview->source_points[i * 2 + 1] = preview->points[i * 2 + 1] + center_source[1];
173 }
174 }
175
176 if(err)
178
179 return err;
180}
181
182static int _init_hardness(dt_masks_form_t *form, const float amount, const dt_masks_increment_t increment, const int flow)
183{
185 increment, flow, _("Hardness: %3.2f%%"), 100.0f);
186 return 1;
187}
188
189static int _init_size(dt_masks_form_t *form, const float amount, const dt_masks_increment_t increment, const int flow)
190{
191
193 increment, flow, _("Size: %3.2f%%"), 2.f * 100.f);
194 return 1;
195}
196
197static int _init_opacity(dt_masks_form_t *form, const float amount, const dt_masks_increment_t increment, const int flow)
198{
199 dt_masks_get_set_conf_value_with_toast(form, "opacity", amount, 0.f, 1.f,
200 increment, flow, _("Opacity: %3.2f%%"), 100.f);
201 return 1;
202}
203
205{
206 if(IS_NULL_PTR(form) || IS_NULL_PTR(form->points)) return NAN;
207 const dt_masks_node_circle_t *circle = (const dt_masks_node_circle_t *)(form->points)->data;
208 if(IS_NULL_PTR(circle)) return NAN;
209
210 switch(interaction)
211 {
213 return circle->radius;
215 return circle->border;
216 default:
217 return NAN;
218 }
219}
220
221static gboolean _circle_get_gravity_center(const dt_masks_form_t *form, float center[2], float *area)
222{
223 if(IS_NULL_PTR(form) || IS_NULL_PTR(form->points) || IS_NULL_PTR(center) || IS_NULL_PTR(area)) return FALSE;
224 const dt_masks_node_circle_t *circle = (const dt_masks_node_circle_t *)(form->points)->data;
225 if(IS_NULL_PTR(circle)) return FALSE;
226 center[0] = circle->center[0];
227 center[1] = circle->center[1];
228 *area = M_PI_F * sqf(circle->radius);
229 return TRUE;
230}
231
232static int _change_hardness(dt_masks_form_t *form, dt_masks_form_gui_t *gui, struct dt_iop_module_t *module,
233 int index, const float amount, const dt_masks_increment_t increment, const int flow);
234static int _change_size(dt_masks_form_t *form, dt_masks_form_gui_t *gui, struct dt_iop_module_t *module,
235 int index, const float amount, const dt_masks_increment_t increment, const int flow);
236
238 dt_masks_increment_t increment, int flow,
239 dt_masks_form_gui_t *gui, struct dt_iop_module_t *module)
240{
241 if(IS_NULL_PTR(form)) return NAN;
242 const int index = 0;
243
244 switch(interaction)
245 {
247 if(!_change_size(form, gui, module, index, value, increment, flow)) return NAN;
248 return _circle_get_interaction_value(form, interaction);
250 if(!_change_hardness(form, gui, module, index, value, increment, flow)) return NAN;
251 return _circle_get_interaction_value(form, interaction);
252 default:
253 return NAN;
254 }
255}
256
257static int _change_hardness(dt_masks_form_t *form, dt_masks_form_gui_t *gui, struct dt_iop_module_t *module, int index, const float amount, const dt_masks_increment_t increment, const int flow)
258{
259 if(IS_NULL_PTR(form) || IS_NULL_PTR(form->points)) return 0;
260 dt_masks_node_circle_t *circle = (dt_masks_node_circle_t *)(form->points)->data;
261 if(IS_NULL_PTR(circle)) return 0;
262
263 circle->border = CLAMPF(dt_masks_apply_increment(circle->border, amount, increment, flow),
265
266 _init_hardness(form, amount, increment, flow);
267
268 // we recreate the form points
269 dt_masks_gui_form_create(form, gui, index, module);
270
271 return 1;
272}
273
274static int _change_size(dt_masks_form_t *form, dt_masks_form_gui_t *gui, struct dt_iop_module_t *module, int index, const float amount, const dt_masks_increment_t increment, const int flow)
275{
276 if(IS_NULL_PTR(form) || IS_NULL_PTR(form->points)) return 0;
277 dt_masks_node_circle_t *circle = (dt_masks_node_circle_t *)(form->points)->data;
278 if(IS_NULL_PTR(circle)) return 0;
279
280 // Sanitize
281 // do not exceed upper limit of 1.0 and lower limit of 0.004
282 if(amount > 1.0f && (circle->border > 1.0f ))
283 return 1;
284
285 const int node_hovered = gui->node_hovered;
286
287 // Growing/shrinking
288 if(node_hovered == -1 || node_hovered == 0)
289 {
290 circle->radius = dt_masks_apply_increment(circle->radius, amount, increment, flow);
291 }
292
293 _init_size(form, amount, increment, flow);
294
295 // we recreate the form points
296 dt_masks_gui_form_create(form, gui, index, module);
297
298 return 1;
299}
300
301/* Shape handlers receive widget-space coordinates, while normalized output-image
302 * coordinates come from `gui->rel_pos` and absolute output-image
303 * coordinates come from `gui->pos`. */
304static int _circle_events_mouse_scrolled(struct dt_iop_module_t *module, double x, double y, int up, const int flow,
305 uint32_t state, dt_masks_form_t *form, int parentid,
306 dt_masks_form_gui_t *gui, int index,
307 dt_masks_interaction_t interaction)
308{
309
310
311
312 if(gui->creation)
313 {
314 if(dt_modifier_is(state, GDK_CONTROL_MASK))
315 return _init_opacity(form, up ? +0.02f : -0.02f, DT_MASKS_INCREMENT_OFFSET, flow);
316 else if(dt_modifier_is(state, GDK_SHIFT_MASK))
317 return _init_hardness(form, up ? +1.02f : 0.98f, DT_MASKS_INCREMENT_SCALE, flow);
318 else
319 return _init_size(form, up ? +1.02f : 0.98f, DT_MASKS_INCREMENT_SCALE, flow);
320 }
321 else if(gui->form_selected)
322 {
323 if(dt_modifier_is(state, GDK_CONTROL_MASK))
324 return dt_masks_form_change_opacity(form, parentid, up, flow);
325 else if(dt_modifier_is(state, GDK_SHIFT_MASK))
326 return _change_hardness(form, gui, module, index, up ? +1.02f : 0.98f, DT_MASKS_INCREMENT_SCALE, flow);
327 else
328 return _change_size(form, gui, module, index, up ? +1.02f : 0.98f, DT_MASKS_INCREMENT_SCALE, flow);
329 }
330 return 0;
331}
332
333static int _circle_events_button_pressed(struct dt_iop_module_t *module, double x, double y,
334 double pressure, int which, int type, uint32_t state,
335 dt_masks_form_t *form, int parentid, dt_masks_form_gui_t *gui, int index)
336{
337 if(which == 1)
338 {
339 if(gui->creation)
340 {
341 if((dt_modifier_is(state, GDK_CONTROL_MASK | GDK_SHIFT_MASK)) || dt_modifier_is(state, GDK_SHIFT_MASK))
342 {
343 // set some absolute or relative position for the source of the clone mask
345 return 1;
346 }
347
348 dt_iop_module_t *crea_module = gui->creation_module;
349 // we create the circle
351 if(IS_NULL_PTR(circle)) return 0;
352
353 _circle_init_new(form, gui, circle);
354 form->points = g_list_append(form->points, circle);
355 dt_masks_gui_form_save_creation(darktable.develop, crea_module, form, gui);
356
357 return 1;
358 }
359 else // creation is FALSE
360 {
361 dt_masks_form_gui_points_t *gpt = (dt_masks_form_gui_points_t *)g_list_nth_data(gui->points, index);
362 if(IS_NULL_PTR(gpt)) return 0;
363
365 {
366 // we start the source dragging
367 gui->delta[0] = gpt->source[0] - gui->pos[0];
368 gui->delta[1] = gpt->source[1] - gui->pos[1];
369 return 1;
370 }
371 else if(gui->form_selected && gui->edit_mode == DT_MASKS_EDIT_FULL)
372 {
373 // we start the form dragging
374 gui->delta[0] = gpt->points[0] - gui->pos[0];
375 gui->delta[1] = gpt->points[1] - gui->pos[1];
376 return 1;
377 }
378 else if(gui->handle_hovered >= 0 && gui->edit_mode == DT_MASKS_EDIT_FULL)
379 {
380 return 1;
381 }
382 }
383 }
384
385 return 0;
386}
387
388static int _circle_events_button_released(struct dt_iop_module_t *module, double x, double y, int which,
389 uint32_t state, dt_masks_form_t *form, int parentid,
390 dt_masks_form_gui_t *gui, int index)
391{
392
393
394
395
396
397
398
399 if(gui->form_dragging)
400 {
401 // we end the form dragging
402 return 1;
403 }
404 else if(gui->source_dragging)
405 {
406 return 1;
407 }
408 return 0;
409}
410
411static int _circle_events_key_pressed(struct dt_iop_module_t *module, GdkEventKey *event, dt_masks_form_t *form,
412 int parentid, dt_masks_form_gui_t *gui, int index)
413{
414 return 0;
415}
416
417static int _circle_events_mouse_moved(struct dt_iop_module_t *module, double x, double y, double pressure,
418 int which, dt_masks_form_t *form, int parentid,
419 dt_masks_form_gui_t *gui, int index)
420{
421 if(gui->creation)
422 {
423 // Let the cursor motion be redrawn as it moves in GUI
424 return 1;
425 }
426
427 if(IS_NULL_PTR(form) || IS_NULL_PTR(form->points)) return 0;
428
429 if(gui->form_dragging || gui->source_dragging)
430 {
432 // apply delta to the current mouse position
433 float pts[2];
434 dt_masks_gui_delta_to_raw_norm(dev, gui, pts);
435
436 // we move all points in normalized input space
437 if(gui->form_dragging)
438 {
439 dt_masks_node_circle_t *circle = (dt_masks_node_circle_t *)((form->points)->data);
440 if(IS_NULL_PTR(circle)) return 0;
441 circle->center[0] = pts[0];
442 circle->center[1] = pts[1];
443 }
444 else if(gui->source_dragging)
445 {
446 form->source[0] = pts[0];
447 form->source[1] = pts[1];
448 }
449
450 // we recreate the form points
451 dt_masks_gui_form_create(form, gui, index, module);
452
453 return 1;
454 }
455 return 0;
456}
457
458static void _circle_draw_shape(cairo_t *cr, const float *points, const int points_count, const int coord_nb, const gboolean border, const gboolean source)
459{
460 cairo_move_to(cr, points[coord_nb * 2 + 2], points[coord_nb * 2 + 3]);
461 for(int i = 2; i < points_count; i++)
462 cairo_line_to(cr, points[i * 2], points[i * 2 + 1]);
463 cairo_close_path(cr);
464}
465
466static float *_points_to_transform(float x, float y, float radius, float wd, float ht, int *points_count)
467{
468 // how many points do we need?
469 const float r = radius * MIN(wd, ht);
470 const size_t l = (size_t)(2.0f * M_PI * r);
471 // allocate buffer
472 float *const restrict points = dt_pixelpipe_cache_alloc_align_float_cache((l + 1) * 2, 0);
473 if(IS_NULL_PTR(points))
474 {
475 *points_count = 0;
476 return NULL;
477 }
478 *points_count = l + 1;
479
480 // now we set the points, first the center, then the circumference
481 float center[2] = { x, y };
483 const float center_x = center[0];
484 const float center_y = center[1];
485 points[0] = center_x;
486 points[1] = center_y;
487 __OMP_PARALLEL_FOR_SIMD__(if(l > 100) aligned(points:64))
488 for(int i = 1; i < l + 1; i++)
489 {
490 const float alpha = (i - 1) * 2.0f * M_PI / (float)l;
491 points[i * 2] = center_x + r * cosf(alpha);
492 points[i * 2 + 1] = center_y + r * sinf(alpha);
493 }
494 return points;
495}
496
497static int _circle_get_points_source(dt_develop_t *dev, float x, float y, float xs, float ys, float radius,
498 float radius2, float rotation, float **points, int *points_count,
499 const dt_iop_module_t *module)
500{
501 // global callback signature
502
503 const float wd = dev->roi.raw_width;
504 const float ht = dev->roi.raw_height;
505
506 // compute the points of the target (center and circumference of circle)
507 // we get the point in RAW image reference
508 *points = _points_to_transform(x, y, radius, wd, ht, points_count);
509 if(IS_NULL_PTR(*points)) return 1;
510
511 // we transform with all distortion that happen *before* the module
512 // so we have now the TARGET points in module input reference
514 *points, *points_count))
515 goto error;
516
517 // now we move all the points by the shift
518 // so we have now the SOURCE points in module input reference
519 float pts[2] = { xs, ys };
522 pts, 1))
523 goto error;
524
525 {
526 const float dx = pts[0] - (*points)[0];
527 const float dy = pts[1] - (*points)[1];
528 __OMP_PARALLEL_FOR_SIMD__(if(*points_count > 100) aligned(points:64))
529 for(int i = 0; i < *points_count; i++)
530 {
531 (*points)[i * 2] += dx;
532 (*points)[i * 2 + 1] += dy;
533 }
534 }
535
536 // we apply the rest of the distortions (those after the module)
537 // so we have now the SOURCE points in final image reference
539 *points, *points_count))
540 goto error;
541
542 return 0;
543
544 // if we failed, then free all and return
545error:
547 *points = NULL;
548 *points_count = 0;
549 return 1;
550}
551
552static int _circle_get_points(dt_develop_t *dev, float x, float y, float radius, float radius2, float rotation,
553 float **points, int *points_count)
554{
555 // global callback signature
556
557 const float wd = dev->roi.raw_width;
558 const float ht = dev->roi.raw_height;
559
560 // compute the points we need to transform (center and circumference of circle)
561 *points = _points_to_transform(x, y, radius, wd, ht, points_count);
562 if(IS_NULL_PTR(*points)) return 1;
563
564 // and transform them with all distorted modules
565 if(!dt_dev_coordinates_raw_abs_to_image_abs(dev, *points, *points_count))
566 {
568 *points = NULL;
569 *points_count = 0;
570 return 1;
571 }
572
573 // if we failed, then free all and return
574 return 0;
575}
576
577static void _circle_events_post_expose(cairo_t *cr, float zoom_scale, dt_masks_form_gui_t *gui, int index, int num_points)
578{
579 // add a preview when creating a circle
580 // in creation mode
581 if(gui->creation)
582 {
585 if(_circle_get_creation_preview(form, gui, &preview)) return;
586
587 dt_masks_draw_preview_shape(cr, zoom_scale, num_points, preview.points, preview.points_count,
588 preview.border, preview.border_count,
589 &dt_masks_functions_circle.draw_shape, CAIRO_LINE_CAP_BUTT,
590 CAIRO_LINE_CAP_ROUND, FALSE, FALSE);
591
592 // draw a cross where the source will be created
593 if(dt_masks_form_is_clone(form))
594 {
595 dt_masks_draw_source_preview(cr, zoom_scale, gui, gui->pos[0], gui->pos[1], gui->pos[0], gui->pos[1], FALSE);
596 dt_masks_draw_preview_shape(cr, zoom_scale, num_points, preview.source_points, preview.points_count,
597 NULL, 0, &dt_masks_functions_circle.draw_shape, CAIRO_LINE_CAP_BUTT,
598 CAIRO_LINE_CAP_ROUND, FALSE, TRUE);
599 }
601
602 return;
603 } // creation
604
605 dt_masks_form_gui_points_t *gpt = (dt_masks_form_gui_points_t *)g_list_nth_data(gui->points, index);
606 if(IS_NULL_PTR(gpt)) return;
607
608 // we draw the main shape
609 const gboolean selected = (gui->group_selected == index) && (gui->form_selected || gui->form_dragging);
610 if(gpt->points && gpt->points_count > 1)
611 dt_draw_shape_lines(DT_MASKS_NO_DASH, FALSE, cr, num_points, selected, zoom_scale, gpt->points,
612 gpt->points_count, &dt_masks_functions_circle.draw_shape, CAIRO_LINE_CAP_BUTT);
613 // we draw the borders
614 if(gui->group_selected == index)
615 {
616 if(gpt->border && gpt->border_count > 1)
617 dt_draw_shape_lines(DT_MASKS_DASH_STICK, FALSE, cr, num_points, (gui->border_selected), zoom_scale, gpt->border,
618 gpt->border_count, &dt_masks_functions_circle.draw_shape, CAIRO_LINE_CAP_ROUND);
619 }
620
621 // draw the source if any
622 if(gpt->source && gpt->source_count > 6 && gpt->points && gpt->points_count > 0)
623 {
624 dt_masks_gui_center_point_t center_pt = { .main = { gpt->points[0], gpt->points[1] },
625 .source = { gpt->source[0], gpt->source[1] }};
626
627 dt_masks_draw_source(cr, gui, index, num_points, zoom_scale, &center_pt, &dt_masks_functions_circle.draw_shape);
628 }
629}
630
631static void _bounding_box(const float *const points, int num_points, int *width, int *height, int *posx, int *posy)
632{
633 // search for min/max X and Y coordinates
634 float xmin = FLT_MAX, xmax = FLT_MIN, ymin = FLT_MAX, ymax = FLT_MIN;
635 for(int i = 1; i < num_points; i++) // skip point[0], which is circle's center
636 {
637 xmin = fminf(points[i * 2], xmin);
638 xmax = fmaxf(points[i * 2], xmax);
639 ymin = fminf(points[i * 2 + 1], ymin);
640 ymax = fmaxf(points[i * 2 + 1], ymax);
641 }
642 // set the min/max values we found
643 *posx = xmin;
644 *posy = ymin;
645 *width = (xmax - xmin);
646 *height = (ymax - ymin);
647}
648
649static int _circle_get_points_border(dt_develop_t *dev, struct dt_masks_form_t *form, float **points,
650 int *points_count, float **border, int *border_count, int source,
651 const dt_iop_module_t *module)
652{
653 if(IS_NULL_PTR(form) || IS_NULL_PTR(form->points)) return 0;
654 dt_masks_node_circle_t *circle = (dt_masks_node_circle_t *)((form->points)->data);
655 if(IS_NULL_PTR(circle)) return 0;
656 float x = circle->center[0];
657 float y = circle->center[1];
658 if(source)
659 {
660 float xs = form->source[0];
661 float ys = form->source[1];
662 return _circle_get_points_source(dev, x, y, xs, ys, circle->radius, circle->radius, 0.0f, points, points_count, module);
663 }
664 else
665 {
666 if(form->functions->get_points(dev, x, y, circle->radius, circle->radius, 0, points, points_count) != 0)
667 return 1;
668 if(!IS_NULL_PTR(border))
669 {
670 float outer_radius = circle->radius + circle->border;
671 return form->functions->get_points(dev, x, y, outer_radius, outer_radius, 0, border, border_count);
672 }
673 return 0;
674 }
675 return 1;
676}
677
680 dt_masks_form_t *form, int *width, int *height, int *posx, int *posy)
681{
682 // we get the circle values
683 if(IS_NULL_PTR(form) || IS_NULL_PTR(form->points)) return 0;
684 dt_masks_node_circle_t *circle = (dt_masks_node_circle_t *)((form->points)->data);
685 if(IS_NULL_PTR(circle)) return 0;
686 float wd = pipe->iwidth, ht = pipe->iheight;
687
688 // compute the points we need to transform (center and circumference of circle)
689 const float outer_radius = circle->radius + circle->border;
690 int num_points;
691 float *const restrict points =
692 _points_to_transform(form->source[0], form->source[1], outer_radius, wd, ht, &num_points);
693 if(IS_NULL_PTR(points))
694 return 1;
695
696 // and transform them with all distorted modules
697 if(!dt_dev_distort_transform_plus(pipe, module->iop_order, DT_DEV_TRANSFORM_DIR_BACK_INCL, points, num_points))
698 {
700 return 1;
701 }
702
703 _bounding_box(points, num_points, width, height, posx, posy);
705 return 0;
706}
707
708static int _circle_get_area(const dt_iop_module_t *const restrict module, dt_dev_pixelpipe_t *pipe,
709 const dt_dev_pixelpipe_iop_t *const restrict piece,
710 dt_masks_form_t *const restrict form,
711 int *width, int *height, int *posx, int *posy)
712{
713 if(IS_NULL_PTR(form) || IS_NULL_PTR(form->points)) return 0;
714 // we get the circle values
715 dt_masks_node_circle_t *circle = (dt_masks_node_circle_t *)((form->points)->data);
716 if(IS_NULL_PTR(circle)) return 0;
717 float wd = pipe->iwidth, ht = pipe->iheight;
718
719 // compute the points we need to transform (center and circumference of circle)
720 const float outer_radius = circle->radius + circle->border;
721 int num_points;
722 float *const restrict points =
723 _points_to_transform(circle->center[0], circle->center[1], outer_radius, wd, ht, &num_points);
724 if(IS_NULL_PTR(points))
725 return 1;
726
727 // and transform them with all distorted modules
728 if(!dt_dev_distort_transform_plus(pipe, module->iop_order, DT_DEV_TRANSFORM_DIR_BACK_INCL, points, num_points))
729 {
731 return 1;
732 }
733
734 _bounding_box(points, num_points, width, height, posx, posy);
736 return 0;
737}
738
739static int _circle_get_mask(const dt_iop_module_t *const restrict module, dt_dev_pixelpipe_t *pipe,
740 const dt_dev_pixelpipe_iop_t *const restrict piece,
741 dt_masks_form_t *const restrict form,
742 float **buffer, int *width, int *height, int *posx, int *posy)
743{
744 double start2 = 0.0;
746
747 // we get the area
748 if(_circle_get_area(module, pipe, piece, form, width, height, posx, posy) != 0) return 1;
749
751 {
752 dt_print(DT_DEBUG_MASKS, "[masks %s] circle area took %0.04f sec\n", form->name, dt_get_wtime() - start2);
753 start2 = dt_get_wtime();
754 }
755
756 // we get the circle values
757 dt_masks_node_circle_t *const restrict circle = (dt_masks_node_circle_t *)((form->points)->data);
758
759 // we create a buffer of points with all points in the area
760 const int w = *width, h = *height;
761 float *const restrict points = dt_pixelpipe_cache_alloc_align_float_cache((size_t)w * h * 2, 0);
762 if(IS_NULL_PTR(points))
763 return 1;
764
765 const float pos_x = *posx;
766 const float pos_y = *posy;
767 __OMP_PARALLEL_FOR__(if(h*w > 50000) num_threads(MIN(darktable.num_openmp_threads,(h*w)/20000)))
768 for(int i = 0; i < h; i++)
769 {
770 float *const restrict p = points + 2 * i * w;
771 const float y = i + pos_y;
772 __OMP_SIMD__(aligned(points : 64))
773 for(int j = 0; j < w; j++)
774 {
775 p[2*j] = pos_x + j;
776 p[2*j + 1] = y;
777 }
778 }
780 {
781 dt_print(DT_DEBUG_MASKS, "[masks %s] circle draw took %0.04f sec\n", form->name, dt_get_wtime() - start2);
782
783 start2 = dt_get_wtime();
784 }
785 // we back transform all this points
786 if(!dt_dev_distort_backtransform_plus(pipe, module->iop_order, DT_DEV_TRANSFORM_DIR_BACK_INCL, points, (size_t)w * h))
787 {
789 return 1;
790 }
791
793 {
794 dt_print(DT_DEBUG_MASKS, "[masks %s] circle transform took %0.04f sec\n", form->name,
795 dt_get_wtime() - start2);
796 start2 = dt_get_wtime();
797 }
798
799 // we allocate the buffer
800 *buffer = dt_pixelpipe_cache_alloc_align_float_cache((size_t)w * h, 0);
801 if(IS_NULL_PTR(*buffer))
802 {
804 return 1;
805 }
806
807 // we populate the buffer
808 float *const restrict ptbuffer = *buffer;
809 const int wi = pipe->iwidth, hi = pipe->iheight;
810 const int mindim = MIN(wi, hi);
811 const float centerx = circle->center[0] * wi;
812 const float centery = circle->center[1] * hi;
813 const float radius2 = circle->radius * mindim * circle->radius * mindim;
814 const float total2 = (circle->radius + circle->border) * mindim * (circle->radius + circle->border) * mindim;
815 const float border2 = total2 - radius2;
816 __OMP_PARALLEL_FOR_SIMD__(if(h*w > 50000) num_threads(MIN(darktable.num_openmp_threads,(h*w)/20000)) aligned(points, ptbuffer : 64))
817 for(int i = 0 ; i < h*w; i++)
818 {
819 // find the square of the distance from the center
820 const float l2 = sqf(points[2 * i] - centerx) + sqf(points[2 * i + 1] - centery);
821 // quadratic falloff between the circle's radius and the radius of the outside of the feathering
822 const float ratio = (total2 - l2) / border2;
823 // enforce 1.0 inside the circle and 0.0 outside the feathering
824 const float f = CLIP(ratio);
825 ptbuffer[i] = sqf(f);
826 }
827
829
831 dt_print(DT_DEBUG_MASKS, "[masks %s] circle fill took %0.04f sec\n", form->name, dt_get_wtime() - start2);
832
833 return 0;
834}
835
836
837static int _circle_get_mask_roi(const dt_iop_module_t *const restrict module, dt_dev_pixelpipe_t *pipe,
838 const dt_dev_pixelpipe_iop_t *const restrict piece,
839 dt_masks_form_t *const form, const dt_iop_roi_t *const roi,
840 float *const restrict buffer)
841{
842 if(IS_NULL_PTR(form) || IS_NULL_PTR(form->points)) return 1;
843 if(IS_NULL_PTR(module)) return 1;
844 double start1 = 0.0;
845 double start2 = start1;
846
847 if(darktable.unmuted & DT_DEBUG_PERF) start2 = start1 = dt_get_wtime();
848
849 // we get the circle parameters
850 dt_masks_node_circle_t *circle = (dt_masks_node_circle_t *)((form->points)->data);
851 if(IS_NULL_PTR(circle)) return 1;
852 const int wi = pipe->iwidth, hi = pipe->iheight;
853 const float centerx = circle->center[0] * wi;
854 const float centery = circle->center[1] * hi;
855 const int min_dimention = MIN(wi, hi);
856 const float total_radius = (circle->radius + circle->border) * min_dimention;
857 const float sqr_radius = circle->radius * min_dimention * circle->radius * min_dimention;
858 const float sqr_total = total_radius * total_radius;
859 const float sqr_border = sqr_total - sqr_radius;
860
861 // we create a buffer of grid points for later interpolation: higher speed and reduced memory footprint;
862 // we match size of buffer to bounding box around the shape
863 const int width = roi->width;
864 const int height = roi->height;
865 const int px = roi->x;
866 const int py = roi->y;
867 const float iscale = 1.0f / roi->scale;
868 // scale dependent resolution: when zoomed in (scale > 1), use finer grid to avoid interpolation holes
869 const float grid_scale = 1.0f / MAX(roi->scale, 1e-6f);
870 const int grid = CLAMP((10.0f * grid_scale + 2.0f) / 3.0f, 1, 4);
871 const int grid_width = (width + grid - 1) / grid + 1; // grid dimension of total roi
872 const int grid_height = (height + grid - 1) / grid + 1; // grid dimension of total roi
873
874 // initialize output buffer with zero
875 memset(buffer, 0, sizeof(float) * width * height);
876
878 {
879 dt_print(DT_DEBUG_MASKS, "[masks %s] circle init took %0.04f sec\n", form->name, dt_get_wtime() - start2);
880 start2 = dt_get_wtime();
881 }
882
883 // we look at the outer circle of the shape - no effects outside of this circle;
884 // we need many points as we do not know how the circle might get distorted in the pixelpipe
885 const size_t circpts = dt_masks_roundup(MIN(360, 2 * M_PI * sqr_total), 8);
886 float *const restrict circ = dt_pixelpipe_cache_alloc_align_float_cache(circpts * 2, 0);
887 if(IS_NULL_PTR(circ)) return 1;
888 __OMP_PARALLEL_FOR__(if(circpts/8 > 1000))
889 for(int n = 0; n < circpts / 8; n++)
890 {
891 const float phi = (2.0f * M_PI * n) / circpts;
892 const float x = total_radius * cosf(phi);
893 const float y = total_radius * sinf(phi);
894 const float cx = centerx;
895 const float cy = centery;
896 const int index_x = 2 * n * 8;
897 const int index_y = 2 * n * 8 + 1;
898 // take advantage of symmetry
899 circ[index_x] = cx + x;
900 circ[index_y] = cy + y;
901 circ[index_x + 2] = cx + x;
902 circ[index_y + 2] = cy - y;
903 circ[index_x + 4] = cx - x;
904 circ[index_y + 4] = cy + y;
905 circ[index_x + 6] = cx - x;
906 circ[index_y + 6] = cy - y;
907 circ[index_x + 8] = cx + y;
908 circ[index_y + 8] = cy + x;
909 circ[index_x + 10] = cx + y;
910 circ[index_y + 10] = cy - x;
911 circ[index_x + 12] = cx - y;
912 circ[index_y + 12] = cy + x;
913 circ[index_x + 14] = cx - y;
914 circ[index_y + 14] = cy - x;
915 }
916
917 // we transform the outer circle from input image coordinates to current point in pixelpipe
918 if(!dt_dev_distort_transform_plus(pipe, module->iop_order, DT_DEV_TRANSFORM_DIR_BACK_INCL, circ,
919 circpts))
920 {
922 return 1;
923 }
924
926 {
927 dt_print(DT_DEBUG_MASKS, "[masks %s] circle outline took %0.04f sec\n", form->name, dt_get_wtime() - start2);
928 start2 = dt_get_wtime();
929 }
930
931 // we get the min/max values ...
932 float xmin = FLT_MAX, ymin = FLT_MAX, xmax = FLT_MIN, ymax = FLT_MIN;
933 for(int n = 0; n < circpts; n++)
934 {
935 // just in case that transform throws surprising values
936 if(!(isnormal(circ[2 * n]) && isnormal(circ[2 * n + 1]))) continue;
937
938 xmin = MIN(xmin, circ[2 * n]);
939 xmax = MAX(xmax, circ[2 * n]);
940 ymin = MIN(ymin, circ[2 * n + 1]);
941 ymax = MAX(ymax, circ[2 * n + 1]);
942 }
943
944#if 0
945 printf("xmin %f, xmax %f, ymin %f, ymax %f\n", xmin, xmax, ymin, ymax);
946 printf("wi %d, hi %d, iscale %f\n", wi, hi, iscale);
947 printf("w %d, h %d, px %d, py %d\n", w, h, px, py);
948#endif
949
950 // ... and calculate the bounding box with a bit of reserve
951 const int bbxm = CLAMP((int)floorf(xmin / iscale - px) / grid - 1, 0, grid_width - 1);
952 const int bbXM = CLAMP((int)ceilf(xmax / iscale - px) / grid + 2, 0, grid_width - 1);
953 const int bbym = CLAMP((int)floorf(ymin / iscale - py) / grid - 1, 0, grid_height - 1);
954 const int bbYM = CLAMP((int)ceilf(ymax / iscale - py) / grid + 2, 0, grid_height - 1);
955 const int bbw = bbXM - bbxm + 1;
956 const int bbh = bbYM - bbym + 1;
957
958#if 0
959 printf("bbxm %d, bbXM %d, bbym %d, bbYM %d\n", bbxm, bbXM, bbym, bbYM);
960 printf("gw %d, gh %d, bbw %d, bbh %d\n", gw, gh, bbw, bbh);
961#endif
962
964
966 {
967 dt_print(DT_DEBUG_MASKS, "[masks %s] circle bounding box took %0.04f sec\n", form->name, dt_get_wtime() - start2);
968 start2 = dt_get_wtime();
969 }
970
971 // check if there is anything to do at all;
972 // only if width and height of bounding box is 2 or greater the shape lies inside of roi and requires action
973 if(bbw <= 1 || bbh <= 1)
974 return 0;
975
976 float *const restrict points = dt_pixelpipe_cache_alloc_align_float_cache((size_t)bbw * bbh * 2, 0);
977 if(IS_NULL_PTR(points)) return 1;
978
979 // we populate the grid points in module coordinates
980 __OMP_PARALLEL_FOR__(collapse(2) if(bbw*bbh > 50000))
981 for(int j = bbym; j <= bbYM; j++)
982 for(int i = bbxm; i <= bbXM; i++)
983 {
984 const size_t index = (size_t)(j - bbym) * bbw + i - bbxm;
985 points[index * 2] = (grid * i + px) * iscale;
986 points[index * 2 + 1] = (grid * j + py) * iscale;
987 }
988
990 {
991 dt_print(DT_DEBUG_MASKS, "[masks %s] circle grid took %0.04f sec\n", form->name, dt_get_wtime() - start2);
992 start2 = dt_get_wtime();
993 }
994
995 // we back transform all these points to the input image coordinates
996 if(!dt_dev_distort_backtransform_plus(pipe, module->iop_order, DT_DEV_TRANSFORM_DIR_BACK_INCL, points,
997 (size_t)bbw * bbh))
998 {
1000 return 1;
1001 }
1002
1004 {
1005 dt_print(DT_DEBUG_MASKS, "[masks %s] circle transform took %0.04f sec\n", form->name,
1006 dt_get_wtime() - start2);
1007 start2 = dt_get_wtime();
1008 }
1009
1010 // we calculate the mask values at the transformed points;
1011 // for results: re-use the points array
1012 __OMP_PARALLEL_FOR__(collapse(2) if(bbh*bbw > 50000) num_threads(MIN(darktable.num_openmp_threads,(height*width)/20000)))
1013 for(int j = 0; j < bbh; j++)
1014 for(int i = 0; i < bbw; i++)
1015 {
1016 const size_t index = (size_t)j * bbw + i;
1017 // find the square of the distance from the center
1018 const float l2 = sqf(points[2 * index] - centerx) + sqf(points[2 * index + 1] - centery);
1019 // quadratic falloff between the circle's radius and the radius of the outside of the feathering
1020 const float ratio = (sqr_total - l2) / sqr_border;
1021 // enforce 1.0 inside the circle and 0.0 outside the feathering
1022 const float f = CLAMP(ratio, 0.0f, 1.0f);
1023 points[2*index] = f * f;
1024 }
1025
1027 {
1028 dt_print(DT_DEBUG_MASKS, "[masks %s] circle draw took %0.04f sec\n", form->name,
1029 dt_get_wtime() - start2);
1030 start2 = dt_get_wtime();
1031 }
1032
1033 // we fill the pre-initialized output buffer by interpolation;
1034 // we only need to take the contents of our bounding box into account
1035 const int endx = MIN(width, bbXM * grid);
1036 const int endy = MIN(height, bbYM * grid);
1037 const float inv_grid2 = 1.0f / (grid * grid);
1038 float w0[4], w1[4];
1039 for(int i = 0; i < grid; i++)
1040 {
1041 w0[i] = (float)(grid - i);
1042 w1[i] = (float)i;
1043 }
1044 __OMP_PARALLEL_FOR__(if((size_t)(endy - bbym * grid) * (size_t)(endx - bbxm * grid) > 50000))
1045 for(int j = bbym * grid; j < endy; j++)
1046 {
1047 const int jj = j % grid;
1048 const int mj = j / grid - bbym;
1049 const float wj0 = w0[jj];
1050 const float wj1 = w1[jj];
1051 const size_t row_base = (size_t)mj * bbw;
1052 float *const row = buffer + (size_t)j * width;
1053 int ii = 0;
1054 int mi = 0;
1055 for(int i = bbxm * grid; i < endx; i++)
1056 {
1057 const size_t mindex = row_base + mi;
1058 const float wii0 = w0[ii];
1059 const float wii1 = w1[ii];
1060 row[i] = (points[mindex * 2] * wii0 * wj0
1061 + points[(mindex + 1) * 2] * wii1 * wj0
1062 + points[(mindex + bbw) * 2] * wii0 * wj1
1063 + points[(mindex + bbw + 1) * 2] * wii1 * wj1) * inv_grid2;
1064 ii++;
1065 if(ii == grid)
1066 {
1067 ii = 0;
1068 mi++;
1069 }
1070 }
1071 }
1072
1074
1076 {
1077 dt_print(DT_DEBUG_MASKS, "[masks %s] circle fill took %0.04f sec\n", form->name, dt_get_wtime() - start2);
1078 dt_print(DT_DEBUG_MASKS, "[masks %s] circle total render took %0.04f sec\n", form->name,
1079 dt_get_wtime() - start1);
1080 }
1081
1082 return 0;
1083}
1084
1086{
1088 {
1089 dt_conf_get_and_sanitize_float("plugins/darkroom/spots/circle/size", 0.001f, 0.5f);
1090 dt_conf_get_and_sanitize_float("plugins/darkroom/spots/circle/border", 0.0005f, 0.5f);
1091 }
1092 else
1093 {
1094 dt_conf_get_and_sanitize_float("plugins/darkroom/masks/circle/size", 0.001f, 0.5f);
1095 dt_conf_get_and_sanitize_float("plugins/darkroom/masks/circle/border", 0.0005f, 0.5f);
1096 }
1097}
1098
1099static void _circle_set_form_name(struct dt_masks_form_t *const form, const size_t nb)
1100{
1101 snprintf(form->name, sizeof(form->name), _("circle #%d"), (int)nb);
1102}
1103
1104static void _circle_set_hint_message(const dt_masks_form_gui_t *const gui, const dt_masks_form_t *const form,
1105 const int opacity, char *const restrict msgbuf, const size_t msgbuf_len)
1106{
1107 // circle has same controls on creation and on edit
1108 g_snprintf(msgbuf, msgbuf_len,
1109 _("<b>Size</b>: scroll, <b>Hardness</b>: shift+scroll\n"
1110 "<b>Opacity</b>: ctrl+scroll (%d%%)"), opacity);
1111}
1112
1114{
1115 // unused arg, keep compiler from complaining
1117}
1118
1119static void _circle_initial_source_pos(const float iwd, const float iht, float *x, float *y)
1120{
1121 const float radius = MIN(0.5f, dt_conf_get_float("plugins/darkroom/spots/circle/size"));
1122 float offset[2] = { radius, -radius };
1124 *x = offset[0];
1125 *y = offset[1];
1126}
1127
1128// The function table for circles. This must be public, i.e. no "static" keyword.
1130 .point_struct_size = sizeof(struct dt_masks_node_circle_t),
1131 .sanitize_config = _circle_sanitize_config,
1132 .set_form_name = _circle_set_form_name,
1133 .set_hint_message = _circle_set_hint_message,
1134 .duplicate_points = _circle_duplicate_points,
1135 .initial_source_pos = _circle_initial_source_pos,
1136 .get_distance = _circle_get_distance,
1137 .get_points = _circle_get_points,
1138 .get_points_border = _circle_get_points_border,
1139 .get_mask = _circle_get_mask,
1140 .get_mask_roi = _circle_get_mask_roi,
1141 .get_area = _circle_get_area,
1142 .get_source_area = _circle_get_source_area,
1143 .get_gravity_center = _circle_get_gravity_center,
1144 .get_interaction_value = _circle_get_interaction_value,
1145 .set_interaction_value = _circle_set_interaction_value,
1146 .update_hover = _find_closest_handle,
1147 .mouse_moved = _circle_events_mouse_moved,
1148 .mouse_scrolled = _circle_events_mouse_scrolled,
1149 .button_pressed = _circle_events_button_pressed,
1150 .button_released = _circle_events_button_released,
1151 .key_pressed = _circle_events_key_pressed,
1152 .post_expose = _circle_events_post_expose,
1153 .draw_shape = _circle_draw_shape
1154};
1155
1156
1157
1158// clang-format off
1159// modelines: These editor modelines have been set for all relevant files by tools/update_modelines.py
1160// vim: shiftwidth=2 expandtab tabstop=2 cindent
1161// kate: tab-indents: off; indent-width 2; replace-tabs on; indent-mode cstyle; remove-trailing-spaces modified;
1162// clang-format on
static double dist(double x1, double y1, double x2, double y2)
Definition ashift_lsd.c:250
static void error(char *msg)
Definition ashift_lsd.c:202
#define TRUE
Definition ashift_lsd.c:162
#define FALSE
Definition ashift_lsd.c:158
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 float _circle_set_interaction_value(dt_masks_form_t *form, dt_masks_interaction_t interaction, float value, dt_masks_increment_t increment, int flow, dt_masks_form_gui_t *gui, struct dt_iop_module_t *module)
Definition circle.c:237
static void _circle_get_distance(float x, float y, float as, dt_masks_form_gui_t *gui, int index, int num_points, int *inside, int *inside_border, int *near, int *inside_source, float *dist)
Definition circle.c:51
static void _circle_sanitize_config(dt_masks_type_t type)
Definition circle.c:1085
static float * _points_to_transform(float x, float y, float radius, float wd, float ht, int *points_count)
Definition circle.c:466
static int _circle_events_mouse_scrolled(struct dt_iop_module_t *module, double x, double y, int up, const int flow, uint32_t state, dt_masks_form_t *form, int parentid, dt_masks_form_gui_t *gui, int index, dt_masks_interaction_t interaction)
Definition circle.c:304
static void _circle_duplicate_points(dt_develop_t *dev, dt_masks_form_t *const base, dt_masks_form_t *const dest)
Definition circle.c:1113
static int _find_closest_handle(dt_masks_form_t *mask_form, dt_masks_form_gui_t *mask_gui, int form_index)
Definition circle.c:109
#define HARDNESS_MIN
Definition circle.c:45
static int _circle_get_mask(const dt_iop_module_t *const restrict module, dt_dev_pixelpipe_t *pipe, const dt_dev_pixelpipe_iop_t *const restrict piece, dt_masks_form_t *const restrict form, float **buffer, int *width, int *height, int *posx, int *posy)
Definition circle.c:739
static void _circle_init_new(dt_masks_form_t *form, dt_masks_form_gui_t *gui, dt_masks_node_circle_t *circle)
Definition circle.c:124
static int _init_hardness(dt_masks_form_t *form, const float amount, const dt_masks_increment_t increment, const int flow)
Definition circle.c:182
static int _circle_events_mouse_moved(struct dt_iop_module_t *module, double x, double y, double pressure, int which, dt_masks_form_t *form, int parentid, dt_masks_form_gui_t *gui, int index)
Definition circle.c:417
static void _circle_events_post_expose(cairo_t *cr, float zoom_scale, dt_masks_form_gui_t *gui, int index, int num_points)
Definition circle.c:577
static float _circle_get_interaction_value(const dt_masks_form_t *form, dt_masks_interaction_t interaction)
Definition circle.c:204
static int _circle_get_points(dt_develop_t *dev, float x, float y, float radius, float radius2, float rotation, float **points, int *points_count)
Definition circle.c:552
static int _init_opacity(dt_masks_form_t *form, const float amount, const dt_masks_increment_t increment, const int flow)
Definition circle.c:197
static void _circle_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)
Circle-specific inside/border hit testing adapter.
Definition circle.c:101
static int _change_size(dt_masks_form_t *form, dt_masks_form_gui_t *gui, struct dt_iop_module_t *module, int index, const float amount, const dt_masks_increment_t increment, const int flow)
Definition circle.c:274
#define HARDNESS_MAX
Definition circle.c:46
static int _circle_get_points_border(dt_develop_t *dev, struct dt_masks_form_t *form, float **points, int *points_count, float **border, int *border_count, int source, const dt_iop_module_t *module)
Definition circle.c:649
static int _circle_get_source_area(dt_iop_module_t *module, dt_dev_pixelpipe_t *pipe, dt_dev_pixelpipe_iop_t *piece, dt_masks_form_t *form, int *width, int *height, int *posx, int *posy)
Definition circle.c:678
static int _change_hardness(dt_masks_form_t *form, dt_masks_form_gui_t *gui, struct dt_iop_module_t *module, int index, const float amount, const dt_masks_increment_t increment, const int flow)
Definition circle.c:257
static int _circle_events_button_released(struct dt_iop_module_t *module, double x, double y, int which, uint32_t state, dt_masks_form_t *form, int parentid, dt_masks_form_gui_t *gui, int index)
Definition circle.c:388
static int _circle_get_mask_roi(const dt_iop_module_t *const restrict module, dt_dev_pixelpipe_t *pipe, const dt_dev_pixelpipe_iop_t *const restrict piece, dt_masks_form_t *const form, const dt_iop_roi_t *const roi, float *const restrict buffer)
Definition circle.c:837
static gboolean _circle_get_gravity_center(const dt_masks_form_t *form, float center[2], float *area)
Definition circle.c:221
static void _circle_initial_source_pos(const float iwd, const float iht, float *x, float *y)
Definition circle.c:1119
static void _circle_get_creation_values(const dt_masks_form_t *form, float *radius, float *border)
Definition circle.c:115
static int _init_size(dt_masks_form_t *form, const float amount, const dt_masks_increment_t increment, const int flow)
Definition circle.c:189
static void _circle_set_form_name(struct dt_masks_form_t *const form, const size_t nb)
Definition circle.c:1099
const dt_masks_functions_t dt_masks_functions_circle
Definition circle.c:1129
static int _circle_get_area(const dt_iop_module_t *const restrict module, dt_dev_pixelpipe_t *pipe, const dt_dev_pixelpipe_iop_t *const restrict piece, dt_masks_form_t *const restrict form, int *width, int *height, int *posx, int *posy)
Definition circle.c:708
static int _circle_get_creation_preview(dt_masks_form_t *form, dt_masks_form_gui_t *gui, dt_masks_preview_buffers_t *preview)
Definition circle.c:140
static void _circle_draw_shape(cairo_t *cr, const float *points, const int points_count, const int coord_nb, const gboolean border, const gboolean source)
Definition circle.c:458
static int _circle_get_points_source(dt_develop_t *dev, float x, float y, float xs, float ys, float radius, float radius2, float rotation, float **points, int *points_count, const dt_iop_module_t *module)
Definition circle.c:497
static int _circle_events_key_pressed(struct dt_iop_module_t *module, GdkEventKey *event, dt_masks_form_t *form, int parentid, dt_masks_form_gui_t *gui, int index)
Definition circle.c:411
static void _bounding_box(const float *const points, int num_points, int *width, int *height, int *posx, int *posy)
Definition circle.c:631
static int _circle_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 *form, int parentid, dt_masks_form_gui_t *gui, int index)
Definition circle.c:333
static void _circle_set_hint_message(const dt_masks_form_gui_t *const gui, const dt_masks_form_t *const form, const int opacity, char *const restrict msgbuf, const size_t msgbuf_len)
Definition circle.c:1104
const dt_aligned_pixel_t f
static const int row
int type
float dt_conf_get_float(const char *name)
float dt_conf_get_and_sanitize_float(const char *name, float min, float max)
darktable_t darktable
Definition darktable.c:181
void dt_print(dt_debug_thread_t thread, const char *msg,...)
Definition darktable.c:1542
#define __OMP_SIMD__(...)
Definition darktable.h:262
@ DT_DEBUG_PERF
Definition darktable.h:719
@ DT_DEBUG_MASKS
Definition darktable.h:727
#define dt_pixelpipe_cache_alloc_align_float_cache(pixels, id)
Definition darktable.h:447
#define dt_pixelpipe_cache_free_align(mem)
Definition darktable.h:453
#define __OMP_PARALLEL_FOR__(...)
Definition darktable.h:258
static const dt_aligned_pixel_simd_t value
Definition darktable.h:577
static double dt_get_wtime(void)
Definition darktable.h:914
#define __OMP_PARALLEL_FOR_SIMD__(...)
Definition darktable.h:259
static gboolean dt_modifier_is(const GdkModifierType state, const GdkModifierType desired_modifier_mask)
Definition darktable.h:893
#define IS_NULL_PTR(p)
C is way too permissive with !=, == and if(var) checks, which can mean too many things depending on w...
Definition darktable.h:281
#define M_PI_F
int dt_dev_coordinates_raw_abs_to_image_abs(dt_develop_t *dev, float *points, size_t points_count)
Definition develop.c:1525
void dt_dev_coordinates_raw_norm_to_raw_abs(dt_develop_t *dev, float *points, size_t num_points)
Definition develop.c:1109
int dt_dev_distort_transform_plus(const dt_dev_pixelpipe_t *pipe, const double iop_order, const int transf_direction, float *points, size_t points_count)
Definition develop.c:1557
int dt_dev_distort_backtransform_plus(const dt_dev_pixelpipe_t *pipe, const double iop_order, const int transf_direction, float *points, size_t points_count)
Definition develop.c:1586
@ 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_MASKS_DASH_STICK
Definition draw.h:94
@ DT_MASKS_NO_DASH
Definition draw.h:93
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 const float x
const float l2
#define w1
Definition lmmse.c:59
void dt_masks_calculate_source_pos_origin(dt_masks_form_gui_t *gui, const float initial_xpos, const float initial_ypos, const float xpos, const float ypos, float *px, float *py, const int adding)
Compute preview-space source position for drawing the clone indicator.
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)
@ DT_MASKS_EDIT_FULL
Definition masks.h:202
static void dt_masks_draw_source_preview(cairo_t *cr, const float zoom_scale, dt_masks_form_gui_t *gui, const float initial_xpos, const float initial_ypos, const float xpos, const float ypos, const int adding)
Definition masks.h:1111
int dt_masks_form_change_opacity(dt_masks_form_t *form, int parentid, int up, const int flow)
int dt_masks_point_in_form_exact(const float *pts, int num_pts, const float *points, int points_start, int points_count)
Check whether any 2D point in pts[] lies inside the form points[].
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.
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.
void dt_masks_draw_source(cairo_t *cr, dt_masks_form_gui_t *gui, const int index, const int nb, const float zoom_scale, struct dt_masks_gui_center_point_t *center_point, const shape_draw_function_t *draw_shape_func)
Draw the source for a correction mask.
dt_masks_type_t
Definition masks.h:129
@ DT_MASKS_NON_CLONE
Definition masks.h:138
@ DT_MASKS_CLONE
Definition masks.h:134
dt_masks_interaction_t
Definition masks.h:302
@ DT_MASKS_INTERACTION_HARDNESS
Definition masks.h:305
@ DT_MASKS_INTERACTION_SIZE
Definition masks.h:304
static void dt_masks_gui_cursor_to_raw_norm(dt_develop_t *dev, const dt_masks_form_gui_t *gui, float point[2])
Definition masks.h:603
static void dt_masks_preview_buffers_cleanup(dt_masks_preview_buffers_t *buffers)
Definition masks.h:807
float dt_masks_apply_increment(float current, float amount, dt_masks_increment_t increment, int flow)
Apply a scroll increment to a scalar value.
static gboolean dt_masks_form_is_clone(const dt_masks_form_t *form)
Definition masks.h:641
float dt_masks_get_set_conf_value_with_toast(dt_masks_form_t *form, const char *feature, float amount, float v_min, float v_max, dt_masks_increment_t increment, int flow, const char *toast_fmt, float toast_scale)
Update a mask configuration value and emit a toast message.
dt_masks_increment_t
Definition masks.h:193
@ DT_MASKS_INCREMENT_SCALE
Definition masks.h:195
@ DT_MASKS_INCREMENT_OFFSET
Definition masks.h:196
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 int dt_masks_roundup(int num, int mult)
Definition masks.h:1441
static void dt_masks_draw_preview_shape(cairo_t *cr, const float zoom_scale, const int num_points, float *points, const int points_count, float *border, const int border_count, void(*const *draw_shape)(cairo_t *cr, const float *points, const int points_count, const int nb, const gboolean border, const gboolean source), const cairo_line_cap_t shape_cap, const cairo_line_cap_t border_cap, const gboolean save_restore, const gboolean source)
Definition masks.h:773
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)
static void dt_masks_gui_delta_to_raw_norm(dt_develop_t *dev, const dt_masks_form_gui_t *gui, float point[2])
Definition masks.h:611
void dt_masks_set_source_pos_initial_state(dt_masks_form_gui_t *gui, const uint32_t state)
Decide initial source positioning mode for clone masks.
static gboolean dt_masks_form_uses_spot_defaults(const dt_masks_form_t *form)
Definition masks.h:636
static void dt_masks_reset_source(dt_masks_form_t *form)
Definition masks.h:646
#define CLAMPF(a, mn, mx)
Definition math.h:89
#define CLIP(x)
Definition math.h:81
#define M_PI
Definition math.h:45
float iscale
Definition mipmap_cache.c:2
static float gh(const float f)
const float uint32_t state[4]
const float r
int32_t num_openmp_threads
Definition darktable.h:758
int32_t unmuted
Definition darktable.h:760
struct dt_develop_t * develop
Definition darktable.h:770
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
Region of interest passed through the pixelpipe.
Definition imageop.h:72
double scale
Definition imageop.h:74
gboolean source_selected
Definition masks.h:478
gboolean source_dragging
Definition masks.h:486
dt_masks_edit_mode_t edit_mode
Definition masks.h:463
dt_iop_module_t * creation_module
Definition masks.h:505
gboolean form_dragging
Definition masks.h:485
gboolean creation
Definition masks.h:503
gboolean form_selected
Definition masks.h:476
gboolean border_selected
Definition masks.h:477
float delta[2]
Definition masks.h:455
const dt_masks_functions_t * functions
Definition masks.h:379
dt_masks_type_t type
Definition masks.h:378
float source[2]
Definition masks.h:385
char name[128]
Definition masks.h:396
GList * points
Definition masks.h:377
int(* get_points)(struct dt_develop_t *dev, float x, float y, float radius_a, float radius_b, float rotation, float **points, int *points_count)
Definition masks.h:324
void(* draw_shape)(cairo_t *cr, const float *points, const int points_count, const int nb, const gboolean border, const gboolean source)
Definition masks.h:368
struct dt_masks_gui_center_point_t::@34 main
#define MIN(a, b)
Definition thinplate.c:32
#define MAX(a, b)
Definition thinplate.c:29