49#define HARDNESS_MIN 0.00001f
50#define HARDNESS_MAX 1.0f
52#define BORDER_MIN 0.00005f
53#define BORDER_MAX 0.5f
62 const float *point_buffer,
const float *payload_buffer)
64 const float point_x = point_buffer[2 * point_index];
65 const float point_y = point_buffer[2 * point_index + 1];
66 const float point_border = payload_buffer[4 * point_index];
67 const float point_hardness = payload_buffer[4 * point_index + 1];
68 const float point_density = payload_buffer[4 * point_index + 2];
69 const float start_x = point_buffer[0];
70 const float start_y = point_buffer[1];
71 const float start_border = payload_buffer[0];
72 const float start_hardness = payload_buffer[1];
73 const float start_density = payload_buffer[2];
74 const float end_x = point_buffer[2 * (point_count - 1)];
75 const float end_y = point_buffer[2 * (point_count - 1) + 1];
76 const float end_border = payload_buffer[4 * (point_count - 1)];
77 const float end_hardness = payload_buffer[4 * (point_count - 1) + 1];
78 const float end_density = payload_buffer[4 * (point_count - 1) + 2];
79 const float bweight = 1.0f;
80 const float hweight = 0.01f;
81 const float dweight = 0.01f;
83 const float segment_dx = end_x - start_x;
84 const float segment_dy = end_y - start_y;
85 const float segment_db = end_border - start_border;
86 const float segment_dh = end_hardness - start_hardness;
87 const float segment_dd = end_density - start_density;
89 const float dot = (point_x - start_x) * segment_dx + (point_y - start_y) * segment_dy;
90 const float segment_len2 = sqf(segment_dx) + sqf(segment_dy);
91 const float t = dot / segment_len2;
93 float dx = 0.0f, dy = 0.0f, db = 0.0f, dh = 0.0f, dd = 0.0f;
95 if(segment_len2 == 0.0f)
97 dx = point_x - start_x;
98 dy = point_y - start_y;
99 db = point_border - start_border;
100 dh = point_hardness - start_hardness;
101 dd = point_density - start_density;
105 dx = point_x - start_x;
106 dy = point_y - start_y;
107 db = point_border - start_border;
108 dh = point_hardness - start_hardness;
109 dd = point_density - start_density;
113 dx = point_x - end_x;
114 dy = point_y - end_y;
115 db = point_border - end_border;
116 dh = point_hardness - end_hardness;
117 dd = point_density - end_density;
121 dx = point_x - (start_x +
t * segment_dx);
122 dy = point_y - (start_y +
t * segment_dy);
123 db = point_border - (start_border +
t * segment_db);
124 dh = point_hardness - (start_hardness +
t * segment_dh);
125 dd = point_density - (start_density +
t * segment_dd);
128 return sqf(dx) + sqf(dy) + bweight * sqf(db) + hweight * dh * dh + dweight * sqf(dd);
135 const float *payload_buffer,
float epsilon2)
137 GList *result_list = NULL;
142 for(
int point_index = 1; point_index < point_count - 1; point_index++)
145 point_buffer, payload_buffer);
148 split_index = point_index;
153 if(dmax2 >= epsilon2)
156 payload_buffer, epsilon2);
158 point_count - split_index,
159 payload_buffer + split_index * 4, epsilon2);
162 GList *end1 = g_list_last(left_list);
164 left_list = g_list_delete_link(left_list, end1);
166 result_list = g_list_concat(left_list, right_list);
171 first_node->
node[0] = point_buffer[0];
172 first_node->
node[1] = point_buffer[1];
173 first_node->
ctrl1[0] = first_node->
ctrl1[1] = first_node->
ctrl2[0] = first_node->
ctrl2[1] = -1.0f;
174 first_node->
border[0] = first_node->
border[1] = payload_buffer[0];
175 first_node->
hardness = payload_buffer[1];
176 first_node->
density = payload_buffer[2];
178 result_list = g_list_append(result_list, (gpointer)first_node);
181 last_node->
node[0] = point_buffer[(point_count - 1) * 2];
182 last_node->
node[1] = point_buffer[(point_count - 1) * 2 + 1];
184 last_node->
border[0] = last_node->
border[1] = payload_buffer[(point_count - 1) * 4];
185 last_node->
hardness = payload_buffer[(point_count - 1) * 4 + 1];
186 last_node->
density = payload_buffer[(point_count - 1) * 4 + 2];
188 result_list = g_list_append(result_list, (gpointer)last_node);
197static void _brush_get_XY(
float p0_x,
float p0_y,
float p1_x,
float p1_y,
float p2_x,
float p2_y,
float p3_x,
198 float p3_y,
float t,
float *out_x,
float *out_y)
200 const float ti = 1.0f -
t;
201 const float a = ti * ti * ti;
202 const float b = 3.0f *
t * ti * ti;
203 const float c = 3.0f * sqf(
t) * ti;
204 const float d =
t *
t *
t;
205 *out_x = p0_x * a + p1_x * b + p2_x * c + p3_x *
d;
206 *out_y = p0_y * a + p1_y * b + p2_y * c + p3_y *
d;
213 float p3_x,
float p3_y,
float t,
float radius,
214 float *point_x,
float *point_y,
float *border_x,
float *border_y)
217 _brush_get_XY(p0_x, p0_y, p1_x, p1_y, p2_x, p2_y, p3_x, p3_y,
t, point_x, point_y);
220 const float ti = 1.0f -
t;
221 const float a = 3.0f * ti * ti;
222 const float b = 3.0f * (ti * ti - 2.0f *
t * ti);
223 const float c = 3.0f * (2.0f *
t * ti -
t *
t);
224 const float d = 3.0f * sqf(
t);
226 const float dx = -p0_x * a + p1_x * b + p2_x * c + p3_x *
d;
227 const float dy = -p0_y * a + p1_y * b + p2_y * c + p3_y *
d;
230 if(dx == 0 && dy == 0)
236 const float l = 1.0f / sqrtf(dx * dx + dy * dy);
237 *border_x = (*point_x) + radius * dy * l;
238 *border_y = (*point_y) - radius * dx * l;
245 float *handle_x,
float *handle_y, gboolean clockwise)
249 *handle_x = point_x + ctrl_y - point_y;
250 *handle_y = point_y + point_x - ctrl_x;
254 *handle_x = point_x - ctrl_y + point_y;
255 *handle_y = point_y - point_x + ctrl_x;
274 int node_index,
float *handle_x,
float *handle_y)
276 if(
IS_NULL_PTR(gui_points) || node_count <= 0 || node_index < 0 || node_index >= node_count)
return FALSE;
278 const int start = node_count * 3;
280 if(max_points <= start)
return FALSE;
282 const float node_x = gui_points->
points[node_index * 6 + 2];
283 const float node_y = gui_points->
points[node_index * 6 + 3];
285 float best_dist2 = FLT_MAX;
288 for(
int i = start;
i < max_points;
i++)
290 const float px = gui_points->
points[
i * 2];
291 const float py = gui_points->
points[
i * 2 + 1];
292 if(isnan(px) || isnan(py))
continue;
294 const float dx = node_x - px;
295 const float dy = node_y - py;
296 const float dist2 = dx * dx + dy * dy;
297 if(dist2 < best_dist2)
304 if(best_idx < 0)
return FALSE;
306 *handle_x = gui_points->
border[best_idx * 2];
307 *handle_y = gui_points->
border[best_idx * 2 + 1];
308 return !(isnan(*handle_x) || isnan(*handle_y));
312 int node_index,
float *handle_x,
float *handle_y)
314 float resampled_x = NAN;
315 float resampled_y = NAN;
318 const float node_x = gui_points->
points[node_index * 6 + 2];
319 const float node_y = gui_points->
points[node_index * 6 + 3];
321 *handle_x = node_x - (resampled_x - node_x);
322 *handle_y = node_y - (resampled_y - node_y);
323 return !(isnan(*handle_x) || isnan(*handle_y));
329 float *ctrl1x,
float *ctrl1y,
330 float *ctrl2x,
float *ctrl2y, gboolean clockwise)
334 *ctrl2x = ptx + pty - fy;
335 *ctrl2y = pty +
fx - ptx;
336 *ctrl1x = ptx - pty + fy;
337 *ctrl1y = pty -
fx + ptx;
341 *ctrl1x = ptx + pty - fy;
342 *ctrl1y = pty +
fx - ptx;
343 *ctrl2x = ptx - pty + fy;
344 *ctrl2y = pty -
fx + ptx;
352 float y4,
float *bezier1_x,
float *bezier1_y,
353 float *bezier2_x,
float *bezier2_y)
355 *bezier1_x = (-x1 + 6 * x2 + x3) / 6;
356 *bezier1_y = (-y1 + 6 * y2 + y3) / 6;
357 *bezier2_x = (x2 + 6 * x3 - x4) / 6;
358 *bezier2_y = (y2 + 6 * y3 - y4) / 6;
375 for(GList *form_points = mask_form->
points; form_points; form_points = g_list_next(form_points))
383 GList *
const prev = g_list_previous(form_points);
384 GList *
const prevprev = prev ? g_list_previous(prev) : NULL;
385 GList *
const next = g_list_next(form_points);
386 GList *
const nextnext = next ? g_list_next(next) : NULL;
395 start_point[0].
node[0] = start_point[1].
node[0] = 2 * point3->
node[0] - point4->
node[0];
396 start_point[0].
node[1] = start_point[1].
node[1] = 2 * point3->
node[1] - point4->
node[1];
397 point1 = &(start_point[0]);
398 point2 = &(start_point[1]);
402 start_point[0].
node[0] = 2 * point2->
node[0] - point3->
node[0];
403 start_point[0].
node[1] = 2 * point2->
node[1] - point3->
node[1];
404 point1 = &(start_point[0]);
409 end_point[0].
node[0] = end_point[1].
node[0] = 2 * point3->
node[0] - point2->
node[0];
410 end_point[0].
node[1] = end_point[1].
node[1] = 2 * point3->
node[1] - point2->
node[1];
411 point4 = &(end_point[0]);
412 point5 = &(end_point[1]);
416 end_point[0].
node[0] = 2 * point4->
node[0] - point3->
node[0];
417 end_point[0].
node[1] = 2 * point4->
node[1] - point3->
node[1];
418 point5 = &(end_point[0]);
422 float bx1 = 0.0f, by1 = 0.0f, bx2 = 0.0f, by2 = 0.0f;
425 &bx1, &by1, &bx2, &by2);
426 if(point2->
ctrl2[0] == -1.0) point2->
ctrl2[0] = bx1;
427 if(point2->
ctrl2[1] == -1.0) point2->
ctrl2[1] = by1;
428 point3->
ctrl1[0] = bx2;
429 point3->
ctrl1[1] = by2;
432 &bx1, &by1, &bx2, &by2);
433 if(point4->
ctrl1[0] == -1.0) point4->
ctrl1[0] = bx2;
434 if(point4->
ctrl1[1] == -1.0) point4->
ctrl1[1] = by2;
435 point3->
ctrl2[0] = bx1;
436 point3->
ctrl2[1] = by1;
449 float a1 = atan2f(bmin[1] - cmax[1], bmin[0] - cmax[0]);
450 float a2 = atan2f(bmax[1] - cmax[1], bmax[0] - cmax[0]);
455 if(a2 < a1 && clockwise)
459 if(a2 > a1 && !clockwise)
465 float r1 = sqrtf((bmin[1] - cmax[1]) * (bmin[1] - cmax[1]) + (bmin[0] - cmax[0]) * (bmin[0] - cmax[0]));
466 float r2 = sqrtf((bmax[1] - cmax[1]) * (bmax[1] - cmax[1]) + (bmax[0] - cmax[0]) * (bmax[0] - cmax[0]));
469 const int l = fabsf(a2 - a1) * fmaxf(r1, r2);
473 const float incra = (a2 - a1) / l;
474 const float incrr = (r2 - r1) / l;
477 const float cos_incra = cosf(incra);
478 const float sin_incra = sinf(incra);
479 float rr = r1 + incrr;
480 float cos_aa = cosf(a1 + incra);
481 float sin_aa = sinf(a1 + incra);
490 for(
int i = 1;
i < l;
i++)
492 *dpoints_ptr++ = cmax[0];
493 *dpoints_ptr++ = cmax[1];
494 *dborder_ptr++ = cmax[0] + rr * cos_aa;
495 *dborder_ptr++ = cmax[1] + rr * sin_aa;
498 const float new_cos = cos_aa * cos_incra - sin_aa * sin_incra;
499 const float new_sin = sin_aa * cos_incra + cos_aa * sin_incra;
514 const float a1 = fmodf(atan2f(bmin[1] - cmax[1], bmin[0] - cmax[0]) + 2.0f *
M_PI, 2.0f *
M_PI);
515 const float a2 = fmodf(atan2f(bmax[1] - cmax[1], bmax[0] - cmax[0]) + 2.0f *
M_PI, 2.0f *
M_PI);
520 const float r1 = sqrtf((bmin[1] - cmax[1]) * (bmin[1] - cmax[1]) + (bmin[0] - cmax[0]) * (bmin[0] - cmax[0]));
521 const float r2 = sqrtf((bmax[1] - cmax[1]) * (bmax[1] - cmax[1]) + (bmax[0] - cmax[0]) * (bmax[0] - cmax[0]));
524 float delta = a2 - a1;
528 const int l = fabsf(
delta) * fmaxf(r1, r2);
532 const float incra =
delta / l;
533 const float incrr = (r2 - r1) / l;
536 const float cos_incra = cosf(incra);
537 const float sin_incra = sinf(incra);
538 float rr = r1 + incrr;
539 float cos_aa = cosf(a1 + incra);
540 float sin_aa = sinf(a1 + incra);
549 for(
int i = 1;
i < l;
i++)
551 *dpoints_ptr++ = cmax[0];
552 *dpoints_ptr++ = cmax[1];
553 *dborder_ptr++ = cmax[0] + rr * cos_aa;
554 *dborder_ptr++ = cmax[1] + rr * sin_aa;
557 const float new_cos = cos_aa * cos_incra - sin_aa * sin_incra;
558 const float new_sin = sin_aa * cos_incra + cos_aa * sin_incra;
573 const float a1 = atan2f(bmin[1] - cmax[1], bmin[0] - cmax[0]);
576 const float rad = sqrtf((bmin[1] - cmax[1]) * (bmin[1] - cmax[1]) + (bmin[0] - cmax[0]) * (bmin[0] - cmax[0]));
579 const int l = 2.0f *
M_PI * rad;
583 const float incra = 2.0f *
M_PI / l;
584 float aa = a1 + incra;
592 for(
int i = 0;
i < l;
i++)
594 *dpoints_ptr++ = cmax[0];
595 *dpoints_ptr++ = cmax[1];
596 *dborder_ptr++ = cmax[0] + rad * cosf(aa);
597 *dborder_ptr++ = cmax[1] + rad * sinf(aa);
605 return abs((
int)
min[0] - (
int)
max[0]) < pixel_threshold &&
606 abs((
int)
min[1] - (
int)
max[1]) < pixel_threshold;
610 const float v0,
const float v1)
614 while(payload_pos < target_pos)
624 float *points_max,
float *border_min,
float *border_max,
float *rpoints,
628 const gboolean withborder = (!
IS_NULL_PTR(dborder));
629 const gboolean withpayload = (!
IS_NULL_PTR(dpayload));
632 if(isnan(points_min[0]))
635 p1[4] + (p2[4] - p1[4]) * tmin * tmin * (3.0 - 2.0 * tmin), points_min,
636 points_min + 1, border_min, border_min + 1);
638 if(isnan(points_max[0]))
641 p1[4] + (p2[4] - p1[4]) * tmax * tmax * (3.0 - 2.0 * tmax), points_max,
642 points_max + 1, border_max, border_max + 1);
646 if((tmax - tmin < 0.0001f)
650 rpoints[0] = points_max[0];
651 rpoints[1] = points_max[1];
656 if(isnan(border_max[0]))
658 border_max[0] = border_min[0];
659 border_max[1] = border_min[1];
661 else if(isnan(border_min[0]))
663 border_min[0] = border_max[0];
664 border_min[1] = border_max[1];
668 if(abs((
int)border_max[0] - (
int)border_min[0]) > 2 || abs((
int)border_max[1] - (
int)border_min[1]) > 2)
673 rborder[0] = border_max[0];
674 rborder[1] = border_max[1];
680 rpayload[0] = p1[5] + tmax * (p2[5] - p1[5]);
681 rpayload[1] = p1[6] + tmax * (p2[6] - p1[6]);
689 double tx = (tmin + tmax) / 2.0;
690 float c[2] = { NAN, NAN }, b[2] = { NAN, NAN };
691 float rc[2], rb[2], rp[2];
692 _brush_points_recurs(p1, p2, tmin, tx, points_min, c, border_min, b, rc, rb, rp, dpoints, dborder, dpayload,
694 _brush_points_recurs(p1, p2, tx, tmax, rc, points_max, rb, border_max, rpoints, rborder, rpayload, dpoints,
695 dborder, dpayload, pixel_threshold);
703 const int o =
n % (2 * nb);
704 const int p = o % nb;
706 return (o <=
p) ? o : o - 2 *
p - 1;
715 const double iop_order,
const int transform_direction,
717 float **border_buffer,
int *border_count,
float **payload_buffer,
718 int *payload_count,
int use_source)
720 *point_buffer = NULL;
722 if(border_buffer) *border_buffer = NULL;
724 if(payload_buffer) *payload_buffer = NULL;
725 if(!
IS_NULL_PTR(payload_buffer)) *payload_count = 0;
731 const float iwd = pipe->
iwidth;
732 const float iht = pipe->
iheight;
763 float dx = 0.0f, dy = 0.0f;
768 dx = (pt->
node[0] - mask_form->
source[0]) * iwd;
769 dy = (pt->
node[1] - mask_form->
source[1]) * iht;
772 const guint node_count = g_list_length(mask_form->
points);
783 guint node_index = 0;
784 for(GList *form_points = mask_form->
points; form_points; form_points = g_list_next(form_points))
791 buf[0] = pt->
ctrl1[0] * iwd - dx;
792 buf[1] = pt->
ctrl1[1] * iht - dy;
793 buf[2] = pt->
node[0] * iwd - dx;
794 buf[3] = pt->
node[1] * iht - dy;
795 buf[4] = pt->
ctrl2[0] * iwd - dx;
796 buf[5] = pt->
ctrl2[1] * iht - dy;
823 for(
int n = 0;
n < 2 * node_count;
n++)
825 float p1[7], p2[7], p3[7], p4[7];
829 const gboolean allow_border_gap_rounding =
FALSE;
836 const float pa[7] = { point1->
node[0] * iwd - dx, point1->
node[1] * iht - dy, point1->
ctrl2[0] * iwd - dx,
839 const float pb[7] = { point2->
node[0] * iwd - dx, point2->
node[1] * iht - dy, point2->
ctrl1[0] * iwd - dx,
842 const float pc[7] = { point2->
node[0] * iwd - dx, point2->
node[1] * iht - dy, point2->
ctrl2[0] * iwd - dx,
845 const float pd[7] = { point3->
node[0] * iwd - dx, point3->
node[1] * iht - dy, point3->
ctrl1[0] * iwd - dx,
848 memcpy(p1, pa,
sizeof(
float) * 7);
849 memcpy(p2, pb,
sizeof(
float) * 7);
850 memcpy(p3, pc,
sizeof(
float) * 7);
851 memcpy(p4, pd,
sizeof(
float) * 7);
855 const float pa[7] = { point1->
node[0] * iwd - dx, point1->
node[1] * iht - dy, point1->
ctrl1[0] * iwd - dx,
858 const float pb[7] = { point2->
node[0] * iwd - dx, point2->
node[1] * iht - dy, point2->
ctrl2[0] * iwd - dx,
861 const float pc[7] = { point2->
node[0] * iwd - dx, point2->
node[1] * iht - dy, point2->
ctrl1[0] * iwd - dx,
864 const float pd[7] = { point3->
node[0] * iwd - dx, point3->
node[1] * iht - dy, point3->
ctrl2[0] * iwd - dx,
867 memcpy(p1, pa,
sizeof(
float) * 7);
868 memcpy(p2, pb,
sizeof(
float) * 7);
869 memcpy(p3, pc,
sizeof(
float) * 7);
870 memcpy(p4, pd,
sizeof(
float) * 7);
874 if((fabsf(p1[5] - p2[5]) > 0.05f || fabsf(p1[6] - p2[6]) > 0.05f)
875 || (start_stamp &&
n == 2 * node_count - 1))
898 if(fabsf(p1[4] - p2[4]) > 0.0001f &&
n > 0)
904 float bmax[2] = { 2 * cmax[0] - bmin[0], 2 * cmax[1] - bmin[1] };
905 if(allow_border_gap_rounding)
922 float bmax[2] = { 2 * cmax[0] - bmin[0], 2 * cmax[1] - bmin[1] };
936 float rc[2], rb[2], rp[2];
937 float bmin[2] = { NAN, NAN };
938 float bmax[2] = { NAN, NAN };
939 float cmin[2] = { NAN, NAN };
940 float cmax[2] = { NAN, NAN };
942 _brush_points_recurs(p1, p2, 0.0, 1.0, cmin, cmax, bmin, bmax, rc, rb, rp, dpoints, dborder, dpayload,
975 _brush_border_get_XY(p3[0], p3[1], p3[2], p3[3], p4[2], p4[3], p4[0], p4[1], 0, p3[4], cmin, cmin + 1,
979 _brush_border_get_XY(p3[0], p3[1], p3[2], p3[3], p4[2], p4[3], p4[0], p4[1], 0.0001, p3[4], cmin,
980 cmin + 1, bmax, bmax + 1);
982 if(bmax[0] - rb[0] > 1 || bmax[0] - rb[0] < -1 || bmax[1] - rb[1] > 1 || bmax[1] - rb[1] < -1)
985 if(allow_border_gap_rounding)
1031 *point_buffer, *point_count))
1035 float pts[2] = { mask_form->
source[0], mask_form->
source[1] };
1040 dx = pts[0] - (*point_buffer)[2];
1041 dy = pts[1] - (*point_buffer)[3];
1043 for(
int i = 0;
i < *point_count;
i++)
1045 (*point_buffer)[
i * 2] += dx;
1046 (*point_buffer)[
i * 2 + 1] += dy;
1052 *point_buffer, *point_count))
1063 *point_buffer, *point_count))
1067 *border_buffer, *border_count))
1080 *point_buffer = NULL;
1085 *border_buffer = NULL;
1091 *payload_buffer = NULL;
1102 int corner_count,
int *inside,
int *inside_border,
1103 int *near,
int *inside_source,
float *distance)
1112 *distance = FLT_MAX;
1118 float min_dist = FLT_MAX;
1120 const float radius2 = radius * radius;
1130 const float dx = -gui_points->
points[2] + gui_points->
source[2];
1131 const float dy = -gui_points->
points[3] + gui_points->
source[3];
1133 int current_seg = 1;
1137 if(gui_points->
points[
i * 2 + 1] == gui_points->
points[current_seg * 6 + 3]
1138 && gui_points->
points[
i * 2] == gui_points->
points[current_seg * 6 + 2])
1140 current_seg = (current_seg + 1) % corner_count;
1143 const float yy = gui_points->
points[
i * 2 + 1] + dy;
1144 const float xx = gui_points->
points[
i * 2] + dx;
1146 const float sdx = point_x - xx;
1147 const float sdy = point_y - yy;
1148 const float dd = (sdx * sdx) + (sdy * sdy);
1153 if(dd < radius2 && *inside == 0)
1155 if(current_seg == 0)
1156 *inside_source = corner_count - 1;
1158 *inside_source = current_seg - 1;
1174 const int start = corner_count * 3;
1181 const int idx =
i * 2;
1182 const float xx =
border[idx];
1183 const float yy =
border[idx + 1];
1185 const float dx = point_x - xx;
1186 const float dy = point_y - yy;
1187 if(dx * dx + dy * dy < radius2) nearest = idx;
1189 if(((point_y <= yy && point_y > last_y) || (point_y >= yy && point_y < last_y)) && (xx > point_x))
1195 *inside = *inside_border = (nearest != -1 || (crossings & 1));
1201 int current_seg = 1;
1205 if(gui_points->
points[
i * 2 + 1] == gui_points->
points[current_seg * 6 + 3]
1206 && gui_points->
points[
i * 2] == gui_points->
points[current_seg * 6 + 2])
1208 current_seg = (current_seg + 1) % corner_count;
1211 const float yy = gui_points->
points[
i * 2 + 1];
1212 const float xx = gui_points->
points[
i * 2];
1214 const float dx = point_x - xx;
1215 const float dy = point_y - yy;
1216 const float dd = (dx * dx) + (dy * dy);
1221 if(current_seg > 0 && dd < radius2)
1223 *near = current_seg - 1;
1229 *distance = min_dist;
1233 float **point_buffer,
int *point_count,
1234 float **border_buffer,
int *border_count,
1238 const double ioporder = (module) ? module->
iop_order : 0.0f;
1240 develop->
virtual_pipe, point_buffer, point_count, border_buffer,
1241 border_count, NULL, NULL, use_source);
1251 GList *first_node_entry = g_list_nth(mask_form->
points, segment_index);
1261 float best_t = 0.0f;
1262 float best_dist2 = FLT_MAX;
1264 for(
int sample_index = 0; sample_index <= 100; sample_index++)
1266 const float t = sample_index / 100.0f;
1267 float sample_x = 0.0f;
1268 float sample_y = 0.0f;
1270 point2->
node[0], point2->
node[1], point3->
node[0], point3->
node[1],
t, &sample_x, &sample_y);
1272 const float dist2 = (point_x - sample_x) * (point_x - sample_x)
1273 + (point_y - sample_y) * (point_y - sample_y);
1274 if(dist2 < best_dist2)
1290 float *handle_x,
float *handle_y,
void *user_data)
1301 float *handle_x,
float *handle_y,
void *user_data)
1304 gui_points->
points[node_index * 6 + 4], gui_points->
points[node_index * 6 + 5],
1305 handle_x, handle_y,
TRUE);
1313 int *inside_border,
int *near,
int *inside_source,
float *
dist,
void *user_data)
1315 _brush_get_distance(pointer_x, pointer_y, cursor_radius, mask_gui, form_index, node_count,
1316 inside, inside_border, near, inside_source,
dist);
1332 _(
"hardness: %3.2f%%"), 100.0f);
1343 _(
"size: %3.2f%%"), 2.f * 100.f);
1353 increment, flow, _(
"opacity: %3.2f%%"), 100.f);
1366 if(
size <= 0.0f)
return NAN;
1371 float hardness_sum = 0.0f;
1372 int hardness_count = 0;
1374 for(
const GList *point_node = mask_form->
points; point_node; point_node = g_list_next(point_node))
1378 hardness_sum +=
node->hardness;
1382 return hardness_count > 0 ? hardness_sum / (float)hardness_count : NAN;
1393 const int points_count = g_list_length(mask_form->
points);
1394 if(points_count <= 0)
return FALSE;
1400 for(
const GList *l = mask_form->
points; l; l = g_list_next(l))
1404 point_buffer[2 *
i] =
point->node[0];
1405 point_buffer[2 *
i + 1] =
point->node[1];
1434 if(!
_change_size(mask_form, 0, mask_gui, module, index,
value, increment, flow))
return NAN;
1450 const float scale_amount = 1.f / powf(amount, (
float)flow);
1451 const float offset_amount = -amount * (float)flow;
1452 float result_amount = 0.0f;
1453 for(GList *node_entry = mask_form->
points; node_entry; node_entry = g_list_next(node_entry), node_index++)
1458 const float current_hardness =
node->hardness;
1481 for(GList *node_entry = mask_form->
points; node_entry; node_entry = g_list_next(node_entry), node_index++)
1487 if(amount > 1.0f && (
node->border[0] > 1.0f ||
node->border[1] > 1.0f))
1493 const float scale_amount = amount;
1494 const float offset_amount = amount;
1496 for(GList *node_entry = mask_form->
points; node_entry; node_entry = g_list_next(node_entry), node_index++)
1503 scale_amount, offset_amount, increment);
1505 scale_amount, offset_amount, increment);
1533 return _init_opacity(mask_form, parentid, mask_gui, scroll_up ? +0.02f : -0.02f,
1550 return _change_hardness(mask_form, parentid, mask_gui, module, index, scroll_up ? -0.01f : 0.01f,
1553 return _change_size(mask_form, parentid, mask_gui, module, index, scroll_up ? 1.02f : 0.98f,
1566 if(!strcmp(psens,
"hardness (absolute)"))
1568 else if(!strcmp(psens,
"hardness (relative)"))
1570 else if(!strcmp(psens,
"opacity (absolute)"))
1572 else if(!strcmp(psens,
"opacity (relative)"))
1574 else if(!strcmp(psens,
"brush size (relative)"))
1583 const guint node_count = g_list_length(mask_form->
points);
1585 if(selected_segment < 0 || selected_segment >= (
int)node_count)
return;
1599 mask_form, selected_segment);
1601 GList *pt = g_list_nth(mask_form->
points, selected_segment);
1620 mask_form->
points = g_list_insert(mask_form->
points, new_node, selected_segment + 1);
1639 for(GList *node_entry = mask_form->
points; node_entry; node_entry = g_list_next(node_entry))
1644 double pressure,
int which,
int type, uint32_t
state,
1649 if(
type == GDK_2BUTTON_PRESS ||
type == GDK_3BUTTON_PRESS)
return 1;
1652 const float masks_density = 1.0f;
1700 const guint node_count = g_list_length(mask_form->
points);
1735 float handle_x, handle_y;
1739 &handle_x, &handle_y,
TRUE);
1741 mask_gui->
delta[0] = handle_x - mask_gui->
pos[0];
1742 mask_gui->
delta[1] = handle_y - mask_gui->
pos[1];
1749 float handle_x = NAN, handle_y = NAN;
1751 &handle_x, &handle_y))
1753 mask_gui->
delta[0] = handle_x - mask_gui->
pos[0];
1754 mask_gui->
delta[1] = handle_y - mask_gui->
pos[1];
1798 if(!strcmp(smoothing,
"low"))
1800 else if(!strcmp(smoothing,
"medium"))
1802 else if(!strcmp(smoothing,
"high"))
1811 float *payload = payload_buffer + 4 *
i;
1812 float pressure = payload[3];
1827 payload[2] =
MAX(0.05f, pressure);
1830 payload[2] =
MAX(0.05f, payload[2] * pressure);
1856 if(mask_gui->
creation && which == 1)
1899 guipoints_payload, epsilon2);
1917 int group_index = 0;
1918 int selected_index = -1;
1919 for(GList *group_entry = grp->
points; group_entry; group_entry = g_list_next(group_entry))
1924 selected_index = group_index;
1929 if(selected_index < 0)
return 1;
2007 const guint node_count = g_list_length(mask_form->
points);
2015 float delta_x = 0.0f;
2016 float delta_y = 0.0f;
2034 if(!segment_node || !segment_node_next)
return 0;
2036 float delta_x = 0.0f;
2037 float delta_y = 0.0f;
2056 float cursor_pos[2];
2060 float control_points[4];
2063 cursor_pos[1], &control_points[0], &control_points[1], &control_points[2],
2064 &control_points[3],
TRUE);
2084 float handle_x = NAN, handle_y = NAN;
2088 const float node_px = gui_points->
points[node_index * 6 + 2];
2089 const float node_py = gui_points->
points[node_index * 6 + 3];
2092 float cursor_pos[2];
2093 const float node_pos_gui[2] = { node_px, node_py };
2094 const float handle_pos[2] = { handle_x, handle_y };
2112 float delta_x = 0.0f;
2113 float delta_y = 0.0f;
2121 mask_form->
source[0] = raw_pos[0];
2122 mask_form->
source[1] = raw_pos[1];
2132static void _brush_draw_shape(cairo_t *cr,
const float *points,
const int points_count,
const int node_nb,
const gboolean
border,
const gboolean source)
2137 for(
int i = node_nb * 3 +
border;
i < points_count;
i++)
2139 if(!isnan(points[
i * 2]) && !isnan(points[
i * 2 + 1]))
2149 cairo_move_to(cr, points[start_idx * 2], points[start_idx * 2 + 1]);
2152 const int end_idx =
border ? points_count : 0.5 * points_count;
2154 for(
int i = start_idx + 1;
i < end_idx;
i++)
2156 if(!isnan(points[
i * 2]) && !isnan(points[
i * 2 + 1]))
2157 cairo_line_to(cr, points[
i * 2], points[
i * 2 + 1]);
2164 float total_len = 0.0f;
2165 for(
int i = first_pt;
i < last_pt;
i++)
2167 const int i0 =
i * 2;
2168 const int i1 = (
i + 1) * 2;
2169 const float x0 = line[i0];
2170 const float y0 = line[i0 + 1];
2171 const float x1 = line[i1];
2172 const float y1 = line[i1 + 1];
2173 if(isnan(x0) || isnan(y0) || isnan(x1) || isnan(y1))
continue;
2174 const float dx = x1 - x0;
2175 const float dy = y1 - y0;
2176 const float len = dx * dx + dy * dy;
2177 if(len > 1e-12f) total_len += sqrtf(len);
2183 const float target_len,
float *
x,
float *y)
2186 if(last_pt <= first_pt)
return FALSE;
2189 gboolean has_fallback =
FALSE;
2190 float fallback_x = NAN, fallback_y = NAN;
2192 for(
int i = first_pt;
i < last_pt;
i++)
2194 const int i0 =
i * 2;
2195 const int i1 = (
i + 1) * 2;
2196 const float x0 = line[i0];
2197 const float y0 = line[i0 + 1];
2198 const float x1 = line[i1];
2199 const float y1 = line[i1 + 1];
2200 if(isnan(x0) || isnan(y0) || isnan(x1) || isnan(y1))
continue;
2202 const float dx = x1 - x0;
2203 const float dy = y1 - y0;
2204 const float len = sqrtf(dx * dx + dy * dy);
2205 if(len <= 1e-6f)
continue;
2207 has_fallback =
TRUE;
2211 if(acc + len >= target_len)
2213 const float t = (target_len - acc) / len;
2221 if(!has_fallback || isnan(fallback_x) || isnan(fallback_y))
return FALSE;
2228 float *mx,
float *my)
2232 if(total_len <= 1e-6f)
return FALSE;
2234 const float half_len = 0.5f * total_len;
2245 const int line_offset_pt = node_count * 3;
2246 const int points_line_end = gui_points->
points_count / 2;
2247 const int source_line_end = gui_points->
source_count / 2;
2248 const int line_end =
MIN(points_line_end, source_line_end);
2249 const int line_count = line_end - line_offset_pt;
2250 if(line_count < 2)
return FALSE;
2252 const float *
const points_line = gui_points->
points + 2 * line_offset_pt;
2253 const float *
const source_line = gui_points->
source + 2 * line_offset_pt;
2254 const int first_pt = 0;
2255 const int last_pt = line_count - 1;
2274 const float min_iwd_iht =
MIN(iwd, iht);
2287 const float radius1 = masks_border * masks_hardness * min_iwd_iht;
2288 const float radius2 = masks_border * min_iwd_iht;
2290 float xpos = mask_gui->
pos[0];
2291 float ypos = mask_gui->
pos[1];
2292 if((xpos == -1.0f && ypos == -1.0f))
2303 cairo_arc(cr, xpos, ypos, radius1, 0, 2.0 *
M_PI);
2304 cairo_fill_preserve(cr);
2305 cairo_set_source_rgba(cr, .8, .8, .8, .8);
2308 cairo_arc(cr, xpos, ypos, radius2, 0, 2.0 *
M_PI);
2318 float masks_border = 0.0f, masks_hardness = 0.0f, masks_density = 0.0f;
2319 float radius = 0.0f, oldradius = 0.0f, opacity = 0.0f, oldopacity = 0.0f, pressure = 0.0f;
2326 cairo_set_line_join(cr, CAIRO_LINE_JOIN_ROUND);
2327 cairo_set_line_cap(cr, CAIRO_LINE_CAP_ROUND);
2328 masks_border = guipoints_payload[0];
2329 masks_hardness = guipoints_payload[1];
2330 masks_density = guipoints_payload[2];
2331 pressure = guipoints_payload[3];
2342 masks_density =
MAX(0.05f, pressure);
2345 masks_density =
MAX(0.05f, masks_density * pressure);
2356 radius = oldradius = masks_border * masks_hardness * min_iwd_iht;
2357 opacity = oldopacity = masks_density;
2362 cairo_move_to(cr, guipoints[0], guipoints[1]);
2365 cairo_line_to(cr, guipoints[
i * 2], guipoints[
i * 2 + 1]);
2367 masks_border = guipoints_payload[
i * 4];
2368 masks_hardness = guipoints_payload[
i * 4 + 1];
2369 masks_density = guipoints_payload[
i * 4 + 2];
2370 pressure = guipoints_payload[
i * 4 + 3];
2381 masks_density =
MAX(0.05f, pressure);
2384 masks_density =
MAX(0.05f, masks_density * pressure);
2395 radius = masks_border * masks_hardness * min_iwd_iht;
2396 opacity = masks_density;
2398 if(radius != oldradius || opacity != oldopacity)
2405 oldopacity = opacity;
2406 cairo_move_to(cr, guipoints[
i * 2], guipoints[
i * 2 + 1]);
2409 if(!stroked) cairo_stroke(cr);
2416 radius, 0, 2.0 *
M_PI);
2417 cairo_fill_preserve(cr);
2418 cairo_set_source_rgba(cr, .8, .8, .8, .8);
2423 guipoints[2 * (mask_gui->
guipoints_count - 1) + 1], masks_border * min_iwd_iht, 0,
2432 guipoints[
i * 2], guipoints[
i * 2 + 1],
TRUE);
2451 if(gui_points->
points_count <= node_count * 3 + 2)
return;
2454 if(node_count > 0 && gui_points->
points_count > node_count * 3 + 6)
2457 node_count, zoom_scale);
2464 int current_seg = 0;
2467 cairo_move_to(cr, gui_points->
points[node_count * 6], gui_points->
points[node_count * 6 + 1]);
2471 for(
int i = node_count * 3;
i < end_idx;
i++)
2473 const double x = gui_points->
points[
i * 2];
2474 const double y = gui_points->
points[
i * 2 + 1];
2475 cairo_line_to(cr,
x, y);
2477 int seg_idx = seg1 * 6;
2478 if((seg_idx + 3) < total_points)
2480 const double segment_x = gui_points->
points[seg_idx + 2];
2481 const double segment_y = gui_points->
points[seg_idx + 3];
2484 if(
x == segment_x && y == segment_y)
2486 const gboolean seg_selected = (mask_gui->
group_selected == index)
2487 && (selected_segment == current_seg);
2488 const gboolean all_selected = (mask_gui->
group_selected == index)
2495 if(mask_gui->
creation && current_seg == node_count - 2)
2499 seg1 = (seg1 + 1) % node_count;
2514 CAIRO_LINE_CAP_ROUND);
2518 if(mask_gui->
node_selected && selected_node >= 0 && selected_node < node_count
2521 const int n = selected_node;
2524 gui_points->
points[
n * 6 + 4], gui_points->
points[
n * 6 + 5], &handle[0], &handle[1],
2526 const float pt[2] = { gui_points->
points[
n * 6 + 2], gui_points->
points[
n * 6 + 3] };
2532 for(
int k = 0;
k < node_count;
k++)
2535 const float x = gui_points->
points[
k * 6 + 2];
2536 const float y = gui_points->
points[
k * 6 + 3];
2538 const gboolean action = (
k == selected_node);
2540 dt_draw_node(cr, corner, action, selected, zoom_scale,
x, y);
2544 if(mask_gui->
node_selected && selected_node >= 0 && selected_node < node_count)
2546 const int edited = selected_node;
2547 const gboolean selected = (mask_gui->
node_hovered == edited
2548 || selected_handle_border == edited
2550 float handle[2] = {NAN, NAN};
2568 for(
int k = 0;
k < node_count;
k++)
2572 || (mask_gui->
creation &&
k == node_count - 1)))
2574 const int node_index =
k * 6 + 2;
2575 const float proj[2] = { gui_points->
source[node_index], gui_points->
source[node_index + 1] };
2592 const int node_count,
const int total_points,
float *x_min,
float *x_max,
2593 float *y_min,
float *y_max)
2596 float min_x = FLT_MAX, max_x = FLT_MIN, min_y = FLT_MAX, max_y = FLT_MIN;
2599 for(
int point_index = node_count * 3; point_index < total_points; point_index++)
2602 const float border_x =
border[point_index * 2];
2603 const float border_y =
border[point_index * 2 + 1];
2604 min_x =
MIN(border_x, min_x);
2605 max_x =
MAX(border_x, max_x);
2606 min_y =
MIN(border_y, min_y);
2607 max_y =
MAX(border_y, max_y);
2609 const float brush_x = points[point_index * 2];
2610 const float brush_y = points[point_index * 2 + 1];
2611 min_x =
MIN(brush_x, min_x);
2612 max_x =
MAX(brush_x, max_x);
2613 min_y =
MIN(brush_y, min_y);
2614 max_y =
MAX(brush_y, max_y);
2628 const int total_points,
int *
width,
int *
height,
int *offset_x,
int *offset_y)
2630 float min_x = FLT_MAX, max_x = FLT_MIN, min_y = FLT_MAX, max_y = FLT_MIN;
2632 *
height = max_y - min_y + 4;
2633 *
width = max_x - min_x + 4;
2634 *offset_x = min_x - 2;
2635 *offset_y = min_y - 2;
2650 float *points = NULL, *
border = NULL;
2651 int points_count, border_count;
2653 &points, &points_count, &
border, &border_count, NULL, NULL, include_source) != 0)
2660 const guint node_count = g_list_length(mask_form->
points);
2691static void _brush_falloff(
float *
const restrict buffer,
int segment_start[2],
int segment_end[2],
2692 int offset_x,
int offset_y,
int buffer_width,
float hardness,
float density)
2695 const int segment_length = sqrt((segment_end[0] - segment_start[0]) * (segment_end[0] - segment_start[0])
2696 + (segment_end[1] - segment_start[1]) * (segment_end[1] - segment_start[1]))
2698 const int solid_length = (int)segment_length *
hardness;
2699 const int soft_length = segment_length - solid_length;
2701 const float segment_dx = segment_end[0] - segment_start[0];
2702 const float segment_dy = segment_end[1] - segment_start[1];
2703 const float inv_length = 1.0f / (float)segment_length;
2704 const float inv_soft = (soft_length > 0) ? 1.0f / (
float)soft_length : 0.0f;
2706 for(
int step = 0; step < segment_length; step++)
2709 const int x = (int)((
float)step * segment_dx * inv_length) + segment_start[0] - offset_x;
2710 const int y = (int)((
float)step * segment_dy * inv_length) + segment_start[1] - offset_y;
2711 const float opacity =
density * ((step <= solid_length) ? 1.0f : 1.0f - (float)(step - solid_length) * inv_soft);
2712 const int buffer_index = y * buffer_width +
x;
2713 buffer[buffer_index] =
MAX(buffer[buffer_index], opacity);
2715 buffer[buffer_index - 1] =
MAX(buffer[buffer_index - 1], opacity);
2717 buffer[buffer_index - buffer_width] =
MAX(buffer[buffer_index - buffer_width], opacity);
2729 float **buffer,
int *
width,
int *
height,
int *offset_x,
int *offset_y)
2732 double timer_start = 0.0;
2733 double timer_step_start = 0.0;
2737 float *points = NULL, *
border = NULL, *payload = NULL;
2738 int points_count, border_count, payload_count;
2740 &points, &points_count,
2741 &
border, &border_count, &payload, &payload_count, 0) != 0)
2756 const guint node_count = g_list_length(mask_form->
points);
2758 || border_count <= (
int)(node_count * 3))
2769 const int sparse_step = use_sparse ? 4 : 1;
2777 const size_t buffer_size = (size_t)(*
width) * (*height);
2787 memset(*buffer, 0,
sizeof(
float) * buffer_size);
2790 int segment_start[2], segment_end[2];
2791 int prev_start[2] = { 0, 0 };
2792 int prev_end[2] = { 0, 0 };
2793 float prev_payload[2] = { 0.0f, 0.0f };
2794 gboolean have_prev =
FALSE;
2797 for(
int border_index = node_count * 3; border_index < border_count; border_index++)
2799 segment_start[0] = points[border_index * 2];
2800 segment_start[1] = points[border_index * 2 + 1];
2801 segment_end[0] =
border[border_index * 2];
2802 segment_end[1] =
border[border_index * 2 + 1];
2804 if(use_sparse && have_prev
2805 && (prev_start[0] != segment_start[0] || prev_start[1] != segment_start[1]
2806 || prev_end[0] != segment_end[0] || prev_end[1] != segment_end[1]))
2809 for(
int step = 1; step < sparse_step; step++)
2811 const float t = (float)step / (
float)sparse_step;
2812 int interp_start[2] = { (int)floorf(prev_start[0] +
t * (segment_start[0] - prev_start[0]) + 0.5f),
2813 (
int)floorf(prev_start[1] +
t * (segment_start[1] - prev_start[1]) + 0.5f) };
2814 int interp_end[2] = { (int)floorf(prev_end[0] +
t * (segment_end[0] - prev_end[0]) + 0.5f),
2815 (
int)floorf(prev_end[1] +
t * (segment_end[1] - prev_end[1]) + 0.5f) };
2816 const float hard = prev_payload[0] +
t * (payload[border_index * 2] - prev_payload[0]);
2817 const float dens = prev_payload[1] +
t * (payload[border_index * 2 + 1] - prev_payload[1]);
2818 _brush_falloff(*buffer, interp_start, interp_end, *offset_x, *offset_y, *
width, hard, dens);
2823 payload[border_index * 2], payload[border_index * 2 + 1]);
2825 prev_start[0] = segment_start[0];
2826 prev_start[1] = segment_start[1];
2827 prev_end[0] = segment_end[0];
2828 prev_end[1] = segment_end[1];
2829 prev_payload[0] = payload[border_index * 2];
2830 prev_payload[1] = payload[border_index * 2 + 1];
2850 const int segment_length = sqrt((segment_end[0] - segment_start[0]) * (segment_end[0] - segment_start[0])
2851 + (segment_end[1] - segment_start[1]) * (segment_end[1] - segment_start[1]))
2853 const int solid_length =
hardness * segment_length;
2855 const float step_x = (float)(segment_end[0] - segment_start[0]) / (float)segment_length;
2856 const float step_y = (float)(segment_end[1] - segment_start[1]) / (float)segment_length;
2858 const int direction_x = step_x <= 0 ? -1 : 1;
2859 const int direction_y = step_y <= 0 ? -1 : 1;
2860 const int neighbor_offset_x = direction_x;
2861 const int neighbor_offset_y = direction_y * buffer_width;
2863 float cursor_x = segment_start[0];
2864 float cursor_y = segment_start[1];
2867 const float opacity_step =
density / (float)(segment_length - solid_length);
2869 const int start_x = segment_start[0], start_y = segment_start[1];
2870 const int end_x = segment_end[0], end_y = segment_end[1];
2871 if((start_x < 0 && end_x < 0) || (start_x >= buffer_width && end_x >= buffer_width)
2872 || (start_y < 0 && end_y < 0) || (start_y >= buffer_height && end_y >= buffer_height))
2874 const int fully_inside = (start_x >= 0 && start_x < buffer_width && end_x >= 0 && end_x < buffer_width
2875 && start_y >= 0 && start_y < buffer_height && end_y >= 0
2876 && end_y < buffer_height);
2878 for(
int step = 0; step < segment_length; step++)
2880 const int x = cursor_x;
2881 const int y = cursor_y;
2885 if(step > solid_length) opacity -= opacity_step;
2887 if(!fully_inside && (x < 0 || x >= buffer_width || y < 0 || y >= buffer_height))
continue;
2889 float *buf = buffer + (size_t)y * buffer_width +
x;
2891 *buf =
MAX(*buf, opacity);
2892 if(
x + direction_x >= 0 &&
x + direction_x < buffer_width)
2893 buf[neighbor_offset_x] =
MAX(buf[neighbor_offset_x], opacity);
2894 if(y + direction_y >= 0 && y + direction_y < buffer_height)
2895 buf[neighbor_offset_y] =
MAX(buf[neighbor_offset_y], opacity);
2911 double timer_start = 0.0;
2912 double timer_step_start = 0.0;
2915 const int roi_offset_x = roi->
x;
2916 const int roi_offset_y = roi->
y;
2917 const int roi_width = roi->
width;
2918 const int roi_height = roi->
height;
2919 const float roi_scale = roi->
scale;
2922 const int sparse_step = use_sparse ? 4 : 1;
2925 float *points = NULL, *
border = NULL, *payload = NULL;
2927 int points_count, border_count, payload_count;
2930 &points, &points_count, &
border, &border_count, &payload, &payload_count, 0) != 0)
2945 const guint node_count = g_list_length(mask_form->
points);
2947 || border_count <= (
int)(node_count * 3))
2957 for(
int border_index = node_count * 3; border_index < border_count; border_index++)
2959 const float border_x =
border[2 * border_index];
2960 const float border_y =
border[2 * border_index + 1];
2961 border[2 * border_index] = border_x * roi_scale - roi_offset_x;
2962 border[2 * border_index + 1] = border_y * roi_scale - roi_offset_y;
2966 for(
int point_index = node_count * 3; point_index < points_count; point_index++)
2968 const float point_x = points[2 * point_index];
2969 const float point_y = points[2 * point_index + 1];
2970 points[2 * point_index] = point_x * roi_scale - roi_offset_x;
2971 points[2 * point_index + 1] = point_y * roi_scale - roi_offset_y;
2975 float min_x = 0.0f, max_x = 0.0f, min_y = 0.0f, max_y = 0.0f;
2986 if(max_x < 0 || max_y < 0 || min_x >= roi_width || min_y >= roi_height)
2999 for(
int border_index = node_count * 3; border_index < border_count; border_index++)
3001 const int segment_start[] = { points[border_index * 2], points[border_index * 2 + 1] };
3002 const int segment_end[] = {
border[border_index * 2],
border[border_index * 2 + 1] };
3004 if(
MAX(segment_start[0], segment_end[0]) < 0 ||
MIN(segment_start[0], segment_end[0]) >= roi_width
3005 ||
MAX(segment_start[1], segment_end[1]) < 0
3006 ||
MIN(segment_start[1], segment_end[1]) >= roi_height)
3009 _brush_falloff_roi(buffer, segment_start, segment_end, roi_width, roi_height, payload[border_index * 2],
3010 payload[border_index * 2 + 1]);
3015 int prev_start[2] = { 0, 0 };
3016 int prev_end[2] = { 0, 0 };
3017 float prev_payload[2] = { 0.0f, 0.0f };
3018 gboolean have_prev =
FALSE;
3021 for(
int border_index = node_count * 3; border_index < border_count; border_index++)
3023 int segment_start[2] = { points[border_index * 2], points[border_index * 2 + 1] };
3024 int segment_end[2] = {
border[border_index * 2],
border[border_index * 2 + 1] };
3026 if(use_sparse && have_prev
3027 && (prev_start[0] != segment_start[0] || prev_start[1] != segment_start[1]
3028 || prev_end[0] != segment_end[0] || prev_end[1] != segment_end[1]))
3030 for(
int step = 1; step < sparse_step; step++)
3032 const float t = (float)step / (
float)sparse_step;
3033 int interp_start[2] = { (int)floorf(prev_start[0] +
t * (segment_start[0] - prev_start[0]) + 0.5f),
3034 (
int)floorf(prev_start[1] +
t * (segment_start[1] - prev_start[1]) + 0.5f) };
3035 int interp_end[2] = { (int)floorf(prev_end[0] +
t * (segment_end[0] - prev_end[0]) + 0.5f),
3036 (
int)floorf(prev_end[1] +
t * (segment_end[1] - prev_end[1]) + 0.5f) };
3037 if(!(
MAX(interp_start[0], interp_end[0]) < 0 ||
MIN(interp_start[0], interp_end[0]) >= roi_width
3038 ||
MAX(interp_start[1], interp_end[1]) < 0 ||
MIN(interp_start[1], interp_end[1]) >= roi_height))
3040 const float hard = prev_payload[0] +
t * (payload[border_index * 2] - prev_payload[0]);
3041 const float dens = prev_payload[1] +
t * (payload[border_index * 2 + 1] - prev_payload[1]);
3042 _brush_falloff_roi(buffer, interp_start, interp_end, roi_width, roi_height, hard, dens);
3047 if(!(
MAX(segment_start[0], segment_end[0]) < 0 ||
MIN(segment_start[0], segment_end[0]) >= roi_width
3048 ||
MAX(segment_start[1], segment_end[1]) < 0
3049 ||
MIN(segment_start[1], segment_end[1]) >= roi_height))
3051 payload[border_index * 2], payload[border_index * 2 + 1]);
3053 prev_start[0] = segment_start[0];
3054 prev_start[1] = segment_start[1];
3055 prev_end[0] = segment_end[0];
3056 prev_end[1] = segment_end[1];
3057 prev_payload[0] = payload[border_index * 2];
3058 prev_payload[1] = payload[border_index * 2 + 1];
3085 snprintf(mask_form->
name,
sizeof(mask_form->
name), _(
"brush #%d"), (
int)form_number);
3090 const int opacity,
char *
const restrict msgbuf,
3091 const size_t msgbuf_len)
3094 g_snprintf(msgbuf, msgbuf_len,
3095 _(
"<b>Size</b>: scroll, <b>Hardness</b>: shift+scroll\n"
3096 "<b>Opacity</b>: ctrl+scroll (%d%%)"), opacity);
3098 g_strlcat(msgbuf, _(
"<b>Size</b>: scroll"), msgbuf_len);
3111 float offset[2] = { 0.01f, 0.01f };
3128 const int form_id = mask_gui->
formid;
3152 const int form_id = mask_gui->
formid;
3163 node_index, &
node->state);
3189 const float pointer_x,
const float pointer_y)
3194 gchar *accel = g_strdup_printf(_(
"%s+Click"), gtk_accelerator_get_label(0, GDK_CONTROL_MASK));
3196 gboolean ret =
FALSE;
3209 gchar *to_change_type = g_strdup_printf(_(
"Switch to %s node"), (is_corner) ? _(
"round") : _(
"cusp"));
static double dist(double x1, double y1, double x2, double y2)
void dt_masks_iop_update(dt_iop_module_t *module)
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)
const char * dt_conf_get_string_const(const char *name)
void dt_control_mouse_is_painting(gboolean state)
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)
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_add_history_item(dev, module, enable, redraw)
static void _brush_initial_source_pos(const float iwd, const float iht, float *x, float *y)
static int _brush_populate_context_menu(GtkWidget *menu, struct dt_masks_form_t *mask_form, struct dt_masks_form_gui_t *mask_gui, const float pointer_x, const float pointer_y)
static gboolean _brush_line_point_at_length(const float *line, const int first_pt, const int last_pt, const float target_len, float *x, float *y)
static void _brush_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)
Brush-specific inside/border/segment hit testing.
static void _brush_points_recurs_border_gaps(float *cmax, float *bmin, float *bmin2, float *bmax, dt_masks_dynbuf_t *dpoints, dt_masks_dynbuf_t *dborder, gboolean clockwise)
static int _brush_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, float **payload_buffer, int *payload_count, int use_source)
static float _get_brush_smoothing()
static int _init_hardness(dt_masks_form_t *mask_form, int parentid, dt_masks_form_gui_t *mask_gui, const float amount, const dt_masks_increment_t increment, const int flow)
static int _find_closest_handle(dt_masks_form_t *mask_form, dt_masks_form_gui_t *mask_gui, int form_index)
static int _change_hardness(dt_masks_form_t *mask_form, int parentid, dt_masks_form_gui_t *mask_gui, struct dt_iop_module_t *module, int index, const float amount, const dt_masks_increment_t increment, const int flow)
static float _brush_get_interaction_value(const dt_masks_form_t *mask_form, dt_masks_interaction_t interaction)
static int _brush_events_button_released(struct dt_iop_module_t *module, double widget_x, double widget_y, int which, uint32_t state, dt_masks_form_t *mask_form, int parentid, dt_masks_form_gui_t *mask_gui, int index)
static void _add_node_to_segment(struct dt_iop_module_t *module, dt_masks_form_t *mask_form, int parentid, dt_masks_form_gui_t *mask_gui, int index)
static void _brush_points_stamp(float *cmax, float *bmin, dt_masks_dynbuf_t *dpoints, dt_masks_dynbuf_t *dborder, gboolean clockwise)
static int _brush_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 use_source, const dt_iop_module_t *module)
static float _brush_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 gboolean _brush_get_border_handle_resampled(const dt_masks_form_gui_points_t *gui_points, int node_count, int node_index, float *handle_x, float *handle_y)
get the border handle position corresponding to a node, by looking for the closest border point in th...
static int _init_opacity(dt_masks_form_t *mask_form, int parentid, dt_masks_form_gui_t *mask_gui, const float amount, const dt_masks_increment_t increment, const int flow)
static void _brush_translate_all_nodes(dt_masks_form_t *mask_form, const float delta_x, const float delta_y)
static void _brush_border_get_XY(float p0_x, float p0_y, float p1_x, float p1_y, float p2_x, float p2_y, float p3_x, float p3_y, float t, float radius, float *point_x, float *point_y, float *border_x, float *border_y)
Evaluate a cubic Bezier and compute its offset border point.
static void _get_pressure_sensitivity(dt_masks_form_gui_t *mask_gui)
static void _brush_translate_node(dt_masks_node_brush_t *node, const float delta_x, const float delta_y)
static void _brush_reset_round_node_callback(GtkWidget *widget, gpointer user_data)
static void _brush_bounding_box_raw(const float *const restrict points, const float *const restrict border, const int node_count, const int total_points, float *x_min, float *x_max, float *y_min, float *y_max)
Compute the bounding box (min/max) for both brush centerline and border points.
static gboolean _brush_get_line_midpoint(const float *line, const int first_pt, const int last_pt, float *mx, float *my)
static void _apply_pen_pressure(dt_masks_form_gui_t *mask_gui, float *payload_buffer)
static int _brush_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 *offset_x, int *offset_y)
static void _brush_bounding_box(const float *const points, const float *const border, const int node_count, const int total_points, int *width, int *height, int *offset_x, int *offset_y)
Compute integer bounds used for buffer allocation.
static void _brush_falloff(float *const restrict buffer, int segment_start[2], int segment_end[2], int offset_x, int offset_y, int buffer_width, float hardness, float density)
static gboolean _is_within_pxl_threshold(float *min, float *max, int pixel_threshold)
static void _brush_curve_handle_cb(const dt_masks_form_gui_points_t *gui_points, int node_index, float *handle_x, float *handle_y, void *user_data)
Brush-specific curve handle lookup.
static gboolean _brush_get_source_center(const dt_masks_form_gui_points_t *gui_points, const int node_count, dt_masks_gui_center_point_t *center_pt)
static int _brush_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)
Build a brush mask directly into an ROI-sized buffer.
static int _brush_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 *offset_x, int *offset_y)
Build a full-resolution brush mask into a newly allocated buffer.
static float _brush_line_length(const float *line, const int first_pt, const int last_pt)
static void _brush_points_recurs_border_small_gaps(float *cmax, float *bmin, float *bmin2, float *bmax, dt_masks_dynbuf_t *dpoints, dt_masks_dynbuf_t *dborder)
static void _brush_ctrl2_to_handle(float point_x, float point_y, float ctrl_x, float ctrl_y, float *handle_x, float *handle_y, gboolean clockwise)
Convert control point 2 into the handle position (orthonormal space).
static void _brush_falloff_roi(float *buffer, const int *segment_start, const int *segment_end, int buffer_width, int buffer_height, float hardness, float density)
static void _brush_draw_shape(cairo_t *cr, const float *points, const int points_count, const int node_nb, const gboolean border, const gboolean source)
static int _brush_events_mouse_moved(struct dt_iop_module_t *module, double widget_x, double widget_y, double pressure, int which, dt_masks_form_t *mask_form, int parentid, dt_masks_form_gui_t *mask_gui, int index)
Brush mouse-move handler.
static int _brush_events_mouse_scrolled(struct dt_iop_module_t *module, double widget_x, double widget_y, int scroll_up, const int flow, uint32_t state, dt_masks_form_t *mask_form, int parentid, dt_masks_form_gui_t *mask_gui, int index, dt_masks_interaction_t interaction)
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 *offset_x, int *offset_y, int include_source)
Compute the minimal bounding box for a brush (optionally including the source path).
static int _brush_cyclic_cursor(int n, int nb)
static float _brush_get_position_in_segment(float point_x, float point_y, dt_masks_form_t *mask_form, int segment_index)
static void _brush_handle_to_ctrl(float ptx, float pty, float fx, float fy, float *ctrl1x, float *ctrl1y, float *ctrl2x, float *ctrl2y, gboolean clockwise)
static int _brush_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 *offset_x, int *offset_y)
static void _brush_set_form_name(struct dt_masks_form_t *const mask_form, const size_t form_number)
static void _brush_points_recurs(float *p1, float *p2, double tmin, double tmax, float *points_min, float *points_max, float *border_min, float *border_max, float *rpoints, float *rborder, float *rpayload, dt_masks_dynbuf_t *dpoints, dt_masks_dynbuf_t *dborder, dt_masks_dynbuf_t *dpayload, const int pixel_threshold)
static void _brush_events_post_expose(cairo_t *cr, float zoom_scale, dt_masks_form_gui_t *mask_gui, int index, int node_count)
static void _brush_add_node_callback(GtkWidget *menu, gpointer user_data)
static int _change_size(dt_masks_form_t *mask_form, int parentid, dt_masks_form_gui_t *mask_gui, struct dt_iop_module_t *module, int index, const float amount, const dt_masks_increment_t increment, const int flow)
static void _brush_switch_node_callback(GtkWidget *widget, gpointer user_data)
static int _brush_events_button_pressed(struct dt_iop_module_t *module, double widget_x, double widget_y, double pressure, int which, int type, uint32_t state, dt_masks_form_t *mask_form, int parentid, dt_masks_form_gui_t *mask_gui, int index)
static GList * _brush_ramer_douglas_peucker(const float *point_buffer, int point_count, const float *payload_buffer, float epsilon2)
Remove unneeded points (Ramer-Douglas-Peucker) and return the reduced path.
static void _brush_get_distance(float point_x, float point_y, float radius, dt_masks_form_gui_t *mask_gui, int form_index, int corner_count, int *inside, int *inside_border, int *near, int *inside_source, float *distance)
Get the distance between a point and the brush path/border.
static void _brush_init_ctrl_points(dt_masks_form_t *mask_form)
Initialize all control points to match a Catmull-Rom-like spline.
static int _brush_events_key_pressed(struct dt_iop_module_t *module, GdkEventKey *event, dt_masks_form_t *mask_form, int parentid, dt_masks_form_gui_t *mask_gui, int index)
static void _brush_catmull_to_bezier(float x1, float y1, float x2, float y2, float x3, float y3, float x4, float y4, float *bezier1_x, float *bezier1_y, float *bezier2_x, float *bezier2_y)
Get Bezier control points that match a Catmull-Rom segment.
const dt_masks_functions_t dt_masks_functions_brush
static void _brush_payload_sync(dt_masks_dynbuf_t *dpayload, dt_masks_dynbuf_t *dpoints, const float v0, const float v1)
static void _brush_sanitize_config(dt_masks_type_t type)
static void _brush_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 _brush_get_XY(float p0_x, float p0_y, float p1_x, float p1_y, float p2_x, float p2_y, float p3_x, float p3_y, float t, float *out_x, float *out_y)
Evaluate a cubic Bezier at t in [0, 1].
static gboolean _brush_get_gravity_center(const dt_masks_form_t *mask_form, float center[2], float *area)
static int _init_size(dt_masks_form_t *mask_form, int parentid, dt_masks_form_gui_t *mask_gui, const float amount, const dt_masks_increment_t increment, const int flow)
static float _brush_point_line_distance2(int point_index, int point_count, const float *point_buffer, const float *payload_buffer)
Get squared distance of a point to the segment, including payload deltas.
static gboolean _brush_get_border_handle_mirrored(const dt_masks_form_gui_points_t *gui_points, int node_count, int node_index, float *handle_x, float *handle_y)
static void _brush_duplicate_points(dt_develop_t *const dev, dt_masks_form_t *const base, dt_masks_form_t *const dest)
static gboolean _brush_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)
Brush-specific border handle lookup.
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
#define DT_DRAW_SIZE_LINE
static void dt_draw_set_dash_style(cairo_t *cr, dt_draw_dash_type_t type, float zoom_scale)
static void dt_draw_stroke_line(const dt_draw_dash_type_t dash_type, const gboolean source, cairo_t *cr, const gboolean selected, const float zoom_scale, const cairo_line_cap_t line_cap)
Stroke a line with style.
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.
void dt_gui_gtk_set_source_rgba(cairo_t *cr, dt_gui_color_t color, float opacity_coef)
@ DT_GUI_COLOR_BRUSH_CURSOR
@ DT_GUI_COLOR_BRUSH_TRACE
#define DT_PIXEL_APPLY_DPI(value)
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)
void dt_masks_change_form_gui(dt_masks_form_t *newform)
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)
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.
void dt_masks_select_form(struct dt_iop_module_t *module, dt_masks_form_t *sel)
Select or clear the current mask form, notifying the owning module if needed.
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 ...
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
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_get_set_conf_value_with_toast(dt_masks_form_t *form, const char *feature, float amount, float v_min, float v_max, dt_masks_increment_t increment, int flow, const char *toast_fmt, float toast_scale)
Update a mask configuration value and emit a toast message.
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_PRESSURE_OPACITY_REL
@ DT_MASKS_PRESSURE_OPACITY_ABS
@ DT_MASKS_PRESSURE_BRUSHSIZE_REL
@ DT_MASKS_PRESSURE_HARDNESS_REL
@ DT_MASKS_PRESSURE_HARDNESS_ABS
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_set_edit_mode(struct dt_iop_module_t *module, dt_masks_edit_mode_t value)
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_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
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_masks_form_gui_t * form_gui
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