59#define FADING_MIN 0.00001f
60#define FADING_MAX 1.0f
62#define BORDER_MIN 0.00005f
63#define BORDER_MAX 0.5f
72 const float *point_buffer,
const float *payload_buffer)
74 const float point_x = point_buffer[2 * point_index];
75 const float point_y = point_buffer[2 * point_index + 1];
76 const float point_border = payload_buffer[4 * point_index];
77 const float point_fading = payload_buffer[4 * point_index + 1];
78 const float point_density = payload_buffer[4 * point_index + 2];
79 const float start_x = point_buffer[0];
80 const float start_y = point_buffer[1];
81 const float start_border = payload_buffer[0];
82 const float start_fading = payload_buffer[1];
83 const float start_density = payload_buffer[2];
84 const float end_x = point_buffer[2 * (point_count - 1)];
85 const float end_y = point_buffer[2 * (point_count - 1) + 1];
86 const float end_border = payload_buffer[4 * (point_count - 1)];
87 const float end_fading = payload_buffer[4 * (point_count - 1) + 1];
88 const float end_density = payload_buffer[4 * (point_count - 1) + 2];
89 const float bweight = 1.0f;
90 const float hweight = 0.01f;
91 const float dweight = 0.01f;
93 const float segment_dx = end_x - start_x;
94 const float segment_dy = end_y - start_y;
95 const float segment_db = end_border - start_border;
96 const float segment_dh = end_fading - start_fading;
97 const float segment_dd = end_density - start_density;
99 const float dot = (point_x - start_x) * segment_dx + (point_y - start_y) * segment_dy;
100 const float segment_len2 = sqf(segment_dx) + sqf(segment_dy);
101 const float t = dot / segment_len2;
103 float dx = 0.0f, dy = 0.0f, db = 0.0f, dh = 0.0f, dd = 0.0f;
105 if(segment_len2 == 0.0f)
107 dx = point_x - start_x;
108 dy = point_y - start_y;
109 db = point_border - start_border;
110 dh = point_fading - start_fading;
111 dd = point_density - start_density;
115 dx = point_x - start_x;
116 dy = point_y - start_y;
117 db = point_border - start_border;
118 dh = point_fading - start_fading;
119 dd = point_density - start_density;
123 dx = point_x - end_x;
124 dy = point_y - end_y;
125 db = point_border - end_border;
126 dh = point_fading - end_fading;
127 dd = point_density - end_density;
131 dx = point_x - (start_x +
t * segment_dx);
132 dy = point_y - (start_y +
t * segment_dy);
133 db = point_border - (start_border +
t * segment_db);
134 dh = point_fading - (start_fading +
t * segment_dh);
135 dd = point_density - (start_density +
t * segment_dd);
138 return sqf(dx) + sqf(dy) + bweight * sqf(db) + hweight * dh * dh + dweight * sqf(dd);
145 const float *payload_buffer,
float epsilon2)
147 GList *result_list = NULL;
152 for(
int point_index = 1; point_index < point_count - 1; point_index++)
155 point_buffer, payload_buffer);
158 split_index = point_index;
163 if(dmax2 >= epsilon2)
166 payload_buffer, epsilon2);
168 point_count - split_index,
169 payload_buffer + split_index * 4, epsilon2);
172 GList *end1 = g_list_last(left_list);
174 left_list = g_list_delete_link(left_list, end1);
176 result_list = g_list_concat(left_list, right_list);
181 first_node->
node[0] = point_buffer[0];
182 first_node->
node[1] = point_buffer[1];
183 first_node->
ctrl1[0] = first_node->
ctrl1[1] = first_node->
ctrl2[0] = first_node->
ctrl2[1] = -1.0f;
184 first_node->
border[0] = first_node->
border[1] = payload_buffer[0];
185 first_node->
fading = payload_buffer[1];
186 first_node->
density = payload_buffer[2];
188 result_list = g_list_append(result_list, (gpointer)first_node);
191 last_node->
node[0] = point_buffer[(point_count - 1) * 2];
192 last_node->
node[1] = point_buffer[(point_count - 1) * 2 + 1];
194 last_node->
border[0] = last_node->
border[1] = payload_buffer[(point_count - 1) * 4];
195 last_node->
fading = payload_buffer[(point_count - 1) * 4 + 1];
196 last_node->
density = payload_buffer[(point_count - 1) * 4 + 2];
198 result_list = g_list_append(result_list, (gpointer)last_node);
207static 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,
208 float p3_y,
float t,
float *out_x,
float *out_y)
210 const float ti = 1.0f -
t;
211 const float a = ti * ti * ti;
212 const float b = 3.0f *
t * ti * ti;
213 const float c = 3.0f * sqf(
t) * ti;
214 const float d =
t *
t *
t;
215 *out_x = p0_x * a + p1_x * b + p2_x * c + p3_x *
d;
216 *out_y = p0_y * a + p1_y * b + p2_y * c + p3_y *
d;
231 float p3_x,
float p3_y,
float t,
float radius,
float radius_rate,
232 float *point_x,
float *point_y,
float *border_x,
float *border_y)
235 _brush_get_XY(p0_x, p0_y, p1_x, p1_y, p2_x, p2_y, p3_x, p3_y,
t, point_x, point_y);
238 const float ti = 1.0f -
t;
239 const float a = 3.0f * ti * ti;
240 const float b = 3.0f * (ti * ti - 2.0f *
t * ti);
241 const float c = 3.0f * (2.0f *
t * ti -
t *
t);
242 const float d = 3.0f * sqf(
t);
245 float dx = -p0_x * a + p1_x * b + p2_x * c + p3_x *
d;
246 float dy = -p0_y * a + p1_y * b + p2_y * c + p3_y *
d;
279 const float span = fmaxf(fmaxf(fabsf(p3_x - p0_x), fabsf(p3_y - p0_y)),
280 fmaxf(fabsf(p2_x - p1_x), fabsf(p2_y - p1_y)));
281 const float degenerate = fmaxf(span, 1.0f) * 1e-3f;
283 if(fabsf(dx) < degenerate && fabsf(dy) < degenerate)
285 if(
t < 0.5f) { dx = p2_x - p0_x; dy = p2_y - p0_y; }
286 else { dx = p3_x - p1_x; dy = p3_y - p1_y; }
288 if(fabsf(dx) < degenerate && fabsf(dy) < degenerate) { dx = p3_x - p0_x; dy = p3_y - p0_y; }
290 if(dx == 0.0f && dy == 0.0f)
return FALSE;
297 const float centre[2] = { *point_x, *point_y };
311 return r1 + (r2 - r1) *
t *
t * (3.0 - 2.0 *
t);
316 return (r2 - r1) * 6.0 *
t * (1.0 -
t);
323 float *handle_x,
float *handle_y, gboolean clockwise)
327 *handle_x = point_x + ctrl_y - point_y;
328 *handle_y = point_y + point_x - ctrl_x;
332 *handle_x = point_x - ctrl_y + point_y;
333 *handle_y = point_y - point_x + ctrl_x;
352 int node_index,
float *handle_x,
float *handle_y)
354 if(
IS_NULL_PTR(gui_points) || node_count <= 0 || node_index < 0 || node_index >= node_count)
return FALSE;
356 const int start = node_count * 3;
358 if(max_points <= start)
return FALSE;
360 const float node_x = gui_points->
points[node_index * 6 + 2];
361 const float node_y = gui_points->
points[node_index * 6 + 3];
363 float best_dist2 = FLT_MAX;
366 for(
int i = start;
i < max_points;
i++)
377 const float px = gui_points->
points[
i * 2];
378 const float py = gui_points->
points[
i * 2 + 1];
379 const float dx = node_x - px;
380 const float dy = node_y - py;
381 const float dist2 = dx * dx + dy * dy;
382 if(dist2 < best_dist2)
389 if(best_idx < 0)
return FALSE;
391 *handle_x = gui_points->
border[best_idx * 2];
392 *handle_y = gui_points->
border[best_idx * 2 + 1];
397 int node_index,
float *handle_x,
float *handle_y)
399 float resampled_x = 0.0f;
400 float resampled_y = 0.0f;
403 const float node_x = gui_points->
points[node_index * 6 + 2];
404 const float node_y = gui_points->
points[node_index * 6 + 3];
406 *handle_x = node_x - (resampled_x - node_x);
407 *handle_y = node_y - (resampled_y - node_y);
414 float *ctrl1x,
float *ctrl1y,
415 float *ctrl2x,
float *ctrl2y, gboolean clockwise)
419 *ctrl2x = ptx + pty - fy;
420 *ctrl2y = pty +
fx - ptx;
421 *ctrl1x = ptx - pty + fy;
422 *ctrl1y = pty -
fx + ptx;
426 *ctrl1x = ptx + pty - fy;
427 *ctrl1y = pty +
fx - ptx;
428 *ctrl2x = ptx - pty + fy;
429 *ctrl2y = pty -
fx + ptx;
437 float y4,
float *bezier1_x,
float *bezier1_y,
438 float *bezier2_x,
float *bezier2_y)
440 *bezier1_x = (-x1 + 6 * x2 + x3) / 6;
441 *bezier1_y = (-y1 + 6 * y2 + y3) / 6;
442 *bezier2_x = (x2 + 6 * x3 - x4) / 6;
443 *bezier2_y = (y2 + 6 * y3 - y4) / 6;
460 for(GList *form_points = mask_form->
points; form_points; form_points = g_list_next(form_points))
468 GList *
const prev = g_list_previous(form_points);
469 GList *
const prevprev = prev ? g_list_previous(prev) : NULL;
470 GList *
const next = g_list_next(form_points);
471 GList *
const nextnext = next ? g_list_next(next) : NULL;
480 start_point[0].
node[0] = start_point[1].
node[0] = 2 * point3->
node[0] - point4->
node[0];
481 start_point[0].
node[1] = start_point[1].
node[1] = 2 * point3->
node[1] - point4->
node[1];
482 point1 = &(start_point[0]);
483 point2 = &(start_point[1]);
487 start_point[0].
node[0] = 2 * point2->
node[0] - point3->
node[0];
488 start_point[0].
node[1] = 2 * point2->
node[1] - point3->
node[1];
489 point1 = &(start_point[0]);
494 end_point[0].
node[0] = end_point[1].
node[0] = 2 * point3->
node[0] - point2->
node[0];
495 end_point[0].
node[1] = end_point[1].
node[1] = 2 * point3->
node[1] - point2->
node[1];
496 point4 = &(end_point[0]);
497 point5 = &(end_point[1]);
501 end_point[0].
node[0] = 2 * point4->
node[0] - point3->
node[0];
502 end_point[0].
node[1] = 2 * point4->
node[1] - point3->
node[1];
503 point5 = &(end_point[0]);
507 float bx1 = 0.0f, by1 = 0.0f, bx2 = 0.0f, by2 = 0.0f;
510 &bx1, &by1, &bx2, &by2);
511 if(point2->
ctrl2[0] == -1.0) point2->
ctrl2[0] = bx1;
512 if(point2->
ctrl2[1] == -1.0) point2->
ctrl2[1] = by1;
513 point3->
ctrl1[0] = bx2;
514 point3->
ctrl1[1] = by2;
517 &bx1, &by1, &bx2, &by2);
518 if(point4->
ctrl1[0] == -1.0) point4->
ctrl1[0] = bx2;
519 if(point4->
ctrl1[1] == -1.0) point4->
ctrl1[1] = by2;
520 point3->
ctrl2[0] = bx1;
521 point3->
ctrl2[1] = by1;
531 gboolean clockwise,
const int step)
534 float a1 = atan2f(bmin[1] - cmax[1], bmin[0] - cmax[0]);
535 float a2 = atan2f(bmax[1] - cmax[1], bmax[0] - cmax[0]);
540 if(a2 < a1 && clockwise)
544 if(a2 > a1 && !clockwise)
550 float r1 = dt_fast_hypotf(bmin[1] - cmax[1], bmin[0] - cmax[0]);
551 float r2 = dt_fast_hypotf(bmax[1] - cmax[1], bmax[0] - cmax[0]);
554 const int l = fabsf(a2 - a1) * fmaxf(r1, r2) / (float)step;
558 const float incra = (a2 - a1) / l;
559 const float incrr = (r2 - r1) / l;
562 const float cos_incra = cosf(incra);
563 const float sin_incra = sinf(incra);
564 float rr = r1 + incrr;
565 float cos_aa = cosf(a1 + incra);
566 float sin_aa = sinf(a1 + incra);
575 for(
int i = 1;
i < l;
i++)
577 *dpoints_ptr++ = cmax[0];
578 *dpoints_ptr++ = cmax[1];
579 *dborder_ptr++ = cmax[0] + rr * cos_aa;
580 *dborder_ptr++ = cmax[1] + rr * sin_aa;
583 const float new_cos = cos_aa * cos_incra - sin_aa * sin_incra;
584 const float new_sin = sin_aa * cos_incra + cos_aa * sin_incra;
600 const float a1 = fmodf(atan2f(bmin[1] - cmax[1], bmin[0] - cmax[0]) + 2.0f *
M_PI, 2.0f *
M_PI);
601 const float a2 = fmodf(atan2f(bmax[1] - cmax[1], bmax[0] - cmax[0]) + 2.0f *
M_PI, 2.0f *
M_PI);
606 const float r1 = dt_fast_hypotf(bmin[1] - cmax[1], bmin[0] - cmax[0]);
607 const float r2 = dt_fast_hypotf(bmax[1] - cmax[1], bmax[0] - cmax[0]);
610 float delta = a2 - a1;
614 const int l = fabsf(
delta) * fmaxf(r1, r2) / (float)step;
618 const float incra =
delta / l;
619 const float incrr = (r2 - r1) / l;
622 const float cos_incra = cosf(incra);
623 const float sin_incra = sinf(incra);
624 float rr = r1 + incrr;
625 float cos_aa = cosf(a1 + incra);
626 float sin_aa = sinf(a1 + incra);
635 for(
int i = 1;
i < l;
i++)
637 *dpoints_ptr++ = cmax[0];
638 *dpoints_ptr++ = cmax[1];
639 *dborder_ptr++ = cmax[0] + rr * cos_aa;
640 *dborder_ptr++ = cmax[1] + rr * sin_aa;
643 const float new_cos = cos_aa * cos_incra - sin_aa * sin_incra;
644 const float new_sin = sin_aa * cos_incra + cos_aa * sin_incra;
665static void _brush_points_stamp(
const float *
const centre,
const float *
const from_border,
const float radius,
668 const float a1 = atan2f(from_border[1] - centre[1], from_border[0] - centre[0]);
671 const int l = 2.0f *
M_PI * radius / (float)step;
675 const float incra = 2.0f *
M_PI / l;
676 float aa = a1 + incra;
684 for(
int i = 0;
i < l;
i++)
686 *dpoints_ptr++ = centre[0];
687 *dpoints_ptr++ = centre[1];
688 *dborder_ptr++ = centre[0] + radius * cosf(aa);
689 *dborder_ptr++ = centre[1] + radius * sinf(aa);
697 return abs((
int)
min[0] - (
int)
max[0]) < pixel_threshold &&
698 abs((
int)
min[1] - (
int)
max[1]) < pixel_threshold;
702 const float v0,
const float v1)
706 while(payload_pos < target_pos)
716static inline gboolean
_brush_span_is_leaf(
const double tmin,
const double tmax,
const float *
const points_min,
717 const float *
const points_max,
const float *
const border_min,
718 const float *
const border_max,
const int pixel_threshold)
720 if(tmax - tmin < 0.0001f)
return TRUE;
737 const gboolean have_min,
const gboolean have_max,
738 const float *
const centre,
const float *
const chord,
const float radius)
745 border_min[0] = border_max[0];
746 border_min[1] = border_max[1];
752 border_max[0] = border_min[0];
753 border_max[1] = border_min[1];
845 const gboolean withborder = (!
IS_NULL_PTR(dborder));
858 points_min + 1, border_min, border_min + 1);
863 points_max + 1, border_max, border_max + 1);
869 const double tx = (span->
tmin + span->
tmax) / 2.0;
870 float c[2] = { 0.0f, 0.0f };
871 float b[2] = { 0.0f, 0.0f };
884 out->point[0] = points_max[0];
885 out->point[1] = points_max[1];
892 const float chord[2] = { p2[0] - p1[0], p2[1] - p1[1] };
897 if(abs((
int)border_max[0] - (
int)border_min[0]) > gap || abs((
int)border_max[1] - (
int)border_min[1]) > gap)
900 out->border[0] = border_max[0];
901 out->border[1] = border_max[1];
908 out->payload[0] = p1[5] + span->
tmax * (p2[5] - p1[5]);
909 out->payload[1] = p1[6] + span->
tmax * (p2[6] - p1[6]);
921 const gboolean forward,
const _brush_frame_t *
const frame,
float p1[7],
924 const float *
const from_ctrl = forward ? from->
ctrl2 : from->
ctrl1;
925 const float *
const to_ctrl = forward ? to->
ctrl1 : to->
ctrl2;
926 const float radius_scale =
MIN(frame->
iwd, frame->
iht);
928 p1[0] = from->
node[0] * frame->
iwd - frame->
dx;
929 p1[1] = from->
node[1] * frame->
iht - frame->
dy;
930 p1[2] = from_ctrl[0] * frame->
iwd - frame->
dx;
931 p1[3] = from_ctrl[1] * frame->
iht - frame->
dy;
932 p1[4] = from->
border[1] * radius_scale;
936 p2[0] = to->
node[0] * frame->
iwd - frame->
dx;
937 p2[1] = to->
node[1] * frame->
iht - frame->
dy;
938 p2[2] = to_ctrl[0] * frame->
iwd - frame->
dx;
939 p2[3] = to_ctrl[1] * frame->
iht - frame->
dy;
940 p2[4] = to->
border[0] * radius_scale;
947static void _brush_joint_arc(
const float *
const centre,
const float *
const from,
const float *
const to,
952 float f[2] = { from[0], from[1] };
953 float t[2] = { to[0], to[1] };
954 float c[2] = { centre[0], centre[1] };
978 const float *
const p1,
const float *
const p2,
const float *
const c0,
979 const float *
const b0,
const gboolean forward)
987 const gboolean payload_step = (fabsf(p1[5] - p2[5]) > 0.05f || fabsf(p1[6] - p2[6]) > 0.05f);
988 const gboolean radius_step = (fabsf(p1[4] - p2[4]) > 0.0001f || fabsf(s->
r - p1[4]) > 0.0001f);
989 if(payload_step || radius_step)
1002 if(fabsf(b0[0] - s->
b[0]) > 1.0f || fabsf(b0[1] - s->
b[1]) > 1.0f)
1012 const int k1,
const gboolean forward)
1023 const gboolean have_b0 =
_brush_border_get_XY(p1[0], p1[1], p1[2], p1[3], p2[2], p2[3], p2[0], p2[1], 0.0f,
1024 p1[4], 0.0f, c0, c0 + 1, b0, b0 + 1);
1025 const gboolean have_b1 =
_brush_border_get_XY(p1[0], p1[1], p1[2], p1[3], p2[2], p2[3], p2[0], p2[1], 1.0f,
1026 p2[4], 0.0f,
c1,
c1 + 1, b1, b1 + 1);
1027 if(!have_b0 && !have_b1)
return FALSE;
1033 float bmin[2] = { b0[0], b0[1] };
1034 float bmax[2] = { b1[0], b1[1] };
1035 float cmin[2] = { c0[0], c0[1] };
1036 float cmax[2] = {
c1[0],
c1[1] };
1067 float centre[2] = { s->
c[0], s->
c[1] };
1068 float from[2] = { s->
b[0], s->
b[1] };
1069 float opposite[2] = { 2.0f * s->
c[0] - s->
b[0], 2.0f * s->
c[1] - s->
b[1] };
1080 for(
int step = 0; step < last; step++)
1082 const int k = forward ? step : last - step;
1083 const int k1 = forward ? step + 1 : last - 1 - step;
1097 const float from[2] = { centre[0] + radius, centre[1] };
1144 const double iop_order,
const int transform_direction,
1157 float pts[2] = { mask_form->
source[0], mask_form->
source[1] };
1161 const float dx = pts[0] - o->
points[2];
1162 const float dy = pts[1] - o->
points[3];
1163 float *
const points = o->
points;
1166 for(
int i = 0;
i < count;
i++)
1168 points[
i * 2] += dx;
1169 points[
i * 2 + 1] += dy;
1187 const double iop_order,
const int transform_direction,
1189 float **border_buffer,
int *border_count,
float **payload_buffer,
1191 int *border_skip_count,
int use_source)
1193 *point_buffer = NULL;
1195 if(border_buffer) *border_buffer = NULL;
1196 if(!
IS_NULL_PTR(border_buffer)) *border_count = 0;
1197 if(payload_buffer) *payload_buffer = NULL;
1198 if(!
IS_NULL_PTR(payload_buffer)) *payload_count = 0;
1199 if(!
IS_NULL_PTR(border_skips)) *border_skips = NULL;
1200 if(!
IS_NULL_PTR(border_skip_count)) *border_skip_count = 0;
1203 double start2 = 0.0;
1206 const float iwd =
dist->iwidth;
1207 const float iht =
dist->iheight;
1211 const int pixel_threshold =
dist->rasterization_step;
1226 dx = (pt->
node[0] - mask_form->
source[0]) * iwd;
1227 dy = (pt->
node[1] - mask_form->
source[1]) * iht;
1230 const guint node_count = g_list_length(mask_form->
points);
1241 guint node_index = 0;
1242 for(GList *form_points = mask_form->
points; form_points; form_points = g_list_next(form_points))
1249 buf[0] = pt->
ctrl1[0] * iwd - dx;
1250 buf[1] = pt->
ctrl1[1] * iht - dy;
1251 buf[2] = pt->
node[0] * iwd - dx;
1252 buf[3] = pt->
node[1] * iht - dy;
1253 buf[4] = pt->
ctrl2[0] * iwd - dx;
1254 buf[5] = pt->
ctrl2[1] * iht - dy;
1278 .node_count = (int)node_count,
1279 .frame = { iwd, iht, dx, dy },
1280 .pixel_threshold = pixel_threshold,
1284 .dpayload = dpayload };
1322 .point_count = *point_count,
1323 .border =
IS_NULL_PTR(border_buffer) ? NULL : *border_buffer,
1324 .border_count =
IS_NULL_PTR(border_buffer) ? 0 : *border_count };
1335 *point_buffer = NULL;
1340 *border_buffer = NULL;
1346 *payload_buffer = NULL;
1357 int corner_count,
int *inside,
int *inside_border,
1358 int *near_handle,
int *inside_source,
float *distance)
1367 *distance = FLT_MAX;
1377 float min_dist = FLT_MAX;
1379 const float radius2 = radius * radius;
1389 const float dx = -gui_points->
points[2] + gui_points->
source[2];
1390 const float dy = -gui_points->
points[3] + gui_points->
source[3];
1392 int current_seg = 1;
1396 if(gui_points->
points[
i * 2 + 1] == gui_points->
points[current_seg * 6 + 3]
1397 && gui_points->
points[
i * 2] == gui_points->
points[current_seg * 6 + 2])
1399 current_seg = (current_seg + 1) % corner_count;
1402 const float yy = gui_points->
points[
i * 2 + 1] + dy;
1403 const float xx = gui_points->
points[
i * 2] + dx;
1405 const float sdx = point_x - xx;
1406 const float sdy = point_y - yy;
1407 const float dd = (sdx * sdx) + (sdy * sdy);
1412 if(dd < radius2 && *inside == 0)
1414 if(current_seg == 0)
1415 *inside_source = corner_count - 1;
1417 *inside_source = current_seg - 1;
1436 float min_dist_points = FLT_MAX;
1437 int current_seg = 1;
1441 if(gui_points->
points[
i * 2 + 1] == gui_points->
points[current_seg * 6 + 3]
1442 && gui_points->
points[
i * 2] == gui_points->
points[current_seg * 6 + 2])
1444 current_seg = (current_seg + 1) % corner_count;
1447 const float yy = gui_points->
points[
i * 2 + 1];
1448 const float xx = gui_points->
points[
i * 2];
1450 const float dx = point_x - xx;
1451 const float dy = point_y - yy;
1452 const float dd = (dx * dx) + (dy * dy);
1453 if(dd < min_dist_points)
1455 min_dist_points = dd;
1457 if(current_seg > 0 && dd < radius2)
1459 *near_handle = current_seg - 1;
1463 min_dist =
MIN(min_dist, min_dist_points);
1471 const int start = corner_count * 3;
1472 const float *
const border = gui_points->
border;
1473 float last_y = border[gui_points->
border_count * 2 - 1];
1478 const int idx =
i * 2;
1479 const float xx = border[idx];
1480 const float yy = border[idx + 1];
1482 const float dx = point_x - xx;
1483 const float dy = point_y - yy;
1484 if(dx * dx + dy * dy < radius2) nearest = idx;
1486 if(((point_y <= yy && point_y > last_y) || (point_y >= yy && point_y < last_y)) && (xx > point_x))
1496 *inside = (nearest != -1 || (crossings & 1));
1497 *inside_border = (nearest != -1) && (*near_handle < 0);
1500 *distance = min_dist;
1507 float **point_buffer,
int *point_count,
1508 float **border_buffer,
int *border_count,
1512 if(!
IS_NULL_PTR(border_skips)) *border_skips = NULL;
1513 if(!
IS_NULL_PTR(border_skip_count)) *border_skip_count = 0;
1517 const double ioporder = (module) ? module->
iop_order : 0.0f;
1521 &gui_dist, point_buffer, point_count, border_buffer,
1522 border_count, NULL, NULL, border_skips, border_skip_count, use_source);
1531 const int header = (int)g_list_length(mask_form->
points) * 3;
1533 *border_skips = NULL;
1548 GList *first_node_entry = g_list_nth(mask_form->
points, segment_index);
1558 float best_t = 0.0f;
1559 float best_dist2 = FLT_MAX;
1561 for(
int sample_index = 0; sample_index <= 100; sample_index++)
1563 const float t = sample_index / 100.0f;
1564 float sample_x = 0.0f;
1565 float sample_y = 0.0f;
1567 point2->
node[0], point2->
node[1], point3->
node[0], point3->
node[1],
t, &sample_x, &sample_y);
1569 const float dist2 = (point_x - sample_x) * (point_x - sample_x)
1570 + (point_y - sample_y) * (point_y - sample_y);
1571 if(dist2 < best_dist2)
1587 float *handle_x,
float *handle_y,
void *user_data)
1598 float *handle_x,
float *handle_y,
void *user_data)
1601 gui_points->
points[node_index * 6 + 4], gui_points->
points[node_index * 6 + 5],
1602 handle_x, handle_y,
TRUE);
1610 int *inside_border,
int *near_handle,
int *inside_source,
float *
dist,
void *user_data)
1612 _brush_get_distance(pointer_x, pointer_y, cursor_radius, mask_gui, form_index, node_count,
1613 inside, inside_border, near_handle, inside_source,
dist);
1629 _(
"fading: %3.2f%%"), 100.0f);
1640 _(
"size: %3.2f%%"), 2.f * 100.f);
1650 increment, flow, _(
"opacity: %3.2f%%"), 100.f);
1663 if(
size <= 0.0f)
return NAN;
1668 float fading_sum = 0.0f;
1669 int fading_count = 0;
1671 for(
const GList *point_node = mask_form->
points; point_node; point_node = g_list_next(point_node))
1675 fading_sum += node->
fading;
1679 return fading_count > 0 ? fading_sum / (float)fading_count : NAN;
1690 const int points_count = g_list_length(mask_form->
points);
1691 if(points_count <= 0)
return FALSE;
1697 for(
const GList *l = mask_form->
points; l; l = g_list_next(l))
1701 point_buffer[2 *
i] =
point->node[0];
1702 point_buffer[2 *
i + 1] =
point->node[1];
1731 if(!
_change_size(mask_form, 0, mask_gui, module, index,
value, increment, flow))
return NAN;
1734 if(!
_change_fading(mask_form, 0, mask_gui, module, index,
value, increment, flow))
return NAN;
1747 const float scale_amount = 1.f / powf(amount, (
float)flow);
1748 const float offset_amount = -amount * (float)flow;
1749 float result_amount = 0.0f;
1750 for(GList *node_entry = mask_form->
points; node_entry; node_entry = g_list_next(node_entry), node_index++)
1755 const float current_fading = node->
fading;
1778 for(GList *node_entry = mask_form->
points; node_entry; node_entry = g_list_next(node_entry), node_index++)
1784 if(amount > 1.0f && (node->
border[0] > 1.0f || node->
border[1] > 1.0f))
1790 const float scale_amount = amount;
1791 const float offset_amount = amount;
1793 for(GList *node_entry = mask_form->
points; node_entry; node_entry = g_list_next(node_entry), node_index++)
1800 scale_amount, offset_amount, increment);
1802 scale_amount, offset_amount, increment);
1836 return _init_fading(mask_form, parentid, mask_gui, scroll_up ? 1.02f : 0.98f,
1839 return _init_opacity(mask_form, parentid, mask_gui, scroll_up ? +0.02f : -0.02f,
1842 return _init_size(mask_form, parentid, mask_gui, scroll_up ? 1.02f : 0.98f,
1862 return _change_fading(mask_form, parentid, mask_gui, module, index, scroll_up ? -0.01f : 0.01f,
1865 return _change_size(mask_form, parentid, mask_gui, module, index, scroll_up ? 1.02f : 0.98f,
1881 if(!strcmp(psens,
"fading (absolute)"))
1883 else if(!strcmp(psens,
"fading (relative)"))
1885 else if(!strcmp(psens,
"opacity (absolute)"))
1887 else if(!strcmp(psens,
"opacity (relative)"))
1889 else if(!strcmp(psens,
"brush size (relative)"))
1898 const guint node_count = g_list_length(mask_form->
points);
1900 if(selected_segment < 0 || selected_segment >= (
int)node_count)
return;
1914 mask_form, selected_segment);
1916 GList *pt = g_list_nth(mask_form->
points, selected_segment);
1935 mask_form->
points = g_list_insert(mask_form->
points, new_node, selected_segment + 1);
1954 for(GList *node_entry = mask_form->
points; node_entry; node_entry = g_list_next(node_entry))
1959 double pressure,
int which,
int type, uint32_t
state,
1964 if(
type == GDK_2BUTTON_PRESS ||
type == GDK_3BUTTON_PRESS)
return 1;
1967 const float masks_density = 1.0f;
2015 const guint node_count = g_list_length(mask_form->
points);
2050 float handle_x, handle_y;
2054 &handle_x, &handle_y,
TRUE);
2056 mask_gui->
delta[0] = handle_x - mask_gui->
pos[0];
2057 mask_gui->
delta[1] = handle_y - mask_gui->
pos[1];
2064 float handle_x = 0.0f, handle_y = 0.0f;
2066 &handle_x, &handle_y))
2068 mask_gui->
delta[0] = handle_x - mask_gui->
pos[0];
2069 mask_gui->
delta[1] = handle_y - mask_gui->
pos[1];
2113 if(!strcmp(smoothing,
"low"))
2115 else if(!strcmp(smoothing,
"medium"))
2117 else if(!strcmp(smoothing,
"high"))
2126 float *payload = payload_buffer + 4 *
i;
2127 float pressure = payload[3];
2142 payload[2] =
MAX(0.05f, pressure);
2145 payload[2] =
MAX(0.05f, payload[2] * pressure);
2171 if(mask_gui->
creation && which == 1)
2214 guipoints_payload, epsilon2);
2232 int group_index = 0;
2233 int selected_index = -1;
2234 for(GList *group_entry = grp->
points; group_entry; group_entry = g_list_next(group_entry))
2239 selected_index = group_index;
2244 if(selected_index < 0)
return 1;
2298 const int iwidth =
geometry.raw_width;
2299 const int iheight =
geometry.raw_height;
2323 const guint node_count = g_list_length(mask_form->
points);
2331 float delta_x = 0.0f;
2332 float delta_y = 0.0f;
2350 if(!segment_node || !segment_node_next)
return 0;
2352 float delta_x = 0.0f;
2353 float delta_y = 0.0f;
2372 float cursor_pos[2];
2376 float control_points[4];
2379 cursor_pos[1], &control_points[0], &control_points[1], &control_points[2],
2380 &control_points[3],
TRUE);
2400 float handle_x = 0.0f, handle_y = 0.0f;
2404 const float node_px = gui_points->
points[node_index * 6 + 2];
2405 const float node_py = gui_points->
points[node_index * 6 + 3];
2408 float cursor_pos[2];
2409 const float node_pos_gui[2] = { node_px, node_py };
2410 const float handle_pos[2] = { handle_x, handle_y };
2428 float delta_x = 0.0f;
2429 float delta_y = 0.0f;
2437 mask_form->
source[0] = raw_pos[0];
2438 mask_form->
source[1] = raw_pos[1];
2462 const int first = node_count * 3;
2463 if(points_count <= first)
return points_count;
2464 return first + (points_count - first) / 2;
2467static void _brush_draw_shape(
struct dt_develop_t *dev, cairo_t *cr,
const float *points,
const int points_count,
const int node_nb,
const gboolean border,
const gboolean source,
2475 const int first = node_nb * 3 + border;
2482 float total_len = 0.0f;
2483 for(
int i = first_pt;
i < last_pt;
i++)
2485 const int i0 =
i * 2;
2486 const int i1 = (
i + 1) * 2;
2487 const float x0 = line[i0];
2488 const float y0 = line[i0 + 1];
2489 const float x1 = line[i1];
2490 const float y1 = line[i1 + 1];
2491 const float dx = x1 - x0;
2492 const float dy = y1 - y0;
2493 const float len = dx * dx + dy * dy;
2494 if(len > 1e-12f) total_len += sqrtf(len);
2500 const float target_len,
float *
x,
float *y)
2503 if(last_pt <= first_pt)
return FALSE;
2506 gboolean has_fallback =
FALSE;
2507 float fallback_x = 0.0f, fallback_y = 0.0f;
2509 for(
int i = first_pt;
i < last_pt;
i++)
2511 const int i0 =
i * 2;
2512 const int i1 = (
i + 1) * 2;
2513 const float x0 = line[i0];
2514 const float y0 = line[i0 + 1];
2515 const float x1 = line[i1];
2516 const float y1 = line[i1 + 1];
2518 const float dx = x1 - x0;
2519 const float dy = y1 - y0;
2520 const float len = dt_fast_hypotf(dx, dy);
2521 if(len <= 1e-6f)
continue;
2523 has_fallback =
TRUE;
2527 if(acc + len >= target_len)
2529 const float t = (target_len - acc) / len;
2537 if(!has_fallback)
return FALSE;
2544 float *mx,
float *my)
2548 if(total_len <= 1e-6f)
return FALSE;
2550 const float half_len = 0.5f * total_len;
2561 const int line_offset_pt = node_count * 3;
2564 const int line_end =
MIN(points_line_end, source_line_end);
2565 const int line_count = line_end - line_offset_pt;
2566 if(line_count < 2)
return FALSE;
2568 const float *
const points_line = gui_points->
points + 2 * line_offset_pt;
2569 const float *
const source_line = gui_points->
source + 2 * line_offset_pt;
2570 const int first_pt = 0;
2571 const int last_pt = line_count - 1;
2589 const float iwd =
geometry.raw_width;
2590 const float iht =
geometry.raw_height;
2591 const float min_iwd_iht =
MIN(iwd, iht);
2604 const float radius1 = masks_border * masks_fading * min_iwd_iht;
2605 const float radius2 = masks_border * min_iwd_iht;
2607 float xpos = mask_gui->
pos[0];
2608 float ypos = mask_gui->
pos[1];
2609 if((xpos == -1.0f && ypos == -1.0f))
2620 cairo_arc(cr, xpos, ypos, radius1, 0, 2.0 *
M_PI);
2621 cairo_fill_preserve(cr);
2622 cairo_set_source_rgba(cr, .8, .8, .8, .8);
2625 cairo_arc(cr, xpos, ypos, radius2, 0, 2.0 *
M_PI);
2635 float masks_border = 0.0f, masks_fading = 0.0f, masks_density = 0.0f;
2636 float radius = 0.0f, oldradius = 0.0f, opacity = 0.0f, oldopacity = 0.0f, pressure = 0.0f;
2643 cairo_set_line_join(cr, CAIRO_LINE_JOIN_ROUND);
2644 cairo_set_line_cap(cr, CAIRO_LINE_CAP_ROUND);
2645 masks_border = guipoints_payload[0];
2646 masks_fading = guipoints_payload[1];
2647 masks_density = guipoints_payload[2];
2648 pressure = guipoints_payload[3];
2659 masks_density =
MAX(0.05f, pressure);
2662 masks_density =
MAX(0.05f, masks_density * pressure);
2673 radius = oldradius = masks_border * masks_fading * min_iwd_iht;
2674 opacity = oldopacity = masks_density;
2679 cairo_move_to(cr, guipoints[0], guipoints[1]);
2682 cairo_line_to(cr, guipoints[
i * 2], guipoints[
i * 2 + 1]);
2684 masks_border = guipoints_payload[
i * 4];
2685 masks_fading = guipoints_payload[
i * 4 + 1];
2686 masks_density = guipoints_payload[
i * 4 + 2];
2687 pressure = guipoints_payload[
i * 4 + 3];
2698 masks_density =
MAX(0.05f, pressure);
2701 masks_density =
MAX(0.05f, masks_density * pressure);
2712 radius = masks_border * masks_fading * min_iwd_iht;
2713 opacity = masks_density;
2715 if(radius != oldradius || opacity != oldopacity)
2722 oldopacity = opacity;
2723 cairo_move_to(cr, guipoints[
i * 2], guipoints[
i * 2 + 1]);
2726 if(!stroked) cairo_stroke(cr);
2733 radius, 0, 2.0 *
M_PI);
2734 cairo_fill_preserve(cr);
2735 cairo_set_source_rgba(cr, .8, .8, .8, .8);
2740 guipoints[2 * (mask_gui->
guipoints_count - 1) + 1], masks_border * min_iwd_iht, 0,
2749 guipoints[
i * 2], guipoints[
i * 2 + 1],
TRUE);
2768 if(gui_points->
points_count <= node_count * 3 + 2)
return;
2771 if(node_count > 0 && gui_points->
points_count > node_count * 3 + 6)
2782 node_count, zoom_scale,
TRUE);
2789 int current_seg = 0;
2792 cairo_move_to(cr, gui_points->
points[node_count * 6], gui_points->
points[node_count * 6 + 1]);
2796 for(
int i = node_count * 3;
i < end_idx;
i++)
2798 const double x = gui_points->
points[
i * 2];
2799 const double y = gui_points->
points[
i * 2 + 1];
2800 cairo_line_to(cr,
x, y);
2802 int seg_idx = seg1 * 6;
2803 if((seg_idx + 3) < total_points)
2805 const double segment_x = gui_points->
points[seg_idx + 2];
2806 const double segment_y = gui_points->
points[seg_idx + 3];
2809 if(
x == segment_x && y == segment_y)
2811 const gboolean seg_selected = (mask_gui->
group_selected == index)
2812 && (selected_segment == current_seg);
2813 const gboolean all_selected = (mask_gui->
group_selected == index)
2820 if(mask_gui->
creation && current_seg == node_count - 2)
2824 seg1 = (seg1 + 1) % node_count;
2843 if(mask_gui->
node_selected && selected_node >= 0 && selected_node < node_count
2846 const int n = selected_node;
2849 gui_points->
points[
n * 6 + 4], gui_points->
points[
n * 6 + 5], &handle[0], &handle[1],
2851 const float pt[2] = { gui_points->
points[
n * 6 + 2], gui_points->
points[
n * 6 + 3] };
2857 for(
int k = 0;
k < node_count;
k++)
2860 const float x = gui_points->
points[
k * 6 + 2];
2861 const float y = gui_points->
points[
k * 6 + 3];
2863 const gboolean action = (
k == selected_node);
2869 if(mask_gui->
node_selected && selected_node >= 0 && selected_node < node_count)
2871 const int edited = selected_node;
2873 || selected_handle_border == edited
2875 float handle[2] = { 0.0f, 0.0f };
2893 for(
int k = 0;
k < node_count;
k++)
2897 || (mask_gui->
creation &&
k == node_count - 1)))
2899 const int node_index =
k * 6 + 2;
2900 const float proj[2] = { gui_points->
source[node_index], gui_points->
source[node_index + 1] };
2917 const int node_count,
const int total_points,
float *x_min,
float *x_max,
2918 float *y_min,
float *y_max)
2921 float min_x = FLT_MAX, max_x = FLT_MIN, min_y = FLT_MAX, max_y = FLT_MIN;
2924 for(
int point_index = node_count * 3; point_index < total_points; point_index++)
2927 const float border_x = border[point_index * 2];
2928 const float border_y = border[point_index * 2 + 1];
2929 min_x =
MIN(border_x, min_x);
2930 max_x =
MAX(border_x, max_x);
2931 min_y =
MIN(border_y, min_y);
2932 max_y =
MAX(border_y, max_y);
2934 const float brush_x = points[point_index * 2];
2935 const float brush_y = points[point_index * 2 + 1];
2936 min_x =
MIN(brush_x, min_x);
2937 max_x =
MAX(brush_x, max_x);
2938 min_y =
MIN(brush_y, min_y);
2939 max_y =
MAX(brush_y, max_y);
2953 const int total_points,
int *
width,
int *
height,
int *offset_x,
int *offset_y)
2955 float min_x = FLT_MAX, max_x = FLT_MIN, min_y = FLT_MAX, max_y = FLT_MIN;
2957 *
height = max_y - min_y + 4;
2958 *
width = max_x - min_x + 4;
2959 *offset_x = min_x - 2;
2960 *offset_y = min_y - 2;
2975 float *points = NULL, *border = NULL;
2976 int points_count, border_count;
2979 &points, &points_count, &border, &border_count, NULL, NULL, NULL, NULL, include_source) != 0)
2986 const guint node_count = g_list_length(mask_form->
points);
2987 if(
IS_NULL_PTR(points) ||
IS_NULL_PTR(border) || points_count <= (
int)(node_count * 3) || border_count <= (
int)(node_count * 3))
3030static void _brush_falloff(
float *
const restrict buffer,
int segment_start[2],
int segment_end[2],
3031 int offset_x,
int offset_y,
int buffer_width,
float fading,
float density)
3034 const int segment_length = sqrt((segment_end[0] - segment_start[0]) * (segment_end[0] - segment_start[0])
3035 + (segment_end[1] - segment_start[1]) * (segment_end[1] - segment_start[1]))
3037 const int solid_length = (int)segment_length * fading;
3038 const int soft_length = segment_length - solid_length;
3040 const float segment_dx = segment_end[0] - segment_start[0];
3041 const float segment_dy = segment_end[1] - segment_start[1];
3042 const float inv_length = 1.0f / (float)segment_length;
3043 const float inv_soft = (soft_length > 0) ? 1.0f / (
float)soft_length : 0.0f;
3045 for(
int step = 0; step < segment_length; step++)
3048 const int x = (int)((
float)step * segment_dx * inv_length) + segment_start[0] - offset_x;
3049 const int y = (int)((
float)step * segment_dy * inv_length) + segment_start[1] - offset_y;
3050 const float opacity = density * ((step <= solid_length) ? 1.0f : 1.0f - (float)(step - solid_length) * inv_soft);
3051 const int buffer_index = y * buffer_width +
x;
3052 buffer[buffer_index] =
MAX(buffer[buffer_index], opacity);
3054 buffer[buffer_index - 1] =
MAX(buffer[buffer_index - 1], opacity);
3056 buffer[buffer_index - buffer_width] =
MAX(buffer[buffer_index - buffer_width], opacity);
3068 float **buffer,
int *
width,
int *
height,
int *offset_x,
int *offset_y)
3076 double timer_start = 0.0;
3077 double timer_step_start = 0.0;
3081 float *points = NULL, *border = NULL, *payload = NULL;
3082 int points_count, border_count, payload_count;
3085 &points, &points_count,
3086 &border, &border_count, &payload, &payload_count, NULL, NULL, 0) != 0)
3101 const guint node_count = g_list_length(mask_form->
points);
3103 || border_count <= (
int)(node_count * 3))
3124 const size_t buffer_size = (size_t)(*
width) * (*height);
3134 memset(*buffer, 0,
sizeof(
float) * buffer_size);
3137 int segment_start[2], segment_end[2];
3138 int prev_start[2] = { 0, 0 };
3139 int prev_end[2] = { 0, 0 };
3140 float prev_payload[2] = { 0.0f, 0.0f };
3141 gboolean have_prev =
FALSE;
3144 for(
int border_index = node_count * 3; border_index < border_count; border_index++)
3146 segment_start[0] = points[border_index * 2];
3147 segment_start[1] = points[border_index * 2 + 1];
3148 segment_end[0] = border[border_index * 2];
3149 segment_end[1] = border[border_index * 2 + 1];
3151 if(use_sparse && have_prev
3152 && (prev_start[0] != segment_start[0] || prev_start[1] != segment_start[1]
3153 || prev_end[0] != segment_end[0] || prev_end[1] != segment_end[1]))
3156 for(
int step = 1; step < sparse_step; step++)
3158 const float t = (float)step / (
float)sparse_step;
3159 int interp_start[2] = { (int)floorf(prev_start[0] +
t * (segment_start[0] - prev_start[0]) + 0.5f),
3160 (
int)floorf(prev_start[1] +
t * (segment_start[1] - prev_start[1]) + 0.5f) };
3161 int interp_end[2] = { (int)floorf(prev_end[0] +
t * (segment_end[0] - prev_end[0]) + 0.5f),
3162 (
int)floorf(prev_end[1] +
t * (segment_end[1] - prev_end[1]) + 0.5f) };
3163 const float hard = prev_payload[0] +
t * (payload[border_index * 2] - prev_payload[0]);
3164 const float dens = prev_payload[1] +
t * (payload[border_index * 2 + 1] - prev_payload[1]);
3165 _brush_falloff(*buffer, interp_start, interp_end, *offset_x, *offset_y, *
width, hard, dens);
3170 payload[border_index * 2], payload[border_index * 2 + 1]);
3172 prev_start[0] = segment_start[0];
3173 prev_start[1] = segment_start[1];
3174 prev_end[0] = segment_end[0];
3175 prev_end[1] = segment_end[1];
3176 prev_payload[0] = payload[border_index * 2];
3177 prev_payload[1] = payload[border_index * 2 + 1];
3194 int buffer_width,
int buffer_height,
float fading,
float density)
3197 const int segment_length = sqrt((segment_end[0] - segment_start[0]) * (segment_end[0] - segment_start[0])
3198 + (segment_end[1] - segment_start[1]) * (segment_end[1] - segment_start[1]))
3200 const int solid_length = fading * segment_length;
3202 const float step_x = (float)(segment_end[0] - segment_start[0]) / (float)segment_length;
3203 const float step_y = (float)(segment_end[1] - segment_start[1]) / (float)segment_length;
3205 const int direction_x = step_x <= 0 ? -1 : 1;
3206 const int direction_y = step_y <= 0 ? -1 : 1;
3207 const int neighbor_offset_x = direction_x;
3208 const int neighbor_offset_y = direction_y * buffer_width;
3210 float cursor_x = segment_start[0];
3211 float cursor_y = segment_start[1];
3213 float opacity = density;
3214 const float opacity_step = density / (float)(segment_length - solid_length);
3216 const int start_x = segment_start[0], start_y = segment_start[1];
3217 const int end_x = segment_end[0], end_y = segment_end[1];
3218 if((start_x < 0 && end_x < 0) || (start_x >= buffer_width && end_x >= buffer_width)
3219 || (start_y < 0 && end_y < 0) || (start_y >= buffer_height && end_y >= buffer_height))
3221 const int fully_inside = (start_x >= 0 && start_x < buffer_width && end_x >= 0 && end_x < buffer_width
3222 && start_y >= 0 && start_y < buffer_height && end_y >= 0
3223 && end_y < buffer_height);
3225 for(
int step = 0; step < segment_length; step++)
3227 const int x = cursor_x;
3228 const int y = cursor_y;
3232 if(step > solid_length) opacity -= opacity_step;
3234 if(!fully_inside && (x < 0 || x >= buffer_width || y < 0 || y >= buffer_height))
continue;
3236 float *buf = buffer + (size_t)y * buffer_width +
x;
3238 *buf =
MAX(*buf, opacity);
3239 if(
x + direction_x >= 0 &&
x + direction_x < buffer_width)
3240 buf[neighbor_offset_x] =
MAX(buf[neighbor_offset_x], opacity);
3241 if(y + direction_y >= 0 && y + direction_y < buffer_height)
3242 buf[neighbor_offset_y] =
MAX(buf[neighbor_offset_y], opacity);
3260 double timer_start = 0.0;
3261 double timer_step_start = 0.0;
3264 const int roi_offset_x = roi->
x;
3265 const int roi_offset_y = roi->
y;
3266 const int roi_width = roi->
width;
3267 const int roi_height = roi->
height;
3268 const float roi_scale = roi->
scale;
3273 float *points = NULL, *border = NULL, *payload = NULL;
3275 int points_count, border_count, payload_count;
3279 &points, &points_count, &border, &border_count, &payload, &payload_count, NULL, NULL, 0) != 0)
3294 const guint node_count = g_list_length(mask_form->
points);
3296 || border_count <= (
int)(node_count * 3))
3306 for(
int border_index = node_count * 3; border_index < border_count; border_index++)
3308 const float border_x = border[2 * border_index];
3309 const float border_y = border[2 * border_index + 1];
3310 border[2 * border_index] = border_x * roi_scale - roi_offset_x;
3311 border[2 * border_index + 1] = border_y * roi_scale - roi_offset_y;
3315 for(
int point_index = node_count * 3; point_index < points_count; point_index++)
3317 const float point_x = points[2 * point_index];
3318 const float point_y = points[2 * point_index + 1];
3319 points[2 * point_index] = point_x * roi_scale - roi_offset_x;
3320 points[2 * point_index + 1] = point_y * roi_scale - roi_offset_y;
3324 float min_x = 0.0f, max_x = 0.0f, min_y = 0.0f, max_y = 0.0f;
3335 if(max_x < 0 || max_y < 0 || min_x >= roi_width || min_y >= roi_height)
3348 for(
int border_index = node_count * 3; border_index < border_count; border_index++)
3350 const int segment_start[] = { points[border_index * 2], points[border_index * 2 + 1] };
3351 const int segment_end[] = { border[border_index * 2], border[border_index * 2 + 1] };
3353 if(
MAX(segment_start[0], segment_end[0]) < 0 ||
MIN(segment_start[0], segment_end[0]) >= roi_width
3354 ||
MAX(segment_start[1], segment_end[1]) < 0
3355 ||
MIN(segment_start[1], segment_end[1]) >= roi_height)
3358 _brush_falloff_roi(buffer, segment_start, segment_end, roi_width, roi_height, payload[border_index * 2],
3359 payload[border_index * 2 + 1]);
3364 int prev_start[2] = { 0, 0 };
3365 int prev_end[2] = { 0, 0 };
3366 float prev_payload[2] = { 0.0f, 0.0f };
3367 gboolean have_prev =
FALSE;
3370 for(
int border_index = node_count * 3; border_index < border_count; border_index++)
3372 int segment_start[2] = { points[border_index * 2], points[border_index * 2 + 1] };
3373 int segment_end[2] = { border[border_index * 2], border[border_index * 2 + 1] };
3375 if(use_sparse && have_prev
3376 && (prev_start[0] != segment_start[0] || prev_start[1] != segment_start[1]
3377 || prev_end[0] != segment_end[0] || prev_end[1] != segment_end[1]))
3379 for(
int step = 1; step < sparse_step; step++)
3381 const float t = (float)step / (
float)sparse_step;
3382 int interp_start[2] = { (int)floorf(prev_start[0] +
t * (segment_start[0] - prev_start[0]) + 0.5f),
3383 (
int)floorf(prev_start[1] +
t * (segment_start[1] - prev_start[1]) + 0.5f) };
3384 int interp_end[2] = { (int)floorf(prev_end[0] +
t * (segment_end[0] - prev_end[0]) + 0.5f),
3385 (
int)floorf(prev_end[1] +
t * (segment_end[1] - prev_end[1]) + 0.5f) };
3386 if(!(
MAX(interp_start[0], interp_end[0]) < 0 ||
MIN(interp_start[0], interp_end[0]) >= roi_width
3387 ||
MAX(interp_start[1], interp_end[1]) < 0 ||
MIN(interp_start[1], interp_end[1]) >= roi_height))
3389 const float hard = prev_payload[0] +
t * (payload[border_index * 2] - prev_payload[0]);
3390 const float dens = prev_payload[1] +
t * (payload[border_index * 2 + 1] - prev_payload[1]);
3391 _brush_falloff_roi(buffer, interp_start, interp_end, roi_width, roi_height, hard, dens);
3396 if(!(
MAX(segment_start[0], segment_end[0]) < 0 ||
MIN(segment_start[0], segment_end[0]) >= roi_width
3397 ||
MAX(segment_start[1], segment_end[1]) < 0
3398 ||
MIN(segment_start[1], segment_end[1]) >= roi_height))
3400 payload[border_index * 2], payload[border_index * 2 + 1]);
3402 prev_start[0] = segment_start[0];
3403 prev_start[1] = segment_start[1];
3404 prev_end[0] = segment_end[0];
3405 prev_end[1] = segment_end[1];
3406 prev_payload[0] = payload[border_index * 2];
3407 prev_payload[1] = payload[border_index * 2 + 1];
3418 dt_masks_touched_set(touched, (
int)floorf(min_x) - 2, (
int)floorf(min_y) - 2, (
int)ceilf(max_x) + 2,
3419 (
int)ceilf(max_y) + 2, roi_width, roi_height);
3439 snprintf(mask_form->
name,
sizeof(mask_form->
name), _(
"brush #%d"), (
int)form_number);
3444 char *
const restrict msgbuf,
const size_t msgbuf_len)
3453 g_strlcat(msgbuf, _(
"<b>Set source</b>: Shift+Click"), msgbuf_len);
3456 g_strlcat(msgbuf, _(
"<b>Move source</b>: Drag"), msgbuf_len);
3458 g_strlcat(msgbuf, _(
"<b>Node size</b>: Drag"), msgbuf_len);
3460 g_strlcat(msgbuf, _(
"<b>Node curvature</b>: Drag"), msgbuf_len);
3463 g_strlcat(msgbuf, _(
"<b>Move node</b>: Drag, <b>Switch smooth/sharp</b>: Ctrl+Click\n"
3464 "<b>Delete node</b>: Right-click or Del"), msgbuf_len);
3466 g_strlcat(msgbuf, _(
"<b>Move node</b>: Drag, <b>Delete node</b>: Right-click"), msgbuf_len);
3468 g_strlcat(msgbuf, _(
"<b>Move segment</b>: Drag, <b>Add node</b>: Ctrl+Click"), msgbuf_len);
3470 g_strlcat(msgbuf, _(
"<b>Move</b>: Drag"), msgbuf_len);
3483 float offset[2] = { 0.01f, 0.01f };
3500 const int form_id = mask_gui->
formid;
3524 const int form_id = mask_gui->
formid;
3535 node_index, &node->
state);
3561 const float pointer_x,
const float pointer_y)
3568 gboolean ret =
FALSE;
3581 gchar *to_change_type = g_strdup_printf(_(
"Switch to %s node"), (is_corner) ? _(
"round") : _(
"cusp"));
Handle default and user-set shortcuts (accelerators)
static double dist(double x1, double y1, double x2, double y2)
void dt_iop_gui_blend_masks_update(dt_iop_module_t *module)
The blending panel's GUI state and API, cut out of develop/blend.h.
typedef void((*dt_cache_allocate_t)(void *userdata, dt_cache_entry_t *entry))
struct _GtkWidget GtkWidget
GtkWidget, opaque, spelled exactly as GTK spells it.
static const float const float const float min
const dt_colormatrix_t dt_aligned_pixel_t out
float dt_conf_get_float(const char *name)
Float for name, clamped to its declared bounds.
const char * dt_conf_get_string_const(const char *name)
Borrow the stored string for name without copying it.
void dt_control_mouse_is_painting(gboolean state)
void * dt_alloc_align(size_t size)
Allocate cacheline-aligned memory.
dt_dev_image_geometry_t dt_dev_geometry_snapshot(const dt_develop_t *dev)
#define dt_dev_add_history_item(dev, module, enable, redraw)
static dt_masks_raster_result_t _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, dt_iop_roi_t *touched)
Build a brush mask directly into an ROI-sized buffer.
static dt_masks_raster_result_t _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_initial_source_pos(struct dt_develop_t *dev, 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 float _brush_radius_at(const float r1, const float r2, const double t)
static gboolean _brush_walk_pass(const _brush_walk_t *const w, const gboolean forward)
static float _get_brush_smoothing()
static int _find_closest_handle(dt_masks_form_t *mask_form, dt_masks_form_gui_t *mask_gui, int form_index)
static int _init_fading(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_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 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 int _change_fading(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 _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 gboolean _brush_walk_segment(const _brush_walk_t *const w, _brush_walk_state_t *const s, const int k, const int k1, const gboolean forward)
static gboolean _brush_outline_transform(dt_develop_t *develop, const dt_masks_form_t *const mask_form, const double iop_order, const int transform_direction, const dt_masks_distort_t *const dist, const int use_source, const _brush_outline_t *const o)
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 gboolean _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 radius_rate, float *point_x, float *point_y, float *border_x, float *border_y)
Evaluate a cubic Bezier and compute its offset border point.
static void _apply_pen_pressure(dt_masks_form_gui_t *mask_gui, float *payload_buffer)
static void _brush_points_recurs_border_small_gaps(float *cmax, float *bmin, float *bmax, dt_masks_dynbuf_t *dpoints, dt_masks_dynbuf_t *dborder, const int step)
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_set_hint_message(const dt_masks_form_gui_t *const mask_gui, const dt_masks_form_t *const mask_form, char *const restrict msgbuf, const size_t msgbuf_len)
static dt_masks_raster_result_t _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_falloff(float *const restrict buffer, int segment_start[2], int segment_end[2], int offset_x, int offset_y, int buffer_width, float fading, float density)
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 int _brush_get_pts_border(dt_develop_t *develop, dt_masks_form_t *mask_form, const double iop_order, const int transform_direction, const dt_masks_distort_t *const dist, float **point_buffer, int *point_count, float **border_buffer, int *border_count, float **payload_buffer, int *payload_count, dt_masks_skip_range_t **border_skips, int *border_skip_count, int use_source)
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 dt_masks_raster_result_t _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, dt_masks_skip_range_t **border_skips, int *border_skip_count, int use_source, const dt_iop_module_t *module)
static float _brush_line_length(const float *line, const int first_pt, const int last_pt)
static void _brush_points_stamp(const float *const centre, const float *const from_border, const float radius, dt_masks_dynbuf_t *dpoints, dt_masks_dynbuf_t *dborder, const int step)
static gboolean _is_within_pxl_threshold(const float *const min, const float *const max, const int pixel_threshold)
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_leaf_borrow_border(float *const border_min, float *const border_max, const gboolean have_min, const gboolean have_max, const float *const centre, const float *const chord, const float radius)
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 void _brush_walk_node(const _brush_walk_t *const w, const _brush_walk_state_t *const s, const float *const p1, const float *const p2, const float *const c0, const float *const b0, const gboolean forward)
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_distance_cb(float pointer_x, float pointer_y, float cursor_radius, dt_masks_form_gui_t *mask_gui, int form_index, int node_count, int *inside, int *inside_border, int *near_handle, int *inside_source, float *dist, void *user_data)
Brush-specific inside/border/segment hit testing.
static gboolean _brush_get_gravity_center(dt_develop_t *dev, const dt_masks_form_t *mask_form, float center[2], float *area)
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 void _brush_set_form_name(struct dt_masks_form_t *const mask_form, const size_t form_number)
static void _brush_segment_load(const dt_masks_node_brush_t *const from, const dt_masks_node_brush_t *const to, const gboolean forward, const _brush_frame_t *const frame, float p1[7], float p2[7])
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 void _brush_points_recurs_border_gaps(float *cmax, float *bmin, float *bmax, dt_masks_dynbuf_t *dpoints, dt_masks_dynbuf_t *dborder, gboolean clockwise, const int step)
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 float _brush_radius_rate_at(const float r1, const float r2, const double t)
static void _brush_joint_arc(const float *const centre, const float *const from, const float *const to, const gboolean forward, dt_masks_dynbuf_t *dpoints, dt_masks_dynbuf_t *dborder, const int step)
static void _brush_points_recurs(const float *const p1, const float *const p2, const _brush_span_t *const span, const _brush_walk_t *const w, _brush_sample_t *const out)
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_walk_sync_payload(const _brush_walk_t *const w, const float *const payload)
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 gboolean _brush_span_is_leaf(const double tmin, const double tmax, const float *const points_min, const float *const points_max, const float *const border_min, const float *const border_max, const int pixel_threshold)
static void _brush_walk_single_dab(const _brush_walk_t *const w)
static void _brush_falloff_roi(float *buffer, const int *segment_start, const int *segment_end, int buffer_width, int buffer_height, float fading, float density)
static void _brush_sanitize_config(dt_masks_type_t type)
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_handle, int *inside_source, float *distance)
Get the distance between a point and the brush path/border.
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_bufs_alloc(const gboolean want_border, const gboolean want_payload, dt_masks_dynbuf_t **dpoints, dt_masks_dynbuf_t **dborder, dt_masks_dynbuf_t **dpayload)
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 void _brush_walk_cap(const _brush_walk_t *const w, const _brush_walk_state_t *const s)
static int _brush_centerline_end(const int points_count, const int node_count)
Index (exclusive) where the centerline's forward pass ends in a brush point array.
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 dt_masks_raster_result_t _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 void _brush_draw_shape(struct dt_develop_t *dev, cairo_t *cr, const float *points, const int points_count, const int node_nb, const gboolean border, const gboolean source, const dt_masks_skip_range_t *skips, const int skip_count)
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)
void dt_dev_coordinates_image_abs_to_raw_norm(dt_develop_t *dev, float *points, size_t num_points)
@ DT_DEV_TRANSFORM_DIR_BACK_EXCL
@ DT_DEV_TRANSFORM_DIR_BACK_INCL
@ DT_DEV_TRANSFORM_DIR_FORW_INCL
@ DT_DEV_TRANSFORM_DIR_ALL
GtkWidget * geometry
its size, under the preview
GHashTable * selected
set of checked row labels, mirrored to conf on every change
GtkWidget * status
result of the last capture
#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)
static void dt_draw_handle(cairo_t *cr, const float pt[2], const float zoom_scale, const float handle[2], const gboolean selected, const gboolean square)
Draw a control handle attached to a point with a tail between the node and the handle.
static void dt_draw_shape_lines(struct dt_develop_t *dev, const dt_draw_dash_type_t dash_type, const gboolean source, cairo_t *cr, const int nb, const gboolean selected, const float zoom_scale, const float *points, const int points_count, const shape_draw_function_t *draw_shape_func, const cairo_line_cap_t line_cap, const struct dt_masks_skip_range_t *skips, const int skip_count)
Draw the lines of a mask shape.
static void dt_draw_node(cairo_t *cr, const gboolean square, const gboolean point_action, const gboolean selected, const float zoom_scale, const float x, const float y)
Draw an node point of a mask.
static const GList * g_list_next_wraparound(const GList *list, const GList *head)
static GList * g_list_next_bounded(GList *list)
static gboolean g_list_shorter_than(const GList *list, unsigned len)
_lib_location_type_t type
int32_t dt_get_debug_flags(void)
void dt_print(dt_debug_thread_t thread, const char *msg,...) __attribute__((format(printf
Print to stdout when thread is enabled, prefixed with seconds since startup.
float *const restrict const size_t k
#define IS_NULL_PTR(p)
C is way too permissive with !=, == and if(var) checks, which can mean too many things depending on w...
gboolean dt_masks_skip_contains(const dt_masks_skip_range_t *skips, const int skip_count, const int index)
static float * dt_masks_dynbuf_buffer(dt_masks_dynbuf_t *a)
static void dt_masks_dynbuf_add_2(dt_masks_dynbuf_t *a, float value1, float value2)
static dt_masks_raster_result_t dt_masks_raster_from_status(const int status)
static dt_masks_dynbuf_t * dt_masks_dynbuf_init(size_t size, const char *tag)
static void dt_masks_translate_source(dt_masks_form_t *form, const float delta_x, const float delta_y)
dt_masks_raster_result_t
What a rasterisation attempt produced.
static 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)
void dt_masks_duplicate_points(const dt_masks_form_t *base, dt_masks_form_t *dest, size_t node_size)
Duplicate a points list for a mask using a fixed node size.
static float * dt_masks_dynbuf_reserve_n(dt_masks_dynbuf_t *a, const int n)
static gboolean dt_masks_center_of_gravity_from_points(const float *points, const int points_count, float center[2], float *area)
static size_t dt_masks_dynbuf_position(dt_masks_dynbuf_t *a)
int dt_masks_form_change_opacity(dt_develop_t *dev, dt_masks_form_t *form, int parentid, int up, const int flow)
static gboolean dt_masks_form_is_clone(const dt_masks_form_t *form)
static void dt_masks_dynbuf_add_zeros(dt_masks_dynbuf_t *a, const int n)
@ DT_MASKS_PRESSURE_OPACITY_REL
@ DT_MASKS_PRESSURE_OPACITY_ABS
@ DT_MASKS_PRESSURE_BRUSHSIZE_REL
@ DT_MASKS_PRESSURE_FADING_REL
@ DT_MASKS_PRESSURE_FADING_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
static float * dt_masks_dynbuf_harvest(dt_masks_dynbuf_t *a)
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 void dt_masks_dynbuf_free(dt_masks_dynbuf_t *a)
static void dt_masks_set_ctrl_points(float ctrl1[2], float ctrl2[2], const float control_points[4])
What a mask outline needs in order to place itself, and who supplies it.
static dt_masks_distort_t dt_masks_distort_for_pipe(dt_dev_pixelpipe_t *pipe, dt_develop_t *dev)
The rendering supplier: compose this pipe, sample as this pipe was told to.
static dt_masks_distort_t dt_masks_distort_for_gui(dt_develop_t *dev)
The GUI supplier: compose through the geometry service, at full resolution, sampled at the density th...
static int dt_masks_distort_transform(const dt_masks_distort_t *const d, const double iop_order, const int transf_direction, float *points, size_t points_count)
Compose forward, bounded exactly as dt_dev_distort_transform_plus() is.
The per-shape function table, private to the masks implementation.
void dt_masks_outline_envelope_offset(const float *centre, float dx, float dy, float radius, float radius_rate, float *out)
int dt_masks_outline_boundary_skips(const float *const points, const float *const border, const int count, const int header, dt_masks_skip_range_t **skips_out)
void dt_masks_outline_offset_along(const float *const centre, float dx, float dy, const float radius, float *const border)
gboolean dt_masks_outline_short_way(const float *const centre, const float *const from, const float *const to, const gboolean default_clockwise)
void dt_masks_draw_source(cairo_t *cr, dt_masks_form_gui_t *mask_gui, const int form_index, const int node_count, const float zoom_scale, struct dt_masks_gui_center_point_t *center_point, const shape_draw_function_t *draw_shape_func)
Draw the source for a correction mask.
gboolean dt_masks_reset_bezier_ctrl_points(struct dt_iop_module_t *module, struct dt_masks_form_t *mask_form, struct dt_masks_form_gui_t *mask_gui, const int form_index, const struct dt_masks_form_gui_points_t *gui_points, const int node_index, dt_masks_points_states_t *state)
float dt_masks_get_set_conf_value(dt_masks_form_t *mask_form, char *feature, float new_value, float value_min, float value_max, dt_masks_increment_t increment, int flow)
Change a numerical property of a mask shape, either by in/de-crementing the current value or setting ...
gboolean dt_masks_toggle_bezier_node_type(struct dt_iop_module_t *module, struct dt_masks_form_t *mask_form, struct dt_masks_form_gui_t *mask_gui, const int form_index, const struct dt_masks_form_gui_points_t *gui_points, const int node_index, float node[2], float ctrl1[2], float ctrl2[2], dt_masks_points_states_t *state)
void dt_masks_change_form_gui(dt_develop_t *dev, dt_masks_form_t *new_form)
gboolean dt_masks_gui_form_create_throttled(dt_masks_form_t *mask_form, dt_masks_form_gui_t *mask_gui, int form_index, dt_iop_module_t *module, float posx, float posy)
dt_masks_form_group_t * dt_masks_form_get_selected_group(const dt_masks_form_t *mask_form, const dt_masks_form_gui_t *mask_gui)
Get the selected group entry from the GUI selection index.
gboolean dt_masks_is_anything_selected(const dt_masks_form_gui_t *mask_gui)
gboolean dt_masks_node_is_cusp(const dt_masks_form_gui_points_t *gui_points, const int node_index)
returns wether a node is a corner or not. A node is a corner if its 2 control handles are at the same...
dt_masks_form_t * dt_masks_get_visible_form(const dt_develop_t *dev)
Return the currently visible form used by the masks GUI.
void dt_masks_draw_path_seg_by_seg(cairo_t *cr, dt_masks_form_gui_t *mask_gui, const int form_index, const float *points, const int points_count, const int node_count, const float zoom_scale, const gboolean round_ends)
gboolean dt_masks_gui_is_dragging(const dt_masks_form_gui_t *gui)
float dt_masks_apply_increment_precomputed(float current, float amount, float scale_amount, float offset_amount, dt_masks_increment_t increment)
Apply a scroll increment using precomputed scale/offset factors.
void dt_masks_draw_outline_runs(cairo_t *cr, const float *const points, const int first, const int last, const dt_masks_skip_range_t *skips, const int skip_count)
void dt_masks_set_source_pos_initial_value(dt_masks_form_gui_t *mask_gui, dt_masks_form_t *mask_form)
Initialize the clone source position based on current GUI state.
int dt_masks_find_closest_handle_common(dt_masks_form_t *mask_form, dt_masks_form_gui_t *mask_gui, int form_index, int node_count_override, dt_masks_border_handle_fn border_handle_cb, dt_masks_curve_handle_fn curve_handle_cb, dt_masks_node_position_fn node_position_cb, dt_masks_distance_fn distance_cb, dt_masks_post_select_fn post_select_cb, void *user_data)
Centralized hit-testing for node/handle/segment selection across shapes.
void dt_masks_set_source_pos_initial_state(dt_masks_form_gui_t *mask_gui, const uint32_t key_state)
Decide initial source positioning mode for clone masks.
void dt_masks_gui_form_create(dt_masks_form_t *mask_form, dt_masks_form_gui_t *mask_gui, int form_index, dt_iop_module_t *module)
float dt_masks_get_set_conf_value_with_toast(dt_masks_form_t *mask_form, const char *feature, float amount, float value_min, float value_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.
void dt_masks_select_form(dt_develop_t *dev, struct dt_iop_module_t *module, dt_masks_form_t *selected_form)
Select or clear the current mask form, notifying the owning module if needed.
void dt_masks_gui_form_save_creation(dt_develop_t *develop, dt_iop_module_t *module, dt_masks_form_t *mask_form, dt_masks_form_gui_t *mask_gui)
Save the form creation right after a shape has been finished drawing.
The interactive half of the masks subsystem: the editing state (dt_masks_form_gui_t),...
static void dt_masks_gui_delta_to_image_abs(const dt_masks_form_gui_t *gui, float point[2])
static int dt_masks_gui_selected_segment_index(const dt_masks_form_gui_t *gui)
static int dt_masks_gui_selected_node_index(const dt_masks_form_gui_t *gui)
static int dt_masks_gui_selected_handle_index(const dt_masks_form_gui_t *gui)
static void dt_masks_draw_source_preview(cairo_t *cr, const float zoom_scale, dt_masks_form_gui_t *gui, const float initial_xpos, const float initial_ypos, const float xpos, const float ypos, const int adding)
static float dt_masks_get_form_size_from_nodes(const GList *points)
static int dt_masks_gui_selected_handle_border_index(const dt_masks_form_gui_t *gui)
static void dt_masks_gui_cursor_to_raw_norm(dt_develop_t *dev, const dt_masks_form_gui_t *gui, float point[2])
static float dt_masks_border_from_projected_handle(dt_develop_t *dev, const float node[2], const float projected_image_pos[2], const float scale_ref)
static gboolean dt_masks_gui_change_affects_selected_node_or_all(const dt_masks_form_gui_t *gui, const int index)
static void dt_masks_gui_delta_from_raw_anchor(dt_develop_t *dev, const dt_masks_form_gui_t *gui, const float anchor[2], float *delta_x, float *delta_y)
static void dt_masks_project_on_line(const float cursor[2], const float node[2], const float handle[2], float point[2])
static gboolean dt_masks_gui_points_reach(const dt_masks_form_gui_points_t *gp, const float x, const float y, const float reach)
static void dt_masks_gui_delta_to_raw_norm(dt_develop_t *dev, const dt_masks_form_gui_t *gui, float point[2])
static void dt_masks_touched_set(dt_iop_roi_t *touched, int x0, int y0, int x1, int y1, const int width, const int height)
static void dt_masks_touched_none(dt_iop_roi_t *touched)
@ DT_MASKS_INTERACTION_OPACITY
@ DT_MASKS_INTERACTION_SIZE
@ DT_MASKS_INTERACTION_FADING
@ DT_MASKS_INCREMENT_SCALE
@ DT_MASKS_INCREMENT_OFFSET
#define CLAMPF(a, mn, mx)
#define dt_free_align(ptr)
Release memory from dt_alloc_align() and set ptr to NULL.
static float * dt_alloc_align_float(size_t pixels)
Allocate pixels floats, cacheline-aligned and marked as such.
#define dt_free(ptr)
g_free() ptr and set it to NULL, skipping both if it is already NULL.
#define __OMP_PARALLEL_FOR__(...)
#define __OMP_PARALLEL_FOR_SIMD__(...)
#define dt_pixelpipe_cache_alloc_align_float_cache(pixels, id)
#define dt_pixelpipe_cache_free_align(mem)
static const dt_aligned_pixel_simd_t value
const float uint32_t state[4]
dt_masks_dynbuf_t * dpayload
dt_masks_dynbuf_t * dpoints
dt_masks_dynbuf_t * dborder
dt_masks_node_brush_t ** nodes
Objective facts about the image a dev is working on.
int mask_rasterization_step
Rasterisation step for drawn masks, in image pixels. Always >= 1.
struct dt_masks_form_gui_t * form_gui
struct dt_develop_t * dev
Region of interest passed through the pixelpipe.
Where an outline builder gets its geometry from.
shape_draw_function_t draw_shape
struct dt_masks_gui_center_point_t::@27 main
struct dt_masks_gui_center_point_t::@28 source
dt_masks_points_states_t state
One cut in a shape's border outline: while walking the border buffer forward, on reaching index jump_...
static double dt_get_wtime(void)