55#define HARDNESS_MIN 0.0005f
56#define HARDNESS_MAX 1.0f
58#define BORDER_MIN 0.00005f
59#define BORDER_MAX 0.5f
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);
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)
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;
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)
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);
97 const double ti = 1.0 - (
double)
t;
100 const double ti_ti = ti * ti;
101 const double t_ti =
t * ti;
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;
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;
112 if(dx == 0 && dy == 0)
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;
129 const float ctrl_x,
const float ctrl_y,
130 float *handle_x,
float *handle_y,
const gboolean clockwise)
132 const float delta_y = ctrl_y - point_y;
133 const float delta_x = point_x - ctrl_x;
136 *handle_x = point_x - delta_y;
137 *handle_y = point_y - delta_x;
141 *handle_x = point_x + delta_y;
142 *handle_y = point_y + delta_x;
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)
156 const float delta_y = handle_y - point_y;
157 const float delta_x = point_x - handle_x;
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;
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;
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)
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;
197 const guint node_count = g_list_length(mask_form->
points);
198 if(node_count < 2)
return;
204 const GList *form_points = mask_form->
points;
205 for(guint node_index = 0; node_index < node_count; node_index++)
208 form_points = g_list_next(form_points);
211 for(guint node_index = 0; node_index < node_count; node_index++)
224 float bezier1_x = 0.0f;
225 float bezier1_y = 0.0f;
226 float bezier2_x = 0.0f;
227 float bezier2_y = 0.0f;
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;
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;
259 for(
const GList *form_points = mask_form->
points; form_points; form_points = g_list_next(form_points))
265 sum += (point2->
node[0] - point1->
node[0]) * (point2->
node[1] + point1->
node[1]);
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);
290 if(abs_dx <= 1 && abs_dy <= 1)
return 1;
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;
302 while(point_x != target_x)
317 while(point_y != target_y)
338 float *border_min2,
float *border_max,
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;
349 if(angle_end < angle_start && clockwise)
351 angle_end += 2 *
M_PI;
353 if(angle_end > angle_start && !clockwise)
355 angle_start += 2 *
M_PI;
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]));
366 if(angle_end > angle_start)
367 step_count = (angle_end - angle_start) * fmaxf(radius_start, radius_end);
369 step_count = (angle_start - angle_end) * fmaxf(radius_start, radius_end);
370 if(step_count < 2)
return;
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;
384 for(
int step_index = 1; step_index < step_count; step_index++)
386 *points_ptr++ = center_max[0];
387 *points_ptr++ = center_max[1];
390 *border_ptr++ = center_max[0] + current_radius * cosf(current_angle);
391 *border_ptr++ = center_max[1] + current_radius * sinf(current_angle);
393 current_radius += radius_step;
394 current_angle += angle_step;
401 return abs((
int)
min[0] - (
int)
max[0]) < pixel_threshold &&
402 abs((
int)
min[1] - (
int)
max[1]) < pixel_threshold;
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,
417 int with_border,
const int pixel_threshold)
420 if(isnan(polygon_min[0]))
423 segment_end[2], segment_end[3], segment_end[0], segment_end[1], t_min,
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);
428 if(isnan(polygon_max[0]))
431 segment_end[2], segment_end[3], segment_end[0], segment_end[1], t_max,
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);
438 if((t_max - t_min < 0.0001)
443 result_polygon[0] = polygon_max[0];
444 result_polygon[1] = polygon_max[1];
449 result_border[0] = border_max[0];
450 result_border[1] = border_max[1];
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 };
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);
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);
473#define POLYGON_MAX_SELF_INTERSECTIONS(nb_nodes) ((nb_nodes) * 4)
479 int node_count,
float *border_points,
480 int border_point_count,
481 int *intersection_count_out)
483 if(node_count == 0 || border_point_count == 0)
485 *intersection_count_out = 0;
489 int intersection_count = 0;
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 };
496 for(
int i = node_count * 3;
i < border_point_count;
i++)
498 if(isnan(border_points[
i * 2]) || isnan(border_points[
i * 2 + 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)
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--;
511 if(prev >= node_count * 3)
513 border_points[
i * 2] = border_points[prev * 2];
514 border_points[
i * 2 + 1] = border_points[prev * 2 + 1];
521 if(xmin_f > border_points[
i * 2])
523 xmin_f = border_points[
i * 2];
524 extrema_index[0] =
i;
526 if(xmax_f < border_points[
i * 2])
528 xmax_f = border_points[
i * 2];
529 extrema_index[1] =
i;
531 if(ymin_f > border_points[
i * 2 + 1])
533 ymin_f = border_points[
i * 2 + 1];
534 extrema_index[2] =
i;
536 if(ymax_f < border_points[
i * 2 + 1])
538 ymax_f = border_points[
i * 2 + 1];
539 extrema_index[3] =
i;
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;
552 const size_t grid_size = (size_t)grid_height * grid_width;
553 if(grid_size < 10 || grid_height < 0 || grid_width < 0)
555 *intersection_count_out = 0;
561 memset(intersection_grid, 0,
sizeof(
int) * grid_size);
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];
580 for(
int ii = node_count * 3; ii < border_point_count; ii++)
583 int i = ii - node_count * 3 + extrema_index[1];
584 if(
i >= border_point_count)
i =
i - border_point_count + node_count * 3;
600 int cell_values[3] = { 0 };
601 const int idx = (grid_y - ymin) * grid_width + (grid_x - xmin);
603 if(idx < 0 || (
size_t)idx >= grid_size)
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];
613 for(
int k = 0;
k < 3;
k++)
615 if(cell_values[
k] > 0)
619 if((grid_x == last_x && grid_y == last_y) || cell_values[
k] ==
i - 1)
623 intersection_grid[idx] =
i;
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))
638 if(cell_values[
k] < node_count * 3 || cell_values[
k] >= border_point_count
639 || i < node_count * 3 || i >= border_point_count)
641 if(intersection_count > 0)
645 if((cell_values[
k] -
i) * (inter_last0 - inter_last1) > 0
646 && inter_last0 >= cell_values[
k] && inter_last1 <=
i)
657 intersection_count++;
664 intersection_count++;
672 intersection_grid[idx] =
i;
684 *intersection_count_out = intersection_count;
694 const double iop_order,
const int transform_direction,
696 float **border_buffer,
int *border_count, gboolean source)
698 *point_buffer = NULL;
700 if(!
IS_NULL_PTR(border_buffer)) *border_buffer = NULL;
708 const float input_width = pipe->
iwidth;
709 const float input_height = pipe->
iheight;
712 const guint node_count = g_list_length(mask_form->
points);
738 float dx = 0.0f, dy = 0.0f;
745 dx = (polygon->
node[0] - mask_form->
source[0]) * input_width;
746 dy = (polygon->
node[1] - mask_form->
source[1]) * input_height;
748 for(
const GList *point_node = mask_form->
points; point_node; point_node = g_list_next(point_node))
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;
769 if(!
IS_NULL_PTR(border_init)) memset(border_init, 0,
sizeof(
float) * 6 * node_count);
788 const GList *form_points = mask_form->
points;
789 for(
int node_index = 0; node_index < node_count; node_index++)
792 border_init[node_index * 6 + 2] = -pb;
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) };
812 form_points = g_list_next(form_points);
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 };
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);
829 if(fabs(lastb0 - rb[0]) > 1.0f || fabs(lastb1 - rb[1]) > 1.0f)
865 if(dborder && node_count >= 3)
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,
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);
877 if(bmax[0] - rb[0] > 1 || bmax[0] - rb[0] < -1 || bmax[1] - rb[1] > 1 || bmax[1] - rb[1] < -1)
931 *point_buffer, *point_count))
935 float pts[2] = { mask_form->
source[0] * input_width, mask_form->
source[1] * input_height };
939 dx = pts[0] - (*point_buffer)[2];
940 dy = pts[1] - (*point_buffer)[3];
942 for(
int i = 0;
i < *point_count;
i++)
944 (*point_buffer)[
i * 2] += dx;
945 (*point_buffer)[
i * 2 + 1] += dy;
951 *point_buffer, *point_count))
964 *point_buffer, *point_count))
968 *border_buffer, *border_count))
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];
986 const int buf_count = *border_count;
987 for(
int i = 0;
i < inter_count;
i++)
992 if(v < 0 || v >= buf_count || w < 0 || w >= buf_count)
continue;
995 (*border_buffer)[
v * 2] = NAN;
996 (*border_buffer)[
v * 2 + 1] = w;
1000 if(w > (
int)(node_count * 3) && (
int)(node_count * 3) < buf_count)
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);
1008 (*border_buffer)[node_count * 6 + 1] = w;
1009 (*border_buffer)[node_count * 6] = NAN;
1011 (*border_buffer)[
v * 2] = NAN;
1012 (*border_buffer)[
v * 2 + 1] = NAN;
1032 *point_buffer = NULL;
1037 *border_buffer = NULL;
1052 GList *firstpt = g_list_nth(mask_form->
points, segment_index);
1063 float min_dist = FLT_MAX;
1065 for(
int i = 0;
i <= 100;
i++)
1067 const float t =
i / 100.0f;
1068 float sample_x = 0.0f;
1069 float sample_y = 0.0f;
1072 &sample_x, &sample_y);
1074 const float dist = (point_x - sample_x) * (point_x - sample_x)
1075 + (point_y - sample_y) * (point_y - sample_y);
1091 const guint node_count = g_list_length(mask_form->
points);
1093 if(selected_segment < 0 || selected_segment >= (
int)node_count)
return;
1107 mask_form, selected_segment);
1109 GList *pt = g_list_nth(mask_form->
points, selected_segment);
1126 mask_form->
points = g_list_insert(mask_form->
points, new_node, selected_segment + 1);
1145 for(GList *node_entry = mask_form->
points; node_entry; node_entry = g_list_next(node_entry))
1150 float **point_buffer,
int *point_count,
1151 float **border_buffer,
int *border_count,
1155 const double ioporder = (module) ? module->
iop_order : 0.0f;
1158 border_buffer, border_count, source);
1163 float *mask_size,
float *border_size)
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 };
1173 float fp1[2] = { FLT_MAX, FLT_MAX };
1174 float fp2[2] = { FLT_MIN, FLT_MIN };
1179 const float x = gui_points->
points[
i * 2];
1180 const float y = gui_points->
points[
i * 2 + 1];
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);
1190 const float fx = gui_points->
border[
i * 2];
1191 const float fy = gui_points->
border[
i * 2 + 1];
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);
1205 float mask_span[2] = { p2[0] - p1[0], p2[1] - p1[1] };
1207 *mask_size = fmaxf(mask_span[0], mask_span[1]);
1211 float border_span[2] = { fp2[0] - fp1[0], fp2[1] - fp1[1] };
1213 *border_size = fmaxf(border_span[0], border_span[1]);
1218 float *center_y,
float *surface);
1230 if(
size <= 0.0f)
return NAN;
1235 float hardness_sum = 0.0f;
1236 int hardness_count = 0;
1238 for(
const GList *point_node = mask_form->
points; point_node; point_node = g_list_next(point_node))
1242 hardness_sum +=
node->border[0] +
node->border[1];
1243 hardness_count += 2;
1246 return hardness_count > 0 ? hardness_sum / (float)hardness_count : NAN;
1254 float center[2],
float *area)
1258 const int points_count = g_list_length(mask_form->
points);
1259 if(points_count <= 0)
return FALSE;
1265 for(
const GList *point_node = mask_form->
points; point_node; point_node = g_list_next(point_node))
1269 point_buffer[2 *
i] =
node->node[0];
1270 point_buffer[2 *
i + 1] =
node->node[1];
1300 if(!
_change_size(mask_form, 0, mask_gui, module, index,
value, increment, flow))
return NAN;
1315 int node_count,
int *inside,
int *inside_border,
1316 int *near,
int *inside_source,
float *
dist)
1330 float min_dist_pixel = FLT_MAX;
1332 const float radius2 = radius * radius;
1333 const float pt[2] = { point_x, point_y };
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;
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])
1356 current_seg = (current_seg + 1) % node_count;
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;
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;
1370 *
dist = min_dist_pixel;
1377 int current_seg = 1;
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])
1384 current_seg = (current_seg + 1) % node_count;
1387 const float yy = gui_points->
points[
i * 2 + 1];
1388 const float xx = gui_points->
points[
i * 2];
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)
1395 min_dist_pixel = dd;
1397 if(current_seg >= 0 && dd < radius2)
1399 if(current_seg == 0)
1400 *near = node_count - 1;
1402 *near = current_seg - 1;
1408 *
dist = min_dist_pixel;
1431 int node_index,
float *handle_x,
float *handle_y,
void *user_data)
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));
1443 float *handle_x,
float *handle_y,
void *user_data)
1447 gui_points->
points[node_index * 6 + 4], gui_points->
points[node_index * 6 + 5],
1448 handle_x, handle_y, gui_points->
clockwise);
1456 int *inside_border,
int *near,
int *inside_source,
float *
dist,
void *user_data)
1460 inside, inside_border, near, inside_source,
dist);
1477 float *center_x,
float *center_y,
float *area)
1479 if(
IS_NULL_PTR(point_buffer) || point_count < 3)
return;
1481 float centroid_x = 0.0f;
1482 float centroid_y = 0.0f;
1483 float signed_area = 0.0f;
1485 for(
int node_index = 0; node_index < point_count; node_index++)
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;
1494 signed_area += cross;
1495 centroid_x += (x0 + x1) * cross;
1496 centroid_y += (y0 + y1) * cross;
1499 if(fabsf(signed_area) > 1e-8f)
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;
1512 float *center_x,
float *center_y,
float *area)
1516 float centroid_x = 0.0f;
1517 float centroid_y = 0.0f;
1518 float signed_area = 0.0f;
1520 for(
const GList *node_iter = mask_form->
points; node_iter; node_iter = g_list_next(node_iter))
1525 if(!node0 || !node1)
return FALSE;
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;
1534 if(fabsf(signed_area) <= 1e-8f)
return FALSE;
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;
1547 const float mask_size,
const float border_size)
1551 dt_toast_log(_(
"Hardness: %3.2f%%"), (border_size * mask_hardness) / mask_size * 100.0f);
1567 float center_x = 0.0f;
1568 float center_y = 0.0f;
1569 float signed_area = 0.0f;
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;
1576 float scale_delta = amount;
1580 scale_delta = powf(amount, (
float)flow);
1584 scale_delta = 1.0f + amount * (float)flow;
1588 scale_delta = amount;
1593 for(GList *node_iter = mask_form->
points; node_iter; node_iter = g_list_next(node_iter), node_index++)
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;
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;
1617 float mask_size = 0.0f;
1635 const float scale_amount = powf(amount, (
float)flow);
1636 const float offset_amount = amount * (float)flow;
1638 for(GList *node_iter = mask_form->
points; node_iter; node_iter = g_list_next(node_iter), node_index++)
1646 offset_amount, increment),
1649 offset_amount, increment),
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);
1658 _init_hardness(mask_form, amount, increment, flow, mask_size, border_size);
1691 return _change_hardness(mask_form, parent_id, mask_gui, module, form_index, up ? +0.01f : -0.01f,
1694 return _change_size(mask_form, parent_id, mask_gui, module, form_index, up ? 1.02f : 0.98f,
1708 dt_toast_log(_(
"Polygon mask requires at least 3 nodes."));
1715 mask_form->
points = g_list_remove(mask_form->
points, last_node);
1727 double pressure,
int which,
int type, uint32_t
state,
1731 if(
type == GDK_2BUTTON_PRESS ||
type == GDK_3BUTTON_PRESS)
return 1;
1754 int node_count = g_list_length(mask_form->
points);
1761 polygon_node->
ctrl1[0] = polygon_node->
ctrl1[1] = polygon_node->
ctrl2[0] = polygon_node->
ctrl2[1] = -1.0;
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;
1774 mask_form->
points = g_list_append(mask_form->
points, polygon_first_node);
1787 mask_form->
points = g_list_append(mask_form->
points, polygon_node);
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];
1846 float handle_x, handle_y;
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);
1854 mask_gui->
delta[0] = handle_x - mask_gui->
pos[0];
1855 mask_gui->
delta[1] = handle_y - mask_gui->
pos[1];
1864 mask_gui->
delta[0] = handle_x - mask_gui->
pos[0];
1865 mask_gui->
delta[1] = handle_y - mask_gui->
pos[1];
1932 case GDK_KEY_BackSpace:
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];
1956 case GDK_KEY_Return:
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;
2015 mask_gui->
pos[0], mask_gui->
pos[1]))
2027 const guint node_count = g_list_length(mask_form->
points);
2052 mask_gui->
pos[0], mask_gui->
pos[1]);
2070 pts[0], pts[1], &
p[0], &
p[1], &
p[2], &
p[3], gui_points->
clockwise);
2081 mask_gui->
pos[0], mask_gui->
pos[1]);
2092 const int base = node_index * 6;
2093 const int node_point_index = base + 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] };
2109 mask_gui->
pos[0], mask_gui->
pos[1]);
2128 mask_form->
source[0] = raw_point[0];
2129 mask_form->
source[1] = raw_point[1];
2143 const int node_count,
const gboolean draw_border,
const gboolean draw_source)
2149 for(
int point_index = node_count * 3 + draw_border; point_index < point_count; point_index++)
2151 if(!isnan(point_buffer[point_index * 2]) && !isnan(point_buffer[point_index * 2 + 1]))
2153 start_idx = point_index;
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++)
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]);
2174 int form_index,
int node_count)
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];
2213 if(gui_points->
points && node_count > 0 && gui_points->
points_count > node_count * 3 + 6)
2216 node_count, zoom_scale);
2226 CAIRO_LINE_CAP_ROUND);
2230 if(mask_gui->
node_selected && selected_node >= 0 && selected_node < node_count
2233 const int node_index = selected_node;
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)
2249 for(
int node_index = 0; node_index < node_count; node_index++)
2252 if(mask_gui->
creation && node_index == node_count - 1)
break;
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];
2262 if(mask_gui->
creation && node_index == 0)
2265 dt_draw_node(cr, squared, action, selected, zoom_scale,
x, y);
2269 if(mask_gui->
node_selected && selected_node >= 0 && selected_node < node_count
2272 const int edited = selected_node;
2273 const gboolean selected = (mask_gui->
node_hovered == edited
2274 || (selected_handle_border == edited)
2276 const int curr_node = edited * 6;
2277 const float handle[2] = { gui_points->
border[curr_node], gui_points->
border[curr_node + 1] };
2288 .source = { gui_points->
source[0], gui_points->
source[1] } };
2292 float offset_x = gui_points->
source[0] - gui_points->
points[0];
2293 float offset_y = gui_points->
source[1] - gui_points->
points[1];
2300 for(
int node_index = 0; node_index < node_count; node_index++)
2303 && (node_index == mask_gui->
node_hovered || node_index == selected_node
2304 || (mask_gui->
creation && node_index == node_count - 1)))
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;
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)
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++)
2331 const float xx = border_buffer[border_index * 2];
2332 const float yy = border_buffer[border_index * 2 + 1];
2335 if(isnan(yy))
break;
2336 border_index = yy - 1;
2339 xmin =
MIN(xx, xmin);
2340 xmax =
MAX(xx, xmax);
2341 ymin =
MIN(yy, ymin);
2342 ymax =
MAX(yy, ymax);
2344 for(
int point_index = corner_count * 3; point_index < point_count; point_index++)
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);
2365 const int corner_count,
const int point_count,
int border_count,
2369 float xmin, xmax, ymin, ymax;
2371 &xmin, &xmax, &ymin, &ymax);
2372 *
height = ymax - ymin + 4;
2373 *
width = xmax - xmin + 4;
2381 gboolean get_source)
2386 float *point_buffer = NULL;
2387 float *border_buffer = NULL;
2388 int point_count = 0;
2389 int border_count = 0;
2392 &point_buffer, &point_count, &border_buffer, &border_count, get_source) != 0)
2399 const guint corner_count = g_list_length(mask_form->
points);
2427 int posx,
int posy,
int buffer_width)
2430 int l = sqrtf(sqf(p1[0] - p0[0]) + sqf(p1[1] - p0[1])) + 1;
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;
2436 for(
int i = 0;
i < l;
i++)
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);
2445 buffer[idx - 1] = fmaxf(buffer[idx - 1], op);
2447 buffer[idx - buffer_width] = fmaxf(buffer[idx - buffer_width], op);
2454 float **buffer,
int *
width,
int *
height,
int *posx,
int *posy)
2458 double start2 = 0.0;
2463 float *point_buffer = NULL;
2464 float *border_buffer = NULL;
2465 int point_count = 0;
2466 int border_count = 0;
2469 &border_buffer, &border_count,
FALSE) != 0)
2484 const guint corner_count = g_list_length(mask_form->
points);
2489 const int wb = *
width;
2492 const int sparse_factor = sparse ? 4 : 1;
2502 const size_t bufsize = (size_t)(*
width) * (*height);
2505 if(!
IS_NULL_PTR(bufptr)) memset(bufptr, 0,
sizeof(
float) * bufsize);
2514 const int border_point_count = border_count;
2515 if(border_point_count > 2)
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];
2521 int just_change_dir = 0;
2522 for(
int ii = corner_count * 3; ii < 2 * border_point_count - corner_count * 3; 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];
2533 if(yy == lasty)
continue;
2536 if(yy - lasty > 1 || yy - lasty < -1)
2540 for(
int j = yy + 1; j < lasty; j++)
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);
2552 for(
int j = lasty + 1; j < yy; j++)
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);
2564 if((lasty - lasty2) * (lasty - yy) > 0)
2566 const size_t idx = (size_t)(lasty - (*posy)) * (*
width) + lastx + 1 - (*posx);
2567 assert(idx < bufsize);
2569 just_change_dir = 1;
2572 if(just_change_dir && ii ==
i)
2576 const size_t idx = (size_t)(yy - (*posy)) * (*
width) + xx - (*posx);
2577 assert(idx < bufsize);
2578 float v = bufptr[idx];
2581 if(xx - (*posx) > 0)
2583 const size_t idx_ = (size_t)(yy - (*posy)) * (*
width) + xx - 1 - (*posx);
2584 assert(idx_ < bufsize);
2585 bufptr[idx_] = 1.0f;
2587 else if(xx - (*posx) < (*
width) - 1)
2589 const size_t idx_ = (size_t)(yy - (*posy)) * (*
width) + xx + 1 - (*posx);
2590 assert(idx_ < bufsize);
2591 bufptr[idx_] = 1.0f;
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;
2604 const size_t idx_ = (size_t)(yy - (*posy)) * (*
width) + xx - (*posx);
2605 assert(idx_ < bufsize);
2606 bufptr[idx_] = 1.0f;
2622 for(
int yy = 0; yy < hb; yy++)
2624 float *
const restrict
row = bufptr + (size_t)yy * wb;
2626 for(
int xx = 0; xx < wb; xx++)
2628 const float v =
row[xx];
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 };
2648 for(
int i = corner_count * 3;
i < border_count;
i++)
2650 p0[0] = point_buffer[
i * 2];
2651 p0[1] = point_buffer[
i * 2 + 1];
2653 p1[0] = pf1[0] = border_buffer[next * 2], p1[1] = pf1[1] = border_buffer[next * 2 + 1];
2655 p1[0] = pf1[0] = border_buffer[
i * 2], p1[1] = pf1[1] = border_buffer[
i * 2 + 1];
2658 if(next ==
i) next = 0;
2659 while(isnan(pf1[0]))
2665 p1[0] = pf1[0] = border_buffer[next * 2];
2666 p1[1] = pf1[1] = border_buffer[next * 2 + 1];
2669 const gboolean used_next = (next > 0);
2671 if(sparse && have_prev && !used_next
2672 && (prev0[0] != p0[0] || prev0[1] != p0[1] || prev1[0] != p1[0] || prev1[1] != p1[1]))
2674 for(
int k = 1;
k < sparse_factor;
k++)
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) };
2686 if(last0[0] != p0[0] || last0[1] != p0[1] || last1[0] != p1[0] || last1[1] != p1[1])
2729 int point_start = -1;
2734 for(
int k = 0;
k < point_count;
k++)
2736 float x = polygon[2 *
k];
2737 float y = polygon[2 *
k + 1];
2739 if(
x >= xmin + 1 && y >= ymin + 1
2740 &&
x <= xmax - 1 && y <= ymax - 1)
2750 if(point_start < 0)
return 0;
2758 } roi_crop_segment_t;
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);
2766 goto fallback_passes;
2769 int xmin_l = -1, xmin_r = -1;
2770 int xmax_l = -1, xmax_r = -1;
2774 for(
int k = 0;
k < point_count;
k++)
2776 const int kk = (
k + point_start) % point_count;
2777 const float x = polygon[2 * kk];
2779 if(xmin_l < 0 &&
x < xmin) xmin_l =
k;
2780 if(xmin_l >= 0 &&
x >= xmin) xmin_r =
k - 1;
2782 if(xmin_l >= 0 && xmin_r >= 0)
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];
2790 for(
int n = 0;
n < count;
n++)
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;
2797 xmin_l = xmin_r = -1;
2800 if(xmax_l < 0 && x > xmax) xmax_l =
k;
2801 if(xmax_l >= 0 &&
x <= xmax) xmax_r =
k - 1;
2803 if(xmax_l >= 0 && xmax_r >= 0)
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];
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;
2817 xmax_l = xmax_r = -1;
2821 for(
int s = 0; s < xmax_count; s++)
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++)
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;
2836 int ymin_l = -1, ymin_r = -1;
2837 int ymax_l = -1, ymax_r = -1;
2841 for(
int k = 0;
k < point_count;
k++)
2843 const int kk = (
k + point_start) % point_count;
2844 const float y = polygon[2 * kk + 1];
2846 if(ymin_l < 0 && y < ymin) ymin_l =
k;
2847 if(ymin_l >= 0 && y >= ymin) ymin_r =
k - 1;
2849 if(ymin_l >= 0 && ymin_r >= 0)
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];
2857 for(
int n = 0;
n < count;
n++)
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;
2864 ymin_l = ymin_r = -1;
2867 if(ymax_l < 0 && y > ymax) ymax_l =
k;
2868 if(ymax_l >= 0 && y <= ymax) ymax_r =
k - 1;
2870 if(ymax_l >= 0 && ymax_r >= 0)
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];
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;
2884 ymax_l = ymax_r = -1;
2888 for(
int s = 0; s < ymax_count; s++)
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++)
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;
2907 for(
int k = 0;
k < point_count;
k++)
2909 const int kk = (
k + point_start) % point_count;
2911 if(l < 0 && polygon[2 * kk] < xmin) l =
k;
2912 if(l >= 0 && polygon[2 * kk] >= xmin)
r =
k - 1;
2915 if(l >= 0 &&
r >= 0)
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];
2923 for(
int n = 0;
n < count;
n++)
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;
2935 for(
int k = 0;
k < point_count;
k++)
2937 const int kk = (
k + point_start) % point_count;
2939 if(l < 0 && polygon[2 * kk] > xmax) l =
k;
2940 if(l >= 0 && polygon[2 * kk] <= xmax)
r =
k - 1;
2943 if(l >= 0 &&
r >= 0)
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];
2951 for(
int n = 0;
n < count;
n++)
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;
2963 for(
int k = 0;
k < point_count;
k++)
2965 const int kk = (
k + point_start) % point_count;
2967 if(l < 0 && polygon[2 * kk + 1] < ymin) l =
k;
2968 if(l >= 0 && polygon[2 * kk + 1] >= ymin)
r =
k - 1;
2971 if(l >= 0 &&
r >= 0)
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];
2979 for(
int n = 0;
n < count;
n++)
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;
2991 for(
int k = 0;
k < point_count;
k++)
2993 const int kk = (
k + point_start) % point_count;
2995 if(l < 0 && polygon[2 * kk + 1] > ymax) l =
k;
2996 if(l >= 0 && polygon[2 * kk + 1] <= ymax)
r =
k - 1;
2999 if(l >= 0 &&
r >= 0)
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];
3007 for(
int n = 0;
n < count;
n++)
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;
3024 const int l = sqrt((p1[0] - p0[0]) * (p1[0] - p0[0]) + (p1[1] - p0[1]) * (p1[1] - p0[1])) + 1;
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;
3030 const int dx = lx < 0 ? -1 : 1;
3031 const int dy = ly < 0 ? -1 : 1;
3032 const int dpy = dy * bw;
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);
3039 for(
int i = 0;
i < l;
i++)
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;
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);
3053 if(
x >= 0 && x < bw && y + dy >= 0 && y + dy < bh)
3054 buf[dpy] =
MAX(buf[dpy], op);
3067 double start2 = 0.0;
3070 const int px = roi->
x;
3071 const int py = roi->
y;
3074 const float scale = roi->
scale;
3077 const int sparse_factor = sparse ? 4 : 1;
3084 int polygon_in_roi = 0;
3085 int feather_in_roi = 0;
3086 int polygon_encircles_roi = 0;
3089 float *points = NULL, *
border = NULL;
3090 int points_count = 0, border_count = 0;
3093 &points, &points_count, &
border, &border_count,
FALSE) != 0)
3099 if(points_count <= 2)
3113 const guint corner_count = g_list_length(mask_form->
points);
3116 for(
int i = corner_count * 3;
i < border_count;
i++)
3118 const float xx =
border[2 *
i];
3119 const float yy =
border[2 *
i + 1];
3122 if(isnan(yy))
break;
3126 border[2 *
i] = xx * scale - px;
3127 border[2 *
i + 1] = yy * scale - py;
3129 for(
int i = corner_count * 3;
i < points_count;
i++)
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;
3138 for(
int i = corner_count * 3;
i < points_count;
i++)
3140 const int xx = points[
i * 2];
3141 const int yy = points[
i * 2 + 1];
3143 if(xx > 1 && yy > 1 && xx <
width - 2 && yy <
height - 2)
3153 int crossing_count = 0;
3156 const int y =
height / 2;
3158 for(
int i = corner_count * 3;
i < points_count;
i++)
3160 const int yy = (int)points[2 *
i + 1];
3161 if(yy != last_y && yy == y)
3163 if(points[2 *
i] >
x) crossing_count++;
3168 if(crossing_count & 1)
3171 polygon_encircles_roi = 1;
3176 for(
int i = corner_count * 3;
i < border_count;
i++)
3178 const float xx =
border[
i * 2];
3179 const float yy =
border[
i * 2 + 1];
3182 if(isnan(yy))
break;
3186 if(xx > 1 && yy > 1 && xx <
width - 2 && yy <
height - 2)
3194 if(!polygon_in_roi && !feather_in_roi)
3202 float xmin, xmax, ymin, ymax;
3204 &xmin, &xmax, &ymin, &ymax);
3231 memcpy(cpoints, points,
sizeof(
float) * 2 * points_count);
3237 points_count - corner_count * 3, 0,
3239 polygon_encircles_roi = polygon_encircles_roi || !crop_success;
3248 if(polygon_encircles_roi)
3258 float xlast = cpoints[(points_count - 1) * 2];
3259 float ylast = cpoints[(points_count - 1) * 2 + 1];
3261 for(
int i = corner_count * 3;
i < points_count;
i++)
3263 float xstart = xlast;
3264 float ystart = ylast;
3266 float xend = xlast = cpoints[
i * 2];
3267 float yend = ylast = cpoints[
i * 2 + 1];
3272 tmp = ystart, ystart = yend, yend = tmp;
3273 tmp = xstart, xstart = xend, xend = tmp;
3276 const float m = (xstart - xend) / (ystart - yend);
3279 for(
int yy = (
int)ceilf(ystart); (float)yy < yend;
3282 const float xcross = xstart +
m * (yy - ystart);
3284 int xx = floorf(xcross);
3285 if((
float)xx + 0.5f <= xcross) xx++;
3287 if(xx < 0 || xx >=
width || yy < 0 || yy >=
height)
3290 const size_t index = (size_t)yy *
width + xx;
3292 buffer[index] = 1.0f - buffer[index];
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);
3310 for(
int yy = yymin; yy <= yymax; yy++)
3312 float *
const restrict
row = buffer + (size_t)yy *
width;
3314 for(
int xx = xxmin; xx <= xxmax; xx++)
3316 const float v =
row[xx];
3333 if(!polygon_encircles_roi)
3335 const int dpoints_capacity = 4 * border_count * (sparse ? sparse_factor : 1);
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 };
3353 for(
int i = corner_count * 3;
i < border_count;
i++)
3355 p0[0] = floorf(points[
i * 2] + 0.5f);
3356 p0[1] = ceilf(points[
i * 2 + 1]);
3359 p1[0] = pf1[0] =
border[next_index * 2];
3360 p1[1] = pf1[1] =
border[next_index * 2 + 1];
3364 p1[0] = pf1[0] =
border[
i * 2];
3365 p1[1] = pf1[1] =
border[
i * 2 + 1];
3369 if(next_index ==
i) next_index = 0;
3370 while(isnan(pf1[0]))
3376 p1[0] = pf1[0] =
border[next_index * 2];
3377 p1[1] = pf1[1] =
border[next_index * 2 + 1];
3380 const gboolean used_next = (next_index > 0);
3382 if(sparse && have_prev && !used_next
3383 && (prev0[0] != p0[0] || prev0[1] != p0[1] || prev1[0] != p1[0] || prev1[1] != p1[1]))
3385 for(
int k = 1;
k < sparse_factor;
k++)
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)
3394 dpoints[dindex] = mp0[0];
3395 dpoints[dindex + 1] = mp0[1];
3396 dpoints[dindex + 2] = mp1[0];
3397 dpoints[dindex + 3] = mp1[1];
3404 if(last0[0] != p0[0] || last0[1] != p0[1] || last1[0] != p1[0] || last1[1] != p1[1])
3406 dpoints[dindex] = p0[0];
3407 dpoints[dindex + 1] = p0[1];
3408 dpoints[dindex + 2] = p1[0];
3409 dpoints[dindex + 3] = p1[1];
3432 for(
int n = 0;
n < dindex;
n += 4)
3466 snprintf(mask_form->
name,
sizeof(mask_form->
name), _(
"polygon #%d"), (
int)form_number);
3471 const int opacity,
char *
const restrict msgbuf,
3472 const size_t msgbuf_len)
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);
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);
3482 g_strlcat(msgbuf, _(
"<b>Node curvature</b>: drag\n<b>Reset curvature</b>: right-click"), msgbuf_len);
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);
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);
3490 g_strlcat(msgbuf, _(
"<b>Move segment</b>: drag\n<b>Add node</b>: ctrl+click"), msgbuf_len);
3492 g_snprintf(msgbuf, msgbuf_len, _(
"<b>Size</b>: scroll, <b>Hardness</b>: shift+scroll\n"
3493 "<b>Opacity</b>: ctrl+scroll (%d%%)"), opacity);
3506 float offset[2] = { 0.1f, 0.1f };
3528 const int form_id = mask_gui->
formid;
3550 const int form_id = mask_gui->
formid;
3564 node_index, &
node->state))
3592 const float pzx,
const float pzy)
3597 gchar *accel = g_strdup_printf(_(
"%s+Click"), gtk_accelerator_get_label(0, GDK_CONTROL_MASK));
3599 gboolean ret =
FALSE;
3625 gchar *to_change_type = g_strdup_printf(_(
"Switch to %s node"), (is_corner) ? _(
"round") : _(
"cusp"));
3636 gtk_widget_set_sensitive(menu_item, !is_corner);
static double dist(double x1, double y1, double x2, double y2)
static const dt_aligned_pixel_simd_t const dt_adaptation_t const float p
static const float const float const float min
float dt_conf_get_float(const char *name)
void dt_toast_log(const char *msg,...)
void dt_print(dt_debug_thread_t thread, const char *msg,...)
#define dt_free_align(ptr)
#define dt_pixelpipe_cache_alloc_align_float_cache(pixels, id)
static float * dt_alloc_align_float(size_t pixels)
#define dt_pixelpipe_cache_alloc_align_cache(size, id)
static const GList * g_list_next_wraparound(const GList *list, const GList *head)
#define dt_pixelpipe_cache_free_align(mem)
static GList * g_list_next_bounded(GList *list)
#define __OMP_PARALLEL_FOR__(...)
static const dt_aligned_pixel_simd_t value
static double dt_get_wtime(void)
#define __OMP_PARALLEL_FOR_SIMD__(...)
static gboolean dt_modifier_is(const GdkModifierType state, const GdkModifierType desired_modifier_mask)
#define IS_NULL_PTR(p)
C is way too permissive with !=, == and if(var) checks, which can mean too many things depending on w...
static gboolean g_list_shorter_than(const GList *list, unsigned len)
#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)
void dt_dev_coordinates_raw_norm_to_raw_abs(dt_develop_t *dev, float *points, size_t num_points)
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)
gboolean dt_dev_pixelpipe_has_preview_output(const dt_develop_t *dev, const dt_dev_pixelpipe_t *pipe, const dt_iop_roi_t *roi)
void dt_dev_coordinates_image_abs_to_raw_norm(dt_develop_t *dev, float *points, size_t num_points)
@ DT_DEV_TRANSFORM_DIR_BACK_EXCL
@ DT_DEV_TRANSFORM_DIR_BACK_INCL
@ DT_DEV_TRANSFORM_DIR_FORW_INCL
@ DT_DEV_TRANSFORM_DIR_ALL
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.
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.
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.
static guint dt_keys_mainpad_alternatives(const guint key_val)
Remap keypad keys to usual mainpad ones.
#define DT_GUI_MOUSE_EFFECT_RADIUS
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])
static int dt_masks_gui_selected_segment_index(const dt_masks_form_gui_t *gui)
static int dt_masks_gui_selected_node_index(const dt_masks_form_gui_t *gui)
static int dt_masks_gui_selected_handle_index(const dt_masks_form_gui_t *gui)
static float * dt_masks_dynbuf_buffer(dt_masks_dynbuf_t *a)
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)
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)
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)
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)
static void dt_masks_translate_source(dt_masks_form_t *form, const float delta_x, const float delta_y)
static float dt_masks_dynbuf_get(dt_masks_dynbuf_t *a, int offset)
static void dt_masks_translate_ctrl_node(float node[2], float ctrl1[2], float ctrl2[2], const float delta_x, const float delta_y)
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)
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)
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)
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_INTERACTION_HARDNESS
@ DT_MASKS_INTERACTION_SIZE
#define menu_item_set_fake_accel(menu_item, keyval, mods)
void _masks_gui_delete_node_callback(GtkWidget *menu, gpointer user_data)
static void dt_masks_gui_cursor_to_raw_norm(dt_develop_t *dev, const dt_masks_form_gui_t *gui, float point[2])
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)
static gboolean dt_masks_center_of_gravity_from_points(const float *points, const int points_count, float center[2], float *area)
static size_t dt_masks_dynbuf_position(dt_masks_dynbuf_t *a)
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)
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)
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)
@ DT_MASKS_INCREMENT_SCALE
@ DT_MASKS_INCREMENT_OFFSET
@ DT_MASKS_INCREMENT_ABSOLUTE
dt_masks_form_t * dt_masks_get_from_id(dt_develop_t *dev, int id)
@ DT_MASKS_POINT_STATE_NORMAL
@ DT_MASKS_POINT_STATE_USER
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])
static float * dt_masks_dynbuf_harvest(dt_masks_dynbuf_t *a)
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)
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)
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])
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)
static void dt_masks_dynbuf_free(dt_masks_dynbuf_t *a)
static void dt_masks_set_ctrl_points(float ctrl1[2], float ctrl2[2], const float control_points[4])
#define CLAMPF(a, mn, mx)
@ DT_DEV_PIXELPIPE_THUMBNAIL
static gboolean _polygon_get_gravity_center(const dt_masks_form_t *mask_form, float center[2], float *area)
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)
static int _find_closest_handle(dt_masks_form_t *mask_form, dt_masks_form_gui_t *mask_gui, int form_index)
#define POLYGON_MAX_SELF_INTERSECTIONS(nb_nodes)
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.
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.
static float _polygon_get_interaction_value(const dt_masks_form_t *mask_form, dt_masks_interaction_t interaction)
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.
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.
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.
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.
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)
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.
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)
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.
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.
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.
static void _polygon_switch_node_callback(GtkWidget *widget, gpointer user_data)
static gboolean _is_within_pxl_threshold(float *min, float *max, int pixel_threshold)
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.
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)
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.
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).
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].
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.
static void _polygon_falloff_roi(float *buffer, int *p0, int *p1, int bw, int bh)
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)
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.
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.
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)
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.
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.
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.
static void _polygon_initial_source_pos(const float iwd, const float iht, float *x, float *y)
const dt_masks_functions_t dt_masks_functions_polygon
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)
static void _polygon_add_node_callback(GtkWidget *menu, gpointer user_data)
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).
static int _polygon_crop_to_roi(float *polygon, const int point_count, float xmin, float xmax, float ymin, float ymax)
static void _polygon_translate_node(dt_masks_node_polygon_t *node, const float delta_x, const float delta_y)
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)
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.
static void _polygon_creation_closing_form_callback(GtkWidget *widget, gpointer user_data)
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.
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)
static void _polygon_translate_all_nodes(dt_masks_form_t *mask_form, const float delta_x, const float delta_y)
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)
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.
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)
static void _polygon_duplicate_points(dt_develop_t *const dev, dt_masks_form_t *const base, dt_masks_form_t *const dest)
static void _polygon_init_ctrl_points(dt_masks_form_t *mask_form)
Initialize control points to match a Catmull-Rom-like spline.
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)
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.
static void _polygon_reset_round_node_callback(GtkWidget *widget, gpointer user_data)
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.
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.
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)
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)
static void _polygon_sanitize_config(dt_masks_type_t type)
static gboolean _polygon_is_clockwise(dt_masks_form_t *mask_form)
Determine polygon winding order.
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].
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.
struct _GtkWidget GtkWidget
const float uint32_t state[4]
struct dt_develop_t * develop
dt_dev_pixelpipe_type_t type
struct dt_dev_pixelpipe_t * virtual_pipe
struct dt_develop_t::@17 roi
struct dt_develop_t * dev
Region of interest passed through the pixelpipe.
void(* draw_shape)(cairo_t *cr, const float *points, const int points_count, const int nb, const gboolean border, const gboolean source)
struct dt_masks_gui_center_point_t::@34 main
struct dt_masks_gui_center_point_t::@35 source
dt_masks_points_states_t state
typedef double((*spd)(unsigned long int wavelength, double TempK))