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];
1426 const int index = 0;
1431 if(!
_change_size(mask_form, 0, mask_gui, module, index,
value, increment, flow))
return NAN;
1447 const float scale_amount = 1.f / powf(amount, (
float)flow);
1448 const float offset_amount = -amount * (float)flow;
1449 float result_amount = 0.0f;
1450 for(GList *node_entry = mask_form->
points; node_entry; node_entry = g_list_next(node_entry), node_index++)
1455 const float current_hardness =
node->hardness;
1478 for(GList *node_entry = mask_form->
points; node_entry; node_entry = g_list_next(node_entry), node_index++)
1484 if(amount > 1.0f && (
node->border[0] > 1.0f ||
node->border[1] > 1.0f))
1490 const float scale_amount = amount;
1491 const float offset_amount = amount;
1493 for(GList *node_entry = mask_form->
points; node_entry; node_entry = g_list_next(node_entry), node_index++)
1500 scale_amount, offset_amount, increment);
1502 scale_amount, offset_amount, increment);
1530 return _init_opacity(mask_form, parentid, mask_gui, scroll_up ? +0.02f : -0.02f,
1547 return _change_hardness(mask_form, parentid, mask_gui, module, index, scroll_up ? -0.01f : 0.01f,
1550 return _change_size(mask_form, parentid, mask_gui, module, index, scroll_up ? 1.02f : 0.98f,
1563 if(!strcmp(psens,
"hardness (absolute)"))
1565 else if(!strcmp(psens,
"hardness (relative)"))
1567 else if(!strcmp(psens,
"opacity (absolute)"))
1569 else if(!strcmp(psens,
"opacity (relative)"))
1571 else if(!strcmp(psens,
"brush size (relative)"))
1580 const guint node_count = g_list_length(mask_form->
points);
1582 if(selected_segment < 0 || selected_segment >= (
int)node_count)
return;
1596 mask_form, selected_segment);
1598 GList *pt = g_list_nth(mask_form->
points, selected_segment);
1617 mask_form->
points = g_list_insert(mask_form->
points, new_node, selected_segment + 1);
1636 for(GList *node_entry = mask_form->
points; node_entry; node_entry = g_list_next(node_entry))
1641 double pressure,
int which,
int type, uint32_t
state,
1646 if(
type == GDK_2BUTTON_PRESS ||
type == GDK_3BUTTON_PRESS)
return 1;
1649 const float masks_density = 1.0f;
1697 const guint node_count = g_list_length(mask_form->
points);
1732 float handle_x, handle_y;
1736 &handle_x, &handle_y,
TRUE);
1738 mask_gui->
delta[0] = handle_x - mask_gui->
pos[0];
1739 mask_gui->
delta[1] = handle_y - mask_gui->
pos[1];
1746 float handle_x = NAN, handle_y = NAN;
1748 &handle_x, &handle_y))
1750 mask_gui->
delta[0] = handle_x - mask_gui->
pos[0];
1751 mask_gui->
delta[1] = handle_y - mask_gui->
pos[1];
1795 if(!strcmp(smoothing,
"low"))
1797 else if(!strcmp(smoothing,
"medium"))
1799 else if(!strcmp(smoothing,
"high"))
1808 float *payload = payload_buffer + 4 *
i;
1809 float pressure = payload[3];
1824 payload[2] =
MAX(0.05f, pressure);
1827 payload[2] =
MAX(0.05f, payload[2] * pressure);
1853 if(mask_gui->
creation && which == 1)
1896 guipoints_payload, epsilon2);
1914 int group_index = 0;
1915 int selected_index = -1;
1916 for(GList *group_entry = grp->
points; group_entry; group_entry = g_list_next(group_entry))
1921 selected_index = group_index;
1926 if(selected_index < 0)
return 1;
2004 const guint node_count = g_list_length(mask_form->
points);
2012 float delta_x = 0.0f;
2013 float delta_y = 0.0f;
2031 if(!segment_node || !segment_node_next)
return 0;
2033 float delta_x = 0.0f;
2034 float delta_y = 0.0f;
2053 float cursor_pos[2];
2057 float control_points[4];
2060 cursor_pos[1], &control_points[0], &control_points[1], &control_points[2],
2061 &control_points[3],
TRUE);
2081 float handle_x = NAN, handle_y = NAN;
2085 const float node_px = gui_points->
points[node_index * 6 + 2];
2086 const float node_py = gui_points->
points[node_index * 6 + 3];
2089 float cursor_pos[2];
2090 const float node_pos_gui[2] = { node_px, node_py };
2091 const float handle_pos[2] = { handle_x, handle_y };
2109 float delta_x = 0.0f;
2110 float delta_y = 0.0f;
2118 mask_form->
source[0] = raw_pos[0];
2119 mask_form->
source[1] = raw_pos[1];
2129static void _brush_draw_shape(cairo_t *cr,
const float *points,
const int points_count,
const int node_nb,
const gboolean
border,
const gboolean source)
2134 for(
int i = node_nb * 3 +
border;
i < points_count;
i++)
2136 if(!isnan(points[
i * 2]) && !isnan(points[
i * 2 + 1]))
2146 cairo_move_to(cr, points[start_idx * 2], points[start_idx * 2 + 1]);
2149 const int end_idx =
border ? points_count : 0.5 * points_count;
2151 for(
int i = start_idx + 1;
i < end_idx;
i++)
2153 if(!isnan(points[
i * 2]) && !isnan(points[
i * 2 + 1]))
2154 cairo_line_to(cr, points[
i * 2], points[
i * 2 + 1]);
2161 float total_len = 0.0f;
2162 for(
int i = first_pt;
i < last_pt;
i++)
2164 const int i0 =
i * 2;
2165 const int i1 = (
i + 1) * 2;
2166 const float x0 = line[i0];
2167 const float y0 = line[i0 + 1];
2168 const float x1 = line[i1];
2169 const float y1 = line[i1 + 1];
2170 if(isnan(x0) || isnan(y0) || isnan(x1) || isnan(y1))
continue;
2171 const float dx = x1 - x0;
2172 const float dy = y1 - y0;
2173 const float len = dx * dx + dy * dy;
2174 if(len > 1e-12f) total_len += sqrtf(len);
2180 const float target_len,
float *
x,
float *y)
2183 if(last_pt <= first_pt)
return FALSE;
2186 gboolean has_fallback =
FALSE;
2187 float fallback_x = NAN, fallback_y = NAN;
2189 for(
int i = first_pt;
i < last_pt;
i++)
2191 const int i0 =
i * 2;
2192 const int i1 = (
i + 1) * 2;
2193 const float x0 = line[i0];
2194 const float y0 = line[i0 + 1];
2195 const float x1 = line[i1];
2196 const float y1 = line[i1 + 1];
2197 if(isnan(x0) || isnan(y0) || isnan(x1) || isnan(y1))
continue;
2199 const float dx = x1 - x0;
2200 const float dy = y1 - y0;
2201 const float len = sqrtf(dx * dx + dy * dy);
2202 if(len <= 1e-6f)
continue;
2204 has_fallback =
TRUE;
2208 if(acc + len >= target_len)
2210 const float t = (target_len - acc) / len;
2218 if(!has_fallback || isnan(fallback_x) || isnan(fallback_y))
return FALSE;
2225 float *mx,
float *my)
2229 if(total_len <= 1e-6f)
return FALSE;
2231 const float half_len = 0.5f * total_len;
2242 const int line_offset_pt = node_count * 3;
2243 const int points_line_end = gui_points->
points_count / 2;
2244 const int source_line_end = gui_points->
source_count / 2;
2245 const int line_end =
MIN(points_line_end, source_line_end);
2246 const int line_count = line_end - line_offset_pt;
2247 if(line_count < 2)
return FALSE;
2249 const float *
const points_line = gui_points->
points + 2 * line_offset_pt;
2250 const float *
const source_line = gui_points->
source + 2 * line_offset_pt;
2251 const int first_pt = 0;
2252 const int last_pt = line_count - 1;
2271 const float min_iwd_iht =
MIN(iwd, iht);
2284 const float radius1 = masks_border * masks_hardness * min_iwd_iht;
2285 const float radius2 = masks_border * min_iwd_iht;
2287 float xpos = mask_gui->
pos[0];
2288 float ypos = mask_gui->
pos[1];
2289 if((xpos == -1.0f && ypos == -1.0f))
2300 cairo_arc(cr, xpos, ypos, radius1, 0, 2.0 *
M_PI);
2301 cairo_fill_preserve(cr);
2302 cairo_set_source_rgba(cr, .8, .8, .8, .8);
2305 cairo_arc(cr, xpos, ypos, radius2, 0, 2.0 *
M_PI);
2315 float masks_border = 0.0f, masks_hardness = 0.0f, masks_density = 0.0f;
2316 float radius = 0.0f, oldradius = 0.0f, opacity = 0.0f, oldopacity = 0.0f, pressure = 0.0f;
2323 cairo_set_line_join(cr, CAIRO_LINE_JOIN_ROUND);
2324 cairo_set_line_cap(cr, CAIRO_LINE_CAP_ROUND);
2325 masks_border = guipoints_payload[0];
2326 masks_hardness = guipoints_payload[1];
2327 masks_density = guipoints_payload[2];
2328 pressure = guipoints_payload[3];
2339 masks_density =
MAX(0.05f, pressure);
2342 masks_density =
MAX(0.05f, masks_density * pressure);
2353 radius = oldradius = masks_border * masks_hardness * min_iwd_iht;
2354 opacity = oldopacity = masks_density;
2359 cairo_move_to(cr, guipoints[0], guipoints[1]);
2362 cairo_line_to(cr, guipoints[
i * 2], guipoints[
i * 2 + 1]);
2364 masks_border = guipoints_payload[
i * 4];
2365 masks_hardness = guipoints_payload[
i * 4 + 1];
2366 masks_density = guipoints_payload[
i * 4 + 2];
2367 pressure = guipoints_payload[
i * 4 + 3];
2378 masks_density =
MAX(0.05f, pressure);
2381 masks_density =
MAX(0.05f, masks_density * pressure);
2392 radius = masks_border * masks_hardness * min_iwd_iht;
2393 opacity = masks_density;
2395 if(radius != oldradius || opacity != oldopacity)
2402 oldopacity = opacity;
2403 cairo_move_to(cr, guipoints[
i * 2], guipoints[
i * 2 + 1]);
2406 if(!stroked) cairo_stroke(cr);
2413 radius, 0, 2.0 *
M_PI);
2414 cairo_fill_preserve(cr);
2415 cairo_set_source_rgba(cr, .8, .8, .8, .8);
2420 guipoints[2 * (mask_gui->
guipoints_count - 1) + 1], masks_border * min_iwd_iht, 0,
2429 guipoints[
i * 2], guipoints[
i * 2 + 1],
TRUE);
2448 if(gui_points->
points_count <= node_count * 3 + 2)
return;
2451 if(node_count > 0 && gui_points->
points_count > node_count * 3 + 6)
2454 node_count, zoom_scale);
2461 int current_seg = 0;
2464 cairo_move_to(cr, gui_points->
points[node_count * 6], gui_points->
points[node_count * 6 + 1]);
2468 for(
int i = node_count * 3;
i < end_idx;
i++)
2470 const double x = gui_points->
points[
i * 2];
2471 const double y = gui_points->
points[
i * 2 + 1];
2472 cairo_line_to(cr,
x, y);
2474 int seg_idx = seg1 * 6;
2475 if((seg_idx + 3) < total_points)
2477 const double segment_x = gui_points->
points[seg_idx + 2];
2478 const double segment_y = gui_points->
points[seg_idx + 3];
2481 if(
x == segment_x && y == segment_y)
2483 const gboolean seg_selected = (mask_gui->
group_selected == index)
2484 && (selected_segment == current_seg);
2485 const gboolean all_selected = (mask_gui->
group_selected == index)
2492 if(mask_gui->
creation && current_seg == node_count - 2)
2496 seg1 = (seg1 + 1) % node_count;
2511 CAIRO_LINE_CAP_ROUND);
2515 if(mask_gui->
node_selected && selected_node >= 0 && selected_node < node_count
2518 const int n = selected_node;
2521 gui_points->
points[
n * 6 + 4], gui_points->
points[
n * 6 + 5], &handle[0], &handle[1],
2523 const float pt[2] = { gui_points->
points[
n * 6 + 2], gui_points->
points[
n * 6 + 3] };
2529 for(
int k = 0;
k < node_count;
k++)
2532 const float x = gui_points->
points[
k * 6 + 2];
2533 const float y = gui_points->
points[
k * 6 + 3];
2535 const gboolean action = (
k == selected_node);
2537 dt_draw_node(cr, corner, action, selected, zoom_scale,
x, y);
2541 if(mask_gui->
node_selected && selected_node >= 0 && selected_node < node_count)
2543 const int edited = selected_node;
2544 const gboolean selected = (mask_gui->
node_hovered == edited
2545 || selected_handle_border == edited
2547 float handle[2] = {NAN, NAN};
2565 for(
int k = 0;
k < node_count;
k++)
2569 || (mask_gui->
creation &&
k == node_count - 1)))
2571 const int node_index =
k * 6 + 2;
2572 const float proj[2] = { gui_points->
source[node_index], gui_points->
source[node_index + 1] };
2589 const int node_count,
const int total_points,
float *x_min,
float *x_max,
2590 float *y_min,
float *y_max)
2593 float min_x = FLT_MAX, max_x = FLT_MIN, min_y = FLT_MAX, max_y = FLT_MIN;
2596 for(
int point_index = node_count * 3; point_index < total_points; point_index++)
2599 const float border_x =
border[point_index * 2];
2600 const float border_y =
border[point_index * 2 + 1];
2601 min_x =
MIN(border_x, min_x);
2602 max_x =
MAX(border_x, max_x);
2603 min_y =
MIN(border_y, min_y);
2604 max_y =
MAX(border_y, max_y);
2606 const float brush_x = points[point_index * 2];
2607 const float brush_y = points[point_index * 2 + 1];
2608 min_x =
MIN(brush_x, min_x);
2609 max_x =
MAX(brush_x, max_x);
2610 min_y =
MIN(brush_y, min_y);
2611 max_y =
MAX(brush_y, max_y);
2625 const int total_points,
int *
width,
int *
height,
int *offset_x,
int *offset_y)
2627 float min_x = FLT_MAX, max_x = FLT_MIN, min_y = FLT_MAX, max_y = FLT_MIN;
2629 *
height = max_y - min_y + 4;
2630 *
width = max_x - min_x + 4;
2631 *offset_x = min_x - 2;
2632 *offset_y = min_y - 2;
2647 float *points = NULL, *
border = NULL;
2648 int points_count, border_count;
2650 &points, &points_count, &
border, &border_count, NULL, NULL, include_source) != 0)
2657 const guint node_count = g_list_length(mask_form->
points);
2688static void _brush_falloff(
float *
const restrict buffer,
int segment_start[2],
int segment_end[2],
2689 int offset_x,
int offset_y,
int buffer_width,
float hardness,
float density)
2692 const int segment_length = sqrt((segment_end[0] - segment_start[0]) * (segment_end[0] - segment_start[0])
2693 + (segment_end[1] - segment_start[1]) * (segment_end[1] - segment_start[1]))
2695 const int solid_length = (int)segment_length *
hardness;
2696 const int soft_length = segment_length - solid_length;
2698 const float segment_dx = segment_end[0] - segment_start[0];
2699 const float segment_dy = segment_end[1] - segment_start[1];
2700 const float inv_length = 1.0f / (float)segment_length;
2701 const float inv_soft = (soft_length > 0) ? 1.0f / (
float)soft_length : 0.0f;
2703 for(
int step = 0; step < segment_length; step++)
2706 const int x = (int)((
float)step * segment_dx * inv_length) + segment_start[0] - offset_x;
2707 const int y = (int)((
float)step * segment_dy * inv_length) + segment_start[1] - offset_y;
2708 const float opacity =
density * ((step <= solid_length) ? 1.0f : 1.0f - (float)(step - solid_length) * inv_soft);
2709 const int buffer_index = y * buffer_width +
x;
2710 buffer[buffer_index] =
MAX(buffer[buffer_index], opacity);
2712 buffer[buffer_index - 1] =
MAX(buffer[buffer_index - 1], opacity);
2714 buffer[buffer_index - buffer_width] =
MAX(buffer[buffer_index - buffer_width], opacity);
2726 float **buffer,
int *
width,
int *
height,
int *offset_x,
int *offset_y)
2729 double timer_start = 0.0;
2730 double timer_step_start = 0.0;
2734 float *points = NULL, *
border = NULL, *payload = NULL;
2735 int points_count, border_count, payload_count;
2737 &points, &points_count,
2738 &
border, &border_count, &payload, &payload_count, 0) != 0)
2753 const guint node_count = g_list_length(mask_form->
points);
2755 || border_count <= (
int)(node_count * 3))
2766 const int sparse_step = use_sparse ? 4 : 1;
2774 const size_t buffer_size = (size_t)(*
width) * (*height);
2784 memset(*buffer, 0,
sizeof(
float) * buffer_size);
2787 int segment_start[2], segment_end[2];
2788 int prev_start[2] = { 0, 0 };
2789 int prev_end[2] = { 0, 0 };
2790 float prev_payload[2] = { 0.0f, 0.0f };
2791 gboolean have_prev =
FALSE;
2794 for(
int border_index = node_count * 3; border_index < border_count; border_index++)
2796 segment_start[0] = points[border_index * 2];
2797 segment_start[1] = points[border_index * 2 + 1];
2798 segment_end[0] =
border[border_index * 2];
2799 segment_end[1] =
border[border_index * 2 + 1];
2801 if(use_sparse && have_prev
2802 && (prev_start[0] != segment_start[0] || prev_start[1] != segment_start[1]
2803 || prev_end[0] != segment_end[0] || prev_end[1] != segment_end[1]))
2806 for(
int step = 1; step < sparse_step; step++)
2808 const float t = (float)step / (
float)sparse_step;
2809 int interp_start[2] = { (int)floorf(prev_start[0] +
t * (segment_start[0] - prev_start[0]) + 0.5f),
2810 (
int)floorf(prev_start[1] +
t * (segment_start[1] - prev_start[1]) + 0.5f) };
2811 int interp_end[2] = { (int)floorf(prev_end[0] +
t * (segment_end[0] - prev_end[0]) + 0.5f),
2812 (
int)floorf(prev_end[1] +
t * (segment_end[1] - prev_end[1]) + 0.5f) };
2813 const float hard = prev_payload[0] +
t * (payload[border_index * 2] - prev_payload[0]);
2814 const float dens = prev_payload[1] +
t * (payload[border_index * 2 + 1] - prev_payload[1]);
2815 _brush_falloff(*buffer, interp_start, interp_end, *offset_x, *offset_y, *
width, hard, dens);
2820 payload[border_index * 2], payload[border_index * 2 + 1]);
2822 prev_start[0] = segment_start[0];
2823 prev_start[1] = segment_start[1];
2824 prev_end[0] = segment_end[0];
2825 prev_end[1] = segment_end[1];
2826 prev_payload[0] = payload[border_index * 2];
2827 prev_payload[1] = payload[border_index * 2 + 1];
2847 const int segment_length = sqrt((segment_end[0] - segment_start[0]) * (segment_end[0] - segment_start[0])
2848 + (segment_end[1] - segment_start[1]) * (segment_end[1] - segment_start[1]))
2850 const int solid_length =
hardness * segment_length;
2852 const float step_x = (float)(segment_end[0] - segment_start[0]) / (float)segment_length;
2853 const float step_y = (float)(segment_end[1] - segment_start[1]) / (float)segment_length;
2855 const int direction_x = step_x <= 0 ? -1 : 1;
2856 const int direction_y = step_y <= 0 ? -1 : 1;
2857 const int neighbor_offset_x = direction_x;
2858 const int neighbor_offset_y = direction_y * buffer_width;
2860 float cursor_x = segment_start[0];
2861 float cursor_y = segment_start[1];
2864 const float opacity_step =
density / (float)(segment_length - solid_length);
2866 const int start_x = segment_start[0], start_y = segment_start[1];
2867 const int end_x = segment_end[0], end_y = segment_end[1];
2868 if((start_x < 0 && end_x < 0) || (start_x >= buffer_width && end_x >= buffer_width)
2869 || (start_y < 0 && end_y < 0) || (start_y >= buffer_height && end_y >= buffer_height))
2871 const int fully_inside = (start_x >= 0 && start_x < buffer_width && end_x >= 0 && end_x < buffer_width
2872 && start_y >= 0 && start_y < buffer_height && end_y >= 0
2873 && end_y < buffer_height);
2875 for(
int step = 0; step < segment_length; step++)
2877 const int x = cursor_x;
2878 const int y = cursor_y;
2882 if(step > solid_length) opacity -= opacity_step;
2884 if(!fully_inside && (x < 0 || x >= buffer_width || y < 0 || y >= buffer_height))
continue;
2886 float *buf = buffer + (size_t)y * buffer_width +
x;
2888 *buf =
MAX(*buf, opacity);
2889 if(
x + direction_x >= 0 &&
x + direction_x < buffer_width)
2890 buf[neighbor_offset_x] =
MAX(buf[neighbor_offset_x], opacity);
2891 if(y + direction_y >= 0 && y + direction_y < buffer_height)
2892 buf[neighbor_offset_y] =
MAX(buf[neighbor_offset_y], opacity);
2908 double timer_start = 0.0;
2909 double timer_step_start = 0.0;
2912 const int roi_offset_x = roi->
x;
2913 const int roi_offset_y = roi->
y;
2914 const int roi_width = roi->
width;
2915 const int roi_height = roi->
height;
2916 const float roi_scale = roi->
scale;
2919 const int sparse_step = use_sparse ? 4 : 1;
2922 float *points = NULL, *
border = NULL, *payload = NULL;
2924 int points_count, border_count, payload_count;
2927 &points, &points_count, &
border, &border_count, &payload, &payload_count, 0) != 0)
2942 const guint node_count = g_list_length(mask_form->
points);
2944 || border_count <= (
int)(node_count * 3))
2954 for(
int border_index = node_count * 3; border_index < border_count; border_index++)
2956 const float border_x =
border[2 * border_index];
2957 const float border_y =
border[2 * border_index + 1];
2958 border[2 * border_index] = border_x * roi_scale - roi_offset_x;
2959 border[2 * border_index + 1] = border_y * roi_scale - roi_offset_y;
2963 for(
int point_index = node_count * 3; point_index < points_count; point_index++)
2965 const float point_x = points[2 * point_index];
2966 const float point_y = points[2 * point_index + 1];
2967 points[2 * point_index] = point_x * roi_scale - roi_offset_x;
2968 points[2 * point_index + 1] = point_y * roi_scale - roi_offset_y;
2972 float min_x = 0.0f, max_x = 0.0f, min_y = 0.0f, max_y = 0.0f;
2983 if(max_x < 0 || max_y < 0 || min_x >= roi_width || min_y >= roi_height)
2996 for(
int border_index = node_count * 3; border_index < border_count; border_index++)
2998 const int segment_start[] = { points[border_index * 2], points[border_index * 2 + 1] };
2999 const int segment_end[] = {
border[border_index * 2],
border[border_index * 2 + 1] };
3001 if(
MAX(segment_start[0], segment_end[0]) < 0 ||
MIN(segment_start[0], segment_end[0]) >= roi_width
3002 ||
MAX(segment_start[1], segment_end[1]) < 0
3003 ||
MIN(segment_start[1], segment_end[1]) >= roi_height)
3006 _brush_falloff_roi(buffer, segment_start, segment_end, roi_width, roi_height, payload[border_index * 2],
3007 payload[border_index * 2 + 1]);
3012 int prev_start[2] = { 0, 0 };
3013 int prev_end[2] = { 0, 0 };
3014 float prev_payload[2] = { 0.0f, 0.0f };
3015 gboolean have_prev =
FALSE;
3018 for(
int border_index = node_count * 3; border_index < border_count; border_index++)
3020 int segment_start[2] = { points[border_index * 2], points[border_index * 2 + 1] };
3021 int segment_end[2] = {
border[border_index * 2],
border[border_index * 2 + 1] };
3023 if(use_sparse && have_prev
3024 && (prev_start[0] != segment_start[0] || prev_start[1] != segment_start[1]
3025 || prev_end[0] != segment_end[0] || prev_end[1] != segment_end[1]))
3027 for(
int step = 1; step < sparse_step; step++)
3029 const float t = (float)step / (
float)sparse_step;
3030 int interp_start[2] = { (int)floorf(prev_start[0] +
t * (segment_start[0] - prev_start[0]) + 0.5f),
3031 (
int)floorf(prev_start[1] +
t * (segment_start[1] - prev_start[1]) + 0.5f) };
3032 int interp_end[2] = { (int)floorf(prev_end[0] +
t * (segment_end[0] - prev_end[0]) + 0.5f),
3033 (
int)floorf(prev_end[1] +
t * (segment_end[1] - prev_end[1]) + 0.5f) };
3034 if(!(
MAX(interp_start[0], interp_end[0]) < 0 ||
MIN(interp_start[0], interp_end[0]) >= roi_width
3035 ||
MAX(interp_start[1], interp_end[1]) < 0 ||
MIN(interp_start[1], interp_end[1]) >= roi_height))
3037 const float hard = prev_payload[0] +
t * (payload[border_index * 2] - prev_payload[0]);
3038 const float dens = prev_payload[1] +
t * (payload[border_index * 2 + 1] - prev_payload[1]);
3039 _brush_falloff_roi(buffer, interp_start, interp_end, roi_width, roi_height, hard, dens);
3044 if(!(
MAX(segment_start[0], segment_end[0]) < 0 ||
MIN(segment_start[0], segment_end[0]) >= roi_width
3045 ||
MAX(segment_start[1], segment_end[1]) < 0
3046 ||
MIN(segment_start[1], segment_end[1]) >= roi_height))
3048 payload[border_index * 2], payload[border_index * 2 + 1]);
3050 prev_start[0] = segment_start[0];
3051 prev_start[1] = segment_start[1];
3052 prev_end[0] = segment_end[0];
3053 prev_end[1] = segment_end[1];
3054 prev_payload[0] = payload[border_index * 2];
3055 prev_payload[1] = payload[border_index * 2 + 1];
3082 snprintf(mask_form->
name,
sizeof(mask_form->
name), _(
"brush #%d"), (
int)form_number);
3087 const int opacity,
char *
const restrict msgbuf,
3088 const size_t msgbuf_len)
3091 g_snprintf(msgbuf, msgbuf_len,
3092 _(
"<b>Size</b>: scroll, <b>Hardness</b>: shift+scroll\n"
3093 "<b>Opacity</b>: ctrl+scroll (%d%%)"), opacity);
3095 g_strlcat(msgbuf, _(
"<b>Size</b>: scroll"), msgbuf_len);
3108 float offset[2] = { 0.01f, 0.01f };
3125 const int form_id = mask_gui->
formid;
3149 const int form_id = mask_gui->
formid;
3160 node_index, &
node->state);
3186 const float pointer_x,
const float pointer_y)
3191 gchar *accel = g_strdup_printf(_(
"%s+Click"), gtk_accelerator_get_label(0, GDK_CONTROL_MASK));
3193 gboolean ret =
FALSE;
3206 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_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