73#define UNBOUND_SHADOWS_L UNBOUND_L
74#define UNBOUND_SHADOWS_A UNBOUND_A
75#define UNBOUND_SHADOWS_B UNBOUND_B
76#define UNBOUND_HIGHLIGHTS_L (UNBOUND_L << 3)
77#define UNBOUND_HIGHLIGHTS_A (UNBOUND_A << 3)
78#define UNBOUND_HIGHLIGHTS_B (UNBOUND_B << 3)
79#define UNBOUND_GAUSSIAN 64
80#define UNBOUND_BILATERAL 128
81#define UNBOUND_DEFAULT \
82 (UNBOUND_SHADOWS_L | UNBOUND_SHADOWS_A | UNBOUND_SHADOWS_B | UNBOUND_HIGHLIGHTS_L | UNBOUND_HIGHLIGHTS_A \
83 | UNBOUND_HIGHLIGHTS_B | UNBOUND_GAUSSIAN)
193 return _(
"shadows and highlights");
214 "of an image by enhancing local contrast."),
215 _(
"corrective and creative"),
216 _(
"linear or non-linear, Lab, display-referred"),
217 _(
"non-linear, Lab"),
218 _(
"non-linear, Lab, display-referred"));
222 void *new_params,
const int new_version)
224 if(old_version == 1 && new_version == 5)
229 new->radius = fabs(old->
radius);
230 new->shadows = 0.5f * old->
shadows;
236 new->shadows_ccorrect = 100.0f;
237 new->highlights_ccorrect = 0.0f;
238 new->low_approximation = 0.01f;
242 else if(old_version == 2 && new_version == 5)
247 new->radius = fabs(old->
radius);
256 new->low_approximation = 0.01f;
260 else if(old_version == 3 && new_version == 5)
265 new->radius = fabs(old->
radius);
273 new->flags = old->
flags;
274 new->low_approximation = 0.01f;
278 else if(old_version == 4 && new_version == 5)
283 new->radius = fabs(old->
radius);
291 new->flags = old->
flags;
302 o[0] =
i[0] / 100.0f;
303 o[1] =
i[1] / 128.0f;
304 o[2] =
i[2] / 128.0f;
310 o[0] =
i[0] * 100.0f;
311 o[1] =
i[1] * 128.0f;
312 o[2] =
i[2] * 128.0f;
317 return (
x < 0 ? -1.0f : 1.0f);
326 const float *
const restrict in = (
float *)ivoid;
327 float *
const restrict
out = (
float *)
ovoid;
332 const int order = data->order;
333 const float radius = fmaxf(0.1f, data->radius);
335 const float shadows = 2.0f * fmin(fmax(-1.0, (data->shadows / 100.0f)), 1.0f);
336 const float highlights = 2.0f * fmin(fmax(-1.0, (data->highlights / 100.0f)), 1.0f);
337 const float whitepoint = fmax(1.0f - data->whitepoint / 100.0f, 0.01f);
339 = fmin(fmax(0, (data->compress / 100.0f)), 0.99f);
340 const float shadows_ccorrect = (fmin(fmax(0.0f, (data->shadows_ccorrect / 100.0f)), 1.0f) - 0.5f)
341 *
sign(shadows) + 0.5f;
342 const float highlights_ccorrect = (fmin(fmax(0.0f, (data->highlights_ccorrect / 100.0f)), 1.0f) - 0.5f)
343 *
sign(-highlights) + 0.5f;
344 const unsigned int flags = data->flags;
347 const float low_approximation = data->low_approximation;
356 for(
int k = 0;
k < 4;
k++) Labmax[
k] = INFINITY;
357 for(
int k = 0;
k < 4;
k++) Labmin[
k] = -INFINITY;
369 const float detail = -1.0f;
381 const float lmin = 0.0f;
382 const float lmax =
max[0] + fabsf(
min[0]);
383 const float halfmax = lmax / 2.0;
384 const float doublemax = lmax * 2.0;
391 out[j + 0] = 100.0f -
out[j + 0];
396 ta[0] = ta[0] > 0.0f ? ta[0] / whitepoint : ta[0];
397 tb[0] = tb[0] > 0.0f ? tb[0] / whitepoint : tb[0];
400 float highlights2 = highlights * highlights;
401 const float highlights_xform = CLAMP(1.0f - tb[0] / (1.0f - compress), 0.0f, 1.0f);
403 while(highlights2 > 0.0f)
406 float lb = (tb[0] - halfmax) *
sign(-highlights) *
sign(lmax - la) + halfmax;
407 lb = unbound_mask ? lb : CLAMP(lb, lmin, lmax);
408 const float lref = copysignf(fabsf(la) > low_approximation ? 1.0f / fabsf(la) : 1.0f / low_approximation, la);
409 const float href = copysignf(
410 fabsf(1.0f - la) > low_approximation ? 1.0f / fabsf(1.0f - la) : 1.0f / low_approximation, 1.0f - la);
412 const float chunk = highlights2 > 1.0f ? 1.0f : highlights2;
413 const float optrans = chunk * highlights_xform;
416 ta[0] = la * (1.0 - optrans)
417 + (la > halfmax ? lmax - (lmax - doublemax * (la - halfmax)) * (lmax - lb) : doublemax * la
422 const float chroma_factor = (ta[0] * lref * (1.0f - highlights_ccorrect)
423 + (1.0f - ta[0]) * href * highlights_ccorrect);
424 ta[1] = ta[1] * (1.0f - optrans) + (ta[1] + tb[1]) * chroma_factor * optrans;
427 ta[2] = ta[2] * (1.0f - optrans) + (ta[2] + tb[2]) * chroma_factor * optrans;
432 float shadows2 = shadows * shadows;
433 const float shadows_xform = CLAMP(tb[0] / (1.0f - compress) - compress / (1.0f - compress), 0.0f, 1.0f);
435 while(shadows2 > 0.0f)
438 float lb = (tb[0] - halfmax) *
sign(shadows) *
sign(lmax - la) + halfmax;
439 lb = unbound_mask ? lb : CLAMP(lb, lmin, lmax);
440 const float lref = copysignf(fabsf(la) > low_approximation ? 1.0f / fabsf(la) : 1.0f / low_approximation, la);
441 const float href = copysignf(
442 fabsf(1.0f - la) > low_approximation ? 1.0f / fabsf(1.0f - la) : 1.0f / low_approximation, 1.0f - la);
445 const float chunk = shadows2 > 1.0f ? 1.0f : shadows2;
446 const float optrans = chunk * shadows_xform;
449 ta[0] = la * (1.0 - optrans)
450 + (la > halfmax ? lmax - (lmax - doublemax * (la - halfmax)) * (lmax - lb) : doublemax * la
455 const float chroma_factor = (ta[0] * lref * shadows_ccorrect
456 + (1.0f - ta[0]) * href * (1.0f - shadows_ccorrect));
457 ta[1] = ta[1] * (1.0f - optrans) + (ta[1] + tb[1]) * chroma_factor * optrans;
460 ta[2] = ta[2] * (1.0f - optrans) + (ta[2] + tb[2]) * chroma_factor * optrans;
483 const float radius = fmax(0.1f,
d->radius);
488 const size_t basebuffer =
sizeof(float) * channels *
width *
height;
518 d->radius =
p->radius;
519 d->shadows =
p->shadows;
520 d->highlights =
p->highlights;
521 d->whitepoint =
p->whitepoint;
522 d->compress =
p->compress;
523 d->shadows_ccorrect =
p->shadows_ccorrect;
524 d->highlights_ccorrect =
p->highlights_ccorrect;
526 d->low_approximation =
p->low_approximation;
527 d->shadhi_algo =
p->shadhi_algo;
558 gtk_widget_set_tooltip_text(
g->shadows, _(
"correct shadows"));
559 gtk_widget_set_tooltip_text(
g->highlights, _(
"correct highlights"));
560 gtk_widget_set_tooltip_text(
g->whitepoint, _(
"shift white point"));
561 gtk_widget_set_tooltip_text(
g->radius, _(
"spatial extent"));
562 gtk_widget_set_tooltip_text(
g->shadhi_algo, _(
"filter to use for softening. bilateral avoids halos"));
563 gtk_widget_set_tooltip_text(
g->compress, _(
"compress the effect on shadows/highlights and\npreserve mid-tones"));
564 gtk_widget_set_tooltip_text(
g->shadows_ccorrect, _(
"adjust saturation of shadows"));
565 gtk_widget_set_tooltip_text(
g->highlights_ccorrect, _(
"adjust saturation of highlights"));
void dt_bauhaus_slider_set_format(GtkWidget *widget, const char *format)
void dt_bilateral_free(dt_bilateral_t *b)
__DT_CLONE_TARGETS__ void dt_bilateral_splat(const dt_bilateral_t *b, const float *const in)
size_t dt_bilateral_memory_use(const int width, const int height, const float sigma_s, const float sigma_r)
dt_bilateral_t * dt_bilateral_init(const int width, const int height, const float sigma_s, const float sigma_r)
size_t dt_bilateral_singlebuffer_size(const int width, const int height, const float sigma_s, const float sigma_r)
__DT_CLONE_TARGETS__ void dt_bilateral_slice(const dt_bilateral_t *const b, const float *const in, float *out, const float detail)
void dt_bilateral_blur(const dt_bilateral_t *b)
typedef void((*dt_cache_allocate_t)(void *userdata, dt_cache_entry_t *entry))
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
@ DT_DEV_PIXELPIPE_DISPLAY_MASK
void dt_gaussian_free(dt_gaussian_t *g)
size_t dt_gaussian_singlebuffer_size(const int width, const int height, const int channels)
void dt_gaussian_blur_4c(dt_gaussian_t *g, const float *const in, float *const out)
dt_gaussian_t * dt_gaussian_init(const int width, const int height, const int channels, const float *max, const float *min, const float sigma, const int order)
size_t dt_gaussian_memory_use(const int width, const int height, const int channels)
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)
@ IOP_FLAGS_INCLUDE_IN_STYLES
@ IOP_FLAGS_SUPPORTS_BLENDING
GtkWidget * dt_bauhaus_slider_from_params(dt_iop_module_t *self, const char *param)
GtkWidget * dt_bauhaus_combobox_from_params(dt_iop_module_t *self, const char *param)
#define IOP_GUI_ALLOC(module)
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.
#define DT_MODULE_INTROSPECTION(MODVER, PARAMSTYPE)
DT_MODULE() for a module whose params struct is introspected.
#define __OMP_PARALLEL_FOR__(...)
void commit_params(struct dt_iop_module_t *self, dt_iop_params_t *p1, dt_dev_pixelpipe_t *pipe, dt_dev_pixelpipe_iop_t *piece)
#define UNBOUND_SHADOWS_L
const char ** description(struct dt_iop_module_t *self)
__DT_CLONE_TARGETS__ int process(struct dt_iop_module_t *self, const dt_dev_pixelpipe_t *pipe, const dt_dev_pixelpipe_iop_t *piece, const void *const ivoid, void *const ovoid)
#define UNBOUND_HIGHLIGHTS_L
#define UNBOUND_HIGHLIGHTS_A
#define UNBOUND_SHADOWS_A
#define UNBOUND_SHADOWS_B
void init_pipe(struct dt_iop_module_t *self, dt_dev_pixelpipe_t *pipe, dt_dev_pixelpipe_iop_t *piece)
void gui_init(struct dt_iop_module_t *self)
static void _Lab_rescale(const float *i, float *o)
void tiling_callback(struct dt_iop_module_t *self, const struct dt_dev_pixelpipe_t *pipe, const struct dt_dev_pixelpipe_iop_t *piece, struct dt_develop_tiling_t *tiling)
int default_colorspace(dt_iop_module_t *self, dt_dev_pixelpipe_t *pipe, const dt_dev_pixelpipe_iop_t *piece)
#define UNBOUND_BILATERAL
#define UNBOUND_HIGHLIGHTS_B
void cleanup_pipe(struct dt_iop_module_t *self, dt_dev_pixelpipe_t *pipe, dt_dev_pixelpipe_iop_t *piece)
static void _Lab_scale(const float *i, float *o)
int legacy_params(dt_iop_module_t *self, const void *const old_params, const int old_version, void *new_params, const int new_version)
DT_ALIGNED_PIXEL float dt_aligned_pixel_t[4]
static const dt_aligned_pixel_simd_t sign
dt_iop_buffer_dsc_t dsc_in
struct dt_iop_module_t *void * data
Region of interest passed through the pixelpipe.
dt_gaussian_order_t order
float highlights_ccorrect
dt_iop_shadhi_algo_t shadhi_algo
GtkWidget * highlights_ccorrect
GtkWidget * shadows_ccorrect
dt_gaussian_order_t order
dt_gaussian_order_t order
float highlights_ccorrect
float highlights_ccorrect
dt_gaussian_order_t order
float highlights_ccorrect
dt_gaussian_order_t order
dt_gaussian_order_t order
dt_iop_shadhi_algo_t shadhi_algo
float highlights_ccorrect
#define __DT_CLONE_TARGETS__