86#pragma GCC diagnostic ignored "-Wshadow"
94#define CONF_RADIUS "plugins/darkroom/liquify/radius"
95#define CONF_ANGLE "plugins/darkroom/liquify/angle"
96#define CONF_STRENGTH "plugins/darkroom/liquify/strength"
141#define COLOR_NULL { 0.0, 0.0, 0.0, 0.8 }
142#define GREY { 0.3, 0.3, 0.3, 0.8 }
143#define LGREY { 0.8, 0.8, 0.8, 1.0 }
144#define COLOR_DEBUG { 0.9, 0.9, 0.0, 1.0 }
319 GtkToggleButton *btn_point_tool, *btn_line_tool, *
btn_curve_tool, *btn_node_tool;
328 return _(
"l_iquify");
335 _(
"linear, RGB, scene-referred"),
337 _(
"linear, RGB, scene-referred"));
388 p->nodes[
k].header.next =
p->nodes[
k].header.prev = -1;
389 p->nodes[
k].header.selected =
p->nodes[
k].header.hovered = 0;
397 if(
n->header.prev == -1)
400 return &
p->nodes[
n->header.prev];
406 return &
p->nodes[index];
413 if(
n->header.next == -1)
416 return &
p->nodes[
n->header.next];
422 new->header.prev = this->header.prev;
423 if(this->header.prev != -1)
424 p->nodes[this->header.prev].header.next =
new->header.idx;
425 this->header.prev =
new->header.idx;
440 for(
int e=0; e<last; e++)
443 if(e >=
k)
p->nodes[e] =
p->nodes[e+1];
445 if(e >=
k)
p->nodes[e].header.idx--;
446 if(
p->nodes[e].header.prev >=
k)
p->nodes[e].header.prev--;
447 if(
p->nodes[e].header.next >=
k)
p->nodes[e].header.next--;
477 this->header.prev = this->header.next = - 1;
617 float *buffer = malloc(
sizeof(
float) * 2 * len);
629 *b++ = crealf(data->
node.
ctrl1) / params->from_scale;
630 *b++ = cimagf(data->
node.
ctrl1) / params->from_scale;
631 *b++ = crealf(data->
node.
ctrl2) / params->from_scale;
632 *b++ = cimagf(data->
node.
ctrl2) / params->from_scale;
636 *b++ = crealf(data->
warp.
point) / params->from_scale;
637 *b++ = cimagf(data->
warp.
point) / params->from_scale;
638 *b++ = crealf(data->
warp.
strength) / params->from_scale;
639 *b++ = cimagf(data->
warp.
strength) / params->from_scale;
640 *b++ = crealf(data->
warp.
radius) / params->from_scale;
641 *b++ = cimagf(data->
warp.
radius) / params->from_scale;
661 else if(params->from_distort_transform)
672 params->transf_direction, buffer, len);
698 data->
node.
ctrl1 = (b[0] + b[1] * I) * params->to_scale;
700 data->
node.
ctrl2 = (b[0] + b[1] * I) * params->to_scale;
705 data->
warp.
point = (b[0] + b[1] * I) * params->to_scale;
707 data->
warp.
strength = (b[0] + b[1] * I) * params->to_scale;
709 data->
warp.
radius = (b[0] + b[1] * I) * params->to_scale;
722 const float roi_in_scale,
724 const gboolean from_distort_transform)
726 const distort_params_t params = {
module->dev, pipe, NULL, 1.f, roi_in_scale, DT_DEV_TRANSFORM_DIR_BACK_EXCL, from_distort_transform };
735 const distort_params_t params = {
module->dev, NULL, chain, 1.f, roi_in_scale,
736 DT_DEV_TRANSFORM_DIR_BACK_EXCL, TRUE };
744 if(cabsf(
v) < 0.000001f)
751static inline float mix(
const float a,
const float b,
const float t)
753 return a + (b - a) *
t;
759static inline float complex
cmix(
const float complex p0,
const float complex p1,
const float t)
761 return p0 + (p1 - p0) *
t;
767 const complex
float pt,
775 result->
radius = pt + radius;
779 float arg1 = cargf(p1);
780 float arg2 = cargf(p2);
781 gboolean invert =
FALSE;
783 if(arg1 > .0f && arg2 < -(
M_PI_F / 2.f))
789 else if(arg1 < -(
M_PI_F / 2.f) && arg2 > .0f)
796 const float r =
mix(cabsf(p1), cabsf(p2),
t);
797 const float phi = invert ?
M_PI_F -
mix(arg1, arg2,
t) :
mix(arg1, arg2,
t);
799 result->
strength = pt +
r * cexpf(phi * I);
806 const float complex p1,
807 const float complex p2,
808 const float complex p3,
809 float complex buffer[],
814 const float complex
A = p3 - 3 * p2 + 3 * p1 - p0;
815 const float complex
B = 3 * p2 - 6 * p1 + 3 * p0;
816 const float complex
C = 3 * p1 - 3 * p0;
817 const float complex D = p0;
819 float complex *buf = buffer;
820 const float step = 1.0f /
n;
824 for(
int i = 1;
i <
n - 1; ++
i)
826 *buf++ = ((
A *
t +
B) *
t +
C) *
t + D;
843 for(
int i = 1;
i < n_points;
i++)
844 length += cabsf(points[
i-1] - points[
i]);
864 float length = restart ? restart->
length : 0.0f;
866 for(
int i = restart ? restart->
i : 1;
i < n_points;
i++)
868 const float prev_length = length;
869 length += cabsf(points[
i-1] - points[
i]);
871 if(length >= arc_length)
873 const float t = (arc_length - prev_length) / (length - prev_length);
877 restart->
length = prev_length;
879 return cmix(points[
i - 1], points[
i],
t);
883 return points[n_points - 1];
932 float complex *cptr = clookup + 1;
933 const float complex *cptr_end = cptr + distance;
934 const float step = 1.0f / (float) distance;
938 for(
int i = 1;
i < distance && cptr < cptr_end;
i++)
941 while(crealf(*cptr) <
x && cptr < cptr_end)
943 const float dx1 = crealf(cptr[0] - cptr[-1]);
944 const float dx2 =
x - crealf(cptr[-1]);
945 *ptr++ = cimagf(cptr[0]) +(dx2 / dx1) * (cimagf(cptr[0]) - cimagf(cptr[-1]));
957 const int iradius = round(cabsf(warp->radius - warp->point));
960 stamp_extent->x = stamp_extent->y = -iradius;
961 stamp_extent->x += crealf(warp->point);
962 stamp_extent->y += cimagf(warp->point);
963 stamp_extent->width = stamp_extent->height = 2 * iradius + 1;
987 cairo_rectangle_int_t *
const restrict stamp_extent,
990 const int iradius = round(cabsf(warp->radius - warp->point));
993 stamp_extent->x = stamp_extent->y = -iradius;
994 stamp_extent->width = stamp_extent->height = 2 * iradius + 1;
998 float complex
strength = 0.5f * (warp->strength - warp->point);
1001 const float abs_strength = cabsf(
strength);
1003 float complex *restrict stamp =
1004 calloc(
sizeof(
float complex), (
size_t)stamp_extent->width * stamp_extent->height);
1009 const float *restrict lookup_table =
build_lookup_table(table_size, warp->control1, warp->control2);
1017 float complex *
const center = stamp + 2 * iradius * iradius + 2 * iradius;
1025 for(
int y = 0; y <= iradius; y++)
1027 for(
int x = 0;
x <= iradius;
x++)
1029 const float dist = dt_fast_hypotf(
x, y);
1031 if(idist >= table_size)
1037 float complex *
const q1 = center - y * stamp_extent->width +
x;
1038 float complex *
const q2 = center - y * stamp_extent->width -
x;
1039 float complex *
const q3 = center + y * stamp_extent->width -
x;
1040 float complex *
const q4 = center + y * stamp_extent->width +
x;
1042 float abs_lookup = abs_strength * lookup_table[idist] / iradius;
1047 *q1 = abs_lookup * (
x - y * I);
1048 *q2 = abs_lookup * (-
x - y * I);
1049 *q3 = abs_lookup * (-
x + y * I);
1050 *q4 = abs_lookup * (
x + y * I);
1054 *q1 = -abs_lookup * (
x - y * I);
1055 *q2 = -abs_lookup * (-
x - y * I);
1056 *q3 = -abs_lookup * (-
x + y * I);
1057 *q4 = -abs_lookup * (
x + y * I);
1061 *q1 = *q2 = *q3 = *q4 =
strength * lookup_table[idist];
1083 const cairo_rectangle_int_t *
const restrict global_map_extent,
1085 const float complex *
const restrict stamp,
1086 const cairo_rectangle_int_t *stamp_extent)
1088 cairo_rectangle_int_t mmext = *stamp_extent;
1089 mmext.x += (int) round(crealf(warp->point));
1090 mmext.y += (int) round(cimagf(warp->point));
1091 cairo_rectangle_int_t cmmext = mmext;
1092 cairo_region_t *mmreg = cairo_region_create_rectangle(&mmext);
1093 cairo_region_intersect_rectangle(mmreg, global_map_extent);
1094 cairo_region_get_extents(mmreg, &cmmext);
1098 #pragma omp parallel for schedule (static) default (shared)
1101 for(
int y = cmmext.y; y < cmmext.y + cmmext.height; y++)
1103 const float complex *
const srcrow = stamp + ((y - mmext.y) * mmext.width);
1104 float complex *
const destrow = global_map + ((y - global_map_extent->y) * global_map_extent->width);
1106 for(
int x = cmmext.x;
x < cmmext.x + cmmext.width;
x++)
1108 destrow[
x - global_map_extent->x] -= srcrow[
x - mmext.x];
1123 const float *
const restrict in,
1124 float *
const restrict
out,
1128 const float complex *
const map,
1129 const cairo_rectangle_int_t *extent)
1131 const int ch_width =
ch * roi_in->
width;
1136 #pragma omp parallel for schedule (static) default (shared)
1139 for(
int y = extent->y; y < extent->y + extent->height; y++)
1142 if(y >= roi_out->
y && y < roi_out->y + roi_out->
height)
1144 const float complex *
row = map + (y - extent->y) * extent->width;
1145 float* out_sample =
out + ((y - roi_out->
y) * roi_out->
width +
1146 extent->x - roi_out->
x) *
ch;
1147 for(
int x = extent->x;
x < extent->x + extent->width;
x++)
1151 (
x >= roi_out->
x && x < roi_out->
x + roi_out->
width) &&
1158 x + crealf(*
row) - roi_in->
x,
1159 y + cimagf(*
row) - roi_in->
y,
1169 x + crealf(*
row) - roi_in->
x,
1170 y + cimagf(*
row) - roi_in->
y,
1186 const GList *interpolated,
1187 cairo_rectangle_int_t *map_extent)
1189 const cairo_rectangle_int_t roi_out_rect = { roi_out->
x, roi_out->
y, roi_out->
width, roi_out->
height };
1190 cairo_region_t *roi_out_region = cairo_region_create_rectangle(&roi_out_rect);
1191 cairo_region_t *map_region = cairo_region_create();
1192 GSList *in_roi = NULL;
1194 for(
const GList *
i = interpolated;
i;
i = g_list_next(
i))
1197 cairo_rectangle_int_t
r;
1200 if(cairo_region_contains_rectangle(roi_out_region, &
r) != CAIRO_REGION_OVERLAP_OUT)
1202 cairo_region_union_rectangle(map_region, &
r);
1203 in_roi = g_slist_prepend(in_roi,
i->data);
1208 cairo_region_get_extents(map_region, map_extent);
1209 cairo_region_destroy(map_region);
1210 cairo_region_destroy(roi_out_region);
1212 return g_slist_reverse(in_roi);
1216 const GSList *interpolated,
1219 const int mapsize = map_extent->width * map_extent->height;
1230 memset(map, 0,
sizeof(
float complex) * mapsize);
1233 for(
const GSList *
i = interpolated;
i;
i = g_slist_next(
i))
1236 float complex *stamp = NULL;
1237 cairo_rectangle_int_t
r;
1255 memset(imap, 0,
sizeof(
float complex) * mapsize);
1261 #pragma omp parallel for schedule (static) default (shared)
1264 for(
int y = 0; y < map_extent->height; y++)
1266 const float complex *
const row = map + y * map_extent->width;
1267 for(
int x = 0;
x < map_extent->width;
x++)
1269 const float complex
d =
row[
x];
1271 const int nx =
x + (int)crealf(
d);
1272 const int ny = y + (int)cimagf(
d);
1275 if(nx>0 && nx<map_extent->
width && ny>0 && ny<map_extent->
height)
1276 imap[nx + ny * map_extent->width] = -
d;
1287 #pragma omp parallel for schedule (static) default (shared)
1290 for(
int y = 0; y < map_extent->height; y++)
1292 float complex *
const row = imap + y * map_extent->width;
1293 float complex last[2] = { 0, 0 };
1294 for(
int x = 0;
x < map_extent->width / 2 + 1;
x++)
1296 float complex *cl =
row +
x;
1297 float complex *cr =
row + map_extent->width -
x;
1300 if(*cl == 0) *cl = last[0];
1301 if(*cr == 0) *cr = last[1];
1303 last[0] = *cl; last[1] = *cr;
1317 cairo_rectangle_int_t *map_extent)
1326 GSList *interpolated_in_roi =
_get_map_extent(roi_out, interpolated, map_extent);
1330 g_slist_free(interpolated_in_roi);
1331 interpolated_in_roi = NULL;
1333 interpolated = NULL;
1366 cairo_rectangle_int_t pipe_rect =
1374 cairo_rectangle_int_t roi_in_rect =
1381 cairo_region_t *roi_in_region = cairo_region_create_rectangle(&roi_in_rect);
1385 cairo_rectangle_int_t extent;
1386 GSList *interpolated_in_roi =
_get_map_extent(roi_out, interpolated, &extent);
1387 g_slist_free(interpolated_in_roi);
1388 interpolated_in_roi = NULL;
1390 interpolated = NULL;
1393 cairo_region_union_rectangle(roi_in_region, &extent);
1395 cairo_region_intersect_rectangle(roi_in_region, &pipe_rect);
1398 cairo_region_get_extents(roi_in_region, &roi_in_rect);
1399 roi_in->
x = roi_in_rect.x;
1400 roi_in->
y = roi_in_rect.y;
1401 roi_in->
width = roi_in_rect.width;
1402 roi_in->
height = roi_in_rect.height;
1405 cairo_region_destroy(roi_in_region);
1421 const size_t points_count,
const gboolean inverted)
1425 float xmin = FLT_MAX, xmax = FLT_MIN, ymin = FLT_MAX, ymax = FLT_MIN;
1427 for(
size_t i = 0;
i < points_count * 2;
i += 2)
1429 const float x = points[
i];
1430 const float y = points[
i + 1];
1431 xmin = fmin(xmin,
x);
1432 xmax = fmax(xmax,
x);
1433 ymin = fmin(ymin, y);
1434 ymax = fmax(ymax, y);
1437 cairo_rectangle_int_t extent = { .x = (int)(xmin - .5), .y = (int)(ymin - .5),
1438 .width = (int)(xmax - xmin + 2.5), .height = (int)(ymax - ymin + 2.5) };
1440 if(extent.width > 0 && extent.height > 0)
1458 dt_iop_roi_t roi_in = { .
x = extent.x, .y = extent.y, .width = extent.width, .height = extent.height };
1459 GSList *interpolated_in_roi =
_get_map_extent(&roi_in, interpolated, &extent);
1462 g_slist_free(interpolated_in_roi);
1463 interpolated_in_roi = NULL;
1465 interpolated = NULL;
1469 const int map_size = extent.width * extent.height;
1470 const int x_last = extent.x + extent.width;
1471 const int y_last = extent.y + extent.height;
1475 for(
size_t i = 0;
i < points_count;
i++)
1477 float *px = &points[
i*2];
1478 float *py = &points[
i*2+1];
1479 const float x = *px;
1480 const float y = *py;
1481 const int map_offset = ((int)(
x - 0.5) - extent.x) + ((
int)(y - 0.5) - extent.y) * extent.width;
1483 if(
x >= extent.x && x < x_last && y >= extent.y && y < y_last && map_offset >= 0 && map_offset < map_size)
1485 const float complex
dist = map[map_offset];
1486 *px += crealf(
dist);
1487 *py += cimagf(
dist);
1499 g->dragging.layer = layer;
1500 g->dragging.elem = elem;
1514 float *
const restrict points,
size_t points_count)
1519 points_count,
TRUE);
1523 float *
const restrict points,
size_t points_count)
1526 points_count,
FALSE);
1584 const float *
const in,
float *
const out,
const dt_iop_roi_t *
const roi_in,
1590 for(
int i = 0;
i < roi_out->
height;
i++)
1592 float *destrow =
out + (size_t)
i * roi_out->
width;
1593 const float *srcrow = in + (size_t) (roi_in->
width * (
i + roi_out->
y - roi_in->
y) + roi_out->
x - roi_in->
x);
1595 memcpy(destrow, srcrow,
sizeof(
float) * roi_out->
width);
1600 cairo_rectangle_int_t map_extent;
1604 if(map_extent.width != 0 && map_extent.height != 0)
return;
1610 if(map_extent.width != 0 && map_extent.height != 0)
1619 const void *
const in,
1632 float *destrow = (
float *)
out + (
size_t)
ch *
i * roi_out->
width;
1633 const float *srcrow = (
float *)in + (
size_t)
ch * (roi_in->
width * (
i + roi_out->
y - roi_in->
y) +
1634 roi_out->
x - roi_in->
x);
1636 memcpy(destrow, srcrow,
sizeof(
float) *
ch *
width);
1641 cairo_rectangle_int_t map_extent;
1645 if(map_extent.width != 0 && map_extent.height != 0)
return 1;
1651 if(map_extent.width != 0 && map_extent.height != 0)
1663static inline float bicubic(
const float a,
const float x)
1665 const float absx = fabsf(
x);
1666 if(absx <= 1)
return ((a + 2) * absx - (a + 3)) * absx * absx + 1;
1667 if(absx < 2)
return ((a * absx - 5 * a) * absx + 8 * a) * absx - 4 * a;
1676 const float absx = fabsf(
x);
1677 const float x2 = absx * absx;
1678 const float x3 = x2 * absx;
1679 if(absx < 1.0f)
return (7.0f / 6.0f) * x3 - 2.0f * x2 + 8.0f / 9.0f;
1680 if(absx < 2.0f)
return -(7.0f / 18.0f) * x3 + 2.0f * x2 - (10.0f / 3.0f) * absx + 16.0f / 9.0f;
1700 const float complex *map,
1701 const cairo_rectangle_int_t *map_extent)
1703 cl_int_t err = CL_MEM_OBJECT_ALLOCATION_FAILURE;
1706 const int devid = pipe->
devid;
1712 switch (interpolation->
id)
1717 k = malloc(
sizeof(
float) * 2);
1726 k = malloc(
sizeof(
float) * ((
size_t)kdesc.
size * kdesc.
resolution + 1));
1733 k = malloc(
sizeof(
float) * ((
size_t)kdesc.
size * kdesc.
resolution + 1));
1749 (devid,
sizeof(
float complex) * map_extent->width * map_extent->height, (
void *) map);
1752 (devid,
sizeof(cairo_rectangle_int_t), (
void *) map_extent);
1758 (devid,
sizeof(
float) * (kdesc.
size * kdesc.
resolution + 1), (
void *)
k);
1774 const size_t sizes[] = {
ROUNDUPDWD(map_extent->width, devid),
ROUNDUPDHT(map_extent->height, devid) };
1798 const int devid = pipe->
devid;
1804 size_t src[] = { roi_out->
x - roi_in->
x, roi_out->
y - roi_in->
y, 0 };
1805 size_t dest[] = { 0, 0, 0 };
1808 if(err != CL_SUCCESS)
goto error;
1812 cairo_rectangle_int_t map_extent;
1816 if(map_extent.width != 0 && map_extent.height != 0)
return FALSE;
1821 if(map_extent.width != 0 && map_extent.height != 0)
1824 if(err != CL_SUCCESS)
goto error;
1838 const int program = 17;
1855 module->default_enabled = 0;
1856 module->params_size = sizeof(dt_iop_liquify_params_t);
1859 module->params = calloc(1, module->params_size);
1860 module->default_params = calloc(1, module->params_size);
1887static float cdot(
const float complex p0,
const float complex p1)
1890 return fma(crealf(p0), crealf(p1), cimagf(p0) * cimagf(p1));
1892 return crealf(p0) * crealf(p1) + cimagf(p0) * cimagf(p1);
1898 const double x = creal(pt), y = cimag(pt);
1900 cairo_translate(cr,
x, y);
1901 cairo_rotate(cr, theta);
1906static void draw_triangle(cairo_t *cr,
const float complex pt,
const double theta,
const double size)
1908 const double x = creal(pt), y = cimag(pt);
1910 cairo_translate(cr,
x, y);
1911 cairo_rotate(cr, theta);
1912 cairo_move_to(cr, -
size, -
size / 2.0);
1913 cairo_line_to(cr, 0, 0 );
1914 cairo_line_to(cr, -
size, +
size / 2.0);
1915 cairo_close_path(cr);
1919static void draw_circle(cairo_t *cr,
const float complex pt,
const double diameter)
1921 const double x = creal(pt), y = cimag(pt);
1923 cairo_new_sub_path(cr);
1924 cairo_arc(cr,
x, y, diameter / 2.0, 0, 2 *
DT_M_PI);
1939#define GET_UI_WIDTH(a) (get_ui_width(scale, DT_LIQUIFY_UI_WIDTH_##a))
1944 cairo_set_line_width(cr,
width);
1950 return g->last_button1_pressed_pos != -1.0 &&
1951 cabsf(pt -
g->last_button1_pressed_pos) >= (
GET_UI_WIDTH(MIN_DRAG) / scale);
1956 guint warp = 0, node = 0;
1966 char str_warp[10], str_node[20];
1967 snprintf(str_warp,
sizeof(str_warp),
"%d", warp);
1968 snprintf(str_node,
sizeof(str_node),
"%d", node);
1969 gtk_label_set_text(
g->label_warp, str_warp);
1970 gtk_label_set_text(
g->label_node, str_node);
1982 const float complex *p2 = &data->
warp.
point;
1991 l = g_list_append(l, w);
1998 const float complex *p1 = &prev->
warp.
point;
2001 const float total_length = cabsf(*p1 - *p2);
2002 float arc_length = 0.0f;
2003 while(arc_length < total_length)
2006 const float t = arc_length / total_length;
2007 const float complex pt =
cmix(*p1, *p2,
t);
2011 l = g_list_append(l, w);
2026 float arc_length = 0.0f;
2029 while(arc_length < total_length)
2032 const float t = arc_length / total_length;
2037 l = g_list_append(l, w);
2046#define FG_COLOR set_source_rgba(cr, fg_color)
2047#define BG_COLOR set_source_rgba(cr, bg_color)
2048#define VERYTHINLINE set_line_width (cr, scale / 2.0f, DT_LIQUIFY_UI_WIDTH_THINLINE)
2049#define THINLINE set_line_width (cr, scale, DT_LIQUIFY_UI_WIDTH_THINLINE)
2050#define THICKLINE set_line_width (cr, scale, DT_LIQUIFY_UI_WIDTH_THICKLINE)
2060 cairo_set_line_cap(cr, CAIRO_LINE_CAP_ROUND);
2067 GList *interpolated = (
is_dragging(
g) ||
g->last_button1_pressed_pos != -1)
2072 for(
const GList *l = layers; l; l = g_list_next(l))
2079 cairo_push_group(cr);
2112 cairo_move_to(cr, crealf(
point), cimagf(
point));
2116 for(
const GList *
i = interpolated;
i;
i = g_list_next(
i))
2127 for(
const GList *
i = interpolated;
i;
i = g_list_next(
i))
2137 for(
const GList *
i = interpolated;
i;
i = g_list_next(
i))
2148 for(
const GList *
i = interpolated;
i;
i = g_list_next(
i))
2151 cairo_move_to(cr, crealf(pwarp->
point), cimagf(pwarp->
point));
2156 for(
const GList *
i = interpolated;
i;
i = g_list_next(
i))
2166 cairo_fill_preserve(cr);
2178 cairo_line_to(cr, crealf(
point), cimagf(
point));
2186 cairo_stroke_preserve(cr);
2216 cairo_fill_preserve(cr);
2245 cairo_fill_preserve(cr);
2254 cairo_fill_preserve(cr);
2266 cairo_stroke_preserve(cr);
2275 cairo_fill_preserve(cr);
2284 cairo_stroke_preserve(cr);
2293 cairo_stroke_preserve(cr);
2304 cairo_fill_preserve(cr);
2315 cairo_fill_preserve(cr);
2322 cairo_move_to(cr, crealf(
point), cimagf(
point));
2329 cairo_line_to(cr, crealf(pt), cimagf(pt));
2334 cairo_stroke_preserve(cr);
2347 cairo_fill_preserve(cr);
2355 cairo_pop_group_to_source(cr);
2361 interpolated = NULL;
2375 const float complex p1,
2376 const float complex p2,
2377 const float complex p3,
2378 const float complex
x,
2381 float min_t = 0.0f, min_dist = cabsf(
x - p0);
2383 for(
int i = 0;
i <
n;
i++)
2385 const float t = (1.0 *
i) /
n;
2386 const float t1 = 1.0 -
t;
2387 const float complex ip =
2389 3 * t1 * t1 *
t * p1 +
2390 3 * t1 *
t *
t * p2 +
2393 const float dist = cabsf(
x - ip);
2413 const float b = cabsf(p1 - p0);
2414 const float dotab =
cdot(
x - p0, p1 - p0);
2415 return dotab / (b * b);
2420static void casteljau(
const float complex *p0,
float complex *p1,
float complex *p2,
float complex *p3,
const float t)
2422 const float complex p01 = *p0 + (*p1 - *p0) *
t;
2423 const float complex p12 = *p1 + (*p2 - *p1) *
t;
2424 const float complex p23 = *p2 + (*p3 - *p2) *
t;
2426 const float complex p012 = p01 + (p12 - p01) *
t;
2427 const float complex p123 = p12 + (p23 - p12) *
t;
2429 const float complex p0123 = p012 + (p123 - p012) *
t;
2436#define CHECK_HIT_PT(point) \
2437 const float d = cabsf(point - (*pt)); \
2441 hit.layer = layer; \
2448 const float complex *pt)
2452 float distance = FLT_MAX;
2454 for(
const GList *l = layers; l; l = g_list_next(l))
2487 const float complex deadzone = (
point - prev->
warp.
point) / 20.0f;
2488 const float complex lp1 = prev->
warp.
point + deadzone;
2489 const float complex lp2 =
point - deadzone;
2492 if(
t > 0.0f &&
t < 1.0f)
2494 const float complex linepoint =
cmix(lp1, lp2,
t);
2495 const float d = cabsf(linepoint - *pt);
2509 const float complex deadzone = (
point - prev->
warp.
point) / 20.0f;
2510 const float complex lp1 = prev->
warp.
point + deadzone;
2511 const float complex lp2 =
point - deadzone;
2514 if(
t > 0.0f &&
t < 1.0f)
2516 float complex curvepoint = lp2;
2521 const float d = cabsf(curvepoint - *pt);
2583 GList *layers = NULL;
2587 if(gtk_toggle_button_get_active(
g->btn_point_tool)
2589 layers = g_list_prepend(layers, GINT_TO_POINTER(layer));
2590 if(gtk_toggle_button_get_active(
g->btn_line_tool)
2592 layers = g_list_prepend(layers, GINT_TO_POINTER(layer));
2593 if(gtk_toggle_button_get_active(
g->btn_curve_tool)
2595 layers = g_list_prepend(layers, GINT_TO_POINTER(layer));
2596 if(gtk_toggle_button_get_active(
g->btn_node_tool)
2598 layers = g_list_prepend(layers, GINT_TO_POINTER(layer));
2600 layers = g_list_reverse(layers);
2604 g_list_free(layers);
2613 GList *layers = NULL;
2618 layers = g_list_prepend(layers, GINT_TO_POINTER(layer));
2620 layers = g_list_reverse(layers);
2622 hit =
_hit_paths(module, params, layers, &pt);
2623 g_list_free(layers);
2683 const float complex *
k,
2686 const int *equation)
2689 float *a = malloc(
sizeof(
float) *
n);
2690 float *b = malloc(
sizeof(
float) *
n);
2691 float *c = malloc(
sizeof(
float) *
n);
2692 float complex *
d = malloc(
sizeof(
float complex) *
n);
2696 for(
int i = 0;
i <
n;
i++)
2698 switch (equation[
i])
2700 #define ABCD(A,B,C,D) { { a[i] = A; b[i] = B; c[i] = C; d[i] = D; continue; } }
2701 case 1:
ABCD(0, 2, 1,
k[
i] + 2 *
k[
i+1] );
break;
2702 case 2:
ABCD(1, 4, 1, 4 *
k[
i] + 2 *
k[
i+1] );
break;
2703 case 3:
ABCD(2, 7, 0, 8 *
k[
i] +
k[
i+1] );
break;
2704 case 4:
ABCD(0, 1, 0,
c1[
i] );
break;
2705 case 5:
ABCD(0, 1, 0,
c1[
i] );
break;
2706 case 6:
ABCD(1, 4, 0, 4 *
k[
i] +
c2[
i] );
break;
2707 case 7:
ABCD(0, 1, 0,
c1[
i] );
break;
2708 case 8:
ABCD(0, 3, 0, 2 *
k[
i] +
k[
i+1] );
break;
2709 case 9:
ABCD(0, 2, 0,
k[
i] +
c2[
i] );
break;
2717 for(
int i = 1;
i <
n;
i++)
2719 const float m = a[
i] / b[
i-1];
2720 b[
i] = b[
i] -
m * c[
i-1];
2724 c1[
n-1] =
d[
n-1] / b[
n-1];
2725 for(
int i =
n - 2;
i >= 0;
i--)
2730 for(
int i = 0;
i <
n;
i++)
2732 switch (equation[
i])
2742 case 8:
c2[
i] = (
c1[
i] +
k[
i+1]) / 2;
break;
2745 default:
c2[
i] = 2 *
k[
i+1] -
c1[
i+1];
2758 while(
n->header.next != -1)
2761 n = &
p->nodes[
n->header.next];
2773 if(params->nodes[
k].header.prev != -1)
2783 float complex *pt = calloc(
n,
sizeof(
float complex));
2784 float complex *
c1 = calloc(
n,
sizeof(
float complex));
2785 float complex *
c2 = calloc(
n,
sizeof(
float complex));
2786 int *eqn = calloc(
n,
sizeof(
int));
2799 c1[idx-1] =
d->node.ctrl1;
2800 c2[idx-1] =
d->node.ctrl2;
2823 if(lineseg) eqn[idx] = 5;
2824 else if(!autosmooth && !next_autosmooth) eqn[idx] = 5;
2825 else if(firstseg && lastseg && !autosmooth && next_autosmooth) eqn[idx] = 7;
2826 else if(firstseg && lastseg && autosmooth && next_autosmooth) eqn[idx] = 8;
2827 else if(firstseg && lastseg && autosmooth && !next_autosmooth) eqn[idx] = 9;
2828 else if(firstseg && autosmooth && !next_autosmooth) eqn[idx] = 5;
2829 else if(firstseg && autosmooth) eqn[idx] = 1;
2830 else if(lastseg && autosmooth && next_autosmooth) eqn[idx] = 3;
2831 else if(lastseg && !autosmooth && next_autosmooth) eqn[idx] = 7;
2832 else if(autosmooth && !next_autosmooth) eqn[idx] = 6;
2833 else if(!autosmooth && next_autosmooth) eqn[idx] = 4;
2843 node = ¶ms->nodes[
k];
2852 d->node.ctrl2 =
c2[idx];
2870 else if(
p->nodes[
k].header.hovered)
2871 return &
p->nodes[
k];
2920 c->node.ctrl1 = c->node.ctrl2 = 0.0;
2932 p->nodes[
k].header.selected = 0;
2957 const float bb_width =
geometry.processed_width;
2958 const float bb_height =
geometry.processed_height;
2959 if(bb_width < 1.0 || bb_height < 1.0)
2988 draw_paths(module, cr, 1.0 / zoom_scale, ©_params);
3033 float pts[2] = { (float)
x, (
float)y };
3042 *pt = pts[0] + pts[1] * I;
3070 gboolean handled =
FALSE;
3071 float complex pt = 0.0f;
3078 const float complex prev_mouse_pos =
g->last_mouse_pos;
3079 g->last_mouse_pos = pt;
3093 if(hit.
elem != last_hovered
3133 const float complex *start_pt = &
d->warp.point;
3135 switch (
g->dragging.layer)
3140 const float complex
delta = prev_mouse_pos == -1 ? 0.0f : pt - prev_mouse_pos;
3141 if(cabsf(
delta) < 1e-6f)
break;
3144 for(
int i = 0;
i < 2;
i++)
3165 switch (
d->header.type)
3168 d->node.ctrl2 += pt -
d->warp.point;
3173 n->node.ctrl1 += pt -
d->warp.point;
3175 p->node.ctrl2 += pt -
d->warp.point;
3176 d->warp.radius += pt -
d->warp.point;
3177 d->warp.strength += pt -
d->warp.point;
3186 switch (
d->header.type)
3192 switch (
p->header.node_type)
3195 p->node.ctrl2 =
p->warp.point +
3196 cabsf(
p->warp.point -
p->node.ctrl2) *
3197 cexpf(cargf(
p->warp.point - pt) * I);
3200 p->node.ctrl2 = 2 *
p->warp.point - pt;
3213 switch (
d->header.type)
3219 switch (
d->header.node_type)
3222 n->node.ctrl1 =
d->warp.point +
3223 cabsf(
d->warp.point -
n->node.ctrl1) *
3224 cexpf(cargf(
d->warp.point - pt) * I);
3227 n->node.ctrl1 = 2 *
d->warp.point - pt;
3240 d->warp.radius = pt;
3245 d->warp.strength = pt;
3251 d->warp.control1 =
MIN(1.0, cabsf(pt - *start_pt) / cabsf(
d->warp.radius - *start_pt));
3255 d->warp.control2 =
MIN(1.0, cabsf(pt - *start_pt) / cabsf(
d->warp.radius - *start_pt));
3284 if (default_value != 0.0f && new_value !=
value) new_value = 0.25f * default_value + 0.75f *
value;
3293 GtkAllocation allocation;
3294 gtk_widget_get_allocation(widget, &allocation);
3295 const int last_win_min =
MIN(allocation.width, allocation.height);
3301 const float im_scale = 0.09f * iwd_min * last_win_min * scale / proc_wdht_min;
3320 const float complex strength_v = warp->
strength - warp->
point;
3324 float radius = 0.0f,
r = 0.0f, phi = 0.0f;
3330 else if(!incr && cabsf(warp->
radius - warp->
point) > 10.0f)
3346 float phi = cargf(strength_v);
3347 const float r = cabsf(strength_v);
3362 const float phi = cargf(strength_v);
3363 float r = cabsf(strength_v);
3390 float complex pt = 0.0f;
3397 g->last_mouse_pos = pt;
3398 g->last_mouse_mods =
state;
3400 g->last_button1_pressed_pos = pt;
3406 if(which == 2)
goto done;
3410 if(which == 1 && gtk_toggle_button_get_active(
g->btn_point_tool))
3418 g->status &= ~DT_LIQUIFY_STATUS_PREVIEW;
3428 if(which == 1 && (gtk_toggle_button_get_active(
g->btn_line_tool)
3429 || gtk_toggle_button_get_active(
g->btn_curve_tool)))
3439 g->temp =
g->last_hit.elem;
3448 if(gtk_toggle_button_get_active(
g->btn_curve_tool))
3453 g->status &= ~DT_LIQUIFY_STATUS_PREVIEW;
3460 if(gtk_toggle_button_get_active(
g->btn_node_tool))
3502 float complex pt = 0.0f;
3505 float radius = 0.0f,
r = 1.0f, phi = 0.0f;
3509 g->temp->warp.radius = pt + radius;
3510 g->temp->warp.strength = pt +
r * cexpf(phi * I);
3514 g->just_started =
TRUE;
3529 float complex pt = 0.0f;
3536 g->last_mouse_pos = pt;
3543 if(gtk_toggle_button_get_active(
g->btn_point_tool))
3549 else if(gtk_toggle_button_get_active(
g->btn_line_tool))
3551 const int prev_index =
g->node_index;
3552 const float complex
strength = (
g->temp->warp.strength -
g->temp->warp.point);
3553 const float radius = cabsf(
g->temp->warp.radius -
g->temp->warp.point);
3556 g->temp->warp.radius = pt + radius;
3559 g->temp->header.prev = prev_index;
3565 else if(gtk_toggle_button_get_active(
g->btn_curve_tool))
3567 const int prev_index =
g->node_index;
3568 const float complex
strength = (
g->temp->warp.strength -
g->temp->warp.point);
3569 const float radius = cabsf(
g->temp->warp.radius -
g->temp->warp.point);
3572 g->temp->warp.radius = pt + radius;
3575 g->temp->header.prev = prev_index;
3581 g->status &= ~DT_LIQUIFY_STATUS_NEW;
3603 g->status &= ~DT_LIQUIFY_STATUS_PREVIEW;
3638 if(gtk_toggle_button_get_active(
g->btn_node_tool))
3647 g->last_hit.elem->header.selected = oldsel ? 0 :
g->last_hit.layer;
3659 if(which == 1 &&
dt_modifier_is(
g->last_mouse_mods, GDK_SHIFT_MASK) && !dragged)
3664 const int oldsel = !!
g->last_hit.elem->header.selected;
3665 g->last_hit.elem->header.selected = oldsel ? 0 :
g->last_hit.layer;
3695 float complex midpoint = warp3->
point;
3697 midpoint = warp1->
point;
3749 const float complex p0 = prev->
warp.
point;
3754 c->node.ctrl1 = (2 * p0 + p1) / 3.0;
3755 c->node.ctrl2 = ( p0 + 2 * p1) / 3.0;
3766 g->last_button1_pressed_pos = -1;
3786 const gboolean creating = gtk_toggle_button_get_active(
g->btn_point_tool)
3787 || gtk_toggle_button_get_active(
g->btn_line_tool)
3788 || gtk_toggle_button_get_active(
g->btn_curve_tool)
3792 if(
key == GDK_KEY_BackSpace)
3797 const gboolean create_tool_active = gtk_toggle_button_get_active(
g->btn_point_tool)
3798 || gtk_toggle_button_get_active(
g->btn_line_tool)
3799 || gtk_toggle_button_get_active(
g->btn_curve_tool);
3800 gboolean restart_shape =
FALSE;
3809 last = &
g->params.nodes[
k];
3814 restart_shape = create_tool_active;
3828 const int prev_index = to_delete->
header.
prev;
3830 g->temp = prev_index >= 0 ?
node_get(&
g->params, prev_index) : NULL;
3838 g->status &= ~DT_LIQUIFY_STATUS_NEW;
3842 restart_shape = create_tool_active;
3857 if(
key == GDK_KEY_Delete)
3883 const int deleted_idx =
selected->header.idx;
3884 const int next_idx =
selected->header.next;
3885 const int prev_idx =
selected->header.prev;
3891 int target_idx = (next_idx != -1) ? next_idx : prev_idx;
3892 if(target_idx > deleted_idx)
3910 if(
key == GDK_KEY_Escape ||
key == GDK_KEY_Return)
3932 gtk_toggle_button_set_active(
g->btn_point_tool,
FALSE);
3933 gtk_toggle_button_set_active(
g->btn_line_tool,
FALSE);
3934 gtk_toggle_button_set_active(
g->btn_curve_tool,
FALSE);
3935 gtk_toggle_button_set_active(
g->btn_node_tool,
TRUE);
3968 g->status &= ~DT_LIQUIFY_STATUS_PREVIEW;
3972 if(
IS_NULL_PTR(btn) || !gtk_toggle_button_get_active(btn))
3974 gtk_toggle_button_set_active(
g->btn_point_tool, btn ==
g->btn_point_tool);
3975 gtk_toggle_button_set_active(
g->btn_line_tool, btn ==
g->btn_line_tool);
3976 gtk_toggle_button_set_active(
g->btn_curve_tool, btn ==
g->btn_curve_tool);
3977 gtk_toggle_button_set_active(
g->btn_node_tool, btn ==
g->btn_node_tool);
3979 if(btn ==
g->btn_point_tool)
3982 "shift+scroll to change strength - ctrl+scroll to change direction"));
3983 else if(btn ==
g->btn_line_tool)
3986 "shift+scroll to change strength - ctrl+scroll to change direction"));
3987 else if(btn ==
g->btn_curve_tool)
3990 "shift+scroll to change strength - ctrl+scroll to change direction"));
3991 else if(btn ==
g->btn_node_tool)
3996 if(btn ==
g->btn_point_tool || btn ==
g->btn_line_tool || btn ==
g->btn_curve_tool)
4005 gtk_toggle_button_set_active(btn,
FALSE);
4025 cairo_surface_t *cs = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, 1, 1);
4026 cairo_surface_destroy(cs);
4032 g->last_button1_pressed_pos = -1;
4039 gtk_widget_set_tooltip_text(hbox, _(
"use a tool to add warps.\nright-click to remove a warp."));
4044 gtk_box_pack_start(GTK_BOX(hbox), lbox,
FALSE,
TRUE, 0);
4048 gtk_box_pack_start(GTK_BOX(labelbox), label,
FALSE,
TRUE, 0);
4050 gtk_box_pack_start(GTK_BOX(labelbox), GTK_WIDGET(
g->label_warp),
FALSE,
TRUE, 0);
4051 gtk_box_pack_start(GTK_BOX(lbox), labelbox,
FALSE,
TRUE, 0);
4055 gtk_box_pack_start(GTK_BOX(labelbox2), label2,
FALSE,
TRUE, 0);
4057 gtk_box_pack_start(GTK_BOX(labelbox2), GTK_WIDGET(
g->label_node),
FALSE,
TRUE, 0);
4058 gtk_box_pack_start(GTK_BOX(lbox), labelbox2,
FALSE,
TRUE, 0);
4067 g->btn_curve_tool = GTK_TOGGLE_BUTTON(
dt_iop_togglebutton_new(self, N_(
"shapes"), N_(
"draw curves"), N_(
"draw multiple curves"),
4071 g->btn_line_tool = GTK_TOGGLE_BUTTON(
dt_iop_togglebutton_new(self, N_(
"shapes"), N_(
"draw lines"), N_(
"draw multiple lines"),
4075 g->btn_point_tool = GTK_TOGGLE_BUTTON(
dt_iop_togglebutton_new(self, N_(
"shapes"), N_(
"draw points"), N_(
"draw multiple points"),
4080 "ctrl+alt+click: toggle line/curve");
4082 "ctrl+click: autosmooth, cusp, smooth, symmetrical"
4083 " - right click to remove");
4090 "ctrl+click: linear, grow, and shrink");
Handle default and user-set shortcuts (accelerators)
GtkWidget * dt_gui_main_window(void)
static double dist(double x1, double y1, double x2, double y2)
static void error(char *msg)
typedef void((*dt_cache_allocate_t)(void *userdata, dt_cache_entry_t *entry))
dt_collection_t * dt_collection_get_global(void)
void dt_collection_hint_message(const dt_collection_t *collection)
static float lookup(read_only image2d_t lut, const float x)
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
static const float const float C
static float strength(float value, float strength)
void dt_conf_set_float(const char *name, float val)
float dt_conf_get_float(const char *name)
Float for name, clamped to its declared bounds.
void dt_control_queue_redraw_center()
Request a redraw of the centre view.
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.
struct dt_control_t * dt_control_get_global(void)
int32_t dt_dev_geometry_processed_height(const dt_develop_t *dev)
int32_t dt_dev_geometry_raw_width(const dt_develop_t *dev)
int32_t dt_dev_geometry_raw_height(const dt_develop_t *dev)
dt_dev_image_geometry_t dt_dev_geometry_snapshot(const dt_develop_t *dev)
int32_t dt_dev_geometry_processed_width(const dt_develop_t *dev)
#define dt_dev_add_history_item(dev, module, enable, redraw)
int32_t dt_dev_viewport_box_height(const dt_develop_t *dev)
float dt_dev_viewport_scaling(const dt_develop_t *dev)
int32_t dt_dev_viewport_box_width(const dt_develop_t *dev)
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_backtransform_gui(dt_develop_t *dev, const double iop_order, const int transf_direction, float *points, size_t points_count)
The inverse of dt_dev_distort_transform_gui(), same rules.
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.
@ DT_DEV_TRANSFORM_DIR_BACK_EXCL
@ DT_DEV_TRANSFORM_DIR_ALL
@ DT_DEV_TRANSFORM_DIR_FORW_EXCL
GtkWidget * geometry
its size, under the preview
GHashTable * selected
set of checked row labels, mirrored to conf on every change
static guint dt_keys_mainpad_alternatives(const guint key_val)
Remap keypad keys to usual mainpad ones.
int dt_geometry_chain_compose(dt_geometry_chain_t *chain, const double iop_order, const int direction, float *points, const size_t points_count)
Compose the chain over points, for a record evaluator that needs the transform stack around its own m...
gboolean dt_geometry_chain_authoritative(const dt_geometry_chain_t *chain)
Can this chain answer questions yet?
Where things are on the image, answered without a pipeline.
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)
void dt_iop_gui_leave_critical_section(dt_iop_module_t *const module)
Release what dt_iop_gui_enter_critical_section() took. Also a no-op headless.
void dt_iop_request_focus(dt_iop_module_t *module)
Move darkroom focus to module, or clear it with NULL.
@ IOP_FLAGS_SUPPORTS_BLENDING
void dt_iop_gui_enter_critical_section(dt_iop_module_t *const module)
Take the module's GUI lock, serialising access to its dt_iop_gui_data_t.
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)
static dt_iop_gui_data_t * dt_iop_gui_data(const struct dt_iop_module_t *m)
The module's GUI data blob, NULL-safe for headless callers: IOP process() implementations read it for...
#define IOP_GUI_ALLOC(module)
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
GtkWidget * dt_ui_label_new(const gchar *str)
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)
static int _liquify_geometry_backtransform(const void *data, const dt_geometry_record_t *const record, dt_geometry_chain_t *chain, float *points, size_t points_count)
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)
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 distort_paths_raw_to_piece_chain(const struct dt_iop_module_t *module, dt_geometry_chain_t *chain, const float roi_in_scale, dt_iop_liquify_params_t *p)
distort_paths_raw_to_piece() for the geometry service: same fold, no pipe.
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)
static int _liquify_warp_points(const dt_iop_liquify_params_t *const params_in, dt_iop_module_t *self, const dt_dev_pixelpipe_t *pipe, dt_geometry_chain_t *chain, float *const restrict points, const size_t points_count, const gboolean inverted)
Warp points, given this module's parameters brought into its own input space.
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)
static const dt_geometry_vtable_t _liquify_geometry_vtable
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 _liquify_geometry_transform(const void *data, const dt_geometry_record_t *const record, dt_geometry_chain_t *chain, float *points, size_t points_count)
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)
gboolean geometry_record(struct dt_iop_module_t *self, const void *params, dt_geometry_record_t *record)
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)
_lib_location_type_t type
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
float *const restrict const size_t const size_t ch
#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_free_align(ptr)
Release memory from dt_alloc_align() and set ptr to NULL.
static void * dt_calloc_align(size_t size)
dt_alloc_align() followed by a zero fill.
static void dt_free_gpointer(gpointer ptr)
g_free() one pointer, with the signature GDestroyNotify wants.
#define dt_free(ptr)
g_free() ptr and set it to NULL, skipping both if it is already NULL.
#define DT_MODULE_INTROSPECTION(MODVER, PARAMSTYPE)
DT_MODULE() for a module whose params struct is introspected.
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)
#define __OMP_PARALLEL_FOR__(...)
#define dt_pixelpipe_cache_alloc_align_float_cache(pixels, id)
#define dt_pixelpipe_cache_alloc_align_cache(size, id)
#define dt_pixelpipe_cache_free_align(mem)
static const dt_aligned_pixel_simd_t value
const float uint32_t state[4]
dt_geometry_chain_t * chain
gboolean from_distort_transform
const dt_dev_pixelpipe_t * pipe
Objective facts about the image a dev is working on.
struct dt_iop_module_t *void * data
struct dt_geometry_chain_t * geometry_chain
One module instance's contribution, as data.
const dt_geometry_vtable_t * vtable
void(* free_data)(void *data)
A module's geometry, evaluated. Pure functions of the record's own data.
void(* map_size)(const void *data, const dt_iop_roi_t *const in, dt_iop_roi_t *out)
Full-resolution input rect -> output rect. Mirrors modify_roi_out() at scale 1.
enum dt_interpolation_type id
dt_iop_buffer_type_t datatype
What the geometry service's record carries: the parameters, and the module whose iop_order bounds the...
dt_iop_liquify_params_t params
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_iop_module_gui_t * gui
struct dt_develop_t * dev
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
#define __DT_CLONE_TARGETS__