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))
637 if(intersection_count > 0)
641 if((cell_values[
k] -
i) * (inter_last0 - inter_last1) > 0
642 && inter_last0 >= cell_values[
k] && inter_last1 <=
i)
653 intersection_count++;
660 intersection_count++;
668 intersection_grid[idx] =
i;
680 *intersection_count_out = intersection_count;
690 const double iop_order,
const int transform_direction,
692 float **border_buffer,
int *border_count, gboolean source)
694 *point_buffer = NULL;
696 if(!
IS_NULL_PTR(border_buffer)) *border_buffer = NULL;
704 const float input_width = pipe->
iwidth;
705 const float input_height = pipe->
iheight;
708 const guint node_count = g_list_length(mask_form->
points);
734 float dx = 0.0f, dy = 0.0f;
741 dx = (polygon->
node[0] - mask_form->
source[0]) * input_width;
742 dy = (polygon->
node[1] - mask_form->
source[1]) * input_height;
744 for(
const GList *point_node = mask_form->
points; point_node; point_node = g_list_next(point_node))
750 buf[0] =
node->ctrl1[0] * input_width - dx;
751 buf[1] =
node->ctrl1[1] * input_height - dy;
752 buf[2] =
node->node[0] * input_width - dx;
753 buf[3] =
node->node[1] * input_height - dy;
754 buf[4] =
node->ctrl2[0] * input_width - dx;
755 buf[5] =
node->ctrl2[1] * input_height - dy;
783 const GList *form_points = mask_form->
points;
784 for(
int node_index = 0; node_index < node_count; node_index++)
787 border_init[node_index * 6 + 2] = -pb;
793 float p1[5] = { point1->
node[0] * input_width - dx, point1->
node[1] * input_height - dy,
794 point1->
ctrl2[0] * input_width - dx, point1->
ctrl2[1] * input_height - dy,
795 cw * point1->
border[1] *
MIN(input_width, input_height) };
796 float p2[5] = { point2->
node[0] * input_width - dx, point2->
node[1] * input_height - dy,
797 point2->
ctrl1[0] * input_width - dx, point2->
ctrl1[1] * input_height - dy,
798 cw * point2->
border[0] *
MIN(input_width, input_height) };
799 float p3[5] = { point2->
node[0] * input_width - dx, point2->
node[1] * input_height - dy,
800 point2->
ctrl2[0] * input_width - dx, point2->
ctrl2[1] * input_height - dy,
801 cw * point2->
border[1] *
MIN(input_width, input_height) };
802 float p4[5] = { point3->
node[0] * input_width - dx, point3->
node[1] * input_height - dy,
803 point3->
ctrl1[0] * input_width - dx, point3->
ctrl1[1] * input_height - dy,
804 cw * point3->
border[0] *
MIN(input_width, input_height) };
807 form_points = g_list_next(form_points);
810 float rc[2] = { 0 }, rb[2] = { 0 };
811 float bmin[2] = { NAN, NAN };
812 float bmax[2] = { NAN, NAN };
813 float cmin[2] = { NAN, NAN };
814 float cmax[2] = { NAN, NAN };
816 _polygon_points_recurs(p1, p2, 0.0, 1.0, cmin, cmax, bmin, bmax, rc, rb, dpoints, dborder,
817 border_buffer && (node_count >= 3), pixel_threshold);
824 if(fabs(lastb0 - rb[0]) > 1.0f || fabs(lastb1 - rb[1]) > 1.0f)
860 if(dborder && node_count >= 3)
865 _polygon_border_get_XY(p3[0], p3[1], p3[2], p3[3], p4[2], p4[3], p4[0], p4[1], 0.00001f, p3[4], cmin, cmin + 1,
869 _polygon_border_get_XY(p3[0], p3[1], p3[2], p3[3], p4[2], p4[3], p4[0], p4[1], 0.00001f, p3[4], cmin,
870 cmin + 1, bmax, bmax + 1);
872 if(bmax[0] - rb[0] > 1 || bmax[0] - rb[0] < -1 || bmax[1] - rb[1] > 1 || bmax[1] - rb[1] < -1)
926 *point_buffer, *point_count))
930 float pts[2] = { mask_form->
source[0] * input_width, mask_form->
source[1] * input_height };
934 dx = pts[0] - (*point_buffer)[2];
935 dy = pts[1] - (*point_buffer)[3];
937 for(
int i = 0;
i < *point_count;
i++)
939 (*point_buffer)[
i * 2] += dx;
940 (*point_buffer)[
i * 2 + 1] += dy;
946 *point_buffer, *point_count))
959 *point_buffer, *point_count))
963 *border_buffer, *border_count))
975 for(
int node_index = 0; node_index < node_count; node_index++)
976 for(
int i = 2;
i < 6;
i++)
977 (*border_buffer)[node_index * 6 +
i] = border_init[node_index * 6 +
i];
980 for(
int i = 0;
i < inter_count;
i++)
986 (*border_buffer)[
v * 2] = NAN;
987 (*border_buffer)[
v * 2 + 1] = w;
991 if(w > node_count * 3)
993 if(isnan((*border_buffer)[node_count * 6]) && isnan((*border_buffer)[node_count * 6 + 1]))
994 (*border_buffer)[node_count * 6 + 1] = w;
995 else if(isnan((*border_buffer)[node_count * 6]))
996 (*border_buffer)[node_count * 6 + 1]
997 =
MAX((*border_buffer)[node_count * 6 + 1], w);
999 (*border_buffer)[node_count * 6 + 1] = w;
1000 (*border_buffer)[node_count * 6] = NAN;
1002 (*border_buffer)[
v * 2] = NAN;
1003 (*border_buffer)[
v * 2 + 1] = NAN;
1023 *point_buffer = NULL;
1028 *border_buffer = NULL;
1043 GList *firstpt = g_list_nth(mask_form->
points, segment_index);
1054 float min_dist = FLT_MAX;
1056 for(
int i = 0;
i <= 100;
i++)
1058 const float t =
i / 100.0f;
1059 float sample_x = 0.0f;
1060 float sample_y = 0.0f;
1063 &sample_x, &sample_y);
1065 const float dist = (point_x - sample_x) * (point_x - sample_x)
1066 + (point_y - sample_y) * (point_y - sample_y);
1082 const guint node_count = g_list_length(mask_form->
points);
1084 if(selected_segment < 0 || selected_segment >= (
int)node_count)
return;
1098 mask_form, selected_segment);
1100 GList *pt = g_list_nth(mask_form->
points, selected_segment);
1117 mask_form->
points = g_list_insert(mask_form->
points, new_node, selected_segment + 1);
1136 for(GList *node_entry = mask_form->
points; node_entry; node_entry = g_list_next(node_entry))
1141 float **point_buffer,
int *point_count,
1142 float **border_buffer,
int *border_count,
1146 const double ioporder = (module) ? module->
iop_order : 0.0f;
1149 border_buffer, border_count, source);
1154 float *mask_size,
float *border_size)
1160 const int node_count = g_list_length(mask_form->
points);
1161 float p1[2] = { FLT_MAX, FLT_MAX };
1162 float p2[2] = { FLT_MIN, FLT_MIN };
1164 float fp1[2] = { FLT_MAX, FLT_MAX };
1165 float fp2[2] = { FLT_MIN, FLT_MIN };
1170 const float x = gui_points->
points[
i * 2];
1171 const float y = gui_points->
points[
i * 2 + 1];
1173 p1[0] = fminf(p1[0],
x);
1174 p2[0] = fmaxf(p2[0],
x);
1175 p1[1] = fminf(p1[1], y);
1176 p2[1] = fmaxf(p2[1], y);
1181 const float fx = gui_points->
border[
i * 2];
1182 const float fy = gui_points->
border[
i * 2 + 1];
1188 fp1[0] = fminf(fp1[0],
fx);
1189 fp2[0] = fmaxf(fp2[0],
fx);
1190 fp1[1] = fminf(fp1[1], fy);
1191 fp2[1] = fmaxf(fp2[1], fy);
1196 float mask_span[2] = { p2[0] - p1[0], p2[1] - p1[1] };
1198 *mask_size = fmaxf(mask_span[0], mask_span[1]);
1202 float border_span[2] = { fp2[0] - fp1[0], fp2[1] - fp1[1] };
1204 *border_size = fmaxf(border_span[0], border_span[1]);
1209 float *center_y,
float *surface);
1221 if(
size <= 0.0f)
return NAN;
1226 float hardness_sum = 0.0f;
1227 int hardness_count = 0;
1229 for(
const GList *point_node = mask_form->
points; point_node; point_node = g_list_next(point_node))
1233 hardness_sum +=
node->border[0] +
node->border[1];
1234 hardness_count += 2;
1237 return hardness_count > 0 ? hardness_sum / (float)hardness_count : NAN;
1245 float center[2],
float *area)
1249 const int points_count = g_list_length(mask_form->
points);
1250 if(points_count <= 0)
return FALSE;
1256 for(
const GList *point_node = mask_form->
points; point_node; point_node = g_list_next(point_node))
1260 point_buffer[2 *
i] =
node->node[0];
1261 point_buffer[2 *
i + 1] =
node->node[1];
1283 const int index = 0;
1288 if(!
_change_size(mask_form, 0, mask_gui, module, index,
value, increment, flow))
return NAN;
1303 int node_count,
int *inside,
int *inside_border,
1304 int *near,
int *inside_source,
float *
dist)
1318 float min_dist_pixel = FLT_MAX;
1320 const float radius2 = radius * radius;
1321 const float pt[2] = { point_x, point_y };
1333 const float offset_x = -gui_points->
points[2] + gui_points->
source[2];
1334 const float offset_y = -gui_points->
points[3] + gui_points->
source[3];
1335 int current_seg = 1;
1341 if(gui_points->
points[
i * 2] == gui_points->
points[current_seg * 6 + 2]
1342 && gui_points->
points[
i * 2 + 1] == gui_points->
points[current_seg * 6 + 3])
1344 current_seg = (current_seg + 1) % node_count;
1348 const float source_x = gui_points->
points[
i * 2] + offset_x;
1349 const float source_y = gui_points->
points[
i * 2 + 1] + offset_y;
1352 const float sdx = point_x - source_x;
1353 const float sdy = point_y - source_y;
1354 const float sdd = sqf(sdx) + sqf(sdy);
1355 if(sdd < min_dist_pixel)
1356 min_dist_pixel = sdd;
1358 *
dist = min_dist_pixel;
1365 int current_seg = 1;
1369 if(gui_points->
points[
i * 2 + 1] == gui_points->
points[current_seg * 6 + 3]
1370 && gui_points->
points[
i * 2] == gui_points->
points[current_seg * 6 + 2])
1372 current_seg = (current_seg + 1) % node_count;
1375 const float yy = gui_points->
points[
i * 2 + 1];
1376 const float xx = gui_points->
points[
i * 2];
1378 const float dx = point_x - xx;
1379 const float dy = point_y - yy;
1380 const float dd = sqf(dx) + sqf(dy);
1381 if(dd < min_dist_pixel)
1383 min_dist_pixel = dd;
1385 if(current_seg >= 0 && dd < radius2)
1387 if(current_seg == 0)
1388 *near = node_count - 1;
1390 *near = current_seg - 1;
1396 *
dist = min_dist_pixel;
1419 int node_index,
float *handle_x,
float *handle_y,
void *user_data)
1421 if(
IS_NULL_PTR(gui_points) || node_index < 0 || node_index >= node_count)
return FALSE;
1422 *handle_x = gui_points->
border[node_index * 6];
1423 *handle_y = gui_points->
border[node_index * 6 + 1];
1424 return !(isnan(*handle_x) || isnan(*handle_y));
1431 float *handle_x,
float *handle_y,
void *user_data)
1435 gui_points->
points[node_index * 6 + 4], gui_points->
points[node_index * 6 + 5],
1436 handle_x, handle_y, gui_points->
clockwise);
1444 int *inside_border,
int *near,
int *inside_source,
float *
dist,
void *user_data)
1448 inside, inside_border, near, inside_source,
dist);
1465 float *center_x,
float *center_y,
float *area)
1467 if(
IS_NULL_PTR(point_buffer) || point_count < 3)
return;
1469 float centroid_x = 0.0f;
1470 float centroid_y = 0.0f;
1471 float signed_area = 0.0f;
1473 for(
int node_index = 0; node_index < point_count; node_index++)
1475 const int next_index = (node_index + 1) % point_count;
1476 const float x0 = point_buffer[node_index * 2];
1477 const float y0 = point_buffer[node_index * 2 + 1];
1478 const float x1 = point_buffer[next_index * 2];
1479 const float y1 = point_buffer[next_index * 2 + 1];
1480 const float cross = x0 * y1 - x1 * y0;
1482 signed_area += cross;
1483 centroid_x += (x0 + x1) * cross;
1484 centroid_y += (y0 + y1) * cross;
1487 if(fabsf(signed_area) > 1e-8f)
1489 const float inv_divisor = 1.0f / (3.0f * signed_area);
1490 if(!
IS_NULL_PTR(center_x)) *center_x = centroid_x * inv_divisor;
1491 if(!
IS_NULL_PTR(center_y)) *center_y = centroid_y * inv_divisor;
1500 float *center_x,
float *center_y,
float *area)
1504 float centroid_x = 0.0f;
1505 float centroid_y = 0.0f;
1506 float signed_area = 0.0f;
1508 for(
const GList *node_iter = mask_form->
points; node_iter; node_iter = g_list_next(node_iter))
1513 if(!node0 || !node1)
return FALSE;
1515 const float cross = node0->
node[0] * node1->
node[1] - node1->
node[0] * node0->
node[1];
1516 signed_area += cross;
1517 centroid_x += (node0->
node[0] + node1->
node[0]) * cross;
1518 centroid_y += (node0->
node[1] + node1->
node[1]) * cross;
1522 if(fabsf(signed_area) <= 1e-8f)
return FALSE;
1524 const float inv_divisor = 1.0f / (3.0f * signed_area);
1525 if(!
IS_NULL_PTR(center_x)) *center_x = centroid_x * inv_divisor;
1526 if(!
IS_NULL_PTR(center_y)) *center_y = centroid_y * inv_divisor;
1535 const float mask_size,
const float border_size)
1539 dt_toast_log(_(
"Hardness: %3.2f%%"), (border_size * mask_hardness) / mask_size * 100.0f);
1555 float center_x = 0.0f;
1556 float center_y = 0.0f;
1557 float signed_area = 0.0f;
1561 if(amount < 1.0f && signed_area < 0.00001f && signed_area > -0.00001f)
return 1;
1562 if(amount > 1.0f && signed_area > 4.0f)
return 1;
1564 float scale_delta = amount;
1568 scale_delta = powf(amount, (
float)flow);
1572 scale_delta = 1.0f + amount * (float)flow;
1576 scale_delta = amount;
1581 for(GList *node_iter = mask_form->
points; node_iter; node_iter = g_list_next(node_iter), node_index++)
1588 const float new_node_x = center_x + (
node->node[0] - center_x) * scale_delta;
1589 const float new_node_y = center_y + (
node->node[1] - center_y) * scale_delta;
1590 const float ctrl1_offset_x = (
node->ctrl1[0] -
node->node[0]) * scale_delta;
1591 const float ctrl1_offset_y = (
node->ctrl1[1] -
node->node[1]) * scale_delta;
1592 const float ctrl2_offset_x = (
node->ctrl2[0] -
node->node[0]) * scale_delta;
1593 const float ctrl2_offset_y = (
node->ctrl2[1] -
node->node[1]) * scale_delta;
1596 node->node[0] = new_node_x;
1597 node->node[1] = new_node_y;
1598 node->ctrl1[0] = new_node_x + ctrl1_offset_x;
1599 node->ctrl1[1] = new_node_y + ctrl1_offset_y;
1600 node->ctrl2[0] = new_node_x + ctrl2_offset_x;
1601 node->ctrl2[1] = new_node_y + ctrl2_offset_y;
1605 float mask_size = 0.0f;
1623 const float scale_amount = powf(amount, (
float)flow);
1624 const float offset_amount = amount * (float)flow;
1626 for(GList *node_iter = mask_form->
points; node_iter; node_iter = g_list_next(node_iter), node_index++)
1634 offset_amount, increment),
1637 offset_amount, increment),
1642 float mask_size = 1.0f;
1643 float border_size = 0.0f;
1644 _polygon_get_sizes(module, mask_form, mask_gui, form_index, &mask_size, &border_size);
1646 _init_hardness(mask_form, amount, increment, flow, mask_size, border_size);
1679 return _change_hardness(mask_form, parent_id, mask_gui, module, form_index, up ? +0.01f : -0.01f,
1682 return _change_size(mask_form, parent_id, mask_gui, module, form_index, up ? 1.02f : 0.98f,
1696 dt_toast_log(_(
"Polygon mask requires at least 3 nodes."));
1703 mask_form->
points = g_list_remove(mask_form->
points, last_node);
1715 double pressure,
int which,
int type, uint32_t
state,
1719 if(
type == GDK_2BUTTON_PRESS ||
type == GDK_3BUTTON_PRESS)
return 1;
1742 int node_count = g_list_length(mask_form->
points);
1749 polygon_node->
ctrl1[0] = polygon_node->
ctrl1[1] = polygon_node->
ctrl2[0] = polygon_node->
ctrl2[1] = -1.0;
1757 polygon_first_node->
node[0] = polygon_node->
node[0];
1758 polygon_first_node->
node[1] = polygon_node->
node[1];
1759 polygon_first_node->
ctrl1[0] = polygon_first_node->
ctrl1[1] = polygon_first_node->
ctrl2[0] = polygon_first_node->
ctrl2[1] = -1.0;
1762 mask_form->
points = g_list_append(mask_form->
points, polygon_first_node);
1775 mask_form->
points = g_list_append(mask_form->
points, polygon_node);
1781 polygon_last_node->
ctrl1[0] = polygon_last_node->
ctrl2[0] = polygon_last_node->
node[0];
1782 polygon_last_node->
ctrl1[1] = polygon_last_node->
ctrl2[1] = polygon_last_node->
node[1];
1834 float handle_x, handle_y;
1837 gui_points->
points[handle_index * 6 + 3],
1838 gui_points->
points[handle_index * 6 + 4],
1839 gui_points->
points[handle_index * 6 + 5],
1840 &handle_x, &handle_y, gui_points->
clockwise);
1842 mask_gui->
delta[0] = handle_x - mask_gui->
pos[0];
1843 mask_gui->
delta[1] = handle_y - mask_gui->
pos[1];
1852 mask_gui->
delta[0] = handle_x - mask_gui->
pos[0];
1853 mask_gui->
delta[1] = handle_y - mask_gui->
pos[1];
1920 case GDK_KEY_BackSpace:
1933 if(!previous_node || !current_node)
return 0;
1934 previous_node->
node[0] = current_node->
node[0];
1935 previous_node->
node[1] = current_node->
node[1];
1944 case GDK_KEY_Return:
1979 const float dx = mask_gui->
pos[0] - gui_points->
points[2];
1980 const float dy = mask_gui->
pos[1] - gui_points->
points[3];
1981 const float dist2 = dx * dx + dy * dy;
2003 mask_gui->
pos[0], mask_gui->
pos[1]))
2015 const guint node_count = g_list_length(mask_form->
points);
2040 mask_gui->
pos[0], mask_gui->
pos[1]);
2058 pts[0], pts[1], &
p[0], &
p[1], &
p[2], &
p[3], gui_points->
clockwise);
2069 mask_gui->
pos[0], mask_gui->
pos[1]);
2080 const int base = node_index * 6;
2081 const int node_point_index = base + 2;
2085 float cursor_pos[2];
2086 const float node_pos_gui[2] = { gui_points->
points[node_point_index],
2087 gui_points->
points[node_point_index + 1] };
2088 const float handle_pos[2] = { gui_points->
border[base], gui_points->
border[base + 1] };
2097 mask_gui->
pos[0], mask_gui->
pos[1]);
2116 mask_form->
source[0] = raw_point[0];
2117 mask_form->
source[1] = raw_point[1];
2131 const int node_count,
const gboolean draw_border,
const gboolean draw_source)
2137 for(
int point_index = node_count * 3 + draw_border; point_index < point_count; point_index++)
2139 if(!isnan(point_buffer[point_index * 2]) && !isnan(point_buffer[point_index * 2 + 1]))
2141 start_idx = point_index;
2149 cairo_move_to(cr, point_buffer[start_idx * 2], point_buffer[start_idx * 2 + 1]);
2150 for(
int point_index = start_idx + 1; point_index < point_count; point_index++)
2152 if(!isnan(point_buffer[point_index * 2]) && !isnan(point_buffer[point_index * 2 + 1]))
2153 cairo_line_to(cr, point_buffer[point_index * 2], point_buffer[point_index * 2 + 1]);
2162 int form_index,
int node_count)
2177 const gboolean have_first_node = node_count && gui_points->
points && gui_points->
points_count > 1;
2178 float node_posx = have_first_node ? gui_points->
points[2] : mask_gui->
pos[0];
2179 float node_posy = have_first_node ? gui_points->
points[3] : mask_gui->
pos[1];
2201 if(gui_points->
points && node_count > 0 && gui_points->
points_count > node_count * 3 + 6)
2204 node_count, zoom_scale);
2214 CAIRO_LINE_CAP_ROUND);
2218 if(mask_gui->
node_selected && selected_node >= 0 && selected_node < node_count
2221 const int node_index = selected_node;
2224 gui_points->
points[node_index * 6 + 4], gui_points->
points[node_index * 6 + 5],
2225 &handle[0], &handle[1], gui_points->
clockwise);
2226 const float pt[2] = { gui_points->
points[node_index * 6 + 2], gui_points->
points[node_index * 6 + 3] };
2227 const gboolean selected = (mask_gui->
node_hovered == node_index
2228 || (selected_handle == node_index)
2237 for(
int node_index = 0; node_index < node_count; node_index++)
2240 if(mask_gui->
creation && node_index == node_count - 1)
break;
2245 const gboolean action = (node_index == selected_node);
2246 const float x = gui_points->
points[node_index * 6 + 2];
2247 const float y = gui_points->
points[node_index * 6 + 3];
2250 if(mask_gui->
creation && node_index == 0)
2253 dt_draw_node(cr, squared, action, selected, zoom_scale,
x, y);
2257 if(mask_gui->
node_selected && selected_node >= 0 && selected_node < node_count
2260 const int edited = selected_node;
2261 const gboolean selected = (mask_gui->
node_hovered == edited
2262 || (selected_handle_border == edited)
2264 const int curr_node = edited * 6;
2265 const float handle[2] = { gui_points->
border[curr_node], gui_points->
border[curr_node + 1] };
2276 .source = { gui_points->
source[0], gui_points->
source[1] } };
2280 float offset_x = gui_points->
source[0] - gui_points->
points[0];
2281 float offset_y = gui_points->
source[1] - gui_points->
points[1];
2288 for(
int node_index = 0; node_index < node_count; node_index++)
2291 && (node_index == mask_gui->
node_hovered || node_index == selected_node
2292 || (mask_gui->
creation && node_index == node_count - 1)))
2294 const int proj_index = node_index * 6 + 2;
2295 if(gui_points->
source_count <= node_index * 3 + 1)
break;
2296 const float proj[2] = { gui_points->
source[proj_index], gui_points->
source[proj_index + 1] };
2297 const gboolean selected = mask_gui->
node_hovered == node_index;
2310 const int corner_count,
const int point_count,
int border_count,
2311 float *x_min,
float *x_max,
float *y_min,
float *y_max)
2313 float xmin, xmax, ymin, ymax;
2314 xmin = ymin = FLT_MAX;
2315 xmax = ymax = FLT_MIN;
2316 for(
int border_index = corner_count * 3; border_index < border_count; border_index++)
2319 const float xx = border_buffer[border_index * 2];
2320 const float yy = border_buffer[border_index * 2 + 1];
2323 if(isnan(yy))
break;
2324 border_index = yy - 1;
2327 xmin =
MIN(xx, xmin);
2328 xmax =
MAX(xx, xmax);
2329 ymin =
MIN(yy, ymin);
2330 ymax =
MAX(yy, ymax);
2332 for(
int point_index = corner_count * 3; point_index < point_count; point_index++)
2335 const float xx = point_buffer[point_index * 2];
2336 const float yy = point_buffer[point_index * 2 + 1];
2337 xmin =
MIN(xx, xmin);
2338 xmax =
MAX(xx, xmax);
2339 ymin =
MIN(yy, ymin);
2340 ymax =
MAX(yy, ymax);
2353 const int corner_count,
const int point_count,
int border_count,
2357 float xmin, xmax, ymin, ymax;
2359 &xmin, &xmax, &ymin, &ymax);
2360 *
height = ymax - ymin + 4;
2361 *
width = xmax - xmin + 4;
2369 gboolean get_source)
2374 float *point_buffer = NULL;
2375 float *border_buffer = NULL;
2376 int point_count = 0;
2377 int border_count = 0;
2380 &point_buffer, &point_count, &border_buffer, &border_count, get_source) != 0)
2387 const guint corner_count = g_list_length(mask_form->
points);
2415 int posx,
int posy,
int buffer_width)
2418 int l = sqrtf(sqf(p1[0] - p0[0]) + sqf(p1[1] - p0[1])) + 1;
2420 const float lx = p1[0] - p0[0];
2421 const float ly = p1[1] - p0[1];
2422 const float inv_l = 1.0f / (float)l;
2424 for(
int i = 0;
i < l;
i++)
2427 const int x = (int)((
float)
i * lx * inv_l) + p0[0] - posx;
2428 const int y = (int)((
float)
i * ly * inv_l) + p0[1] - posy;
2429 const float op = 1.0f - (float)
i * inv_l;
2430 const size_t idx = y * buffer_width +
x;
2431 buffer[idx] = fmaxf(buffer[idx], op);
2433 buffer[idx - 1] = fmaxf(buffer[idx - 1], op);
2435 buffer[idx - buffer_width] = fmaxf(buffer[idx - buffer_width], op);
2442 float **buffer,
int *
width,
int *
height,
int *posx,
int *posy)
2446 double start2 = 0.0;
2451 float *point_buffer = NULL;
2452 float *border_buffer = NULL;
2453 int point_count = 0;
2454 int border_count = 0;
2457 &border_buffer, &border_count,
FALSE) != 0)
2472 const guint corner_count = g_list_length(mask_form->
points);
2477 const int wb = *
width;
2480 const int sparse_factor = sparse ? 4 : 1;
2490 const size_t bufsize = (size_t)(*
width) * (*height);
2493 if(!
IS_NULL_PTR(bufptr)) memset(bufptr, 0,
sizeof(
float) * bufsize);
2502 const int border_point_count = border_count;
2503 if(border_point_count > 2)
2505 int lastx = (int)point_buffer[(border_point_count - 1) * 2];
2506 int lasty = (int)point_buffer[(border_point_count - 1) * 2 + 1];
2507 int lasty2 = (int)point_buffer[(border_point_count - 2) * 2 + 1];
2509 int just_change_dir = 0;
2510 for(
int ii = corner_count * 3; ii < 2 * border_point_count - corner_count * 3; ii++)
2515 if(ii >= border_point_count)
2516 i = (ii - corner_count * 3) % (border_point_count - corner_count * 3) + corner_count * 3;
2517 const int xx = (int)point_buffer[
i * 2];
2518 const int yy = (int)point_buffer[
i * 2 + 1];
2521 if(yy == lasty)
continue;
2524 if(yy - lasty > 1 || yy - lasty < -1)
2528 for(
int j = yy + 1; j < lasty; j++)
2530 const int nx = (j - yy) * (lastx - xx) / (float)(lasty - yy) + xx;
2531 const size_t idx = (size_t)(j - (*posy)) * (*
width) + nx - (*posx);
2532 assert(idx < bufsize);
2540 for(
int j = lasty + 1; j < yy; j++)
2542 const int nx = (j - lasty) * (xx - lastx) / (float)(yy - lasty) + lastx;
2543 const size_t idx = (size_t)(j - (*posy)) * (*
width) + nx - (*posx);
2544 assert(idx < bufsize);
2552 if((lasty - lasty2) * (lasty - yy) > 0)
2554 const size_t idx = (size_t)(lasty - (*posy)) * (*
width) + lastx + 1 - (*posx);
2555 assert(idx < bufsize);
2557 just_change_dir = 1;
2560 if(just_change_dir && ii ==
i)
2564 const size_t idx = (size_t)(yy - (*posy)) * (*
width) + xx - (*posx);
2565 assert(idx < bufsize);
2566 float v = bufptr[idx];
2569 if(xx - (*posx) > 0)
2571 const size_t idx_ = (size_t)(yy - (*posy)) * (*
width) + xx - 1 - (*posx);
2572 assert(idx_ < bufsize);
2573 bufptr[idx_] = 1.0f;
2575 else if(xx - (*posx) < (*
width) - 1)
2577 const size_t idx_ = (size_t)(yy - (*posy)) * (*
width) + xx + 1 - (*posx);
2578 assert(idx_ < bufsize);
2579 bufptr[idx_] = 1.0f;
2584 const size_t idx_ = (size_t)(yy - (*posy)) * (*
width) + xx - (*posx);
2585 assert(idx_ < bufsize);
2586 bufptr[idx_] = 1.0f;
2587 just_change_dir = 0;
2592 const size_t idx_ = (size_t)(yy - (*posy)) * (*
width) + xx - (*posx);
2593 assert(idx_ < bufsize);
2594 bufptr[idx_] = 1.0f;
2610 for(
int yy = 0; yy < hb; yy++)
2612 float *
const restrict
row = bufptr + (size_t)yy * wb;
2614 for(
int xx = 0; xx < wb; xx++)
2616 const float v =
row[xx];
2630 int p0[2] = { 0 }, p1[2] = { 0 };
2631 float pf1[2] = { 0.0f };
2632 int prev0[2] = { 0 }, prev1[2] = { 0 };
2633 gboolean have_prev =
FALSE;
2634 int last0[2] = { -100, -100 }, last1[2] = { -100, -100 };
2636 for(
int i = corner_count * 3;
i < border_count;
i++)
2638 p0[0] = point_buffer[
i * 2];
2639 p0[1] = point_buffer[
i * 2 + 1];
2641 p1[0] = pf1[0] = border_buffer[next * 2], p1[1] = pf1[1] = border_buffer[next * 2 + 1];
2643 p1[0] = pf1[0] = border_buffer[
i * 2], p1[1] = pf1[1] = border_buffer[
i * 2 + 1];
2646 if(next ==
i) next = 0;
2647 while(isnan(pf1[0]))
2653 p1[0] = pf1[0] = border_buffer[next * 2];
2654 p1[1] = pf1[1] = border_buffer[next * 2 + 1];
2657 const gboolean used_next = (next > 0);
2659 if(sparse && have_prev && !used_next
2660 && (prev0[0] != p0[0] || prev0[1] != p0[1] || prev1[0] != p1[0] || prev1[1] != p1[1]))
2662 for(
int k = 1;
k < sparse_factor;
k++)
2664 const float t = (float)
k / (
float)sparse_factor;
2665 int mp0[2] = { (int)floorf(prev0[0] +
t * (p0[0] - prev0[0]) + 0.5f),
2666 (
int)floorf(prev0[1] +
t * (p0[1] - prev0[1]) + 0.5f) };
2667 int mp1[2] = { (int)floorf(prev1[0] +
t * (p1[0] - prev1[0]) + 0.5f),
2668 (
int)floorf(prev1[1] +
t * (p1[1] - prev1[1]) + 0.5f) };
2674 if(last0[0] != p0[0] || last0[1] != p0[1] || last1[0] != p1[0] || last1[1] != p1[1])
2717 int point_start = -1;
2722 for(
int k = 0;
k < point_count;
k++)
2724 float x = polygon[2 *
k];
2725 float y = polygon[2 *
k + 1];
2727 if(
x >= xmin + 1 && y >= ymin + 1
2728 &&
x <= xmax - 1 && y <= ymax - 1)
2738 if(point_start < 0)
return 0;
2746 } roi_crop_segment_t;
2748 roi_crop_segment_t *xmax_segs = malloc(
sizeof(*xmax_segs) * point_count);
2749 roi_crop_segment_t *ymax_segs = malloc(
sizeof(*ymax_segs) * point_count);
2754 goto fallback_passes;
2757 int xmin_l = -1, xmin_r = -1;
2758 int xmax_l = -1, xmax_r = -1;
2762 for(
int k = 0;
k < point_count;
k++)
2764 const int kk = (
k + point_start) % point_count;
2765 const float x = polygon[2 * kk];
2767 if(xmin_l < 0 &&
x < xmin) xmin_l =
k;
2768 if(xmin_l >= 0 &&
x >= xmin) xmin_r =
k - 1;
2770 if(xmin_l >= 0 && xmin_r >= 0)
2772 const int count = xmin_r - xmin_l + 1;
2773 const int ll = (xmin_l - 1 + point_start) % point_count;
2774 const int rr = (xmin_r + 1 + point_start) % point_count;
2775 const float delta_y = (count == 1) ? 0 : (polygon[2 * rr + 1] - polygon[2 * ll + 1]) / (count - 1);
2776 const float start_y = polygon[2 * ll + 1];
2778 for(
int n = 0;
n < count;
n++)
2780 const int nn = (
n + xmin_l + point_start) % point_count;
2781 polygon[2 * nn] = xmin;
2782 polygon[2 * nn + 1] = start_y +
n * delta_y;
2785 xmin_l = xmin_r = -1;
2788 if(xmax_l < 0 && x > xmax) xmax_l =
k;
2789 if(xmax_l >= 0 &&
x <= xmax) xmax_r =
k - 1;
2791 if(xmax_l >= 0 && xmax_r >= 0)
2793 const int count = xmax_r - xmax_l + 1;
2794 const int ll = (xmax_l - 1 + point_start) % point_count;
2795 const int rr = (xmax_r + 1 + point_start) % point_count;
2796 const float delta_y = (count == 1) ? 0 : (polygon[2 * rr + 1] - polygon[2 * ll + 1]) / (count - 1);
2797 const float start_y = polygon[2 * ll + 1];
2799 xmax_segs[xmax_count].l = xmax_l;
2800 xmax_segs[xmax_count].r = xmax_r;
2801 xmax_segs[xmax_count].start = start_y;
2802 xmax_segs[xmax_count].delta = delta_y;
2805 xmax_l = xmax_r = -1;
2809 for(
int s = 0; s < xmax_count; s++)
2811 const int count = xmax_segs[s].r - xmax_segs[s].l + 1;
2812 const float start_y = xmax_segs[s].start;
2813 const float delta_y = xmax_segs[s].delta;
2814 for(
int n = 0;
n < count;
n++)
2816 const int nn = (
n + xmax_segs[s].l + point_start) % point_count;
2817 polygon[2 * nn] = xmax;
2818 polygon[2 * nn + 1] = start_y +
n * delta_y;
2824 int ymin_l = -1, ymin_r = -1;
2825 int ymax_l = -1, ymax_r = -1;
2829 for(
int k = 0;
k < point_count;
k++)
2831 const int kk = (
k + point_start) % point_count;
2832 const float y = polygon[2 * kk + 1];
2834 if(ymin_l < 0 && y < ymin) ymin_l =
k;
2835 if(ymin_l >= 0 && y >= ymin) ymin_r =
k - 1;
2837 if(ymin_l >= 0 && ymin_r >= 0)
2839 const int count = ymin_r - ymin_l + 1;
2840 const int ll = (ymin_l - 1 + point_start) % point_count;
2841 const int rr = (ymin_r + 1 + point_start) % point_count;
2842 const float delta_x = (count == 1) ? 0 : (polygon[2 * rr] - polygon[2 * ll]) / (count - 1);
2843 const float start_x = polygon[2 * ll];
2845 for(
int n = 0;
n < count;
n++)
2847 const int nn = (
n + ymin_l + point_start) % point_count;
2848 polygon[2 * nn] = start_x +
n * delta_x;
2849 polygon[2 * nn + 1] = ymin;
2852 ymin_l = ymin_r = -1;
2855 if(ymax_l < 0 && y > ymax) ymax_l =
k;
2856 if(ymax_l >= 0 && y <= ymax) ymax_r =
k - 1;
2858 if(ymax_l >= 0 && ymax_r >= 0)
2860 const int count = ymax_r - ymax_l + 1;
2861 const int ll = (ymax_l - 1 + point_start) % point_count;
2862 const int rr = (ymax_r + 1 + point_start) % point_count;
2863 const float delta_x = (count == 1) ? 0 : (polygon[2 * rr] - polygon[2 * ll]) / (count - 1);
2864 const float start_x = polygon[2 * ll];
2866 ymax_segs[ymax_count].l = ymax_l;
2867 ymax_segs[ymax_count].r = ymax_r;
2868 ymax_segs[ymax_count].start = start_x;
2869 ymax_segs[ymax_count].delta = delta_x;
2872 ymax_l = ymax_r = -1;
2876 for(
int s = 0; s < ymax_count; s++)
2878 const int count = ymax_segs[s].r - ymax_segs[s].l + 1;
2879 const float start_x = ymax_segs[s].start;
2880 const float delta_x = ymax_segs[s].delta;
2881 for(
int n = 0;
n < count;
n++)
2883 const int nn = (
n + ymax_segs[s].l + point_start) % point_count;
2884 polygon[2 * nn] = start_x +
n * delta_x;
2885 polygon[2 * nn + 1] = ymax;
2895 for(
int k = 0;
k < point_count;
k++)
2897 const int kk = (
k + point_start) % point_count;
2899 if(l < 0 && polygon[2 * kk] < xmin) l =
k;
2900 if(l >= 0 && polygon[2 * kk] >= xmin)
r =
k - 1;
2903 if(l >= 0 &&
r >= 0)
2905 const int count =
r - l + 1;
2906 const int ll = (l - 1 + point_start) % point_count;
2907 const int rr = (
r + 1 + point_start) % point_count;
2908 const float delta_y = (count == 1) ? 0 : (polygon[2 * rr + 1] - polygon[2 * ll + 1]) / (count - 1);
2909 const float start_y = polygon[2 * ll + 1];
2911 for(
int n = 0;
n < count;
n++)
2913 const int nn = (
n + l + point_start) % point_count;
2914 polygon[2 * nn] = xmin;
2915 polygon[2 * nn + 1] = start_y +
n * delta_y;
2923 for(
int k = 0;
k < point_count;
k++)
2925 const int kk = (
k + point_start) % point_count;
2927 if(l < 0 && polygon[2 * kk] > xmax) l =
k;
2928 if(l >= 0 && polygon[2 * kk] <= xmax)
r =
k - 1;
2931 if(l >= 0 &&
r >= 0)
2933 const int count =
r - l + 1;
2934 const int ll = (l - 1 + point_start) % point_count;
2935 const int rr = (
r + 1 + point_start) % point_count;
2936 const float delta_y = (count == 1) ? 0 : (polygon[2 * rr + 1] - polygon[2 * ll + 1]) / (count - 1);
2937 const float start_y = polygon[2 * ll + 1];
2939 for(
int n = 0;
n < count;
n++)
2941 const int nn = (
n + l + point_start) % point_count;
2942 polygon[2 * nn] = xmax;
2943 polygon[2 * nn + 1] = start_y +
n * delta_y;
2951 for(
int k = 0;
k < point_count;
k++)
2953 const int kk = (
k + point_start) % point_count;
2955 if(l < 0 && polygon[2 * kk + 1] < ymin) l =
k;
2956 if(l >= 0 && polygon[2 * kk + 1] >= ymin)
r =
k - 1;
2959 if(l >= 0 &&
r >= 0)
2961 const int count =
r - l + 1;
2962 const int ll = (l - 1 + point_start) % point_count;
2963 const int rr = (
r + 1 + point_start) % point_count;
2964 const float delta_x = (count == 1) ? 0 : (polygon[2 * rr] - polygon[2 * ll]) / (count - 1);
2965 const float start_x = polygon[2 * ll];
2967 for(
int n = 0;
n < count;
n++)
2969 const int nn = (
n + l + point_start) % point_count;
2970 polygon[2 * nn] = start_x +
n * delta_x;
2971 polygon[2 * nn + 1] = ymin;
2979 for(
int k = 0;
k < point_count;
k++)
2981 const int kk = (
k + point_start) % point_count;
2983 if(l < 0 && polygon[2 * kk + 1] > ymax) l =
k;
2984 if(l >= 0 && polygon[2 * kk + 1] <= ymax)
r =
k - 1;
2987 if(l >= 0 &&
r >= 0)
2989 const int count =
r - l + 1;
2990 const int ll = (l - 1 + point_start) % point_count;
2991 const int rr = (
r + 1 + point_start) % point_count;
2992 const float delta_x = (count == 1) ? 0 : (polygon[2 * rr] - polygon[2 * ll]) / (count - 1);
2993 const float start_x = polygon[2 * ll];
2995 for(
int n = 0;
n < count;
n++)
2997 const int nn = (
n + l + point_start) % point_count;
2998 polygon[2 * nn] = start_x +
n * delta_x;
2999 polygon[2 * nn + 1] = ymax;
3012 const int l = sqrt((p1[0] - p0[0]) * (p1[0] - p0[0]) + (p1[1] - p0[1]) * (p1[1] - p0[1])) + 1;
3014 const float lx = p1[0] - p0[0];
3015 const float ly = p1[1] - p0[1];
3016 const float inv_l = 1.0f / (float)l;
3018 const int dx = lx < 0 ? -1 : 1;
3019 const int dy = ly < 0 ? -1 : 1;
3020 const int dpy = dy * bw;
3022 const int x0 = p0[0], y0 = p0[1];
3023 const int x1 = p1[0], y1 = p1[1];
3024 if((x0 < 0 && x1 < 0) || (x0 >= bw && x1 >= bw) || (y0 < 0 && y1 < 0) || (y0 >= bh && y1 >= bh))
return;
3025 const int inside = (x0 >= 0 && x0 < bw && x1 >= 0 && x1 < bw && y0 >= 0 && y0 < bh && y1 >= 0 && y1 < bh);
3027 for(
int i = 0;
i < l;
i++)
3030 const int x = (int)((
float)
i * lx * inv_l) + p0[0];
3031 const int y = (int)((
float)
i * ly * inv_l) + p0[1];
3032 const float op = 1.0f - (float)
i * inv_l;
3033 if(!inside && (x < 0 || x >= bw || y < 0 || y >= bh))
continue;
3034 float *buf = buffer + (size_t)y * bw +
x;
3036 buf[0] =
MAX(buf[0], op);
3037 else if(
x >= 0 && x < bw && y >= 0 && y < bh)
3038 buf[0] =
MAX(buf[0], op);
3039 if(
x + dx >= 0 &&
x + dx < bw && y >= 0 && y < bh)
3040 buf[dx] =
MAX(buf[dx], op);
3041 if(
x >= 0 && x < bw && y + dy >= 0 && y + dy < bh)
3042 buf[dpy] =
MAX(buf[dpy], op);
3055 double start2 = 0.0;
3058 const int px = roi->
x;
3059 const int py = roi->
y;
3062 const float scale = roi->
scale;
3065 const int sparse_factor = sparse ? 4 : 1;
3072 int polygon_in_roi = 0;
3073 int feather_in_roi = 0;
3074 int polygon_encircles_roi = 0;
3077 float *points = NULL, *
border = NULL;
3078 int points_count = 0, border_count = 0;
3081 &points, &points_count, &
border, &border_count,
FALSE) != 0)
3087 if(points_count <= 2)
3101 const guint corner_count = g_list_length(mask_form->
points);
3104 for(
int i = corner_count * 3;
i < border_count;
i++)
3106 const float xx =
border[2 *
i];
3107 const float yy =
border[2 *
i + 1];
3110 if(isnan(yy))
break;
3114 border[2 *
i] = xx * scale - px;
3115 border[2 *
i + 1] = yy * scale - py;
3117 for(
int i = corner_count * 3;
i < points_count;
i++)
3119 const float xx = points[2 *
i];
3120 const float yy = points[2 *
i + 1];
3121 points[2 *
i] = xx * scale - px;
3122 points[2 *
i + 1] = yy * scale - py;
3126 for(
int i = corner_count * 3;
i < points_count;
i++)
3128 const int xx = points[
i * 2];
3129 const int yy = points[
i * 2 + 1];
3131 if(xx > 1 && yy > 1 && xx <
width - 2 && yy <
height - 2)
3141 int crossing_count = 0;
3144 const int y =
height / 2;
3146 for(
int i = corner_count * 3;
i < points_count;
i++)
3148 const int yy = (int)points[2 *
i + 1];
3149 if(yy != last_y && yy == y)
3151 if(points[2 *
i] >
x) crossing_count++;
3156 if(crossing_count & 1)
3159 polygon_encircles_roi = 1;
3164 for(
int i = corner_count * 3;
i < border_count;
i++)
3166 const float xx =
border[
i * 2];
3167 const float yy =
border[
i * 2 + 1];
3170 if(isnan(yy))
break;
3174 if(xx > 1 && yy > 1 && xx <
width - 2 && yy <
height - 2)
3182 if(!polygon_in_roi && !feather_in_roi)
3190 float xmin, xmax, ymin, ymax;
3192 &xmin, &xmax, &ymin, &ymax);
3219 memcpy(cpoints, points,
sizeof(
float) * 2 * points_count);
3225 points_count - corner_count * 3, 0,
3227 polygon_encircles_roi = polygon_encircles_roi || !crop_success;
3236 if(polygon_encircles_roi)
3246 float xlast = cpoints[(points_count - 1) * 2];
3247 float ylast = cpoints[(points_count - 1) * 2 + 1];
3249 for(
int i = corner_count * 3;
i < points_count;
i++)
3251 float xstart = xlast;
3252 float ystart = ylast;
3254 float xend = xlast = cpoints[
i * 2];
3255 float yend = ylast = cpoints[
i * 2 + 1];
3260 tmp = ystart, ystart = yend, yend = tmp;
3261 tmp = xstart, xstart = xend, xend = tmp;
3264 const float m = (xstart - xend) / (ystart - yend);
3267 for(
int yy = (
int)ceilf(ystart); (float)yy < yend;
3270 const float xcross = xstart +
m * (yy - ystart);
3272 int xx = floorf(xcross);
3273 if((
float)xx + 0.5f <= xcross) xx++;
3275 if(xx < 0 || xx >=
width || yy < 0 || yy >=
height)
3278 const size_t index = (size_t)yy *
width + xx;
3280 buffer[index] = 1.0f - buffer[index];
3293 const int xxmin =
MAX(xmin, 0);
3294 const int xxmax =
MIN(xmax,
width - 1);
3295 const int yymin =
MAX(ymin, 0);
3296 const int yymax =
MIN(ymax,
height - 1);
3298 for(
int yy = yymin; yy <= yymax; yy++)
3300 float *
const restrict
row = buffer + (size_t)yy *
width;
3302 for(
int xx = xxmin; xx <= xxmax; xx++)
3304 const float v =
row[xx];
3321 if(!polygon_encircles_roi)
3323 const int dpoints_capacity = 4 * border_count * (sparse ? sparse_factor : 1);
3335 int prev0[2] = { 0, 0 };
3336 int prev1[2] = { 0, 0 };
3337 gboolean have_prev =
FALSE;
3338 int last0[2] = { -100, -100 };
3339 int last1[2] = { -100, -100 };
3341 for(
int i = corner_count * 3;
i < border_count;
i++)
3343 p0[0] = floorf(points[
i * 2] + 0.5f);
3344 p0[1] = ceilf(points[
i * 2 + 1]);
3347 p1[0] = pf1[0] =
border[next_index * 2];
3348 p1[1] = pf1[1] =
border[next_index * 2 + 1];
3352 p1[0] = pf1[0] =
border[
i * 2];
3353 p1[1] = pf1[1] =
border[
i * 2 + 1];
3357 if(next_index ==
i) next_index = 0;
3358 while(isnan(pf1[0]))
3364 p1[0] = pf1[0] =
border[next_index * 2];
3365 p1[1] = pf1[1] =
border[next_index * 2 + 1];
3368 const gboolean used_next = (next_index > 0);
3370 if(sparse && have_prev && !used_next
3371 && (prev0[0] != p0[0] || prev0[1] != p0[1] || prev1[0] != p1[0] || prev1[1] != p1[1]))
3373 for(
int k = 1;
k < sparse_factor;
k++)
3375 const float t = (float)
k / (
float)sparse_factor;
3376 const int mp0[2] = { (int)floorf(prev0[0] +
t * (p0[0] - prev0[0]) + 0.5f),
3377 (
int)floorf(prev0[1] +
t * (p0[1] - prev0[1]) + 0.5f) };
3378 const int mp1[2] = { (int)floorf(prev1[0] +
t * (p1[0] - prev1[0]) + 0.5f),
3379 (
int)floorf(prev1[1] +
t * (p1[1] - prev1[1]) + 0.5f) };
3380 if(dindex + 4 <= dpoints_capacity)
3382 dpoints[dindex] = mp0[0];
3383 dpoints[dindex + 1] = mp0[1];
3384 dpoints[dindex + 2] = mp1[0];
3385 dpoints[dindex + 3] = mp1[1];
3392 if(last0[0] != p0[0] || last0[1] != p0[1] || last1[0] != p1[0] || last1[1] != p1[1])
3394 dpoints[dindex] = p0[0];
3395 dpoints[dindex + 1] = p0[1];
3396 dpoints[dindex + 2] = p1[0];
3397 dpoints[dindex + 3] = p1[1];
3420 for(
int n = 0;
n < dindex;
n += 4)
3454 snprintf(mask_form->
name,
sizeof(mask_form->
name), _(
"polygon #%d"), (
int)form_number);
3459 const int opacity,
char *
const restrict msgbuf,
3460 const size_t msgbuf_len)
3462 const guint node_count = mask_form->
points ? g_list_length(mask_form->
points) : 0;
3463 if(mask_gui->
creation && node_count < 4)
3464 g_strlcat(msgbuf, _(
"<b>Add node</b>: click, <b>Add sharp node</b>:ctrl+click\n"
3465 "<b>Cancel</b>: right-click or Esc"), msgbuf_len);
3467 g_strlcat(msgbuf, _(
"<b>Add node</b>: click, <b>Add sharp node</b>:ctrl+click\n"
3468 "<b>Finish polygon</b>: Enter or click on first node"), msgbuf_len);
3470 g_strlcat(msgbuf, _(
"<b>Node curvature</b>: drag\n<b>Reset curvature</b>: right-click"), msgbuf_len);
3472 g_strlcat(msgbuf, _(
"<b>NODE:</b> <b>Move</b>: drag, <b>Delete</b>: right-click or Del\n"
3473 "<b>Hardness</b>: scroll, <b>Switch smooth/sharp</b>: ctrl+click"), msgbuf_len);
3475 g_strlcat(msgbuf, _(
"<b>Move node</b>: drag\n<b>Delete node</b>: right-click\n"
3476 "<b>Hardness</b>: scroll, <b>Switch smooth/sharp</b>: ctrl+click"), msgbuf_len);
3478 g_strlcat(msgbuf, _(
"<b>Move segment</b>: drag\n<b>Add node</b>: ctrl+click"), msgbuf_len);
3480 g_snprintf(msgbuf, msgbuf_len, _(
"<b>Size</b>: scroll, <b>Hardness</b>: shift+scroll\n"
3481 "<b>Opacity</b>: ctrl+scroll (%d%%)"), opacity);
3494 float offset[2] = { 0.1f, 0.1f };
3516 const int form_id = mask_gui->
formid;
3538 const int form_id = mask_gui->
formid;
3552 node_index, &
node->state))
3580 const float pzx,
const float pzy)
3585 gchar *accel = g_strdup_printf(_(
"%s+Click"), gtk_accelerator_get_label(0, GDK_CONTROL_MASK));
3587 gboolean ret =
FALSE;
3613 gchar *to_change_type = g_strdup_printf(_(
"Switch to %s node"), (is_corner) ? _(
"round") : _(
"cusp"));
3624 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))