Ctrl K
ComunidadGuías…

data/kernels/retouch.cl

Rama master · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
/*
    This file is part of darktable,
    Copyright (C) 2017-2026 darktable developers.

    darktable is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    darktable is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with darktable.  If not, see <http://www.gnu.org/licenses/>.
*/

#include "colorspace.h"
#include "common.h"

typedef struct dt_iop_roi_t
{
  int x, y, width, height;
  float scale;
} dt_iop_roi_t;

kernel void
retouch_clear_alpha(global float4 *in, int width, int height)
{
  const int x = get_global_id(0);
  const int y = get_global_id(1);

  if(x >= width || y >= height) return;

  const int idx = mad24(y, width, x);
  in[idx].w = 0.f;
}

kernel void
retouch_copy_alpha(__read_only image2d_t in,
                   global float4 *out,
                   int width,
                   int height)
{
  const int x = get_global_id(0);
  const int y = get_global_id(1);

  if(x >= width || y >= height) return;

  float alpha = readalpha(in, x, y);
  const int idx = mad24(y, width, x);
  out[idx].w = alpha;
}

kernel void
retouch_copy_buffer_to_image(global float4 *in,
                             global dt_iop_roi_t *roi_in,
                             __write_only image2d_t out,
                             global dt_iop_roi_t *roi_out,
                             const int xoffs,
                             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;

  // 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
retouch_copy_buffer_to_buffer(global float4 *in,
                              global dt_iop_roi_t *roi_in,
                              global float4 *out,
                              global dt_iop_roi_t *roi_out,
                              const int xoffs,
                              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;

  // 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
retouch_copy_mask_to_alpha(global float4 *in,
                           global dt_iop_roi_t *roi_in,
                           global float *mask_scaled,
                           global dt_iop_roi_t *roi_mask_scaled,
                           const float opacity)
{
  const int x = get_global_id(0);
  const int y = get_global_id(1);

  if(x >= roi_mask_scaled->width || y >= roi_mask_scaled->height) return;

  const int mask_index = mad24(y, roi_mask_scaled->width, x);
  const int dest_index = mad24(y + roi_mask_scaled->y - roi_in->y, roi_in->width, x + roi_mask_scaled->x - roi_in->x);

  const float f = clipf(mask_scaled[mask_index] * opacity);

  if(f > in[dest_index].w) in[dest_index].w = f;
}

kernel void
retouch_fill(global float4 *in,
             global dt_iop_roi_t *roi_in,
             global float *mask_scaled,
             global dt_iop_roi_t *roi_mask_scaled,
             const float opacity,
             const float color_x,
             const float color_y,
             const float color_z)
{
  const int x = get_global_id(0);
  const int y = get_global_id(1);

  if(x >= roi_mask_scaled->width || y >= roi_mask_scaled->height) return;

  const int mask_index = mad24(y, roi_mask_scaled->width, x);
  const int dest_index = mad24(y + roi_mask_scaled->y - roi_in->y, roi_in->width, x + roi_mask_scaled->x - roi_in->x);

  float4 fill_color = { color_x, color_y, color_z, 0.f };
  const float f = clipf(mask_scaled[mask_index] * opacity);
  const float w = in[dest_index].w;

  in[dest_index] = in[dest_index] * (1.0f - f) + fill_color * f;

  in[dest_index].w = w;
}

kernel void
retouch_copy_buffer_to_buffer_masked(global float4 *buffer_src,
                                     global float4 *buffer_dest,
                                     global dt_iop_roi_t *roi_buffer_dest,
                                     global float *mask_scaled,
                                     global dt_iop_roi_t *roi_mask_scaled,
                                     const float opacity)
{
  const int x = get_global_id(0);
  const int y = get_global_id(1);

  if(x >= roi_mask_scaled->width || y >= roi_mask_scaled->height) return;

  const int idx_dest = mad24(y + roi_mask_scaled->y - roi_buffer_dest->y, roi_buffer_dest->width,
                             x + roi_mask_scaled->x - roi_buffer_dest->x);
  const int idx_src = mad24(y, roi_mask_scaled->width, x);
  const int idx_mask = mad24(y, roi_mask_scaled->width, x);

  const float f = clipf(mask_scaled[idx_mask] * opacity);
  const float w = buffer_dest[idx_dest].w;

  buffer_dest[idx_dest] = buffer_dest[idx_dest] * (1.0f - f) + buffer_src[idx_src] * f;

  buffer_dest[idx_dest].w = w;
}

kernel void
retouch_copy_image_to_buffer_masked(__read_only image2d_t buffer_src,
                                    global float4 *buffer_dest,
                                    global dt_iop_roi_t *roi_buffer_dest,
                                    global float *mask_scaled,
                                    global dt_iop_roi_t *roi_mask_scaled,
                                    const float opacity)
{
  const int x = get_global_id(0);
  const int y = get_global_id(1);

  if(x >= roi_mask_scaled->width || y >= roi_mask_scaled->height) return;

  const int idx_dest = mad24(y + roi_mask_scaled->y - roi_buffer_dest->y, roi_buffer_dest->width,
                             x + roi_mask_scaled->x - roi_buffer_dest->x);
  const int idx_mask = mad24(y, roi_mask_scaled->width, x);

  const float f = clipf(mask_scaled[idx_mask] * opacity);
  const float w = buffer_dest[idx_dest].w;

  float4 pix = readpixel(buffer_src, x, y);
  buffer_dest[idx_dest] = buffer_dest[idx_dest] * (1.0f - f) + pix * f;

  buffer_dest[idx_dest].w = w;
}

kernel void
retouch_image_rgb2lab(global float4 *in,
                      int width,
                      int height)
{
  const int x = get_global_id(0);
  const int y = get_global_id(1);

  if(x >= width || y >= height) return;

  const int idx = mad24(y, width, x);

  in[idx] = XYZ_to_Lab(sRGB_to_XYZ(in[idx]));
}

kernel void
retouch_image_lab2rgb(global float4 *in,
                      int width,
                      int height)
{
  const int x = get_global_id(0);
  const int y = get_global_id(1);

  if(x >= width || y >= height) return;

  const int idx = mad24(y, width, x);

  in[idx] = XYZ_to_sRGB(Lab_to_XYZ(in[idx]));
}

// the heal solver of src/common/heal.c. The image difference is split into
// 'red' and 'black' checkerboard pixels, each in its own buffer of subwidth
// pixels per row plus a zero padding row at the top and bottom. A pixel of
// split row r (1..height) and column j sits at original row r-1 and column
// 2j + (cs ^ ((r-1) & 1)), with cs 1 for red and 0 for black.

static inline int _heal_col(const int j, const int r, const int cs)
{
  return 2 * j + (cs ^ ((r - 1) & 1));
}

// _heal_sub(): top minus bottom into one color's split buffer
kernel void
retouch_heal_sub(global const float4 *top,
                 global const float4 *bottom,
                 global float4 *split,
                 const int width,
                 const int height,
                 const int subwidth,
                 const int cs)
{
  const int j = get_global_id(0);
  const int r = get_global_id(1);
  if(j >= subwidth || r >= height + 2) return;

  float4 val = (float4)0.0f;
  if(r > 0 && r <= height)
  {
    const int col = _heal_col(j, r, cs);
    if(col < width)
    {
      const int idx = mad24(r - 1, width, col);
      val = top[idx] - bottom[idx];
    }
  }
  split[mad24(r, subwidth, j)] = val;
}

// one half of a Gauss-Seidel iteration with over-relaxation, as
// _heal_laplace_iteration(): updates the masked pixels of one color and
// writes the work group's sum of squared changes. Does nothing once
// retouch_heal_converged() has set the flag.
kernel void
retouch_heal_iterate(global float4 *active,
                     global const float4 *neighbor,
                     global const float *mask,
                     const int width,
                     const int height,
                     const int subwidth,
                     const int cs,
                     const float w,
                     global const int *converged,
                     global float *partial,
                     local float *buffer)
{
  if(converged[0]) return;

  const int j = get_global_id(0);
  const int r = get_global_id(1) + 1;
  const int l = mad24((int)get_local_id(1), (int)get_local_size(0), (int)get_local_id(0));

  float err = 0.0f;
  if(j < subwidth && r <= height)
  {
    const int col = _heal_col(j, r, cs);
    if(col < width && mask[mad24(r - 1, width, col)] != 0.0f)
    {
      const int idx = mad24(r, subwidth, j);
      const int lro = cs ^ ((r - 1) & 1);
      float a = 4.0f;
      if(r == 1) a -= 1.0f;
      if(r == height) a -= 1.0f;
      // the first and last pixel of a row have no left or right neighbor
      float4 left = (float4)0.0f;
      float4 right = (float4)0.0f;
      if(j > 0 || lro)
        left = neighbor[idx - 1 + lro];
      else
        a -= 1.0f;
      if(j + 1 < subwidth || lro == 0)
        right = neighbor[idx + lro];
      else
        a -= 1.0f;

      const float4 act = active[idx];
      const float4 diff = w * ((a * act) - (neighbor[idx - subwidth] + neighbor[idx + subwidth] + left + right));
      active[idx] = act - diff;
      err = diff.x * diff.x + diff.y * diff.y + diff.z * diff.z;
    }
  }

  buffer[l] = err;
  barrier(CLK_LOCAL_MEM_FENCE);
  const int lsz = mul24((int)get_local_size(0), (int)get_local_size(1));
  for(int offset = lsz / 2; offset > 0; offset = offset / 2)
  {
    if(l < offset) buffer[l] += buffer[l + offset];
    barrier(CLK_LOCAL_MEM_FENCE);
  }
  if(l == 0)
    partial[mad24((int)get_group_id(1), (int)get_num_groups(0), (int)get_group_id(0))] = buffer[0];
}

// adds up the partial sums of both colors in one work group and sets the
// flag when the iteration has converged
kernel void
retouch_heal_converged(global const float *partial_black,
                       global const float *partial_red,
                       const int groups,
                       const float err_exit,
                       global int *converged,
                       local float *buffer)
{
  if(converged[0]) return;

  const int l = get_local_id(0);
  const int lsz = get_local_size(0);
  float sum = 0.0f;
  for(int k = l; k < groups; k += lsz)
    sum += partial_black[k] + partial_red[k];

  buffer[l] = sum;
  barrier(CLK_LOCAL_MEM_FENCE);
  for(int offset = lsz / 2; offset > 0; offset = offset / 2)
  {
    if(l < offset) buffer[l] += buffer[l + offset];
    barrier(CLK_LOCAL_MEM_FENCE);
  }
  if(l == 0 && buffer[0] < err_exit) converged[0] = 1;
}

// _heal_add(): the solution plus bottom, back in the original layout
kernel void
retouch_heal_add(global const float4 *red,
                 global const float4 *black,
                 global const float4 *bottom,
                 global float4 *dest,
                 const int width,
                 const int height,
                 const int subwidth)
{
  const int x = get_global_id(0);
  const int y = get_global_id(1);
  if(x >= width || y >= height) return;

  // the pixel's color: red sits at odd columns of even rows
  const int r = y + 1;
  const int j = x / 2;
  const int is_red = (x & 1) ^ (y & 1);
  const int idx = mad24(r, subwidth, j);
  const int pix = mad24(y, width, x);
  dest[pix] = (is_red ? red[idx] : black[idx]) + bottom[pix];
}