64#define FADING_MIN 0.0005f
65#define FADING_MAX 1.0f
67#define BORDER_MIN 0.00005f
68#define BORDER_MAX 0.5f
71 const int corner_count,
const int point_count,
int border_count,
72 float *x_min,
float *x_max,
float *y_min,
float *y_max);
79static void _polygon_get_XY(
const float p0_x,
const float p0_y,
const float p1_x,
const float p1_y,
80 const float p2_x,
const float p2_y,
const float p3_x,
const float p3_y,
81 const float t,
float *out_x,
float *out_y)
83 const float one_minus_t = 1.0f -
t;
84 const float a = one_minus_t * one_minus_t * one_minus_t;
85 const float b = 3.0f *
t * one_minus_t * one_minus_t;
86 const float c = 3.0f *
t *
t * one_minus_t;
87 const float d =
t *
t *
t;
88 *out_x = p0_x * a + p1_x * b + p2_x * c + p3_x *
d;
89 *out_y = p0_y * a + p1_y * b + p2_y * c + p3_y *
d;
98 const float p2_x,
const float p2_y,
const float p3_x,
const float p3_y,
99 const float t,
const float radius,
float radius_rate,
100 float *center_x,
float *center_y,
float *border_x,
float *border_y)
103 _polygon_get_XY(p0_x, p0_y, p1_x, p1_y, p2_x, p2_y, p3_x, p3_y,
t, center_x, center_y);
106 const double ti = 1.0 - (
double)
t;
109 const double ti_ti = ti * ti;
110 const double t_ti =
t * ti;
112 const double a = 3.0 * ti_ti;
113 const double b = 3.0 * (ti_ti - 2.0 * t_ti);
114 const double c = 3.0 * (2.0 * t_ti - t_t);
115 const double d = 3.0 * t_t;
118 double dx = -p0_x * a + p1_x * b + p2_x * c + p3_x *
d;
119 double dy = -p0_y * a + p1_y * b + p2_y * c + p3_y *
d;
139 const double span = fmax(fmax(fabs((
double)p3_x - p0_x), fabs((
double)p3_y - p0_y)),
140 fmax(fabs((
double)p2_x - p1_x), fabs((
double)p2_y - p1_y)));
141 const double degenerate = fmax(span, 1.0) * 1e-9;
143 if(fabs(dx) < degenerate && fabs(dy) < degenerate)
146 if(
t < 0.5f) { dx = (
double)p2_x - p0_x; dy = (
double)p2_y - p0_y; }
147 else { dx = (
double)p3_x - p1_x; dy = (
double)p3_y - p1_y; }
149 if(fabs(dx) < degenerate && fabs(dy) < degenerate)
155 if(dx == 0.0 && dy == 0.0)
return FALSE;
163 const float centre[2] = { *center_x, *center_y };
176 return r1 + (r2 - r1) *
t *
t * (3.0 - 2.0 *
t);
181 return (r2 - r1) * 6.0 *
t * (1.0 -
t);
190 const float ctrl_x,
const float ctrl_y,
191 float *handle_x,
float *handle_y,
const gboolean clockwise)
193 const float delta_y = ctrl_y - point_y;
194 const float delta_x = point_x - ctrl_x;
197 *handle_x = point_x - delta_y;
198 *handle_y = point_y - delta_x;
202 *handle_x = point_x + delta_y;
203 *handle_y = point_y + delta_x;
213 const float handle_x,
const float handle_y,
214 float *ctrl1_x,
float *ctrl1_y,
float *ctrl2_x,
float *ctrl2_y,
215 const gboolean clockwise)
217 const float delta_y = handle_y - point_y;
218 const float delta_x = point_x - handle_x;
222 *ctrl1_x = point_x - delta_y;
223 *ctrl1_y = point_y - delta_x;
224 *ctrl2_x = point_x + delta_y;
225 *ctrl2_y = point_y + delta_x;
229 *ctrl1_x = point_x + delta_y;
230 *ctrl1_y = point_y + delta_x;
231 *ctrl2_x = point_x - delta_y;
232 *ctrl2_y = point_y - delta_x;
240 const float x3,
const float y3,
const float x4,
const float y4,
241 float *bezier_x1,
float *bezier_y1,
242 float *bezier_x2,
float *bezier_y2)
244 *bezier_x1 = (-x1 + 6 * x2 + x3) / 6;
245 *bezier_y1 = (-y1 + 6 * y2 + y3) / 6;
246 *bezier_x2 = (x2 + 6 * x3 - x4) / 6;
247 *bezier_y2 = (y2 + 6 * y3 - y4) / 6;
258 const guint node_count = g_list_length(mask_form->
points);
259 if(node_count < 2)
return;
265 const GList *form_points = mask_form->
points;
266 for(guint node_index = 0; node_index < node_count; node_index++)
269 form_points = g_list_next(form_points);
272 for(guint node_index = 0; node_index < node_count; node_index++)
285 float bezier1_x = 0.0f;
286 float bezier1_y = 0.0f;
287 float bezier2_x = 0.0f;
288 float bezier2_y = 0.0f;
290 point3->
node[0], point3->
node[1], point4->node[0], point4->node[1],
291 &bezier1_x, &bezier1_y, &bezier2_x, &bezier2_y);
292 if(point2->ctrl2[0] == -1.0) point2->ctrl2[0] = bezier1_x;
293 if(point2->ctrl2[1] == -1.0) point2->ctrl2[1] = bezier1_y;
294 point3->
ctrl1[0] = bezier2_x;
295 point3->
ctrl1[1] = bezier2_y;
297 point4->node[0], point4->node[1], point5->node[0], point5->node[1],
298 &bezier1_x, &bezier1_y, &bezier2_x, &bezier2_y);
299 if(point4->ctrl1[0] == -1.0) point4->ctrl1[0] = bezier2_x;
300 if(point4->ctrl1[1] == -1.0) point4->ctrl1[1] = bezier2_y;
301 point3->
ctrl2[0] = bezier1_x;
302 point3->
ctrl2[1] = bezier1_y;
320 for(
const GList *form_points = mask_form->
points; form_points; form_points = g_list_next(form_points))
326 sum += (point2->
node[0] - point1->
node[0]) * (point2->
node[1] + point1->
node[1]);
342 gboolean clockwise,
const int step)
345 double angle_start = atan2f(border_min[1] - center_max[1], border_min[0] - center_max[0]);
346 double angle_end = atan2f(border_max[1] - center_max[1], border_max[0] - center_max[0]);
347 if(angle_start == angle_end)
return;
350 if(angle_end < angle_start && clockwise)
352 angle_end += 2 *
M_PI;
354 if(angle_end > angle_start && !clockwise)
356 angle_start += 2 *
M_PI;
360 const float radius_start = sqrtf((border_min[1] - center_max[1]) * (border_min[1] - center_max[1])
361 + (border_min[0] - center_max[0]) * (border_min[0] - center_max[0]));
362 const float radius_end = sqrtf((border_max[1] - center_max[1]) * (border_max[1] - center_max[1])
363 + (border_max[0] - center_max[0]) * (border_max[0] - center_max[0]));
367 if(angle_end > angle_start)
368 step_count = (angle_end - angle_start) * fmaxf(radius_start, radius_end) / (float)step;
370 step_count = (angle_start - angle_end) * fmaxf(radius_start, radius_end) / (float)step;
371 if(step_count < 2)
return;
374 const float angle_step = (angle_end - angle_start) / step_count;
375 const float radius_step = (radius_end - radius_start) / step_count;
376 float current_radius = radius_start + radius_step;
377 float current_angle = angle_start + angle_step;
385 for(
int step_index = 1; step_index < step_count; step_index++)
387 *points_ptr++ = center_max[0];
388 *points_ptr++ = center_max[1];
391 *border_ptr++ = center_max[0] + current_radius * cosf(current_angle);
392 *border_ptr++ = center_max[1] + current_radius * sinf(current_angle);
394 current_radius += radius_step;
395 current_angle += angle_step;
402 return abs((
int)
min[0] - (
int)
max[0]) < pixel_threshold &&
403 abs((
int)
min[1] - (
int)
max[1]) < pixel_threshold;
413 double t_min,
double t_max,
414 float *polygon_min,
float *polygon_max,
415 float *border_min,
float *border_max,
416 float *result_polygon,
float *result_border,
418 int with_border,
const int pixel_threshold,
419 const gboolean have_min,
const gboolean have_max,
420 gboolean have_border_min, gboolean have_border_max,
421 gboolean *out_have_border)
430 segment_end[2], segment_end[3], segment_end[0], segment_end[1], t_min,
433 polygon_min, polygon_min + 1, border_min, border_min + 1);
439 segment_end[2], segment_end[3], segment_end[0], segment_end[1], t_max,
442 polygon_max, polygon_max + 1, border_max, border_max + 1);
446 if((t_max - t_min < 0.0001)
451 result_polygon[0] = polygon_max[0];
452 result_polygon[1] = polygon_max[1];
457 if(!have_border_max && have_border_min)
459 border_max[0] = border_min[0];
460 border_max[1] = border_min[1];
461 have_border_max =
TRUE;
463 else if(!have_border_max)
473 -(segment_end[0] - segment_start[0]), radius, border_max);
474 have_border_max =
TRUE;
477 result_border[0] = border_max[0];
478 result_border[1] = border_max[1];
479 if(!
IS_NULL_PTR(out_have_border)) *out_have_border = have_border_max;
485 double t_mid = (t_min + t_max) / 2.0;
486 float polygon_mid[2] = { 0.0f, 0.0f };
487 float border_mid[2] = { 0.0f, 0.0f };
488 float polygon_result_left[2] = { 0 };
489 float border_result_left[2] = { 0 };
491 polygon_min, polygon_mid, border_min, border_mid,
492 polygon_result_left, border_result_left,
493 draw_points, draw_border, with_border, pixel_threshold,
494 have_min,
FALSE, have_border_min,
FALSE, NULL);
496 polygon_result_left, polygon_max, border_result_left, border_max,
497 result_polygon, result_border,
498 draw_points, draw_border, with_border, pixel_threshold,
499 TRUE, have_max,
TRUE, have_border_max, out_have_border);
542 float p1[5],
float p2[5])
545 p1[0] = from->
node[0] *
f->iwd -
f->dx;
546 p1[1] = from->
node[1] *
f->iht -
f->dy;
547 p1[2] = from->
ctrl2[0] *
f->iwd -
f->dx;
548 p1[3] = from->
ctrl2[1] *
f->iht -
f->dy;
549 p1[4] = from->
border[1] * scale;
550 p2[0] = to->
node[0] *
f->iwd -
f->dx;
551 p2[1] = to->
node[1] *
f->iht -
f->dy;
552 p2[2] = to->
ctrl1[0] *
f->iwd -
f->dx;
553 p2[3] = to->
ctrl1[1] *
f->iht -
f->dy;
554 p2[4] = to->
border[0] * scale;
586 const float *
const to)
588 if(fabsf(to[0] - from[0]) <= 1.0f && fabsf(to[1] - from[1]) <= 1.0f)
return;
607 const gboolean have_b0 =
_polygon_border_get_XY(p1[0], p1[1], p1[2], p1[3], p2[2], p2[3], p2[0], p2[1], 0.0f,
608 p1[4], 0.0f, c0, c0 + 1, b0, b0 + 1);
609 const gboolean have_b1 =
_polygon_border_get_XY(p1[0], p1[1], p1[2], p1[3], p2[2], p2[3], p2[0], p2[1], 1.0f,
610 p2[4], 0.0f,
c1,
c1 + 1, b1, b1 + 1);
611 if(!have_b0 && !have_b1)
return FALSE;
633 float bmin[2] = { b0[0], b0[1] };
634 float bmax[2] = { b1[0], b1[1] };
635 float cmin[2] = { c0[0], c0[1] };
636 float cmax[2] = {
c1[0],
c1[1] };
637 gboolean have_rb =
FALSE;
638 _polygon_points_recurs(p1, p2, 0.0, 1.0, cmin, cmax, bmin, bmax, rc, rb, w->
dpoints, w->
dborder,
675 const double iop_order,
const int transform_direction,
677 float **border_buffer,
int *border_count, gboolean source)
679 *point_buffer = NULL;
681 if(!
IS_NULL_PTR(border_buffer)) *border_buffer = NULL;
689 const float input_width =
dist->iwidth;
690 const float input_height =
dist->iheight;
696 const int node_count = (int)g_list_length(mask_form->
points);
712 float *node_border =
dt_alloc_align((
size_t)node_count * 2 *
sizeof(
float));
723 memset(node_has_border, 0, (
size_t)node_count);
731 dx = (first->
node[0] - mask_form->
source[0]) * input_width;
732 dy = (first->
node[1] - mask_form->
source[1]) * input_height;
737 for(
const GList *point_node = mask_form->
points; point_node; point_node = g_list_next(point_node))
740 nodes[node_index++] = node;
744 buf[0] = node->
ctrl1[0] * input_width - dx;
745 buf[1] = node->
ctrl1[1] * input_height - dy;
746 buf[2] = node->
node[0] * input_width - dx;
747 buf[3] = node->
node[1] * input_height - dy;
748 buf[4] = node->
ctrl2[0] * input_width - dx;
749 buf[5] = node->
ctrl2[1] * input_height - dy;
763 .node_count = node_count,
764 .frame = { input_width, input_height, dx, dy, clockwise ? 1.0f : -1.0f },
765 .pixel_threshold = pixel_threshold,
766 .with_border = (!
IS_NULL_PTR(dborder) && node_count >= 3),
767 .clockwise = clockwise,
770 .node_border = node_border,
771 .node_has_border = node_has_border };
806 *point_buffer, *point_count))
810 float pts[2] = { mask_form->
source[0] * input_width, mask_form->
source[1] * input_height };
814 dx = pts[0] - (*point_buffer)[2];
815 dy = pts[1] - (*point_buffer)[3];
817 for(
int i = 0;
i < *point_count;
i++)
819 (*point_buffer)[
i * 2] += dx;
820 (*point_buffer)[
i * 2 + 1] += dy;
826 *point_buffer, *point_count))
848 *point_buffer = NULL;
853 *border_buffer = NULL;
868 GList *firstpt = g_list_nth(mask_form->
points, segment_index);
879 float min_dist = FLT_MAX;
881 for(
int i = 0;
i <= 100;
i++)
883 const float t =
i / 100.0f;
884 float sample_x = 0.0f;
885 float sample_y = 0.0f;
888 &sample_x, &sample_y);
890 const float dist = (point_x - sample_x) * (point_x - sample_x)
891 + (point_y - sample_y) * (point_y - sample_y);
907 const guint node_count = g_list_length(mask_form->
points);
909 if(selected_segment < 0 || selected_segment >= (
int)node_count)
return;
923 mask_form, selected_segment);
925 GList *pt = g_list_nth(mask_form->
points, selected_segment);
942 mask_form->
points = g_list_insert(mask_form->
points, new_node, selected_segment + 1);
961 for(GList *node_entry = mask_form->
points; node_entry; node_entry = g_list_next(node_entry))
966 float **point_buffer,
int *point_count,
967 float **border_buffer,
int *border_count,
973 const double ioporder = (module) ? module->
iop_order : 0.0f;
975 if(!
IS_NULL_PTR(border_skips)) *border_skips = NULL;
976 if(!
IS_NULL_PTR(border_skip_count)) *border_skip_count = 0;
978 point_buffer, point_count, border_buffer, border_count, source);
991 const int header = (int)g_list_length(mask_form->
points) * 3;
1000 float *mask_size,
float *border_size)
1006 const int node_count = g_list_length(mask_form->
points);
1007 float p1[2] = { FLT_MAX, FLT_MAX };
1008 float p2[2] = { -FLT_MAX, -FLT_MAX };
1010 float fp1[2] = { FLT_MAX, FLT_MAX };
1011 float fp2[2] = { -FLT_MAX, -FLT_MAX };
1016 const float x = gui_points->
points[
i * 2];
1017 const float y = gui_points->
points[
i * 2 + 1];
1019 p1[0] = fminf(p1[0],
x);
1020 p2[0] = fmaxf(p2[0],
x);
1021 p1[1] = fminf(p1[1], y);
1022 p2[1] = fmaxf(p2[1], y);
1033 && i < gui_points->border_count)
1036 const float fx = gui_points->
border[
i * 2];
1037 const float fy = gui_points->
border[
i * 2 + 1];
1043 fp1[0] = fminf(fp1[0],
fx);
1044 fp2[0] = fmaxf(fp2[0],
fx);
1045 fp1[1] = fminf(fp1[1], fy);
1046 fp2[1] = fmaxf(fp2[1], fy);
1050 float mask_span[2] = { p2[0] - p1[0], p2[1] - p1[1] };
1052 *mask_size = fmaxf(mask_span[0], mask_span[1]);
1056 float border_span[2] = { fp2[0] - fp1[0], fp2[1] - fp1[1] };
1058 *border_size = fmaxf(border_span[0], border_span[1]);
1063 float *center_y,
float *surface);
1075 if(
size <= 0.0f)
return NAN;
1080 float fading_sum = 0.0f;
1081 int fading_count = 0;
1083 for(
const GList *point_node = mask_form->
points; point_node; point_node = g_list_next(point_node))
1091 return fading_count > 0 ? fading_sum / (float)fading_count : NAN;
1099 float center[2],
float *area)
1103 const int points_count = g_list_length(mask_form->
points);
1104 if(points_count <= 0)
return FALSE;
1110 for(
const GList *point_node = mask_form->
points; point_node; point_node = g_list_next(point_node))
1114 point_buffer[2 *
i] = node->
node[0];
1115 point_buffer[2 *
i + 1] = node->
node[1];
1145 if(!
_change_size(mask_form, 0, mask_gui, module, index,
value, increment, flow))
return NAN;
1148 if(!
_change_fading(mask_form, 0, mask_gui, module, index,
value, increment, flow))
return NAN;
1160 int node_count,
int *inside,
int *inside_border,
1161 int *near_handle,
int *inside_source,
float *
dist)
1179 float min_dist_pixel = FLT_MAX;
1181 const float radius2 = radius * radius;
1182 const float pt[2] = { point_x, point_y };
1194 const float offset_x = -gui_points->
points[2] + gui_points->
source[2];
1195 const float offset_y = -gui_points->
points[3] + gui_points->
source[3];
1196 int current_seg = 1;
1202 if(gui_points->
points[
i * 2] == gui_points->
points[current_seg * 6 + 2]
1203 && gui_points->
points[
i * 2 + 1] == gui_points->
points[current_seg * 6 + 3])
1205 current_seg = (current_seg + 1) % node_count;
1209 const float source_x = gui_points->
points[
i * 2] + offset_x;
1210 const float source_y = gui_points->
points[
i * 2 + 1] + offset_y;
1213 const float sdx = point_x - source_x;
1214 const float sdy = point_y - source_y;
1215 const float sdd = sqf(sdx) + sqf(sdy);
1216 if(sdd < min_dist_pixel)
1217 min_dist_pixel = sdd;
1219 *
dist = min_dist_pixel;
1226 int current_seg = 1;
1230 if(gui_points->
points[
i * 2 + 1] == gui_points->
points[current_seg * 6 + 3]
1231 && gui_points->
points[
i * 2] == gui_points->
points[current_seg * 6 + 2])
1233 current_seg = (current_seg + 1) % node_count;
1236 const float yy = gui_points->
points[
i * 2 + 1];
1237 const float xx = gui_points->
points[
i * 2];
1239 const float dx = point_x - xx;
1240 const float dy = point_y - yy;
1241 const float dd = sqf(dx) + sqf(dy);
1242 if(dd < min_dist_pixel)
1244 min_dist_pixel = dd;
1246 if(current_seg >= 0 && dd < radius2)
1248 if(current_seg == 0)
1249 *near_handle = node_count - 1;
1251 *near_handle = current_seg - 1;
1257 *
dist = min_dist_pixel;
1262 int near_border = 0;
1263 for(
int i = node_count * 3;
i < gui_points->
border_count && !near_border;
i++)
1265 const float bdx = point_x - gui_points->
border[
i * 2];
1266 const float bdy = point_y - gui_points->
border[
i * 2 + 1];
1267 near_border = (sqf(bdx) + sqf(bdy)) < radius2;
1281 *inside = enclosed || near_border;
1282 *inside_border = near_border && (*near_handle < 0);
1291 int node_index,
float *handle_x,
float *handle_y,
void *user_data)
1293 if(
IS_NULL_PTR(gui_points) || node_index < 0 || node_index >= node_count)
return FALSE;
1294 *handle_x = gui_points->
border[node_index * 6];
1295 *handle_y = gui_points->
border[node_index * 6 + 1];
1303 float *handle_x,
float *handle_y,
void *user_data)
1307 gui_points->
points[node_index * 6 + 4], gui_points->
points[node_index * 6 + 5],
1308 handle_x, handle_y, gui_points->
clockwise);
1316 int *inside_border,
int *near_handle,
int *inside_source,
float *
dist,
void *user_data)
1320 inside, inside_border, near_handle, inside_source,
dist);
1337 float *center_x,
float *center_y,
float *area)
1339 if(
IS_NULL_PTR(point_buffer) || point_count < 3)
return;
1341 float centroid_x = 0.0f;
1342 float centroid_y = 0.0f;
1343 float signed_area = 0.0f;
1345 for(
int node_index = 0; node_index < point_count; node_index++)
1347 const int next_index = (node_index + 1) % point_count;
1348 const float x0 = point_buffer[node_index * 2];
1349 const float y0 = point_buffer[node_index * 2 + 1];
1350 const float x1 = point_buffer[next_index * 2];
1351 const float y1 = point_buffer[next_index * 2 + 1];
1352 const float cross = x0 * y1 - x1 * y0;
1354 signed_area += cross;
1355 centroid_x += (x0 + x1) * cross;
1356 centroid_y += (y0 + y1) * cross;
1359 if(fabsf(signed_area) > 1e-8f)
1361 const float inv_divisor = 1.0f / (3.0f * signed_area);
1362 if(!
IS_NULL_PTR(center_x)) *center_x = centroid_x * inv_divisor;
1363 if(!
IS_NULL_PTR(center_y)) *center_y = centroid_y * inv_divisor;
1372 float *center_x,
float *center_y,
float *area)
1376 float centroid_x = 0.0f;
1377 float centroid_y = 0.0f;
1378 float signed_area = 0.0f;
1380 for(
const GList *node_iter = mask_form->
points; node_iter; node_iter = g_list_next(node_iter))
1385 if(!node0 || !node1)
return FALSE;
1387 const float cross = node0->
node[0] * node1->
node[1] - node1->
node[0] * node0->
node[1];
1388 signed_area += cross;
1389 centroid_x += (node0->
node[0] + node1->
node[0]) * cross;
1390 centroid_y += (node0->
node[1] + node1->
node[1]) * cross;
1394 if(fabsf(signed_area) <= 1e-8f)
return FALSE;
1396 const float inv_divisor = 1.0f / (3.0f * signed_area);
1397 if(!
IS_NULL_PTR(center_x)) *center_x = centroid_x * inv_divisor;
1398 if(!
IS_NULL_PTR(center_y)) *center_y = centroid_y * inv_divisor;
1407 const float mask_size,
const float border_size)
1411 dt_toast_log(_(
"Fading: %3.2f%%"), (border_size * mask_fading) / mask_size * 100.0f);
1427 float center_x = 0.0f;
1428 float center_y = 0.0f;
1429 float signed_area = 0.0f;
1433 if(amount < 1.0f && signed_area < 0.00001f && signed_area > -0.00001f)
return 1;
1434 if(amount > 1.0f && signed_area > 4.0f)
return 1;
1436 float scale_delta = amount;
1440 scale_delta = powf(amount, (
float)flow);
1444 scale_delta = 1.0f + amount * (float)flow;
1448 scale_delta = amount;
1453 for(GList *node_iter = mask_form->
points; node_iter; node_iter = g_list_next(node_iter), node_index++)
1460 const float new_node_x = center_x + (node->
node[0] - center_x) * scale_delta;
1461 const float new_node_y = center_y + (node->
node[1] - center_y) * scale_delta;
1462 const float ctrl1_offset_x = (node->
ctrl1[0] - node->
node[0]) * scale_delta;
1463 const float ctrl1_offset_y = (node->
ctrl1[1] - node->
node[1]) * scale_delta;
1464 const float ctrl2_offset_x = (node->
ctrl2[0] - node->
node[0]) * scale_delta;
1465 const float ctrl2_offset_y = (node->
ctrl2[1] - node->
node[1]) * scale_delta;
1468 node->
node[0] = new_node_x;
1469 node->
node[1] = new_node_y;
1470 node->
ctrl1[0] = new_node_x + ctrl1_offset_x;
1471 node->
ctrl1[1] = new_node_y + ctrl1_offset_y;
1472 node->
ctrl2[0] = new_node_x + ctrl2_offset_x;
1473 node->
ctrl2[1] = new_node_y + ctrl2_offset_y;
1477 float mask_size = 0.0f;
1495 const float scale_amount = powf(amount, (
float)flow);
1496 const float offset_amount = amount * (float)flow;
1498 for(GList *node_iter = mask_form->
points; node_iter; node_iter = g_list_next(node_iter), node_index++)
1506 offset_amount, increment),
1509 offset_amount, increment),
1514 float mask_size = 1.0f;
1515 float border_size = 0.0f;
1516 _polygon_get_sizes(module, mask_form, mask_gui, form_index, &mask_size, &border_size);
1518 _init_fading(mask_form, amount, increment, flow, mask_size, border_size);
1559 return _change_fading(mask_form, parent_id, mask_gui, module, form_index, up ? +0.01f : -0.01f,
1562 return _change_size(mask_form, parent_id, mask_gui, module, form_index, up ? 1.02f : 0.98f,
1579 dt_toast_log(_(
"Polygon mask requires at least 3 nodes."));
1586 mask_form->
points = g_list_remove(mask_form->
points, last_node);
1598 double pressure,
int which,
int type, uint32_t
state,
1602 if(
type == GDK_2BUTTON_PRESS ||
type == GDK_3BUTTON_PRESS)
return 1;
1625 int node_count = g_list_length(mask_form->
points);
1632 polygon_node->
ctrl1[0] = polygon_node->
ctrl1[1] = polygon_node->
ctrl2[0] = polygon_node->
ctrl2[1] = -1.0;
1640 polygon_first_node->
node[0] = polygon_node->
node[0];
1641 polygon_first_node->
node[1] = polygon_node->
node[1];
1642 polygon_first_node->
ctrl1[0] = polygon_first_node->
ctrl1[1] = polygon_first_node->
ctrl2[0] = polygon_first_node->
ctrl2[1] = -1.0;
1645 mask_form->
points = g_list_append(mask_form->
points, polygon_first_node);
1658 mask_form->
points = g_list_append(mask_form->
points, polygon_node);
1664 polygon_last_node->
ctrl1[0] = polygon_last_node->
ctrl2[0] = polygon_last_node->
node[0];
1665 polygon_last_node->
ctrl1[1] = polygon_last_node->
ctrl2[1] = polygon_last_node->
node[1];
1717 float handle_x, handle_y;
1720 gui_points->
points[handle_index * 6 + 3],
1721 gui_points->
points[handle_index * 6 + 4],
1722 gui_points->
points[handle_index * 6 + 5],
1723 &handle_x, &handle_y, gui_points->
clockwise);
1725 mask_gui->
delta[0] = handle_x - mask_gui->
pos[0];
1726 mask_gui->
delta[1] = handle_y - mask_gui->
pos[1];
1735 mask_gui->
delta[0] = handle_x - mask_gui->
pos[0];
1736 mask_gui->
delta[1] = handle_y - mask_gui->
pos[1];
1803 case GDK_KEY_BackSpace:
1816 if(!previous_node || !current_node)
return 0;
1817 previous_node->
node[0] = current_node->
node[0];
1818 previous_node->
node[1] = current_node->
node[1];
1827 case GDK_KEY_Return:
1853 const int iwidth =
geometry.raw_width;
1854 const int iheight =
geometry.raw_height;
1863 const float dx = mask_gui->
pos[0] - gui_points->
points[2];
1864 const float dy = mask_gui->
pos[1] - gui_points->
points[3];
1865 const float dist2 = dx * dx + dy * dy;
1887 mask_gui->
pos[0], mask_gui->
pos[1]))
1899 const guint node_count = g_list_length(mask_form->
points);
1924 mask_gui->
pos[0], mask_gui->
pos[1]);
1942 pts[0], pts[1], &
p[0], &
p[1], &
p[2], &
p[3], gui_points->
clockwise);
1953 mask_gui->
pos[0], mask_gui->
pos[1]);
1964 const int base = node_index * 6;
1965 const int node_point_index = base + 2;
1969 float cursor_pos[2];
1970 const float node_pos_gui[2] = { gui_points->
points[node_point_index],
1971 gui_points->
points[node_point_index + 1] };
1972 const float handle_pos[2] = { gui_points->
border[base], gui_points->
border[base + 1] };
1981 mask_gui->
pos[0], mask_gui->
pos[1]);
2000 mask_form->
source[0] = raw_point[0];
2001 mask_form->
source[1] = raw_point[1];
2015 const int node_count,
const gboolean draw_border,
const gboolean draw_source,
2034 int form_index,
int node_count)
2049 const gboolean have_first_node = node_count && gui_points->
points && gui_points->
points_count > 1;
2050 float node_posx = have_first_node ? gui_points->
points[2] : mask_gui->
pos[0];
2051 float node_posy = have_first_node ? gui_points->
points[3] : mask_gui->
pos[1];
2073 if(gui_points->
points && node_count > 0 && gui_points->
points_count > node_count * 3 + 6)
2076 node_count, zoom_scale,
FALSE);
2090 if(mask_gui->
node_selected && selected_node >= 0 && selected_node < node_count
2093 const int node_index = selected_node;
2096 gui_points->
points[node_index * 6 + 4], gui_points->
points[node_index * 6 + 5],
2097 &handle[0], &handle[1], gui_points->
clockwise);
2098 const float pt[2] = { gui_points->
points[node_index * 6 + 2], gui_points->
points[node_index * 6 + 3] };
2100 || (selected_handle == node_index)
2109 for(
int node_index = 0; node_index < node_count; node_index++)
2112 if(mask_gui->
creation && node_index == node_count - 1)
break;
2117 const gboolean action = (node_index == selected_node);
2118 const float x = gui_points->
points[node_index * 6 + 2];
2119 const float y = gui_points->
points[node_index * 6 + 3];
2122 if(mask_gui->
creation && node_index == 0)
2129 if(mask_gui->
node_selected && selected_node >= 0 && selected_node < node_count
2132 const int edited = selected_node;
2134 || (selected_handle_border == edited)
2136 const int curr_node = edited * 6;
2137 const float handle[2] = { gui_points->
border[curr_node], gui_points->
border[curr_node + 1] };
2148 .source = { gui_points->
source[0], gui_points->
source[1] } };
2152 float offset_x = gui_points->
source[0] - gui_points->
points[0];
2153 float offset_y = gui_points->
source[1] - gui_points->
points[1];
2160 for(
int node_index = 0; node_index < node_count; node_index++)
2163 && (node_index == mask_gui->
node_hovered || node_index == selected_node
2164 || (mask_gui->
creation && node_index == node_count - 1)))
2166 const int proj_index = node_index * 6 + 2;
2167 if(gui_points->
source_count <= node_index * 3 + 1)
break;
2168 const float proj[2] = { gui_points->
source[proj_index], gui_points->
source[proj_index + 1] };
2182 const int corner_count,
const int point_count,
int border_count,
2183 float *x_min,
float *x_max,
float *y_min,
float *y_max)
2187 float xmin = FLT_MAX;
2188 float ymin = FLT_MAX;
2189 float xmax = -FLT_MAX;
2190 float ymax = -FLT_MAX;
2191 for(
int border_index = corner_count * 3; border_index < border_count; border_index++)
2196 const float xx = border_buffer[border_index * 2];
2197 const float yy = border_buffer[border_index * 2 + 1];
2198 xmin =
MIN(xx, xmin);
2199 xmax =
MAX(xx, xmax);
2200 ymin =
MIN(yy, ymin);
2201 ymax =
MAX(yy, ymax);
2203 for(
int point_index = corner_count * 3; point_index < point_count; point_index++)
2206 const float xx = point_buffer[point_index * 2];
2207 const float yy = point_buffer[point_index * 2 + 1];
2208 xmin =
MIN(xx, xmin);
2209 xmax =
MAX(xx, xmax);
2210 ymin =
MIN(yy, ymin);
2211 ymax =
MAX(yy, ymax);
2224 const int corner_count,
const int point_count,
int border_count,
2228 float xmin, xmax, ymin, ymax;
2230 &xmin, &xmax, &ymin, &ymax);
2231 *
height = ymax - ymin + 4;
2232 *
width = xmax - xmin + 4;
2240 gboolean get_source)
2245 float *point_buffer = NULL;
2246 float *border_buffer = NULL;
2247 int point_count = 0;
2248 int border_count = 0;
2252 &point_buffer, &point_count, &border_buffer, &border_count, get_source) != 0)
2259 const guint corner_count = g_list_length(mask_form->
points);
2297 int posx,
int posy,
int buffer_width)
2300 int l = dt_fast_hypotf(p1[0] - p0[0], p1[1] - p0[1]) + 1;
2302 const float lx = p1[0] - p0[0];
2303 const float ly = p1[1] - p0[1];
2304 const float inv_l = 1.0f / (float)l;
2306 for(
int i = 0;
i < l;
i++)
2309 const int x = (int)((
float)
i * lx * inv_l) + p0[0] - posx;
2310 const int y = (int)((
float)
i * ly * inv_l) + p0[1] - posy;
2311 const float op = 1.0f - (float)
i * inv_l;
2312 const size_t idx = y * buffer_width +
x;
2313 buffer[idx] = fmaxf(buffer[idx], op);
2315 buffer[idx - 1] = fmaxf(buffer[idx - 1], op);
2317 buffer[idx - buffer_width] = fmaxf(buffer[idx - buffer_width], op);
2333 const int *
const p0,
const int *
const p1)
2335 const int jump =
MAX(
MAX(abs(p0[0] - last0[0]), abs(p0[1] - last0[1])),
2336 MAX(abs(p1[0] - last1[0]), abs(p1[1] - last1[1])));
2338 return MIN(jump, 64);
2344 out[0] = (int)floorf(from[0] +
t * (to[0] - from[0]) + 0.5f);
2345 out[1] = (int)floorf(from[1] +
t * (to[1] - from[1]) + 0.5f);
2350 const int *
const last1,
const int *
const p0,
2351 const int *
const p1,
const int posx,
const int posy,
2355 for(
int k = 1;
k < steps;
k++)
2357 const float t = (float)
k / (
float)steps;
2369 const int *
const last0,
const int *
const last1,
2370 const int *
const p0,
const int *
const p1)
2373 for(
int k = 1;
k < steps && dindex + 4 <= capacity;
k++)
2375 const float t = (float)
k / (
float)steps;
2380 dpoints[dindex] = b0[0];
2381 dpoints[dindex + 1] = b0[1];
2382 dpoints[dindex + 2] = b1[0];
2383 dpoints[dindex + 3] = b1[1];
2392 float **buffer,
int *
width,
int *
height,
int *posx,
int *posy)
2401 double start2 = 0.0;
2406 float *point_buffer = NULL;
2407 float *border_buffer = NULL;
2408 int point_count = 0;
2409 int border_count = 0;
2413 &border_buffer, &border_count,
FALSE) != 0)
2428 const guint corner_count = g_list_length(mask_form->
points);
2433 const int wb = *
width;
2439 const gboolean sparse =
FALSE;
2440 const int sparse_factor = 1;
2450 const size_t bufsize = (size_t)(*
width) * (*height);
2453 if(!
IS_NULL_PTR(bufptr)) memset(bufptr, 0,
sizeof(
float) * bufsize);
2462 const int border_point_count = border_count;
2463 if(border_point_count > 2)
2465 int lastx = (int)point_buffer[(border_point_count - 1) * 2];
2466 int lasty = (int)point_buffer[(border_point_count - 1) * 2 + 1];
2467 int lasty2 = (int)point_buffer[(border_point_count - 2) * 2 + 1];
2469 int just_change_dir = 0;
2470 for(
int ii = corner_count * 3; ii < 2 * border_point_count - corner_count * 3; ii++)
2475 if(ii >= border_point_count)
2476 i = (ii - corner_count * 3) % (border_point_count - corner_count * 3) + corner_count * 3;
2477 const int xx = (int)point_buffer[
i * 2];
2478 const int yy = (int)point_buffer[
i * 2 + 1];
2481 if(yy == lasty)
continue;
2484 if(yy - lasty > 1 || yy - lasty < -1)
2488 for(
int j = yy + 1; j < lasty; j++)
2490 const int nx = (j - yy) * (lastx - xx) / (float)(lasty - yy) + xx;
2491 const size_t idx = (size_t)(j - (*posy)) * (*
width) + nx - (*posx);
2492 assert(idx < bufsize);
2500 for(
int j = lasty + 1; j < yy; j++)
2502 const int nx = (j - lasty) * (xx - lastx) / (float)(yy - lasty) + lastx;
2503 const size_t idx = (size_t)(j - (*posy)) * (*
width) + nx - (*posx);
2504 assert(idx < bufsize);
2512 if((lasty - lasty2) * (lasty - yy) > 0)
2514 const size_t idx = (size_t)(lasty - (*posy)) * (*
width) + lastx + 1 - (*posx);
2515 assert(idx < bufsize);
2517 just_change_dir = 1;
2520 if(just_change_dir && ii ==
i)
2524 const size_t idx = (size_t)(yy - (*posy)) * (*
width) + xx - (*posx);
2525 assert(idx < bufsize);
2526 float v = bufptr[idx];
2529 if(xx - (*posx) > 0)
2531 const size_t idx_ = (size_t)(yy - (*posy)) * (*
width) + xx - 1 - (*posx);
2532 assert(idx_ < bufsize);
2533 bufptr[idx_] = 1.0f;
2535 else if(xx - (*posx) < (*
width) - 1)
2537 const size_t idx_ = (size_t)(yy - (*posy)) * (*
width) + xx + 1 - (*posx);
2538 assert(idx_ < bufsize);
2539 bufptr[idx_] = 1.0f;
2544 const size_t idx_ = (size_t)(yy - (*posy)) * (*
width) + xx - (*posx);
2545 assert(idx_ < bufsize);
2546 bufptr[idx_] = 1.0f;
2547 just_change_dir = 0;
2552 const size_t idx_ = (size_t)(yy - (*posy)) * (*
width) + xx - (*posx);
2553 assert(idx_ < bufsize);
2554 bufptr[idx_] = 1.0f;
2570 for(
int yy = 0; yy < hb; yy++)
2572 float *
const restrict
row = bufptr + (size_t)yy * wb;
2574 for(
int xx = 0; xx < wb; xx++)
2576 const float v =
row[xx];
2590 int p0[2] = { 0 }, p1[2] = { 0 };
2591 int prev0[2] = { 0 }, prev1[2] = { 0 };
2592 gboolean have_prev =
FALSE;
2593 int last0[2] = { -100, -100 }, last1[2] = { -100, -100 };
2594 for(
int i = corner_count * 3;
i < border_count;
i++)
2596 p0[0] = point_buffer[
i * 2];
2597 p0[1] = point_buffer[
i * 2 + 1];
2602 const int border_index =
i;
2603 p1[0] = border_buffer[border_index * 2];
2604 p1[1] = border_buffer[border_index * 2 + 1];
2606 const gboolean used_next =
FALSE;
2608 if(sparse && have_prev && !used_next
2609 && (prev0[0] != p0[0] || prev0[1] != p0[1] || prev1[0] != p1[0] || prev1[1] != p1[1]))
2611 for(
int k = 1;
k < sparse_factor;
k++)
2613 const float t = (float)
k / (
float)sparse_factor;
2614 int mp0[2] = { (int)floorf(prev0[0] +
t * (p0[0] - prev0[0]) + 0.5f),
2615 (
int)floorf(prev0[1] +
t * (p0[1] - prev0[1]) + 0.5f) };
2616 int mp1[2] = { (int)floorf(prev1[0] +
t * (p1[0] - prev1[0]) + 0.5f),
2617 (
int)floorf(prev1[1] +
t * (p1[1] - prev1[1]) + 0.5f) };
2637 if(last0[0] != p0[0] || last0[1] != p0[1] || last1[0] != p1[0] || last1[1] != p1[1])
2639 if(last0[0] > -100 || last0[1] > -100)
2682 int point_start = -1;
2687 for(
int k = 0;
k < point_count;
k++)
2689 float x = polygon[2 *
k];
2690 float y = polygon[2 *
k + 1];
2692 if(
x >= xmin + 1 && y >= ymin + 1
2693 &&
x <= xmax - 1 && y <= ymax - 1)
2703 if(point_start < 0)
return 0;
2711 } roi_crop_segment_t;
2713 roi_crop_segment_t *xmax_segs =
dt_alloc_align(
sizeof(*xmax_segs) * point_count);
2714 roi_crop_segment_t *ymax_segs =
dt_alloc_align(
sizeof(*ymax_segs) * point_count);
2719 goto fallback_passes;
2722 int xmin_l = -1, xmin_r = -1;
2723 int xmax_l = -1, xmax_r = -1;
2727 for(
int k = 0;
k < point_count;
k++)
2729 const int kk = (
k + point_start) % point_count;
2730 const float x = polygon[2 * kk];
2732 if(xmin_l < 0 &&
x < xmin) xmin_l =
k;
2733 if(xmin_l >= 0 &&
x >= xmin) xmin_r =
k - 1;
2735 if(xmin_l >= 0 && xmin_r >= 0)
2737 const int count = xmin_r - xmin_l + 1;
2738 const int ll = (xmin_l - 1 + point_start) % point_count;
2739 const int rr = (xmin_r + 1 + point_start) % point_count;
2740 const float delta_y = (count == 1) ? 0 : (polygon[2 * rr + 1] - polygon[2 * ll + 1]) / (count - 1);
2741 const float start_y = polygon[2 * ll + 1];
2743 for(
int n = 0;
n < count;
n++)
2745 const int nn = (
n + xmin_l + point_start) % point_count;
2746 polygon[2 * nn] = xmin;
2747 polygon[2 * nn + 1] = start_y +
n * delta_y;
2750 xmin_l = xmin_r = -1;
2753 if(xmax_l < 0 && x > xmax) xmax_l =
k;
2754 if(xmax_l >= 0 &&
x <= xmax) xmax_r =
k - 1;
2756 if(xmax_l >= 0 && xmax_r >= 0)
2758 const int count = xmax_r - xmax_l + 1;
2759 const int ll = (xmax_l - 1 + point_start) % point_count;
2760 const int rr = (xmax_r + 1 + point_start) % point_count;
2761 const float delta_y = (count == 1) ? 0 : (polygon[2 * rr + 1] - polygon[2 * ll + 1]) / (count - 1);
2762 const float start_y = polygon[2 * ll + 1];
2764 xmax_segs[xmax_count].l = xmax_l;
2765 xmax_segs[xmax_count].r = xmax_r;
2766 xmax_segs[xmax_count].start = start_y;
2767 xmax_segs[xmax_count].delta = delta_y;
2770 xmax_l = xmax_r = -1;
2774 for(
int s = 0; s < xmax_count; s++)
2776 const int count = xmax_segs[s].r - xmax_segs[s].l + 1;
2777 const float start_y = xmax_segs[s].start;
2778 const float delta_y = xmax_segs[s].delta;
2779 for(
int n = 0;
n < count;
n++)
2781 const int nn = (
n + xmax_segs[s].l + point_start) % point_count;
2782 polygon[2 * nn] = xmax;
2783 polygon[2 * nn + 1] = start_y +
n * delta_y;
2789 int ymin_l = -1, ymin_r = -1;
2790 int ymax_l = -1, ymax_r = -1;
2794 for(
int k = 0;
k < point_count;
k++)
2796 const int kk = (
k + point_start) % point_count;
2797 const float y = polygon[2 * kk + 1];
2799 if(ymin_l < 0 && y < ymin) ymin_l =
k;
2800 if(ymin_l >= 0 && y >= ymin) ymin_r =
k - 1;
2802 if(ymin_l >= 0 && ymin_r >= 0)
2804 const int count = ymin_r - ymin_l + 1;
2805 const int ll = (ymin_l - 1 + point_start) % point_count;
2806 const int rr = (ymin_r + 1 + point_start) % point_count;
2807 const float delta_x = (count == 1) ? 0 : (polygon[2 * rr] - polygon[2 * ll]) / (count - 1);
2808 const float start_x = polygon[2 * ll];
2810 for(
int n = 0;
n < count;
n++)
2812 const int nn = (
n + ymin_l + point_start) % point_count;
2813 polygon[2 * nn] = start_x +
n * delta_x;
2814 polygon[2 * nn + 1] = ymin;
2817 ymin_l = ymin_r = -1;
2820 if(ymax_l < 0 && y > ymax) ymax_l =
k;
2821 if(ymax_l >= 0 && y <= ymax) ymax_r =
k - 1;
2823 if(ymax_l >= 0 && ymax_r >= 0)
2825 const int count = ymax_r - ymax_l + 1;
2826 const int ll = (ymax_l - 1 + point_start) % point_count;
2827 const int rr = (ymax_r + 1 + point_start) % point_count;
2828 const float delta_x = (count == 1) ? 0 : (polygon[2 * rr] - polygon[2 * ll]) / (count - 1);
2829 const float start_x = polygon[2 * ll];
2831 ymax_segs[ymax_count].l = ymax_l;
2832 ymax_segs[ymax_count].r = ymax_r;
2833 ymax_segs[ymax_count].start = start_x;
2834 ymax_segs[ymax_count].delta = delta_x;
2837 ymax_l = ymax_r = -1;
2841 for(
int s = 0; s < ymax_count; s++)
2843 const int count = ymax_segs[s].r - ymax_segs[s].l + 1;
2844 const float start_x = ymax_segs[s].start;
2845 const float delta_x = ymax_segs[s].delta;
2846 for(
int n = 0;
n < count;
n++)
2848 const int nn = (
n + ymax_segs[s].l + point_start) % point_count;
2849 polygon[2 * nn] = start_x +
n * delta_x;
2850 polygon[2 * nn + 1] = ymax;
2860 for(
int k = 0;
k < point_count;
k++)
2862 const int kk = (
k + point_start) % point_count;
2864 if(l < 0 && polygon[2 * kk] < xmin) l =
k;
2865 if(l >= 0 && polygon[2 * kk] >= xmin)
r =
k - 1;
2868 if(l >= 0 &&
r >= 0)
2870 const int count =
r - l + 1;
2871 const int ll = (l - 1 + point_start) % point_count;
2872 const int rr = (
r + 1 + point_start) % point_count;
2873 const float delta_y = (count == 1) ? 0 : (polygon[2 * rr + 1] - polygon[2 * ll + 1]) / (count - 1);
2874 const float start_y = polygon[2 * ll + 1];
2876 for(
int n = 0;
n < count;
n++)
2878 const int nn = (
n + l + point_start) % point_count;
2879 polygon[2 * nn] = xmin;
2880 polygon[2 * nn + 1] = start_y +
n * delta_y;
2888 for(
int k = 0;
k < point_count;
k++)
2890 const int kk = (
k + point_start) % point_count;
2892 if(l < 0 && polygon[2 * kk] > xmax) l =
k;
2893 if(l >= 0 && polygon[2 * kk] <= xmax)
r =
k - 1;
2896 if(l >= 0 &&
r >= 0)
2898 const int count =
r - l + 1;
2899 const int ll = (l - 1 + point_start) % point_count;
2900 const int rr = (
r + 1 + point_start) % point_count;
2901 const float delta_y = (count == 1) ? 0 : (polygon[2 * rr + 1] - polygon[2 * ll + 1]) / (count - 1);
2902 const float start_y = polygon[2 * ll + 1];
2904 for(
int n = 0;
n < count;
n++)
2906 const int nn = (
n + l + point_start) % point_count;
2907 polygon[2 * nn] = xmax;
2908 polygon[2 * nn + 1] = start_y +
n * delta_y;
2916 for(
int k = 0;
k < point_count;
k++)
2918 const int kk = (
k + point_start) % point_count;
2920 if(l < 0 && polygon[2 * kk + 1] < ymin) l =
k;
2921 if(l >= 0 && polygon[2 * kk + 1] >= ymin)
r =
k - 1;
2924 if(l >= 0 &&
r >= 0)
2926 const int count =
r - l + 1;
2927 const int ll = (l - 1 + point_start) % point_count;
2928 const int rr = (
r + 1 + point_start) % point_count;
2929 const float delta_x = (count == 1) ? 0 : (polygon[2 * rr] - polygon[2 * ll]) / (count - 1);
2930 const float start_x = polygon[2 * ll];
2932 for(
int n = 0;
n < count;
n++)
2934 const int nn = (
n + l + point_start) % point_count;
2935 polygon[2 * nn] = start_x +
n * delta_x;
2936 polygon[2 * nn + 1] = ymin;
2944 for(
int k = 0;
k < point_count;
k++)
2946 const int kk = (
k + point_start) % point_count;
2948 if(l < 0 && polygon[2 * kk + 1] > ymax) l =
k;
2949 if(l >= 0 && polygon[2 * kk + 1] <= ymax)
r =
k - 1;
2952 if(l >= 0 &&
r >= 0)
2954 const int count =
r - l + 1;
2955 const int ll = (l - 1 + point_start) % point_count;
2956 const int rr = (
r + 1 + point_start) % point_count;
2957 const float delta_x = (count == 1) ? 0 : (polygon[2 * rr] - polygon[2 * ll]) / (count - 1);
2958 const float start_x = polygon[2 * ll];
2960 for(
int n = 0;
n < count;
n++)
2962 const int nn = (
n + l + point_start) % point_count;
2963 polygon[2 * nn] = start_x +
n * delta_x;
2964 polygon[2 * nn + 1] = ymax;
2977 const int l = sqrt((p1[0] - p0[0]) * (p1[0] - p0[0]) + (p1[1] - p0[1]) * (p1[1] - p0[1])) + 1;
2979 const float lx = p1[0] - p0[0];
2980 const float ly = p1[1] - p0[1];
2981 const float inv_l = 1.0f / (float)l;
2983 const int dx = lx < 0 ? -1 : 1;
2984 const int dy = ly < 0 ? -1 : 1;
2985 const int dpy = dy * bw;
2987 const int x0 = p0[0], y0 = p0[1];
2988 const int x1 = p1[0], y1 = p1[1];
2989 if((x0 < 0 && x1 < 0) || (x0 >= bw && x1 >= bw) || (y0 < 0 && y1 < 0) || (y0 >= bh && y1 >= bh))
return;
2990 const int inside = (x0 >= 0 && x0 < bw && x1 >= 0 && x1 < bw && y0 >= 0 && y0 < bh && y1 >= 0 && y1 < bh);
2992 for(
int i = 0;
i < l;
i++)
2995 const int x = (int)((
float)
i * lx * inv_l) + p0[0];
2996 const int y = (int)((
float)
i * ly * inv_l) + p0[1];
2997 const float op = 1.0f - (float)
i * inv_l;
2998 if(!inside && (x < 0 || x >= bw || y < 0 || y >= bh))
continue;
2999 float *buf = buffer + (size_t)y * bw +
x;
3001 buf[0] =
MAX(buf[0], op);
3002 else if(
x >= 0 && x < bw && y >= 0 && y < bh)
3003 buf[0] =
MAX(buf[0], op);
3004 if(
x + dx >= 0 &&
x + dx < bw && y >= 0 && y < bh)
3005 buf[dx] =
MAX(buf[dx], op);
3006 if(
x >= 0 && x < bw && y + dy >= 0 && y + dy < bh)
3007 buf[dpy] =
MAX(buf[dpy], op);
3022 double start2 = 0.0;
3025 const int px = roi->
x;
3026 const int py = roi->
y;
3029 const float scale = roi->
scale;
3035 const gboolean sparse =
FALSE;
3036 const int sparse_factor = 1;
3043 int polygon_in_roi = 0;
3044 int feather_in_roi = 0;
3045 int polygon_encircles_roi = 0;
3048 float *points = NULL;
3049 float *border = NULL;
3050 int points_count = 0;
3051 int border_count = 0;
3055 &points, &points_count, &border, &border_count,
FALSE) != 0)
3062 if(points_count <= 2 || points_count <= (
int)g_list_length(mask_form->
points) * 3)
3076 const guint corner_count = g_list_length(mask_form->
points);
3083 for(
int i = corner_count * 3;
i < border_count;
i++)
3085 border[2 *
i] = border[2 *
i] * scale - px;
3086 border[2 *
i + 1] = border[2 *
i + 1] * scale - py;
3088 for(
int i = corner_count * 3;
i < points_count;
i++)
3090 const float xx = points[2 *
i];
3091 const float yy = points[2 *
i + 1];
3092 points[2 *
i] = xx * scale - px;
3093 points[2 *
i + 1] = yy * scale - py;
3097 for(
int i = corner_count * 3;
i < points_count;
i++)
3099 const int xx = points[
i * 2];
3100 const int yy = points[
i * 2 + 1];
3102 if(xx > 1 && yy > 1 && xx <
width - 2 && yy <
height - 2)
3112 int crossing_count = 0;
3115 const int y =
height / 2;
3117 for(
int i = corner_count * 3;
i < points_count;
i++)
3119 const int yy = (int)points[2 *
i + 1];
3120 if(yy != last_y && yy == y)
3122 if(points[2 *
i] >
x) crossing_count++;
3127 if(crossing_count & 1)
3130 polygon_encircles_roi = 1;
3135 for(
int i = corner_count * 3;
i < border_count;
i++)
3137 const float xx = border[
i * 2];
3138 const float yy = border[
i * 2 + 1];
3139 if(xx > 1 && yy > 1 && xx <
width - 2 && yy <
height - 2)
3147 if(!polygon_in_roi && !feather_in_roi)
3155 float xmin, xmax, ymin, ymax;
3157 &xmin, &xmax, &ymin, &ymax);
3184 memcpy(cpoints, points,
sizeof(
float) * 2 * points_count);
3190 points_count - corner_count * 3, 0,
3192 polygon_encircles_roi = polygon_encircles_roi || !crop_success;
3201 if(polygon_encircles_roi)
3212 float xlast = cpoints[(points_count - 1) * 2];
3213 float ylast = cpoints[(points_count - 1) * 2 + 1];
3215 for(
int i = corner_count * 3;
i < points_count;
i++)
3217 float xstart = xlast;
3218 float ystart = ylast;
3220 float xend = xlast = cpoints[
i * 2];
3221 float yend = ylast = cpoints[
i * 2 + 1];
3226 tmp = ystart, ystart = yend, yend = tmp;
3227 tmp = xstart, xstart = xend, xend = tmp;
3230 const float m = (xstart - xend) / (ystart - yend);
3233 for(
int yy = (
int)ceilf(ystart); (float)yy < yend;
3236 const float xcross = xstart +
m * (yy - ystart);
3238 int xx = floorf(xcross);
3239 if((
float)xx + 0.5f <= xcross) xx++;
3241 if(xx < 0 || xx >=
width || yy < 0 || yy >=
height)
3244 const size_t index = (size_t)yy *
width + xx;
3246 buffer[index] = 1.0f - buffer[index];
3259 const int xxmin =
MAX(xmin, 0);
3260 const int xxmax =
MIN(xmax,
width - 1);
3261 const int yymin =
MAX(ymin, 0);
3262 const int yymax =
MIN(ymax,
height - 1);
3264 for(
int yy = yymin; yy <= yymax; yy++)
3266 float *
const restrict
row = buffer + (size_t)yy *
width;
3268 for(
int xx = xxmin; xx <= xxmax; xx++)
3270 const float v =
row[xx];
3287 if(!polygon_encircles_roi)
3291 const int dpoints_capacity = 4 * (border_count + 1024) * (sparse ? sparse_factor : 1);
3302 int prev0[2] = { 0, 0 };
3303 int prev1[2] = { 0, 0 };
3304 gboolean have_prev =
FALSE;
3305 int last0[2] = { -100, -100 };
3306 int last1[2] = { -100, -100 };
3307 gboolean have_last =
FALSE;
3308 for(
int i = corner_count * 3;
i < border_count;
i++)
3310 p0[0] = floorf(points[
i * 2] + 0.5f);
3311 p0[1] = ceilf(points[
i * 2 + 1]);
3316 const int border_index =
i;
3317 p1[0] = border[border_index * 2];
3318 p1[1] = border[border_index * 2 + 1];
3320 const gboolean used_next =
FALSE;
3322 if(sparse && have_prev && !used_next
3323 && (prev0[0] != p0[0] || prev0[1] != p0[1] || prev1[0] != p1[0] || prev1[1] != p1[1]))
3325 for(
int k = 1;
k < sparse_factor;
k++)
3327 const float t = (float)
k / (
float)sparse_factor;
3328 const int mp0[2] = { (int)floorf(prev0[0] +
t * (p0[0] - prev0[0]) + 0.5f),
3329 (
int)floorf(prev0[1] +
t * (p0[1] - prev0[1]) + 0.5f) };
3330 const int mp1[2] = { (int)floorf(prev1[0] +
t * (p1[0] - prev1[0]) + 0.5f),
3331 (
int)floorf(prev1[1] +
t * (p1[1] - prev1[1]) + 0.5f) };
3332 if(dindex + 4 <= dpoints_capacity)
3334 dpoints[dindex] = mp0[0];
3335 dpoints[dindex + 1] = mp0[1];
3336 dpoints[dindex + 2] = mp1[0];
3337 dpoints[dindex + 3] = mp1[1];
3360 if(last0[0] != p0[0] || last0[1] != p0[1] || last1[0] != p1[0] || last1[1] != p1[1])
3364 last0, last1, p0, p1);
3366 if(dindex + 4 > dpoints_capacity)
break;
3367 dpoints[dindex] = p0[0];
3368 dpoints[dindex + 1] = p0[1];
3369 dpoints[dindex + 2] = p1[0];
3370 dpoints[dindex + 3] = p1[1];
3394 for(
int n = 0;
n < dindex;
n += 4)
3413 if(!polygon_encircles_roi)
3414 dt_masks_touched_set(touched, (
int)floorf(xmin) - 2, (
int)floorf(ymin) - 2, (
int)ceilf(xmax) + 2,
3435 snprintf(mask_form->
name,
sizeof(mask_form->
name), _(
"polygon #%d"), (
int)form_number);
3440 char *
const restrict msgbuf,
const size_t msgbuf_len)
3446 const guint node_count = mask_form->
points ? g_list_length(mask_form->
points) : 0;
3447 if(mask_gui->
creation && node_count < 4)
3448 g_strlcat(msgbuf, _(
"<b>Add sharp node</b>: Ctrl+Click\n"
3449 "<b>Remove last node</b>: Backspace, <b>Cancel</b>: Esc"), msgbuf_len);
3451 g_strlcat(msgbuf, _(
"<b>Add sharp node</b>: Ctrl+Click, <b>Close path</b>: Enter, or Click on the first node\n"
3452 "<b>Remove last node</b>: Backspace, <b>Cancel</b>: Esc"), msgbuf_len);
3454 g_strlcat(msgbuf, _(
"<b>Move source</b>: Drag"), msgbuf_len);
3456 g_strlcat(msgbuf, _(
"<b>Node fading</b>: Drag"), msgbuf_len);
3458 g_strlcat(msgbuf, _(
"<b>Node curvature</b>: Drag"), msgbuf_len);
3461 g_strlcat(msgbuf, _(
"<b>Move node</b>: Drag, <b>Switch smooth/sharp</b>: Ctrl+Click\n"
3462 "<b>Delete node</b>: Right-click or Del"), msgbuf_len);
3464 g_strlcat(msgbuf, _(
"<b>Move node</b>: Drag, <b>Delete node</b>: Right-click"), msgbuf_len);
3466 g_strlcat(msgbuf, _(
"<b>Move segment</b>: Drag, <b>Add node</b>: Ctrl+Click"), msgbuf_len);
3468 g_strlcat(msgbuf, _(
"<b>Move</b>: Drag"), msgbuf_len);
3481 float offset[2] = { 0.1f, 0.1f };
3503 const int form_id = mask_gui->
formid;
3525 const int form_id = mask_gui->
formid;
3539 node_index, &node->
state))
3567 const float pzx,
const float pzy)
3574 gboolean ret =
FALSE;
3600 gchar *to_change_type = g_strdup_printf(_(
"Switch to %s node"), (is_corner) ? _(
"round") : _(
"cusp"));
3611 gtk_widget_set_sensitive(menu_item, !is_corner);
Handle default and user-set shortcuts (accelerators)
static double dist(double x1, double y1, double x2, double y2)
typedef void((*dt_cache_allocate_t)(void *userdata, dt_cache_entry_t *entry))
struct _GtkWidget GtkWidget
GtkWidget, opaque, spelled exactly as GTK spells it.
static const float const float const float min
const dt_colormatrix_t dt_aligned_pixel_t out
float dt_conf_get_float(const char *name)
Float for name, clamped to its declared bounds.
void dt_toast_log(const char *msg,...)
void * dt_alloc_align(size_t size)
Allocate cacheline-aligned memory.
dt_dev_image_geometry_t dt_dev_geometry_snapshot(const dt_develop_t *dev)
#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)
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
GtkWidget * geometry
its size, under the preview
GHashTable * selected
set of checked row labels, mirrored to conf on every change
GtkWidget * status
result of the last capture
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(struct dt_develop_t *dev, 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, const struct dt_masks_skip_range_t *skips, const int skip_count)
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.
static const GList * g_list_next_wraparound(const GList *list, const GList *head)
static GList * g_list_next_bounded(GList *list)
static gboolean g_list_shorter_than(const GList *list, unsigned len)
_lib_location_type_t type
int32_t dt_get_debug_flags(void)
void dt_print(dt_debug_thread_t thread, const char *msg,...) __attribute__((format(printf
Print to stdout when thread is enabled, prefixed with seconds since startup.
float *const restrict const size_t k
#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 float * dt_masks_dynbuf_buffer(dt_masks_dynbuf_t *a)
static void dt_masks_dynbuf_add_2(dt_masks_dynbuf_t *a, float value1, float value2)
static dt_masks_raster_result_t dt_masks_raster_from_status(const int status)
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)
dt_masks_raster_result_t
What a rasterisation attempt produced.
static void dt_masks_translate_ctrl_node(float node[2], float ctrl1[2], float ctrl2[2], const float delta_x, const float delta_y)
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_dynbuf_reserve_n(dt_masks_dynbuf_t *a, const int n)
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)
int dt_masks_form_change_opacity(dt_develop_t *dev, dt_masks_form_t *form, int parentid, int up, const int flow)
static void dt_masks_dynbuf_add_zeros(dt_masks_dynbuf_t *a, const int n)
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
static float * dt_masks_dynbuf_harvest(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])
What a mask outline needs in order to place itself, and who supplies it.
static dt_masks_distort_t dt_masks_distort_for_pipe(dt_dev_pixelpipe_t *pipe, dt_develop_t *dev)
The rendering supplier: compose this pipe, sample as this pipe was told to.
static dt_masks_distort_t dt_masks_distort_for_gui(dt_develop_t *dev)
The GUI supplier: compose through the geometry service, at full resolution, sampled at the density th...
static int dt_masks_distort_transform(const dt_masks_distort_t *const d, const double iop_order, const int transf_direction, float *points, size_t points_count)
Compose forward, bounded exactly as dt_dev_distort_transform_plus() is.
The per-shape function table, private to the masks implementation.
int dt_masks_point_in_form_exact(const float *pts, int num_pts, const float *points, int points_start, int points_count, const dt_masks_skip_range_t *skips, int skip_count)
Ray-cast point-in-polygon over a form point stream, honouring skip ranges.
void dt_masks_outline_envelope_offset(const float *centre, float dx, float dy, float radius, float radius_rate, float *out)
int dt_masks_outline_boundary_skips(const float *const points, const float *const border, const int count, const int header, dt_masks_skip_range_t **skips_out)
void dt_masks_outline_offset_along(const float *const centre, float dx, float dy, const float radius, float *const border)
gboolean dt_masks_outline_short_way(const float *const centre, const float *const from, const float *const to, const gboolean default_clockwise)
void dt_masks_draw_source(cairo_t *cr, dt_masks_form_gui_t *mask_gui, const int form_index, const int node_count, 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.
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_remove_node(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, int node_index)
float dt_masks_get_set_conf_value(dt_masks_form_t *mask_form, char *feature, float new_value, float value_min, float value_max, dt_masks_increment_t increment, int flow)
Change a numerical property of a mask shape, either by in/de-crementing the current value or setting ...
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)
gboolean dt_masks_gui_form_create_throttled(dt_masks_form_t *mask_form, dt_masks_form_gui_t *mask_gui, int form_index, dt_iop_module_t *module, float posx, float posy)
dt_masks_form_group_t * dt_masks_form_get_selected_group(const dt_masks_form_t *mask_form, const dt_masks_form_gui_t *mask_gui)
Get the selected group entry from the GUI selection index.
gboolean dt_masks_is_anything_selected(const dt_masks_form_gui_t *mask_gui)
void _masks_gui_delete_node_callback(GtkWidget *menu, gpointer user_data)
gboolean dt_masks_node_is_cusp(const dt_masks_form_gui_points_t *gui_points, const int node_index)
returns wether a node is a corner or not. A node is a corner if its 2 control handles are at the same...
dt_masks_form_t * dt_masks_get_visible_form(const dt_develop_t *dev)
Return the currently visible form used by the masks GUI.
void dt_masks_draw_path_seg_by_seg(cairo_t *cr, dt_masks_form_gui_t *mask_gui, const int form_index, const float *points, const int points_count, const int node_count, const float zoom_scale, const gboolean round_ends)
gboolean dt_masks_gui_is_dragging(const dt_masks_form_gui_t *gui)
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.
void dt_masks_draw_outline_runs(cairo_t *cr, const float *const points, const int first, const int last, const dt_masks_skip_range_t *skips, const int skip_count)
void dt_masks_set_source_pos_initial_value(dt_masks_form_gui_t *mask_gui, dt_masks_form_t *mask_form)
Initialize the clone source position based on current GUI state.
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)
Centralized hit-testing for node/handle/segment selection across shapes.
void dt_masks_set_source_pos_initial_state(dt_masks_form_gui_t *mask_gui, const uint32_t key_state)
Decide initial source positioning mode for clone masks.
void dt_masks_gui_form_create(dt_masks_form_t *mask_form, dt_masks_form_gui_t *mask_gui, int form_index, dt_iop_module_t *module)
gboolean dt_masks_form_exit_creation(dt_iop_module_t *module, dt_masks_form_gui_t *mask_gui)
void dt_masks_gui_form_save_creation(dt_develop_t *develop, dt_iop_module_t *module, dt_masks_form_t *mask_form, dt_masks_form_gui_t *mask_gui)
Save the form creation right after a shape has been finished drawing.
The interactive half of the masks subsystem: the editing state (dt_masks_form_gui_t),...
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 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)
static float dt_masks_get_form_size_from_nodes(const GList *points)
static int dt_masks_gui_selected_handle_border_index(const dt_masks_form_gui_t *gui)
#define menu_item_set_fake_accel(menu_item, keyval, mods)
static void dt_masks_gui_cursor_to_raw_norm(dt_develop_t *dev, const dt_masks_form_gui_t *gui, float point[2])
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_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)
static void dt_masks_project_on_line(const float cursor[2], const float node[2], const float handle[2], float point[2])
static gboolean dt_masks_gui_points_reach(const dt_masks_form_gui_points_t *gp, const float x, const float y, const float reach)
static void dt_masks_gui_delta_to_raw_norm(dt_develop_t *dev, const dt_masks_form_gui_t *gui, float point[2])
static void dt_masks_touched_set(dt_iop_roi_t *touched, int x0, int y0, int x1, int y1, const int width, const int height)
static void dt_masks_touched_full(dt_iop_roi_t *touched, const int width, const int height)
static void dt_masks_touched_none(dt_iop_roi_t *touched)
@ DT_MASKS_INTERACTION_OPACITY
@ DT_MASKS_INTERACTION_SIZE
@ DT_MASKS_INTERACTION_FADING
@ DT_MASKS_INCREMENT_SCALE
@ DT_MASKS_INCREMENT_OFFSET
@ DT_MASKS_INCREMENT_ABSOLUTE
#define CLAMPF(a, mn, mx)
#define dt_free_align(ptr)
Release memory from dt_alloc_align() and set ptr to NULL.
static float * dt_alloc_align_float(size_t pixels)
Allocate pixels floats, cacheline-aligned and marked as such.
#define dt_free(ptr)
g_free() ptr and set it to NULL, skipping both if it is already NULL.
#define __OMP_PARALLEL_FOR__(...)
#define __OMP_PARALLEL_FOR_SIMD__(...)
#define dt_pixelpipe_cache_alloc_align_float_cache(pixels, id)
#define dt_pixelpipe_cache_alloc_align_cache(size, id)
#define dt_pixelpipe_cache_free_align(mem)
static void _polygon_set_hint_message(const dt_masks_form_gui_t *const mask_gui, const dt_masks_form_t *const mask_form, char *const restrict msgbuf, const size_t msgbuf_len)
static gboolean _polygon_get_gravity_center(dt_develop_t *dev, const dt_masks_form_t *mask_form, float center[2], float *area)
static dt_masks_raster_result_t _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, dt_iop_roi_t *touched)
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 void _polygon_joint_arc(const _polygon_walk_t *const w, const float *const centre, const float *const from, const float *const to)
static int _find_closest_handle(dt_masks_form_t *mask_form, dt_masks_form_gui_t *mask_gui, int form_index)
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 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 gboolean _polygon_walk_segment(const _polygon_walk_t *const w, _polygon_walk_state_t *const s, const int k)
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 gboolean _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 radius_rate, 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_radius_rate_at(const float r1, const float r2, const double t)
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 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_handle, int *inside_source, float *dist, void *user_data)
Polygon-specific inside/border/segment hit testing.
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 dt_masks_raster_result_t _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 dt_masks_raster_result_t _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, dt_masks_skip_range_t **border_skips, int *border_skip_count, int source, const dt_iop_module_t *module)
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 float _polygon_radius_at(const float r1, const float r2, const double t)
static void _polygon_falloff_roi(float *buffer, int *p0, int *p1, int bw, int bh)
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 _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/fading/opacity.
static void _polygon_points_recurs_border_gaps(const float *const center_max, const float *const border_min, const float *const border_max, dt_masks_dynbuf_t *draw_points, dt_masks_dynbuf_t *draw_border, gboolean clockwise, const int step)
Fill gaps between border points with a circular arc.
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 _init_fading(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 fading from config and emit the toast with a size-normalized percentage.
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 int _falloff_bridge_queue(int *const dpoints, int dindex, const int capacity, const int *const last0, const int *const last1, const int *const p0, const int *const p1)
static dt_masks_raster_result_t _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_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 void _polygon_segment_load(const dt_masks_node_polygon_t *const from, const dt_masks_node_polygon_t *const to, const _polygon_frame_t *const f, float p1[5], float p2[5])
static int _change_fading(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 fading for the active node scope or the full shape.
static void _polygon_initial_source_pos(struct dt_develop_t *dev, const float iwd, const float iht, float *x, float *y)
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_draw_shape(struct dt_develop_t *dev, cairo_t *cr, const float *point_buffer, const int point_count, const int node_count, const gboolean draw_border, const gboolean draw_source, const dt_masks_skip_range_t *skips, const int skip_count)
Draw a polygon or border polyline, skipping NaN points.
static void _polygon_init_ctrl_points(dt_masks_form_t *mask_form)
Initialize control points to match a Catmull-Rom-like spline.
static dt_masks_raster_result_t _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 int _falloff_bridge_steps(const int *const last0, const int *const last1, const int *const p0, const int *const p1)
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, const gboolean have_min, const gboolean have_max, gboolean have_border_min, gboolean have_border_max, gboolean *out_have_border)
Recursive subdivision to sample polygon and border points.
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_handle, 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 void _polygon_walk_write_header(const _polygon_walk_t *const w)
static void _falloff_bridge_stamp(float *const restrict buffer, const int *const last0, const int *const last1, const int *const p0, const int *const p1, const int posx, const int posy, const int width)
static void _falloff_bridge_lerp(const int *const from, const int *const to, const float t, int *const out)
static int _polygon_get_pts_border(dt_develop_t *develop, dt_masks_form_t *mask_form, const double iop_order, const int transform_direction, const dt_masks_distort_t *const dist, float **point_buffer, int *point_count, float **border_buffer, int *border_count, gboolean source)
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.
static const dt_aligned_pixel_simd_t value
const float uint32_t state[4]
dt_masks_node_polygon_t ** nodes
dt_masks_dynbuf_t * dpoints
dt_masks_dynbuf_t * dborder
uint8_t * node_has_border
Objective facts about the image a dev is working on.
struct dt_develop_t * dev
Region of interest passed through the pixelpipe.
Where an outline builder gets its geometry from.
shape_draw_function_t draw_shape
struct dt_masks_gui_center_point_t::@27 main
struct dt_masks_gui_center_point_t::@28 source
dt_masks_points_states_t state
One cut in a shape's border outline: while walking the border buffer forward, on reaching index jump_...
typedef double((*spd)(unsigned long int wavelength, double TempK))
static double dt_get_wtime(void)
Telling the user something happened.