Ctrl K
ComunidadGuías…

data/kernels/contrastntexture.cl

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

Archivo original ↗Preguntar a KiBot
/*
    This file is part of darktable,
    Copyright (C) 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 "common.h"

// the luminance goes to both buffers: the blur of the first level works in
// place on one, the band pass needs the other unblurred
kernel void contrastntexture_luminance(read_only image2d_t in,
                                       global float *const luminance,
                                       global float *const highpass,
                                       const int width,
                                       const int height,
                                       const float noise_bias)
{
  const int x = get_global_id(0);
  const int y = get_global_id(1);
  if(x >= width || y >= height) return;

  const float4 pixel = Areadpixel(in, x, y);
  const float norm = fmax(dtcl_sqrt(pixel.x * pixel.x + pixel.y * pixel.y + pixel.z * pixel.z),
                          NORM_MIN);
  const int k = mad24(y, width, x);
  const float lum = (1.0f / sqrt(3.0f)) * norm + noise_bias;
  luminance[k] = lum;
  highpass[k] = lum;
}

// one pyramid level: the band pass between the luminance before and after
// this level's blur, see extract_details(). The blurred luminance, less the
// noise bias step, is the input of the next level, so it goes to both buffers
kernel void contrastntexture_details(global float *const highpass,
                                     global float *const lowpass,
                                     global float *const corrections,
                                     const int width,
                                     const int height,
                                     const float gain,
                                     const float noise_bias,
                                     const float next_bias_step,
                                     const int accumulate)
{
  const int x = get_global_id(0);
  const int y = get_global_id(1);
  if(x >= width || y >= height) return;

  const int k = mad24(y, width, x);
  const float pixel = highpass[k];
  const float smoothed = lowpass[k];

  const float log_pixel = dtcl_log2(fmax(pixel, NORM_MIN));
  const float log_smoothed = dtcl_log2(fmax(smoothed, NORM_MIN));
  const float noise_power = noise_bias * noise_bias;
  const float combined_power = smoothed * smoothed;
  const float weiner_gain = fmax(combined_power - noise_power, 0.0f) / fmax(combined_power, NORM_MIN);
  const float detail = gain * (weiner_gain * clamp(log_pixel - log_smoothed, -5.0f, 5.0f));
  corrections[k] = accumulate ? corrections[k] + detail : detail;

  const float next = smoothed - next_bias_step;
  lowpass[k] = next;
  highpass[k] = next;
}

// see apply_shadows_highlights()
static inline float _shadows_highlights(const float luminance_lowpass,
                                        const float slope_shadows,
                                        const float slope_highlights,
                                        const float midtones_width,
                                        const float4 polynomial,
                                        const float pivot_offset)
{
  const float normalized_ev = dtcl_log2(fmax(luminance_lowpass, NORM_MIN)) - pivot_offset;
  float correction_ev;
  if(normalized_ev <= -midtones_width)
    correction_ev = slope_shadows * normalized_ev;
  else if(normalized_ev >= midtones_width)
    correction_ev = slope_highlights * normalized_ev;
  else
  {
    const float shifted_ev = normalized_ev + midtones_width;
    correction_ev = polynomial.x + polynomial.y * shifted_ev + polynomial.z * shifted_ev * shifted_ev;
  }
  return correction_ev - normalized_ev;
}

kernel void contrastntexture_apply(read_only image2d_t in,
                                   write_only image2d_t out,
                                   global const float *const lowpass,
                                   global const float *const corrections,
                                   const int width,
                                   const int height,
                                   const float slope_shadows,
                                   const float slope_highlights,
                                   const float midtones_width,
                                   const float4 polynomial,
                                   const float pivot_offset)
{
  const int x = get_global_id(0);
  const int y = get_global_id(1);
  if(x >= width || y >= height) return;

  const int k = mad24(y, width, x);
  const float4 pixel = Areadpixel(in, x, y);
  const float lowpass_correction = _shadows_highlights(lowpass[k], slope_shadows, slope_highlights,
                                                       midtones_width, polynomial, pivot_offset);
  const float multiplier = dtcl_exp2(corrections[k] + lowpass_correction);
  float4 o = pixel * multiplier;
  o.w = pixel.w;
  write_imagef(out, (int2)(x, y), o);
}

// see display_local_mask()
kernel void contrastntexture_display_details(write_only image2d_t out,
                                             global const float *const corrections,
                                             const int width,
                                             const int height)
{
  const int x = get_global_id(0);
  const int y = get_global_id(1);
  if(x >= width || y >= height) return;

  const float local_ev = corrections[mad24(y, width, x)];
  const float intensity = local_ev / dtcl_sqrt(local_ev * local_ev + 1.0f) * 0.5f + 0.5f;
  write_imagef(out, (int2)(x, y), (float4)(intensity, intensity, intensity, 1.0f));
}

// see display_lowpass_mask(): blue below the pivot, yellow above
kernel void contrastntexture_display_lowpass(write_only image2d_t out,
                                             global const float *const lowpass,
                                             const int width,
                                             const int height,
                                             const float pivot_offset)
{
  const int x = get_global_id(0);
  const int y = get_global_id(1);
  if(x >= width || y >= height) return;

  const float l = lowpass[mad24(y, width, x)];
  const float intensity = l / (pivot_offset + l);
  float saturation = (intensity - 0.5f) * 2.0f;
  saturation *= saturation;
  const float4 tint = intensity < 0.5f ? (float4)(0.0f, 0.0f, 1.0f, 0.0f)
                                       : (float4)(1.0f, 1.0f, 0.0f, 0.0f);
  float4 o = intensity * ((1.0f - saturation) + saturation * tint);
  o.w = 1.0f;
  write_imagef(out, (int2)(x, y), o);
}