Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
114 changes: 107 additions & 7 deletions data/kernels/retouch.cl
Original file line number Diff line number Diff line change
Expand Up @@ -59,16 +59,66 @@ retouch_copy_buffer_to_image(global float4 *in,
__write_only image2d_t out,
global dt_iop_roi_t *roi_out,
const int xoffs,
const int yoffs)
const int yoffs,
const float angle,
const float cx,
const float cy)
{
const int x = get_global_id(0);
const int y = get_global_id(1);

if(x >= roi_out->width || y >= roi_out->height) return;
if(x + xoffs >= roi_in->width || y + yoffs >= roi_in->height) return;

const int idx = mad24(y + yoffs, roi_in->width, x + xoffs);
write_imagef(out, (int2)(x, y), in[idx]);
// skip the rotation math for negligibly small angles (cheaper, and avoids
// resampling artifacts on a near-zero rotation from repeated UI actions)
if (fabs(angle) < 0.01f)
{
if(x + xoffs >= roi_in->width || y + yoffs >= roi_in->height || x + xoffs < 0 || y + yoffs < 0) return;
const int idx = mad24(y + yoffs, roi_in->width, x + xoffs);
write_imagef(out, (int2)(x, y), in[idx]);
}
else
{
// same rotation as the on-screen source outline, so the overlay marks the
// region actually copied (see rt_copy_in_to_out in retouch.c)
const float c = dtcl_cos(angle);
const float s = dtcl_sin(angle);
// (cx, cy) is the mask centroid (rotation pivot), in roi_out-local coords
const float cx_source = cx + xoffs;
const float cy_source = cy + yoffs;

const float sx = x + xoffs;
const float sy = y + yoffs;
const float rx = sx - cx_source;
const float ry = sy - cy_source;
const float ix = cx_source + rx * c - ry * s;
const float iy = cy_source + rx * s + ry * c;

// Edge-clamp (replicate) out-of-bounds samples instead of zero-filling:
// rt_compute_roi_in grows roi_in to the rotation-aware source area, but not
// past the image border, so a rotated source near the edge can still sample
// just outside roi_in. Replicating avoids a hard black seam.
const float ixc = clamp(ix, 0.0f, (float)(roi_in->width - 1));
const float iyc = clamp(iy, 0.0f, (float)(roi_in->height - 1));

int x0 = (int)ixc;
int y0 = (int)iyc;
x0 = clamp(x0, 0, roi_in->width - 2);
y0 = clamp(y0, 0, roi_in->height - 2);
const float dx0 = ixc - x0;
const float dy0 = iyc - y0;

float4 in00 = in[mad24(y0, roi_in->width, x0)];
float4 in10 = in[mad24(y0, roi_in->width, x0 + 1)];
float4 in01 = in[mad24(y0 + 1, roi_in->width, x0)];
float4 in11 = in[mad24(y0 + 1, roi_in->width, x0 + 1)];

float4 val = in00 * (1.0f - dx0) * (1.0f - dy0) +
in10 * dx0 * (1.0f - dy0) +
in01 * (1.0f - dx0) * dy0 +
in11 * dx0 * dy0;
write_imagef(out, (int2)(x, y), val);
}
}

kernel void
Expand All @@ -77,15 +127,65 @@ retouch_copy_buffer_to_buffer(global float4 *in,
global float4 *out,
global dt_iop_roi_t *roi_out,
const int xoffs,
const int yoffs)
const int yoffs,
const float angle,
const float cx,
const float cy)
{
const int x = get_global_id(0);
const int y = get_global_id(1);

if(x >= roi_out->width || y >= roi_out->height) return;
if(x + xoffs >= roi_in->width || y + yoffs >= roi_in->height) return;

out[mad24(y, roi_out->width, x)] = in[mad24(y + yoffs, roi_in->width, x + xoffs)];
// skip the rotation math for negligibly small angles (cheaper, and avoids
// resampling artifacts on a near-zero rotation from repeated UI actions)
if (fabs(angle) < 0.01f)
{
if(x + xoffs >= roi_in->width || y + yoffs >= roi_in->height || x + xoffs < 0 || y + yoffs < 0) return;
out[mad24(y, roi_out->width, x)] = in[mad24(y + yoffs, roi_in->width, x + xoffs)];
}
else
{
// same rotation as the on-screen source outline, so the overlay marks the
// region actually copied (see rt_copy_in_to_out in retouch.c)
const float c = dtcl_cos(angle);
const float s = dtcl_sin(angle);
// (cx, cy) is the mask centroid (rotation pivot), in roi_out-local coords
const float cx_source = cx + xoffs;
const float cy_source = cy + yoffs;

const float sx = x + xoffs;
const float sy = y + yoffs;
const float rx = sx - cx_source;
const float ry = sy - cy_source;
const float ix = cx_source + rx * c - ry * s;
const float iy = cy_source + rx * s + ry * c;

// Edge-clamp (replicate) out-of-bounds samples instead of zero-filling:
// rt_compute_roi_in grows roi_in to the rotation-aware source area, but not
// past the image border, so a rotated source near the edge can still sample
// just outside roi_in. Replicating avoids a hard black seam.
const float ixc = clamp(ix, 0.0f, (float)(roi_in->width - 1));
const float iyc = clamp(iy, 0.0f, (float)(roi_in->height - 1));

int x0 = (int)ixc;
int y0 = (int)iyc;
x0 = clamp(x0, 0, roi_in->width - 2);
y0 = clamp(y0, 0, roi_in->height - 2);
const float dx0 = ixc - x0;
const float dy0 = iyc - y0;

float4 in00 = in[mad24(y0, roi_in->width, x0)];
float4 in10 = in[mad24(y0, roi_in->width, x0 + 1)];
float4 in01 = in[mad24(y0 + 1, roi_in->width, x0)];
float4 in11 = in[mad24(y0 + 1, roi_in->width, x0 + 1)];

float4 val = in00 * (1.0f - dx0) * (1.0f - dy0) +
in10 * dx0 * (1.0f - dy0) +
in01 * (1.0f - dx0) * dy0 +
in11 * dx0 * dy0;
out[mad24(y, roi_out->width, x)] = val;
}
}

kernel void
Expand Down
32 changes: 30 additions & 2 deletions src/develop/masks.h
Original file line number Diff line number Diff line change
Expand Up @@ -346,8 +346,8 @@ typedef struct dt_masks_form_t
dt_masks_type_t type;
const dt_masks_functions_t *functions;

// position of the source (used only for clone)
float source[2];
// position of the source (used only for clone). [0]=dx, [1]=dy, [2]=angle
float source[3];
// name of the form
char name[128];
// id used to store the form
Expand Down Expand Up @@ -401,6 +401,14 @@ typedef struct dt_masks_form_gui_t
gboolean form_selected;
gboolean border_selected;
gboolean source_selected;
gboolean source_rotating;
gboolean counter_rotate_source;
// joint rotation grabbed from the source shape: the mouse circles the source,
// so its angular sweep must be measured about the source centroid (not the
// destination centroid) to keep the rotation gain symmetric with grabbing the
// target. The applied angle is identical either way; only the pivot used to
// read the mouse motion differs.
gboolean rotate_about_source;
gboolean pivot_selected;
gboolean select_only_border;
dt_masks_edit_mode_t edit_mode;
Expand Down Expand Up @@ -1133,6 +1141,26 @@ void dt_masks_closest_point(const int count,
float *x,
float *y);

/* Rotate the control points of a path/brush outline in screen space and project
them back to normalized image coordinates. `gpt_points` is the gui display
buffer (interleaved x,y) whose first `nb*3` pairs are the control points,
stored per node as ctrl1, corner, ctrl2; `points_count` is its number of
(x,y) pairs. Each control point is rotated by (cos_a, sin_a) around the screen
pivot (cx, cy), back-transformed through the pipe in a single batch, and
written to `out` (normalized, same interleaving, nb*6 floats). Shared by the
path and brush rotate gestures. */
void dt_masks_rotate_ctrl_points(dt_develop_t *dev,
const float *const gpt_points,
const int points_count,
const int nb,
const float cx,
const float cy,
const float cos_a,
const float sin_a,
const float iwidth,
const float iheight,
float *const out);

/* draw a line from -> to with an arrow at the end.
if touch_dest is true then the arrow will be at the
(to_x, to_y) location, otherwise a small space will
Expand Down
Loading
Loading