77#pragma GCC diagnostic ignored "-Wshadow"
85#define CONF_RADIUS "plugins/darkroom/liquify/radius"
86#define CONF_ANGLE "plugins/darkroom/liquify/angle"
87#define CONF_STRENGTH "plugins/darkroom/liquify/strength"
132#define COLOR_NULL { 0.0, 0.0, 0.0, 0.8 }
133#define GREY { 0.3, 0.3, 0.3, 0.8 }
134#define LGREY { 0.8, 0.8, 0.8, 1.0 }
135#define COLOR_DEBUG { 0.9, 0.9, 0.0, 1.0 }
310 GtkToggleButton *btn_point_tool, *btn_line_tool, *
btn_curve_tool, *btn_node_tool;
319 return _(
"l_iquify");
326 _(
"linear, RGB, scene-referred"),
328 _(
"linear, RGB, scene-referred"));
379 p->nodes[
k].header.next =
p->nodes[
k].header.prev = -1;
380 p->nodes[
k].header.selected =
p->nodes[
k].header.hovered = 0;
388 if(
n->header.prev == -1)
391 return &
p->nodes[
n->header.prev];
397 return &
p->nodes[index];
404 if(
n->header.next == -1)
407 return &
p->nodes[
n->header.next];
413 new->header.prev = this->header.prev;
414 if(this->header.prev != -1)
415 p->nodes[this->header.prev].header.next =
new->header.idx;
416 this->header.prev =
new->header.idx;
431 for(
int e=0; e<last; e++)
434 if(e >=
k)
p->nodes[e] =
p->nodes[e+1];
436 if(e >=
k)
p->nodes[e].header.idx--;
437 if(
p->nodes[e].header.prev >=
k)
p->nodes[e].header.prev--;
438 if(
p->nodes[e].header.next >=
k)
p->nodes[e].header.next--;
468 this->header.prev = this->header.next = - 1;
595 float *buffer = malloc(
sizeof(
float) * 2 * len);
607 *b++ = crealf(data->
node.
ctrl1) / params->from_scale;
608 *b++ = cimagf(data->
node.
ctrl1) / params->from_scale;
609 *b++ = crealf(data->
node.
ctrl2) / params->from_scale;
610 *b++ = cimagf(data->
node.
ctrl2) / params->from_scale;
614 *b++ = crealf(data->
warp.
point) / params->from_scale;
615 *b++ = cimagf(data->
warp.
point) / params->from_scale;
616 *b++ = crealf(data->
warp.
strength) / params->from_scale;
617 *b++ = cimagf(data->
warp.
strength) / params->from_scale;
618 *b++ = crealf(data->
warp.
radius) / params->from_scale;
619 *b++ = cimagf(data->
warp.
radius) / params->from_scale;
625 if(params->from_distort_transform)
636 params->transf_direction, buffer, len);
662 data->
node.
ctrl1 = (b[0] + b[1] * I) * params->to_scale;
664 data->
node.
ctrl2 = (b[0] + b[1] * I) * params->to_scale;
669 data->
warp.
point = (b[0] + b[1] * I) * params->to_scale;
671 data->
warp.
strength = (b[0] + b[1] * I) * params->to_scale;
673 data->
warp.
radius = (b[0] + b[1] * I) * params->to_scale;
686 const float roi_in_scale,
688 const gboolean from_distort_transform)
690 const distort_params_t params = {
module->dev, pipe, 1.f, roi_in_scale, DT_DEV_TRANSFORM_DIR_BACK_EXCL, from_distort_transform };
698 if(cabsf(
v) < 0.000001f)
705static inline float mix(
const float a,
const float b,
const float t)
707 return a + (b - a) *
t;
713static inline float complex
cmix(
const float complex p0,
const float complex p1,
const float t)
715 return p0 + (p1 - p0) *
t;
721 const complex
float pt,
729 result->
radius = pt + radius;
733 float arg1 = cargf(p1);
734 float arg2 = cargf(p2);
735 gboolean invert =
FALSE;
737 if(arg1 > .0f && arg2 < -(
M_PI_F / 2.f))
743 else if(arg1 < -(
M_PI_F / 2.f) && arg2 > .0f)
750 const float r =
mix(cabsf(p1), cabsf(p2),
t);
751 const float phi = invert ?
M_PI_F -
mix(arg1, arg2,
t) :
mix(arg1, arg2,
t);
753 result->
strength = pt +
r * cexpf(phi * I);
760 const float complex p1,
761 const float complex p2,
762 const float complex p3,
763 float complex buffer[],
768 const float complex
A = p3 - 3 * p2 + 3 * p1 - p0;
769 const float complex
B = 3 * p2 - 6 * p1 + 3 * p0;
770 const float complex
C = 3 * p1 - 3 * p0;
771 const float complex D = p0;
773 float complex *buf = buffer;
774 const float step = 1.0f /
n;
778 for(
int i = 1;
i <
n - 1; ++
i)
780 *buf++ = ((
A *
t +
B) *
t +
C) *
t + D;
797 for(
int i = 1;
i < n_points;
i++)
798 length += cabsf(points[
i-1] - points[
i]);
818 float length = restart ? restart->
length : 0.0f;
820 for(
int i = restart ? restart->
i : 1;
i < n_points;
i++)
822 const float prev_length = length;
823 length += cabsf(points[
i-1] - points[
i]);
825 if(length >= arc_length)
827 const float t = (arc_length - prev_length) / (length - prev_length);
831 restart->
length = prev_length;
833 return cmix(points[
i - 1], points[
i],
t);
837 return points[n_points - 1];
886 float complex *cptr = clookup + 1;
887 const float complex *cptr_end = cptr + distance;
888 const float step = 1.0f / (float) distance;
892 for(
int i = 1;
i < distance && cptr < cptr_end;
i++)
895 while(crealf(*cptr) <
x && cptr < cptr_end)
897 const float dx1 = crealf(cptr[0] - cptr[-1]);
898 const float dx2 =
x - crealf(cptr[-1]);
899 *ptr++ = cimagf(cptr[0]) +(dx2 / dx1) * (cimagf(cptr[0]) - cimagf(cptr[-1]));
911 const int iradius = round(cabsf(warp->radius - warp->point));
914 stamp_extent->x = stamp_extent->y = -iradius;
915 stamp_extent->x += crealf(warp->point);
916 stamp_extent->y += cimagf(warp->point);
917 stamp_extent->width = stamp_extent->height = 2 * iradius + 1;
941 cairo_rectangle_int_t *
const restrict stamp_extent,
944 const int iradius = round(cabsf(warp->radius - warp->point));
947 stamp_extent->x = stamp_extent->y = -iradius;
948 stamp_extent->width = stamp_extent->height = 2 * iradius + 1;
952 float complex
strength = 0.5f * (warp->strength - warp->point);
955 const float abs_strength = cabsf(
strength);
957 float complex *restrict stamp =
958 calloc(
sizeof(
float complex), (
size_t)stamp_extent->width * stamp_extent->height);
963 const float *restrict lookup_table =
build_lookup_table(table_size, warp->control1, warp->control2);
971 float complex *
const center = stamp + 2 * iradius * iradius + 2 * iradius;
979 for(
int y = 0; y <= iradius; y++)
981 for(
int x = 0;
x <= iradius;
x++)
983 const float dist = dt_fast_hypotf(
x, y);
985 if(idist >= table_size)
991 float complex *
const q1 = center - y * stamp_extent->width +
x;
992 float complex *
const q2 = center - y * stamp_extent->width -
x;
993 float complex *
const q3 = center + y * stamp_extent->width -
x;
994 float complex *
const q4 = center + y * stamp_extent->width +
x;
996 float abs_lookup = abs_strength * lookup_table[idist] / iradius;
1001 *q1 = abs_lookup * (
x - y * I);
1002 *q2 = abs_lookup * (-
x - y * I);
1003 *q3 = abs_lookup * (-
x + y * I);
1004 *q4 = abs_lookup * (
x + y * I);
1008 *q1 = -abs_lookup * (
x - y * I);
1009 *q2 = -abs_lookup * (-
x - y * I);
1010 *q3 = -abs_lookup * (-
x + y * I);
1011 *q4 = -abs_lookup * (
x + y * I);
1015 *q1 = *q2 = *q3 = *q4 =
strength * lookup_table[idist];
1037 const cairo_rectangle_int_t *
const restrict global_map_extent,
1039 const float complex *
const restrict stamp,
1040 const cairo_rectangle_int_t *stamp_extent)
1042 cairo_rectangle_int_t mmext = *stamp_extent;
1043 mmext.x += (int) round(crealf(warp->point));
1044 mmext.y += (int) round(cimagf(warp->point));
1045 cairo_rectangle_int_t cmmext = mmext;
1046 cairo_region_t *mmreg = cairo_region_create_rectangle(&mmext);
1047 cairo_region_intersect_rectangle(mmreg, global_map_extent);
1048 cairo_region_get_extents(mmreg, &cmmext);
1052 #pragma omp parallel for schedule (static) default (shared)
1055 for(
int y = cmmext.y; y < cmmext.y + cmmext.height; y++)
1057 const float complex *
const srcrow = stamp + ((y - mmext.y) * mmext.width);
1058 float complex *
const destrow = global_map + ((y - global_map_extent->y) * global_map_extent->width);
1060 for(
int x = cmmext.x;
x < cmmext.x + cmmext.width;
x++)
1062 destrow[
x - global_map_extent->x] -= srcrow[
x - mmext.x];
1077 const float *
const restrict in,
1078 float *
const restrict
out,
1082 const float complex *
const map,
1083 const cairo_rectangle_int_t *extent)
1085 const int ch_width =
ch * roi_in->
width;
1090 #pragma omp parallel for schedule (static) default (shared)
1093 for(
int y = extent->y; y < extent->y + extent->height; y++)
1096 if(y >= roi_out->
y && y < roi_out->y + roi_out->
height)
1098 const float complex *
row = map + (y - extent->y) * extent->width;
1099 float* out_sample =
out + ((y - roi_out->
y) * roi_out->
width +
1100 extent->x - roi_out->
x) *
ch;
1101 for(
int x = extent->x;
x < extent->x + extent->width;
x++)
1105 (
x >= roi_out->
x && x < roi_out->
x + roi_out->
width) &&
1112 x + crealf(*
row) - roi_in->
x,
1113 y + cimagf(*
row) - roi_in->
y,
1123 x + crealf(*
row) - roi_in->
x,
1124 y + cimagf(*
row) - roi_in->
y,
1140 const GList *interpolated,
1141 cairo_rectangle_int_t *map_extent)
1143 const cairo_rectangle_int_t roi_out_rect = { roi_out->
x, roi_out->
y, roi_out->
width, roi_out->
height };
1144 cairo_region_t *roi_out_region = cairo_region_create_rectangle(&roi_out_rect);
1145 cairo_region_t *map_region = cairo_region_create();
1146 GSList *in_roi = NULL;
1148 for(
const GList *
i = interpolated;
i;
i = g_list_next(
i))
1151 cairo_rectangle_int_t
r;
1154 if(cairo_region_contains_rectangle(roi_out_region, &
r) != CAIRO_REGION_OVERLAP_OUT)
1156 cairo_region_union_rectangle(map_region, &
r);
1157 in_roi = g_slist_prepend(in_roi,
i->data);
1162 cairo_region_get_extents(map_region, map_extent);
1163 cairo_region_destroy(map_region);
1164 cairo_region_destroy(roi_out_region);
1166 return g_slist_reverse(in_roi);
1170 const GSList *interpolated,
1173 const int mapsize = map_extent->width * map_extent->height;
1184 memset(map, 0,
sizeof(
float complex) * mapsize);
1187 for(
const GSList *
i = interpolated;
i;
i = g_slist_next(
i))
1190 float complex *stamp = NULL;
1191 cairo_rectangle_int_t
r;
1209 memset(imap, 0,
sizeof(
float complex) * mapsize);
1215 #pragma omp parallel for schedule (static) default (shared)
1218 for(
int y = 0; y < map_extent->height; y++)
1220 const float complex *
const row = map + y * map_extent->width;
1221 for(
int x = 0;
x < map_extent->width;
x++)
1223 const float complex
d =
row[
x];
1225 const int nx =
x + (int)crealf(
d);
1226 const int ny = y + (int)cimagf(
d);
1229 if(nx>0 && nx<map_extent->
width && ny>0 && ny<map_extent->
height)
1230 imap[nx + ny * map_extent->width] = -
d;
1241 #pragma omp parallel for schedule (static) default (shared)
1244 for(
int y = 0; y < map_extent->height; y++)
1246 float complex *
const row = imap + y * map_extent->width;
1247 float complex last[2] = { 0, 0 };
1248 for(
int x = 0;
x < map_extent->width / 2 + 1;
x++)
1250 float complex *cl =
row +
x;
1251 float complex *cr =
row + map_extent->width -
x;
1254 if(*cl == 0) *cl = last[0];
1255 if(*cr == 0) *cr = last[1];
1257 last[0] = *cl; last[1] = *cr;
1271 cairo_rectangle_int_t *map_extent)
1280 GSList *interpolated_in_roi =
_get_map_extent(roi_out, interpolated, map_extent);
1284 g_slist_free(interpolated_in_roi);
1285 interpolated_in_roi = NULL;
1287 interpolated = NULL;
1320 cairo_rectangle_int_t pipe_rect =
1328 cairo_rectangle_int_t roi_in_rect =
1335 cairo_region_t *roi_in_region = cairo_region_create_rectangle(&roi_in_rect);
1339 cairo_rectangle_int_t extent;
1340 GSList *interpolated_in_roi =
_get_map_extent(roi_out, interpolated, &extent);
1341 g_slist_free(interpolated_in_roi);
1342 interpolated_in_roi = NULL;
1344 interpolated = NULL;
1347 cairo_region_union_rectangle(roi_in_region, &extent);
1349 cairo_region_intersect_rectangle(roi_in_region, &pipe_rect);
1352 cairo_region_get_extents(roi_in_region, &roi_in_rect);
1353 roi_in->
x = roi_in_rect.x;
1354 roi_in->
y = roi_in_rect.y;
1355 roi_in->
width = roi_in_rect.width;
1356 roi_in->
height = roi_in_rect.height;
1359 cairo_region_destroy(roi_in_region);
1364 float *
const restrict points,
const size_t points_count,
1365 const gboolean inverted)
1369 float xmin = FLT_MAX, xmax = FLT_MIN, ymin = FLT_MAX, ymax = FLT_MIN;
1371 for(
size_t i = 0;
i < points_count * 2;
i += 2)
1373 const float x = points[
i];
1374 const float y = points[
i + 1];
1375 xmin = fmin(xmin,
x);
1376 xmax = fmax(xmax,
x);
1377 ymin = fmin(ymin, y);
1378 ymax = fmax(ymax, y);
1381 cairo_rectangle_int_t extent = { .x = (int)(xmin - .5), .y = (int)(ymin - .5),
1382 .width = (int)(xmax - xmin + 2.5), .height = (int)(ymax - ymin + 2.5) };
1384 if(extent.width > 0 && extent.height > 0)
1399 dt_iop_roi_t roi_in = { .
x = extent.x, .y = extent.y, .width = extent.width, .height = extent.height };
1400 GSList *interpolated_in_roi =
_get_map_extent(&roi_in, interpolated, &extent);
1403 g_slist_free(interpolated_in_roi);
1404 interpolated_in_roi = NULL;
1406 interpolated = NULL;
1410 const int map_size = extent.width * extent.height;
1411 const int x_last = extent.x + extent.width;
1412 const int y_last = extent.y + extent.height;
1416 for(
size_t i = 0;
i < points_count;
i++)
1418 float *px = &points[
i*2];
1419 float *py = &points[
i*2+1];
1420 const float x = *px;
1421 const float y = *py;
1422 const int map_offset = ((int)(
x - 0.5) - extent.x) + ((
int)(y - 0.5) - extent.y) * extent.width;
1424 if(
x >= extent.x && x < x_last && y >= extent.y && y < y_last && map_offset >= 0 && map_offset < map_size)
1426 const float complex
dist = map[map_offset];
1427 *px += crealf(
dist);
1428 *py += cimagf(
dist);
1440 g->dragging.layer = layer;
1441 g->dragging.elem = elem;
1455 float *
const restrict points,
size_t points_count)
1463 float *
const restrict points,
size_t points_count)
1469 const float *
const in,
float *
const out,
const dt_iop_roi_t *
const roi_in,
1475 for(
int i = 0;
i < roi_out->
height;
i++)
1477 float *destrow =
out + (size_t)
i * roi_out->
width;
1478 const float *srcrow = in + (size_t) (roi_in->
width * (
i + roi_out->
y - roi_in->
y) + roi_out->
x - roi_in->
x);
1480 memcpy(destrow, srcrow,
sizeof(
float) * roi_out->
width);
1485 cairo_rectangle_int_t map_extent;
1489 if(map_extent.width != 0 && map_extent.height != 0)
return;
1495 if(map_extent.width != 0 && map_extent.height != 0)
1504 const void *
const in,
1517 float *destrow = (
float *)
out + (
size_t)
ch *
i * roi_out->
width;
1518 const float *srcrow = (
float *)in + (
size_t)
ch * (roi_in->
width * (
i + roi_out->
y - roi_in->
y) +
1519 roi_out->
x - roi_in->
x);
1521 memcpy(destrow, srcrow,
sizeof(
float) *
ch *
width);
1526 cairo_rectangle_int_t map_extent;
1530 if(map_extent.width != 0 && map_extent.height != 0)
return 1;
1536 if(map_extent.width != 0 && map_extent.height != 0)
1548static inline float bicubic(
const float a,
const float x)
1550 const float absx = fabsf(
x);
1551 if(absx <= 1)
return ((a + 2) * absx - (a + 3)) * absx * absx + 1;
1552 if(absx < 2)
return ((a * absx - 5 * a) * absx + 8 * a) * absx - 4 * a;
1561 const float absx = fabsf(
x);
1562 const float x2 = absx * absx;
1563 const float x3 = x2 * absx;
1564 if(absx < 1.0f)
return (7.0f / 6.0f) * x3 - 2.0f * x2 + 8.0f / 9.0f;
1565 if(absx < 2.0f)
return -(7.0f / 18.0f) * x3 + 2.0f * x2 - (10.0f / 3.0f) * absx + 16.0f / 9.0f;
1585 const float complex *map,
1586 const cairo_rectangle_int_t *map_extent)
1588 cl_int_t err = CL_MEM_OBJECT_ALLOCATION_FAILURE;
1591 const int devid = pipe->
devid;
1597 switch (interpolation->
id)
1602 k = malloc(
sizeof(
float) * 2);
1611 k = malloc(
sizeof(
float) * ((
size_t)kdesc.
size * kdesc.
resolution + 1));
1618 k = malloc(
sizeof(
float) * ((
size_t)kdesc.
size * kdesc.
resolution + 1));
1634 (devid,
sizeof(
float complex) * map_extent->width * map_extent->height, (
void *) map);
1637 (devid,
sizeof(cairo_rectangle_int_t), (
void *) map_extent);
1643 (devid,
sizeof(
float) * (kdesc.
size * kdesc.
resolution + 1), (
void *)
k);
1659 const size_t sizes[] = {
ROUNDUPDWD(map_extent->width, devid),
ROUNDUPDHT(map_extent->height, devid) };
1683 const int devid = pipe->
devid;
1689 size_t src[] = { roi_out->
x - roi_in->
x, roi_out->
y - roi_in->
y, 0 };
1690 size_t dest[] = { 0, 0, 0 };
1693 if(err != CL_SUCCESS)
goto error;
1697 cairo_rectangle_int_t map_extent;
1701 if(map_extent.width != 0 && map_extent.height != 0)
return FALSE;
1706 if(map_extent.width != 0 && map_extent.height != 0)
1709 if(err != CL_SUCCESS)
goto error;
1723 const int program = 17;
1740 module->default_enabled = 0;
1741 module->params_size = sizeof(dt_iop_liquify_params_t);
1742 module->gui_data = NULL;
1745 module->params = calloc(1, module->params_size);
1746 module->default_params = calloc(1, module->params_size);
1773static float cdot(
const float complex p0,
const float complex p1)
1776 return fma(crealf(p0), crealf(p1), cimagf(p0) * cimagf(p1));
1778 return crealf(p0) * crealf(p1) + cimagf(p0) * cimagf(p1);
1784 const double x = creal(pt), y = cimag(pt);
1786 cairo_translate(cr,
x, y);
1787 cairo_rotate(cr, theta);
1792static void draw_triangle(cairo_t *cr,
const float complex pt,
const double theta,
const double size)
1794 const double x = creal(pt), y = cimag(pt);
1796 cairo_translate(cr,
x, y);
1797 cairo_rotate(cr, theta);
1798 cairo_move_to(cr, -
size, -
size / 2.0);
1799 cairo_line_to(cr, 0, 0 );
1800 cairo_line_to(cr, -
size, +
size / 2.0);
1801 cairo_close_path(cr);
1805static void draw_circle(cairo_t *cr,
const float complex pt,
const double diameter)
1807 const double x = creal(pt), y = cimag(pt);
1809 cairo_new_sub_path(cr);
1810 cairo_arc(cr,
x, y, diameter / 2.0, 0, 2 *
DT_M_PI);
1825#define GET_UI_WIDTH(a) (get_ui_width(scale, DT_LIQUIFY_UI_WIDTH_##a))
1830 cairo_set_line_width(cr,
width);
1836 return g->last_button1_pressed_pos != -1.0 &&
1837 cabsf(pt -
g->last_button1_pressed_pos) >= (
GET_UI_WIDTH(MIN_DRAG) / scale);
1842 guint warp = 0, node = 0;
1852 char str_warp[10], str_node[20];
1853 snprintf(str_warp,
sizeof(str_warp),
"%d", warp);
1854 snprintf(str_node,
sizeof(str_node),
"%d", node);
1855 gtk_label_set_text(
g->label_warp, str_warp);
1856 gtk_label_set_text(
g->label_node, str_node);
1868 const float complex *p2 = &data->
warp.
point;
1877 l = g_list_append(l, w);
1884 const float complex *p1 = &prev->
warp.
point;
1887 const float total_length = cabsf(*p1 - *p2);
1888 float arc_length = 0.0f;
1889 while(arc_length < total_length)
1892 const float t = arc_length / total_length;
1893 const float complex pt =
cmix(*p1, *p2,
t);
1897 l = g_list_append(l, w);
1912 float arc_length = 0.0f;
1915 while(arc_length < total_length)
1918 const float t = arc_length / total_length;
1923 l = g_list_append(l, w);
1932#define FG_COLOR set_source_rgba(cr, fg_color)
1933#define BG_COLOR set_source_rgba(cr, bg_color)
1934#define VERYTHINLINE set_line_width (cr, scale / 2.0f, DT_LIQUIFY_UI_WIDTH_THINLINE)
1935#define THINLINE set_line_width (cr, scale, DT_LIQUIFY_UI_WIDTH_THINLINE)
1936#define THICKLINE set_line_width (cr, scale, DT_LIQUIFY_UI_WIDTH_THICKLINE)
1946 cairo_set_line_cap(cr, CAIRO_LINE_CAP_ROUND);
1953 GList *interpolated = (
is_dragging(
g) ||
g->last_button1_pressed_pos != -1)
1958 for(
const GList *l = layers; l; l = g_list_next(l))
1965 cairo_push_group(cr);
1998 cairo_move_to(cr, crealf(
point), cimagf(
point));
2002 for(
const GList *
i = interpolated;
i;
i = g_list_next(
i))
2013 for(
const GList *
i = interpolated;
i;
i = g_list_next(
i))
2023 for(
const GList *
i = interpolated;
i;
i = g_list_next(
i))
2034 for(
const GList *
i = interpolated;
i;
i = g_list_next(
i))
2037 cairo_move_to(cr, crealf(pwarp->
point), cimagf(pwarp->
point));
2042 for(
const GList *
i = interpolated;
i;
i = g_list_next(
i))
2052 cairo_fill_preserve(cr);
2064 cairo_line_to(cr, crealf(
point), cimagf(
point));
2072 cairo_stroke_preserve(cr);
2102 cairo_fill_preserve(cr);
2131 cairo_fill_preserve(cr);
2140 cairo_fill_preserve(cr);
2152 cairo_stroke_preserve(cr);
2161 cairo_fill_preserve(cr);
2170 cairo_stroke_preserve(cr);
2179 cairo_stroke_preserve(cr);
2190 cairo_fill_preserve(cr);
2201 cairo_fill_preserve(cr);
2208 cairo_move_to(cr, crealf(
point), cimagf(
point));
2215 cairo_line_to(cr, crealf(pt), cimagf(pt));
2220 cairo_stroke_preserve(cr);
2233 cairo_fill_preserve(cr);
2241 cairo_pop_group_to_source(cr);
2247 interpolated = NULL;
2261 const float complex p1,
2262 const float complex p2,
2263 const float complex p3,
2264 const float complex
x,
2267 float min_t = 0.0f, min_dist = cabsf(
x - p0);
2269 for(
int i = 0;
i <
n;
i++)
2271 const float t = (1.0 *
i) /
n;
2272 const float t1 = 1.0 -
t;
2273 const float complex ip =
2275 3 * t1 * t1 *
t * p1 +
2276 3 * t1 *
t *
t * p2 +
2279 const float dist = cabsf(
x - ip);
2299 const float b = cabsf(p1 - p0);
2300 const float dotab =
cdot(
x - p0, p1 - p0);
2301 return dotab / (b * b);
2306static void casteljau(
const float complex *p0,
float complex *p1,
float complex *p2,
float complex *p3,
const float t)
2308 const float complex p01 = *p0 + (*p1 - *p0) *
t;
2309 const float complex p12 = *p1 + (*p2 - *p1) *
t;
2310 const float complex p23 = *p2 + (*p3 - *p2) *
t;
2312 const float complex p012 = p01 + (p12 - p01) *
t;
2313 const float complex p123 = p12 + (p23 - p12) *
t;
2315 const float complex p0123 = p012 + (p123 - p012) *
t;
2322#define CHECK_HIT_PT(point) \
2323 const float d = cabsf(point - (*pt)); \
2327 hit.layer = layer; \
2334 const float complex *pt)
2338 float distance = FLT_MAX;
2340 for(
const GList *l = layers; l; l = g_list_next(l))
2373 const float complex deadzone = (
point - prev->
warp.
point) / 20.0f;
2374 const float complex lp1 = prev->
warp.
point + deadzone;
2375 const float complex lp2 =
point - deadzone;
2378 if(
t > 0.0f &&
t < 1.0f)
2380 const float complex linepoint =
cmix(lp1, lp2,
t);
2381 const float d = cabsf(linepoint - *pt);
2395 const float complex deadzone = (
point - prev->
warp.
point) / 20.0f;
2396 const float complex lp1 = prev->
warp.
point + deadzone;
2397 const float complex lp2 =
point - deadzone;
2400 if(
t > 0.0f &&
t < 1.0f)
2402 float complex curvepoint = lp2;
2407 const float d = cabsf(curvepoint - *pt);
2469 GList *layers = NULL;
2473 if(gtk_toggle_button_get_active(
g->btn_point_tool)
2475 layers = g_list_prepend(layers, GINT_TO_POINTER(layer));
2476 if(gtk_toggle_button_get_active(
g->btn_line_tool)
2478 layers = g_list_prepend(layers, GINT_TO_POINTER(layer));
2479 if(gtk_toggle_button_get_active(
g->btn_curve_tool)
2481 layers = g_list_prepend(layers, GINT_TO_POINTER(layer));
2482 if(gtk_toggle_button_get_active(
g->btn_node_tool)
2484 layers = g_list_prepend(layers, GINT_TO_POINTER(layer));
2486 layers = g_list_reverse(layers);
2490 g_list_free(layers);
2499 GList *layers = NULL;
2504 layers = g_list_prepend(layers, GINT_TO_POINTER(layer));
2506 layers = g_list_reverse(layers);
2508 hit =
_hit_paths(module, params, layers, &pt);
2509 g_list_free(layers);
2569 const float complex *
k,
2572 const int *equation)
2575 float *a = malloc(
sizeof(
float) *
n);
2576 float *b = malloc(
sizeof(
float) *
n);
2577 float *c = malloc(
sizeof(
float) *
n);
2578 float complex *
d = malloc(
sizeof(
float complex) *
n);
2582 for(
int i = 0;
i <
n;
i++)
2584 switch (equation[
i])
2586 #define ABCD(A,B,C,D) { { a[i] = A; b[i] = B; c[i] = C; d[i] = D; continue; } }
2587 case 1:
ABCD(0, 2, 1,
k[
i] + 2 *
k[
i+1] );
break;
2588 case 2:
ABCD(1, 4, 1, 4 *
k[
i] + 2 *
k[
i+1] );
break;
2589 case 3:
ABCD(2, 7, 0, 8 *
k[
i] +
k[
i+1] );
break;
2590 case 4:
ABCD(0, 1, 0,
c1[
i] );
break;
2591 case 5:
ABCD(0, 1, 0,
c1[
i] );
break;
2592 case 6:
ABCD(1, 4, 0, 4 *
k[
i] +
c2[
i] );
break;
2593 case 7:
ABCD(0, 1, 0,
c1[
i] );
break;
2594 case 8:
ABCD(0, 3, 0, 2 *
k[
i] +
k[
i+1] );
break;
2595 case 9:
ABCD(0, 2, 0,
k[
i] +
c2[
i] );
break;
2603 for(
int i = 1;
i <
n;
i++)
2605 const float m = a[
i] / b[
i-1];
2606 b[
i] = b[
i] -
m * c[
i-1];
2610 c1[
n-1] =
d[
n-1] / b[
n-1];
2611 for(
int i =
n - 2;
i >= 0;
i--)
2616 for(
int i = 0;
i <
n;
i++)
2618 switch (equation[
i])
2628 case 8:
c2[
i] = (
c1[
i] +
k[
i+1]) / 2;
break;
2631 default:
c2[
i] = 2 *
k[
i+1] -
c1[
i+1];
2644 while(
n->header.next != -1)
2647 n = &
p->nodes[
n->header.next];
2659 if(params->nodes[
k].header.prev != -1)
2669 float complex *pt = calloc(
n,
sizeof(
float complex));
2670 float complex *
c1 = calloc(
n,
sizeof(
float complex));
2671 float complex *
c2 = calloc(
n,
sizeof(
float complex));
2672 int *eqn = calloc(
n,
sizeof(
int));
2685 c1[idx-1] =
d->node.ctrl1;
2686 c2[idx-1] =
d->node.ctrl2;
2709 if(lineseg) eqn[idx] = 5;
2710 else if(!autosmooth && !next_autosmooth) eqn[idx] = 5;
2711 else if(firstseg && lastseg && !autosmooth && next_autosmooth) eqn[idx] = 7;
2712 else if(firstseg && lastseg && autosmooth && next_autosmooth) eqn[idx] = 8;
2713 else if(firstseg && lastseg && autosmooth && !next_autosmooth) eqn[idx] = 9;
2714 else if(firstseg && autosmooth && !next_autosmooth) eqn[idx] = 5;
2715 else if(firstseg && autosmooth) eqn[idx] = 1;
2716 else if(lastseg && autosmooth && next_autosmooth) eqn[idx] = 3;
2717 else if(lastseg && !autosmooth && next_autosmooth) eqn[idx] = 7;
2718 else if(autosmooth && !next_autosmooth) eqn[idx] = 6;
2719 else if(!autosmooth && next_autosmooth) eqn[idx] = 4;
2729 node = ¶ms->nodes[
k];
2738 d->node.ctrl2 =
c2[idx];
2756 else if(
p->nodes[
k].header.hovered)
2757 return &
p->nodes[
k];
2806 c->node.ctrl1 = c->node.ctrl2 = 0.0;
2818 p->nodes[
k].header.selected = 0;
2844 if(bb_width < 1.0 || bb_height < 1.0)
2867 draw_paths(module, cr, 1.0 / zoom_scale, ©_params);
2912 float pts[2] = { (float)
x, (
float)y };
2921 *pt = pts[0] + pts[1] * I;
2949 gboolean handled =
FALSE;
2950 float complex pt = 0.0f;
2957 const float complex prev_mouse_pos =
g->last_mouse_pos;
2958 g->last_mouse_pos = pt;
2968 if(hit.
elem != last_hovered
2975 last_hovered->header.hovered = 0;
3008 const float complex *start_pt = &
d->warp.point;
3010 switch (
g->dragging.layer)
3015 const float complex
delta = prev_mouse_pos == -1 ? 0.0f : pt - prev_mouse_pos;
3016 if(cabsf(
delta) < 1e-6f)
break;
3019 for(
int i = 0;
i < 2;
i++)
3040 switch (
d->header.type)
3043 d->node.ctrl2 += pt -
d->warp.point;
3048 n->node.ctrl1 += pt -
d->warp.point;
3050 p->node.ctrl2 += pt -
d->warp.point;
3051 d->warp.radius += pt -
d->warp.point;
3052 d->warp.strength += pt -
d->warp.point;
3061 switch (
d->header.type)
3067 switch (
p->header.node_type)
3070 p->node.ctrl2 =
p->warp.point +
3071 cabsf(
p->warp.point -
p->node.ctrl2) *
3072 cexpf(cargf(
p->warp.point - pt) * I);
3075 p->node.ctrl2 = 2 *
p->warp.point - pt;
3088 switch (
d->header.type)
3094 switch (
d->header.node_type)
3097 n->node.ctrl1 =
d->warp.point +
3098 cabsf(
d->warp.point -
n->node.ctrl1) *
3099 cexpf(cargf(
d->warp.point - pt) * I);
3102 n->node.ctrl1 = 2 *
d->warp.point - pt;
3115 d->warp.radius = pt;
3120 d->warp.strength = pt;
3126 d->warp.control1 =
MIN(1.0, cabsf(pt - *start_pt) / cabsf(
d->warp.radius - *start_pt));
3130 d->warp.control2 =
MIN(1.0, cabsf(pt - *start_pt) / cabsf(
d->warp.radius - *start_pt));
3159 if (default_value != 0.0f && new_value !=
value) new_value = 0.25f * default_value + 0.75f *
value;
3168 GtkAllocation allocation;
3169 gtk_widget_get_allocation(widget, &allocation);
3170 const int last_win_min =
MIN(allocation.width, allocation.height);
3176 const float im_scale = 0.09f * iwd_min * last_win_min * scale / proc_wdht_min;
3195 const float complex strength_v = warp->
strength - warp->
point;
3199 float radius = 0.0f,
r = 0.0f, phi = 0.0f;
3205 else if(!incr && cabsf(warp->
radius - warp->
point) > 10.0f)
3221 float phi = cargf(strength_v);
3222 const float r = cabsf(strength_v);
3237 const float phi = cargf(strength_v);
3238 float r = cabsf(strength_v);
3265 float complex pt = 0.0f;
3272 g->last_mouse_pos = pt;
3273 g->last_mouse_mods =
state;
3275 g->last_button1_pressed_pos = pt;
3281 if(which == 2)
goto done;
3285 if(which == 1 && gtk_toggle_button_get_active(
g->btn_point_tool))
3293 g->status &= ~DT_LIQUIFY_STATUS_PREVIEW;
3303 if(which == 1 && (gtk_toggle_button_get_active(
g->btn_line_tool)
3304 || gtk_toggle_button_get_active(
g->btn_curve_tool)))
3314 g->temp =
g->last_hit.elem;
3323 if(gtk_toggle_button_get_active(
g->btn_curve_tool))
3328 g->status &= ~DT_LIQUIFY_STATUS_PREVIEW;
3335 if(gtk_toggle_button_get_active(
g->btn_node_tool))
3337 if(which == 1 &&
dt_modifier_is(
g->last_mouse_mods, GDK_CONTROL_MASK) &&
3346 if(which == 1 &&
dt_modifier_is(
g->last_mouse_mods, GDK_CONTROL_MASK) &&
3377 float complex pt = 0.0f;
3380 float radius = 0.0f,
r = 1.0f, phi = 0.0f;
3384 g->temp->warp.radius = pt + radius;
3385 g->temp->warp.strength = pt +
r * cexpf(phi * I);
3389 g->just_started =
TRUE;
3404 float complex pt = 0.0f;
3411 g->last_mouse_pos = pt;
3418 if(gtk_toggle_button_get_active(
g->btn_point_tool))
3424 else if(gtk_toggle_button_get_active(
g->btn_line_tool))
3426 const int prev_index =
g->node_index;
3427 const float complex
strength = (
g->temp->warp.strength -
g->temp->warp.point);
3428 const float radius = cabsf(
g->temp->warp.radius -
g->temp->warp.point);
3431 g->temp->warp.radius = pt + radius;
3434 g->temp->header.prev = prev_index;
3440 else if(gtk_toggle_button_get_active(
g->btn_curve_tool))
3442 const int prev_index =
g->node_index;
3443 const float complex
strength = (
g->temp->warp.strength -
g->temp->warp.point);
3444 const float radius = cabsf(
g->temp->warp.radius -
g->temp->warp.point);
3447 g->temp->warp.radius = pt + radius;
3450 g->temp->header.prev = prev_index;
3456 g->status &= ~DT_LIQUIFY_STATUS_NEW;
3478 g->status &= ~DT_LIQUIFY_STATUS_PREVIEW;
3513 if(gtk_toggle_button_get_active(
g->btn_node_tool))
3522 g->last_hit.elem->header.selected = oldsel ? 0 :
g->last_hit.layer;
3534 if(which == 1 &&
dt_modifier_is(
g->last_mouse_mods, GDK_SHIFT_MASK) && !dragged)
3539 const int oldsel = !!
g->last_hit.elem->header.selected;
3540 g->last_hit.elem->header.selected = oldsel ? 0 :
g->last_hit.layer;
3545 if(which == 1 &&
dt_modifier_is(
g->last_mouse_mods, GDK_CONTROL_MASK) && !dragged)
3570 float complex midpoint = warp3->
point;
3572 midpoint = warp1->
point;
3604 &&
dt_modifier_is(
g->last_mouse_mods, GDK_MOD1_MASK | GDK_CONTROL_MASK)
3624 const float complex p0 = prev->
warp.
point;
3629 c->node.ctrl1 = (2 * p0 + p1) / 3.0;
3630 c->node.ctrl2 = ( p0 + 2 * p1) / 3.0;
3641 g->last_button1_pressed_pos = -1;
3661 const gboolean creating = gtk_toggle_button_get_active(
g->btn_point_tool)
3662 || gtk_toggle_button_get_active(
g->btn_line_tool)
3663 || gtk_toggle_button_get_active(
g->btn_curve_tool)
3667 if(
key == GDK_KEY_BackSpace)
3672 const gboolean create_tool_active = gtk_toggle_button_get_active(
g->btn_point_tool)
3673 || gtk_toggle_button_get_active(
g->btn_line_tool)
3674 || gtk_toggle_button_get_active(
g->btn_curve_tool);
3675 gboolean restart_shape =
FALSE;
3684 last = &
g->params.nodes[
k];
3689 restart_shape = create_tool_active;
3703 const int prev_index = to_delete->
header.
prev;
3705 g->temp = prev_index >= 0 ?
node_get(&
g->params, prev_index) : NULL;
3713 g->status &= ~DT_LIQUIFY_STATUS_NEW;
3717 restart_shape = create_tool_active;
3732 if(
key == GDK_KEY_Delete)
3746 selected = &
g->params.nodes[
k];
3758 const int deleted_idx = selected->
header.
idx;
3766 int target_idx = (next_idx != -1) ? next_idx : prev_idx;
3767 if(target_idx > deleted_idx)
3785 if(
key == GDK_KEY_Escape ||
key == GDK_KEY_Return)
3807 gtk_toggle_button_set_active(
g->btn_point_tool,
FALSE);
3808 gtk_toggle_button_set_active(
g->btn_line_tool,
FALSE);
3809 gtk_toggle_button_set_active(
g->btn_curve_tool,
FALSE);
3810 gtk_toggle_button_set_active(
g->btn_node_tool,
TRUE);
3843 g->status &= ~DT_LIQUIFY_STATUS_PREVIEW;
3847 if(
IS_NULL_PTR(btn) || !gtk_toggle_button_get_active(btn))
3849 gtk_toggle_button_set_active(
g->btn_point_tool, btn ==
g->btn_point_tool);
3850 gtk_toggle_button_set_active(
g->btn_line_tool, btn ==
g->btn_line_tool);
3851 gtk_toggle_button_set_active(
g->btn_curve_tool, btn ==
g->btn_curve_tool);
3852 gtk_toggle_button_set_active(
g->btn_node_tool, btn ==
g->btn_node_tool);
3854 if(btn ==
g->btn_point_tool)
3856 (
darktable.
control, _(
"click and drag to add point\nscroll to change size - "
3857 "shift+scroll to change strength - ctrl+scroll to change direction"));
3858 else if(btn ==
g->btn_line_tool)
3861 "shift+scroll to change strength - ctrl+scroll to change direction"));
3862 else if(btn ==
g->btn_curve_tool)
3865 "shift+scroll to change strength - ctrl+scroll to change direction"));
3866 else if(btn ==
g->btn_node_tool)
3871 if(btn ==
g->btn_point_tool || btn ==
g->btn_line_tool || btn ==
g->btn_curve_tool)
3880 gtk_toggle_button_set_active(btn,
FALSE);
3900 cairo_surface_t *cs = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, 1, 1);
3901 cairo_surface_destroy(cs);
3907 g->last_button1_pressed_pos = -1;
3914 gtk_widget_set_tooltip_text(hbox, _(
"use a tool to add warps.\nright-click to remove a warp."));
3919 gtk_box_pack_start(GTK_BOX(hbox), lbox,
FALSE,
TRUE, 0);
3923 gtk_box_pack_start(GTK_BOX(labelbox), label,
FALSE,
TRUE, 0);
3925 gtk_box_pack_start(GTK_BOX(labelbox), GTK_WIDGET(
g->label_warp),
FALSE,
TRUE, 0);
3926 gtk_box_pack_start(GTK_BOX(lbox), labelbox,
FALSE,
TRUE, 0);
3930 gtk_box_pack_start(GTK_BOX(labelbox2), label2,
FALSE,
TRUE, 0);
3932 gtk_box_pack_start(GTK_BOX(labelbox2), GTK_WIDGET(
g->label_node),
FALSE,
TRUE, 0);
3933 gtk_box_pack_start(GTK_BOX(lbox), labelbox2,
FALSE,
TRUE, 0);
3942 g->btn_curve_tool = GTK_TOGGLE_BUTTON(
dt_iop_togglebutton_new(self, N_(
"shapes"), N_(
"draw curves"), N_(
"draw multiple curves"),
3946 g->btn_line_tool = GTK_TOGGLE_BUTTON(
dt_iop_togglebutton_new(self, N_(
"shapes"), N_(
"draw lines"), N_(
"draw multiple lines"),
3950 g->btn_point_tool = GTK_TOGGLE_BUTTON(
dt_iop_togglebutton_new(self, N_(
"shapes"), N_(
"draw points"), N_(
"draw multiple points"),
3955 "ctrl+alt+click: toggle line/curve");
3957 "ctrl+click: autosmooth, cusp, smooth, symmetrical"
3958 " - right click to remove");
3965 "ctrl+click: linear, grow, and shrink");
static double dist(double x1, double y1, double x2, double y2)
static void error(char *msg)
static const dt_aligned_pixel_simd_t const dt_adaptation_t const float p
void dt_collection_hint_message(const dt_collection_t *collection)
static float lookup(read_only image2d_t lut, const float x)
static const float const float const float min
const dt_colormatrix_t dt_aligned_pixel_t out
static const float const float C
static float strength(float value, float strength)
typedef void((*dt_cache_allocate_t)(void *userdata, dt_cache_entry_t *entry))
void dt_conf_set_float(const char *name, float val)
float dt_conf_get_float(const char *name)
void dt_control_queue_redraw_center()
request redraw of center window. This redraws the center view within a gdk critical section to preven...
void dt_control_hinter_message(const struct dt_control_t *s, const char *message)
void dt_control_queue_cursor_by_name(const char *curs_str)
Queue a GTK named cursor for the next cursor commit.
void dt_print(dt_debug_thread_t thread, const char *msg,...)
#define dt_free_align(ptr)
static void * dt_calloc_align(size_t size)
#define dt_pixelpipe_cache_alloc_align_float_cache(pixels, id)
#define dt_pixelpipe_cache_alloc_align_cache(size, id)
static void dt_free_gpointer(gpointer ptr)
#define DT_MODULE_INTROSPECTION(MODVER, PARAMSTYPE)
#define dt_pixelpipe_cache_free_align(mem)
#define __DT_CLONE_TARGETS__
#define __OMP_PARALLEL_FOR__(...)
static const dt_aligned_pixel_simd_t value
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...
#define dt_dev_add_history_item(dev, module, enable, redraw)
void dt_dev_pixelpipe_sync_virtual(dt_develop_t *dev, dt_dev_pixelpipe_change_t flag)
int dt_dev_distort_transform_locked(const dt_dev_pixelpipe_t *pipe, const double iop_order, const int transf_direction, float *points, size_t points_count)
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_rescale_roi_to_input(dt_develop_t *dev, cairo_t *cr, int32_t width, int32_t height)
Scale the ROI to fit the input size within given width/height, centered.
float dt_dev_get_zoom_scale(const dt_develop_t *dev, const gboolean preview)
void dt_dev_coordinates_image_norm_to_image_abs(dt_develop_t *dev, float *points, size_t num_points)
void dt_dev_coordinates_widget_to_image_norm(dt_develop_t *dev, float *points, size_t num_points)
Coordinate conversion helpers between widget, normalized image, and absolute image spaces.
int dt_dev_distort_backtransform_plus(const dt_dev_pixelpipe_t *pipe, const double iop_order, const int transf_direction, float *points, size_t points_count)
static uint64_t dt_dev_get_history_hash(const dt_develop_t *dev)
@ DT_DEV_TRANSFORM_DIR_BACK_EXCL
@ DT_DEV_TRANSFORM_DIR_ALL
@ DT_DEV_TRANSFORM_DIR_FORW_EXCL
void dtgtk_liquify_cairo_paint_curve_tool(cairo_t *cr, const gint x, const gint y, const gint w, const gint h, const gint flags, void *data)
void dtgtk_liquify_cairo_paint_line_tool(cairo_t *cr, const gint x, const gint y, const gint w, const gint h, const gint flags, void *data)
void dtgtk_liquify_cairo_paint_node_tool(cairo_t *cr, const gint x, const gint y, const gint w, const gint h, const gint flags, void *data)
void dtgtk_cairo_paint_masks_edit(cairo_t *cr, gint x, gint y, gint w, gint h, gint flags, void *data)
static guint dt_keys_mainpad_alternatives(const guint key_val)
Remap keypad keys to usual mainpad ones.
#define DT_GUI_MOUSE_EFFECT_RADIUS
GtkWidget * dt_ui_main_window(dt_ui_t *ui)
get the main window widget
#define DT_GUI_BOX_SPACING
#define DT_PIXEL_APPLY_DPI(value)
static GtkWidget * dt_ui_label_new(const gchar *str)
void dt_iop_request_focus(dt_iop_module_t *module)
const char ** dt_iop_set_description(dt_iop_module_t *module, const char *main_text, const char *purpose, const char *input, const char *process, const char *output)
static void dt_iop_gui_enter_critical_section(dt_iop_module_t *const module) ACQUIRE(&module -> gui_lock)
@ IOP_FLAGS_SUPPORTS_BLENDING
static void dt_iop_gui_leave_critical_section(dt_iop_module_t *const module) RELEASE(&module -> gui_lock)
#define IOP_GUI_ALLOC(module)
GtkWidget * dt_iop_togglebutton_new(dt_iop_module_t *self, const char *section, const gchar *label, const gchar *ctrl_label, GCallback callback, gboolean local, guint accel_key, GdkModifierType mods, DTGTKCairoPaintIconFunc paint, GtkWidget *box)
gboolean dt_mask_scroll_increases(int up)
const struct dt_interpolation * dt_interpolation_new(enum dt_interpolation_type type)
__DT_CLONE_TARGETS__ void dt_interpolation_compute_pixel4c(const struct dt_interpolation *itor, const float *in, float *out, const float x, const float y, const int width, const int height, const int linestride)
__DT_CLONE_TARGETS__ float dt_interpolation_compute_sample(const struct dt_interpolation *itor, const float *in, const float x, const float y, const int width, const int height, const int samplestride, const int linestride)
@ DT_INTERPOLATION_BICUBIC
@ DT_INTERPOLATION_BILINEAR
@ DT_INTERPOLATION_MITCHELL
@ DT_INTERPOLATION_USERPREF_WARP
void commit_params(struct dt_iop_module_t *module, dt_iop_params_t *params, dt_dev_pixelpipe_t *pipe, dt_dev_pixelpipe_iop_t *piece)
dt_liquify_path_data_enum_t
@ DT_LIQUIFY_PATH_LINE_TO_V1
@ DT_LIQUIFY_PATH_INVALIDATED
@ DT_LIQUIFY_PATH_CURVE_TO_V1
@ DT_LIQUIFY_PATH_MOVE_TO_V1
static void _start_new_shape(dt_iop_module_t *module)
int process_cl(struct dt_iop_module_t *module, const dt_dev_pixelpipe_t *pipe, const dt_dev_pixelpipe_iop_t *piece, const cl_mem_t dev_in, const cl_mem_t dev_out)
void init(dt_iop_module_t *module)
void distort_mask(struct dt_iop_module_t *self, const struct dt_dev_pixelpipe_t *pipe, struct dt_dev_pixelpipe_iop_t *piece, const float *const in, float *const out, const dt_iop_roi_t *const roi_in, const dt_iop_roi_t *const roi_out)
static void casteljau(const float complex *p0, float complex *p1, float complex *p2, float complex *p3, const float t)
void gui_post_expose(struct dt_iop_module_t *module, cairo_t *cr, int32_t width, int32_t height, int32_t pointerx, int32_t pointery)
const char ** description(struct dt_iop_module_t *self)
static __DT_CLONE_TARGETS__ void apply_global_distortion_map(struct dt_iop_module_t *module, const dt_dev_pixelpipe_iop_t *piece, const float *const restrict in, float *const restrict out, const dt_iop_roi_t *const roi_in, const dt_iop_roi_t *const roi_out, const int ch, const float complex *const map, const cairo_rectangle_int_t *extent)
int distort_backtransform(dt_iop_module_t *self, const dt_dev_pixelpipe_t *pipe, const dt_dev_pixelpipe_iop_t *piece, float *const restrict points, size_t points_count)
static void get_stamp_params(dt_iop_module_t *module, float *radius, float *r_strength, float *phi)
void gui_reset(dt_iop_module_t *self)
float dt_liquify_ui_widths[]
static dt_liquify_path_data_t * alloc_line_to(dt_iop_module_t *module, float complex end_point)
static gboolean _is_movable_layer(const dt_liquify_layer_enum_t layer)
static int _distort_xtransform(dt_iop_module_t *self, const dt_dev_pixelpipe_t *pipe, const dt_dev_pixelpipe_iop_t *piece, float *const restrict points, const size_t points_count, const gboolean inverted)
dt_liquify_layer_t dt_liquify_layers[]
static dt_liquify_hit_t _hit_paths(dt_iop_module_t *module, dt_iop_liquify_params_t *p, GList *layers, const float complex *pt)
static gboolean is_dragging(const dt_iop_liquify_gui_data_t *g)
int distort_transform(dt_iop_module_t *self, const dt_dev_pixelpipe_t *pipe, const dt_dev_pixelpipe_iop_t *piece, float *const restrict points, size_t points_count)
static dt_liquify_path_data_t * alloc_move_to(dt_iop_module_t *module, float complex start_point)
void cleanup_pipe(struct dt_iop_module_t *module, dt_dev_pixelpipe_t *pipe, dt_dev_pixelpipe_iop_t *piece)
static void get_point_scale(struct dt_iop_module_t *module, float x, float y, float complex *pt, float *scale)
static void _draw_paths(dt_iop_module_t *module, cairo_t *cr, const float scale, dt_iop_liquify_params_t *p, GList *layers)
static void node_gc(dt_iop_liquify_params_t *p)
static float mix(const float a, const float b, const float t)
int scrolled(struct dt_iop_module_t *module, double x, double y, int up, uint32_t state)
static cl_int_t apply_global_distortion_map_cl(struct dt_iop_module_t *module, const dt_dev_pixelpipe_t *pipe, const dt_dev_pixelpipe_iop_t *piece, const cl_mem_t dev_in, const cl_mem_t dev_out, const dt_iop_roi_t *roi_in, const dt_iop_roi_t *roi_out, const float complex *map, const cairo_rectangle_int_t *map_extent)
static void draw_paths(struct dt_iop_module_t *module, cairo_t *cr, const float scale, dt_iop_liquify_params_t *params)
int button_pressed(struct dt_iop_module_t *module, double x, double y, double pressure, int which, int type, uint32_t state)
static void draw_triangle(cairo_t *cr, const float complex pt, const double theta, const double size)
int mouse_moved(struct dt_iop_module_t *module, double x, double y, double pressure, int which)
dt_liquify_layer_flag_enum_t
@ DT_LIQUIFY_LAYER_FLAG_CURVE_TOOL
show if line tool active
@ DT_LIQUIFY_LAYER_FLAG_POINT_TOOL
show if point tool active
@ DT_LIQUIFY_LAYER_FLAG_HIT_TEST
include layer in hit testing
@ DT_LIQUIFY_LAYER_FLAG_ANY_TOOL
@ DT_LIQUIFY_LAYER_FLAG_NODE_SELECTED
show if node is selected
@ DT_LIQUIFY_LAYER_FLAG_LINE_TOOL
show if line tool active
@ DT_LIQUIFY_LAYER_FLAG_PREV_SELECTED
show if previous node is selected
@ DT_LIQUIFY_LAYER_FLAG_NODE_TOOL
show if node tool active
__DT_CLONE_TARGETS__ int process(struct dt_iop_module_t *module, const dt_dev_pixelpipe_t *pipe, const dt_dev_pixelpipe_iop_t *piece, const void *const in, void *const out)
dt_liquify_node_type_enum_t
@ DT_LIQUIFY_NODE_TYPE_LAST
@ DT_LIQUIFY_NODE_TYPE_CUSP
@ DT_LIQUIFY_NODE_TYPE_SYMMETRICAL
@ DT_LIQUIFY_NODE_TYPE_AUTOSMOOTH
@ DT_LIQUIFY_NODE_TYPE_SMOOTH
void gui_update(dt_iop_module_t *module)
Refresh GUI controls from current params and configuration.
static float get_rot(const dt_liquify_warp_type_enum_t warp_type)
static float complex * create_global_distortion_map(const cairo_rectangle_int_t *map_extent, const GSList *interpolated, gboolean inverted)
static void node_delete(dt_iop_liquify_params_t *p, dt_liquify_path_data_t *this)
static int build_round_stamp(float complex **pstamp, cairo_rectangle_int_t *const restrict stamp_extent, const dt_liquify_warp_t *const restrict warp)
static float complex normalize(const float complex v)
static float cdot(const float complex p0, const float complex p1)
static void set_line_width(cairo_t *cr, double scale, dt_liquify_ui_width_enum_t w)
#define CHECK_HIT_PT(point)
static void smooth_path_linsys(size_t n, const float complex *k, float complex *c1, float complex *c2, const int *equation)
static gboolean detect_drag(const dt_iop_liquify_gui_data_t *g, const double scale, const float complex pt)
dt_liquify_warp_type_enum_t
@ DT_LIQUIFY_WARP_TYPE_RADIAL_GROW
@ DT_LIQUIFY_WARP_TYPE_LINEAR
@ DT_LIQUIFY_WARP_TYPE_LAST
@ DT_LIQUIFY_WARP_TYPE_RADIAL_SHRINK
static const dt_liquify_rgba_t DT_LIQUIFY_COLOR_SELECTED
static void draw_rectangle(cairo_t *cr, const float complex pt, const double theta, const double size)
static void unselect_all(dt_iop_liquify_params_t *p)
static float mitchell(const float x)
dt_liquify_ui_width_enum_t
@ DT_LIQUIFY_UI_WIDTH_GIZMO_SMALL
@ DT_LIQUIFY_UI_WIDTH_DOUBLELINE
@ DT_LIQUIFY_UI_WIDTH_LAST
@ DT_LIQUIFY_UI_WIDTH_DEFAULT_STRENGTH
@ DT_LIQUIFY_UI_WIDTH_THICKLINE
@ DT_LIQUIFY_UI_WIDTH_THINLINE
@ DT_LIQUIFY_UI_WIDTH_DEFAULT_RADIUS
@ DT_LIQUIFY_UI_WIDTH_GIZMO
@ DT_LIQUIFY_UI_WIDTH_MIN_DRAG
void gui_init(dt_iop_module_t *self)
static dt_liquify_path_data_t * find_hovered(dt_iop_liquify_params_t *p)
static float dt_conf_get_sanitize_float(const char *name, float min, float max, float default_value)
static float * build_lookup_table(const int distance, const float control1, const float control2)
static void distort_paths_raw_to_piece(const struct dt_iop_module_t *module, const dt_dev_pixelpipe_t *pipe, const float roi_in_scale, dt_iop_liquify_params_t *p, const gboolean from_distort_transform)
static float complex point_at_arc_length(const float complex points[], const int n_points, const float arc_length, restart_cookie_t *restart)
static void smooth_paths_linsys(dt_iop_liquify_params_t *params)
static void add_to_global_distortion_map(float complex *global_map, const cairo_rectangle_int_t *const restrict global_map_extent, const dt_liquify_warp_t *const restrict warp, const float complex *const restrict stamp, const cairo_rectangle_int_t *stamp_extent)
static void _distort_paths(const struct dt_iop_module_t *module, const distort_params_t *params, const dt_iop_liquify_params_t *p)
static float get_zoom_scale(const dt_develop_t *develop)
static dt_liquify_hit_t _hit_test_paths(struct dt_iop_module_t *module, dt_iop_liquify_params_t *params, float complex pt)
static void end_drag(dt_iop_liquify_gui_data_t *g)
void modify_roi_in(struct dt_iop_module_t *module, const struct dt_dev_pixelpipe_t *pipe, struct dt_dev_pixelpipe_iop_t *piece, const dt_iop_roi_t *roi_out, dt_iop_roi_t *roi_in)
void gui_cleanup(dt_iop_module_t *self)
static const dt_liquify_rgba_t DT_LIQUIFY_COLOR_HOVER
static dt_liquify_path_data_t * node_alloc(dt_iop_liquify_params_t *p, int *node_index)
static float get_ui_width(const float scale, const dt_liquify_ui_width_enum_t w)
static void compute_round_stamp_extent(cairo_rectangle_int_t *const restrict stamp_extent, const dt_liquify_warp_t *const restrict warp)
void cleanup_global(dt_iop_module_so_t *module)
static dt_liquify_path_data_t * node_prev(dt_iop_liquify_params_t *p, const dt_liquify_path_data_t *n)
static float bicubic(const float a, const float x)
@ DT_LIQUIFY_STATUS_PREVIEW
@ DT_LIQUIFY_STATUS_INTERPOLATED
int default_colorspace(dt_iop_module_t *self, dt_dev_pixelpipe_t *pipe, const dt_dev_pixelpipe_iop_t *piece)
void input_format(dt_iop_module_t *self, dt_dev_pixelpipe_t *pipe, dt_dev_pixelpipe_iop_t *piece, dt_iop_buffer_dsc_t *dsc)
static void interpolate_cubic_bezier(const float complex p0, const float complex p1, const float complex p2, const float complex p3, float complex buffer[], const int n)
static GList * interpolate_paths(dt_iop_liquify_params_t *p)
static dt_liquify_path_data_t * node_get(dt_iop_liquify_params_t *p, const int index)
const float STAMP_RELOCATION
const int LOOKUP_OVERSAMPLE
const int INTERPOLATION_POINTS
static void start_drag(dt_iop_liquify_gui_data_t *g, dt_liquify_layer_enum_t layer, dt_liquify_path_data_t *elem)
static float find_nearest_on_line_t(const float complex p0, const float complex p1, const float complex x)
static void path_delete(dt_iop_liquify_params_t *p, dt_liquify_path_data_t *this)
static float find_nearest_on_curve_t(const float complex p0, const float complex p1, const float complex p2, const float complex p3, const float complex x, const int n)
static GSList * _get_map_extent(const dt_iop_roi_t *roi_out, const GList *interpolated, cairo_rectangle_int_t *map_extent)
int button_released(struct dt_iop_module_t *module, double x, double y, int which, uint32_t state)
static int path_length(dt_iop_liquify_params_t *p, dt_liquify_path_data_t *n)
static dt_liquify_path_data_t * node_next(dt_iop_liquify_params_t *p, const dt_liquify_path_data_t *n)
static const dt_liquify_hit_t NOWHERE
void init_pipe(struct dt_iop_module_t *module, dt_dev_pixelpipe_t *pipe, dt_dev_pixelpipe_iop_t *piece)
static float complex * build_global_distortion_map(struct dt_iop_module_t *module, const dt_dev_pixelpipe_t *pipe, const dt_dev_pixelpipe_iop_t *piece, const dt_iop_roi_t *roi_in, const dt_iop_roi_t *roi_out, cairo_rectangle_int_t *map_extent)
static float complex cmix(const float complex p0, const float complex p1, const float t)
static gboolean btn_make_radio_callback(GtkToggleButton *btn, GdkEventButton *event, dt_iop_module_t *module)
static void draw_circle(cairo_t *cr, const float complex pt, const double diameter)
static void update_warp_count(const dt_iop_liquify_gui_data_t *g)
@ DT_LIQUIFY_LAYER_STRENGTHPOINT
@ DT_LIQUIFY_LAYER_HARDNESSPOINT2_HANDLE
@ DT_LIQUIFY_LAYER_HARDNESSPOINT1
@ DT_LIQUIFY_LAYER_HARDNESSPOINT2
@ DT_LIQUIFY_LAYER_CTRLPOINT1
@ DT_LIQUIFY_LAYER_HARDNESS2
@ DT_LIQUIFY_LAYER_HARDNESS1
@ DT_LIQUIFY_LAYER_RADIUSPOINT
@ DT_LIQUIFY_LAYER_RADIUS
@ DT_LIQUIFY_LAYER_HARDNESSPOINT1_HANDLE
@ DT_LIQUIFY_LAYER_CTRLPOINT2
@ DT_LIQUIFY_LAYER_CENTERPOINT
@ DT_LIQUIFY_LAYER_RADIUSPOINT_HANDLE
@ DT_LIQUIFY_LAYER_CTRLPOINT1_HANDLE
@ DT_LIQUIFY_LAYER_STRENGTHPOINT_HANDLE
@ DT_LIQUIFY_LAYER_CTRLPOINT2_HANDLE
@ DT_LIQUIFY_LAYER_BACKGROUND
static void init_warp(dt_liquify_warp_t *warp, float complex point)
static void node_insert_before(dt_iop_liquify_params_t *p, dt_liquify_path_data_t *this, dt_liquify_path_data_t *new)
void init_global(dt_iop_module_so_t *module)
static void set_source_rgba(cairo_t *cr, dt_liquify_rgba_t rgba)
void gui_focus(struct dt_iop_module_t *module, gboolean in)
static void sync_pipe(struct dt_iop_module_t *module, gboolean history)
static dt_liquify_path_data_t * alloc_curve_to(dt_iop_module_t *module, float complex end_point)
static void mix_warps(dt_liquify_warp_t *result, const dt_liquify_warp_t *warp1, const dt_liquify_warp_t *warp2, const complex float pt, const float t)
void modify_roi_out(struct dt_iop_module_t *module, const struct dt_dev_pixelpipe_t *pipe, struct dt_dev_pixelpipe_iop_t *piece, dt_iop_roi_t *roi_out, const dt_iop_roi_t *roi_in)
static float get_arc_length(const float complex points[], const int n_points)
int key_pressed(struct dt_iop_module_t *self, GdkEventKey *event)
float *const restrict const size_t k
float *const restrict const size_t const size_t ch
int dt_opencl_enqueue_kernel_2d(const int dev, const int kernel, const size_t *sizes)
int dt_opencl_create_kernel(const int prog, const char *name)
void * dt_opencl_copy_host_to_device_constant(const int devid, const size_t size, void *host)
int dt_opencl_enqueue_copy_image(const int devid, cl_mem src, cl_mem dst, size_t *orig_src, size_t *orig_dst, size_t *region)
void dt_opencl_free_kernel(const int kernel)
int dt_opencl_set_kernel_arg(const int dev, const int kernel, const int num, const size_t size, const void *arg)
void dt_opencl_release_mem_object(cl_mem mem)
@ DT_DEV_PIPE_TOP_CHANGED
static uint64_t dt_dev_pixelpipe_get_history_hash(const dt_dev_pixelpipe_t *pipe)
struct _GtkWidget GtkWidget
const float uint32_t state[4]
struct dt_gui_gtk_t * gui
struct dt_collection_t * collection
struct dt_develop_t * develop
struct dt_control_t * control
gboolean from_distort_transform
const dt_dev_pixelpipe_t * pipe
struct dt_iop_module_t *void * data
struct dt_dev_pixelpipe_t * virtual_pipe
struct dt_develop_t::@17 roi
enum dt_interpolation_type id
dt_iop_buffer_type_t datatype
dt_iop_liquify_params_t params
GdkModifierType last_mouse_mods
GDK modifiers at the time mouse button was pressed.
dt_liquify_path_data_t * temp
Points to the element under construction or NULL.
dt_liquify_hit_t dragging
Element being dragged with mouse button.
GtkToggleButton * btn_curve_tool
float complex last_button1_pressed_pos
dt_liquify_hit_t last_hit
Element last hit with mouse button.
float complex last_mouse_pos
dt_liquify_status_enum_t status
Various flags.
dt_iop_global_data_t * data
struct dt_develop_t * dev
dt_iop_gui_data_t * gui_data
dt_iop_global_data_t * global_data
Region of interest passed through the pixelpipe.
dt_liquify_layer_enum_t layer
dt_liquify_path_data_t * elem
const char * hint
hint displayed when hovering
dt_liquify_layer_enum_t hover_master
hover whenever master layer hovers, eg. to
dt_liquify_rgba_t bg
the background color for this layer
dt_liquify_layer_flag_enum_t flags
various flags for layer
dt_liquify_rgba_t fg
the foreground color for this layer
dt_liquify_path_header_t header
float complex radius
a point (the effective radius scalar is: cabs(radius - point))
float control2
range 0.0 .. 1.0 == radius
float complex strength
a point (the effective strength vector is: strength - point)
dt_liquify_warp_type_enum_t type
dt_liquify_status_enum_t status
float control1
range 0.0 .. 1.0 == radius