Skip to content
Open
7 changes: 7 additions & 0 deletions data/darktableconfig.xml.in
Original file line number Diff line number Diff line change
Expand Up @@ -3173,6 +3173,13 @@
<shortdescription>height of color balance rgb graph in per cent</shortdescription>
<longdescription>height of color balance rgb graph in per cent</longdescription>
</dtconfig>
<dtconfig>
<name>plugins/darkroom/toneequal/graphheight</name>
<type min="200" max="400">int</type>
<default>300</default>
<shortdescription>height of tone equalizer graph in per cent</shortdescription>
<longdescription>height of tone equalizer graph in per cent</longdescription>
</dtconfig>
<dtconfig>
<name>plugins/darkroom/histogram/mode</name>
<type>
Expand Down
136 changes: 90 additions & 46 deletions src/common/luminance_mask.h
Original file line number Diff line number Diff line change
Expand Up @@ -43,10 +43,12 @@ typedef enum dt_iop_luminance_mask_method_t
DT_TONEEQ_LIGHTNESS, // $DESCRIPTION: "HSL lightness"
DT_TONEEQ_VALUE, // $DESCRIPTION: "HSV value / RGB max"
DT_TONEEQ_NORM_1, // $DESCRIPTION: "RGB sum"
DT_TONEEQ_NORM_2, // $DESCRIPTION: "RGB euclidean norm")
DT_TONEEQ_NORM_2, // $DESCRIPTION: "RGB euclidean norm"
DT_TONEEQ_NORM_POWER, // $DESCRIPTION: "RGB power norm"
DT_TONEEQ_GEOMEAN, // $DESCRIPTION: "RGB geometric mean"
DT_TONEEQ_LAST
DT_TONEEQ_REC709W, // $DESCRIPTION: "Rec. 709 weights"
DT_TONEEQ_CUSTOM, // $DESCRIPTION: "Custom"
DT_TONEEQ_LAST,
} dt_iop_luminance_mask_method_t;

/**
Expand Down Expand Up @@ -78,11 +80,9 @@ static float linear_contrast(const float pixel, const float fulcrum, const float

DT_OMP_DECLARE_SIMD(aligned(image, luminance:64) uniform(image, luminance))
__DT_CLONE_TARGETS__
static void pixel_rgb_mean(const float *const restrict image,
static float pixel_rgb_mean(const float *const restrict image,
float *const restrict luminance,
const size_t k,
const float exposure_boost,
const float fulcrum, const float contrast_boost)
const size_t k)
{
// mean(RGB) is the intensity

Expand All @@ -92,67 +92,59 @@ static void pixel_rgb_mean(const float *const restrict image,
for(int c = 0; c < 3; ++c)
lum += image[k + c];

luminance[k / 4] = linear_contrast(exposure_boost * lum / 3.0f, fulcrum, contrast_boost);
return lum / 3.0f;
}


DT_OMP_DECLARE_SIMD(aligned(image, luminance:64) uniform(image, luminance))
__DT_CLONE_TARGETS__
static void pixel_rgb_value(const float *const restrict image,
static float pixel_rgb_value(const float *const restrict image,
float *const restrict luminance,
const size_t k,
const float exposure_boost,
const float fulcrum, const float contrast_boost)
const size_t k)
{
// max(RGB) is equivalent to HSV value

const float lum = exposure_boost * MAX(MAX(image[k], image[k + 1]), image[k + 2]);
luminance[k / 4] = linear_contrast(lum, fulcrum, contrast_boost);
const float lum = MAX(MAX(image[k], image[k + 1]), image[k + 2]);
return lum;
}


DT_OMP_DECLARE_SIMD(aligned(image, luminance:64) uniform(image, luminance))
__DT_CLONE_TARGETS__
static void pixel_rgb_lightness(const float *const restrict image,
static float pixel_rgb_lightness(const float *const restrict image,
float *const restrict luminance,
const size_t k,
const float exposure_boost,
const float fulcrum, const float contrast_boost)
const size_t k)
{
// (max(RGB) + min(RGB)) / 2 is equivalent to HSL lightness

const float max_rgb = MAX(MAX(image[k], image[k + 1]), image[k + 2]);
const float min_rgb = MIN(MIN(image[k], image[k + 1]), image[k + 2]);
luminance[k / 4] = linear_contrast(exposure_boost * (max_rgb + min_rgb) / 2.0f, fulcrum, contrast_boost);
return (max_rgb + min_rgb) / 2.0f;
}

DT_OMP_DECLARE_SIMD(aligned(image, luminance:64) uniform(image, luminance))
__DT_CLONE_TARGETS__
static void pixel_rgb_norm_1(const float *const restrict image,
static float pixel_rgb_norm_1(const float *const restrict image,
float *const restrict luminance,
const size_t k,
const float exposure_boost,
const float fulcrum, const float contrast_boost)
const size_t k)
{
// vector norm L1

float lum = 0.0f;

DT_OMP_SIMD(reduction(+:lum) aligned(image:64))
for(int c = 0; c < 3; ++c)
lum += fabsf(image[k + c]);
DT_OMP_SIMD(reduction(+:lum) aligned(image:64))
for(int c = 0; c < 3; ++c)
lum += fabsf(image[k + c]);

luminance[k / 4] = linear_contrast(exposure_boost * lum, fulcrum, contrast_boost);
return lum;
}


DT_OMP_DECLARE_SIMD(aligned(image, luminance:64) uniform(image, luminance))
__DT_CLONE_TARGETS__
static void pixel_rgb_norm_2(const float *const restrict image,
static float pixel_rgb_norm_2(const float *const restrict image,
float *const restrict luminance,
const size_t k,
const float exposure_boost,
const float fulcrum, const float contrast_boost)
const size_t k)
{
// vector norm L2 : euclidean norm

Expand All @@ -162,17 +154,15 @@ static void pixel_rgb_norm_2(const float *const restrict image,
for(int c = 0; c < 3; ++c)
result += image[k + c] * image[k + c];

luminance[k / 4] = linear_contrast(exposure_boost * sqrtf(result), fulcrum, contrast_boost);
return sqrtf(result);
}


DT_OMP_DECLARE_SIMD(aligned(image, luminance:64) uniform(image, luminance))
__DT_CLONE_TARGETS__
static void pixel_rgb_norm_power(const float *const restrict image,
static float pixel_rgb_norm_power(const float *const restrict image,
float *const restrict luminance,
const size_t k,
const float exposure_boost,
const float fulcrum, const float contrast_boost)
const size_t k)
{
// weird norm sort of perceptual. This is black magic really, but it looks good.

Expand All @@ -189,16 +179,14 @@ static void pixel_rgb_norm_power(const float *const restrict image,
denominator += RGB_square;
}

luminance[k / 4] = linear_contrast(exposure_boost * numerator / denominator, fulcrum, contrast_boost);
return numerator / denominator;
}

DT_OMP_DECLARE_SIMD(aligned(image, luminance:64) uniform(image, luminance))
__DT_CLONE_TARGETS__
static void pixel_rgb_geomean(const float *const restrict image,
static float pixel_rgb_geomean(const float *const restrict image,
float *const restrict luminance,
const size_t k,
const float exposure_boost,
const float fulcrum, const float contrast_boost)
const size_t k)
{
// geometric_mean(RGB). Kind of interesting for saturated colours (maps them to shadows)

Expand All @@ -210,7 +198,35 @@ static void pixel_rgb_geomean(const float *const restrict image,
lum *= fabsf(image[k + c]);
}

luminance[k / 4] = linear_contrast(exposure_boost * powf(lum, 1.0f / 3.0f), fulcrum, contrast_boost);
return powf(lum, 1.0f / 3.0f);
}

DT_OMP_DECLARE_SIMD(aligned(image, luminance:64) uniform(image, luminance))
__DT_CLONE_TARGETS__
static float pixel_rgb_r709w(const float *const restrict image,
float *const restrict luminance,
const size_t k)
{
// Rec. 709 weights. Green has more influence than red and blue.

const float lum = 0.2126 * image[k] + 0.7152 * image[k + 1] + 0.0722 * image[k + 2];
return lum;
}


DT_OMP_DECLARE_SIMD(aligned(image, luminance:64) uniform(image, luminance))
__DT_CLONE_TARGETS__
static float pixel_rgb_custom(const float *const restrict image,
float *const restrict luminance,
const size_t k,
const float r_weight,
const float g_weight,
const float b_weight)
{
// User can mix the greyscale image using sliders.

const float lum = MAX(MIN_FLOAT, r_weight * image[k] + g_weight * image[k + 1] + b_weight * image[k + 2]);
return lum;
}


Expand All @@ -221,11 +237,16 @@ static void pixel_rgb_geomean(const float *const restrict image,
#define LOOP(fn) \
{ \
_Pragma ("omp parallel for simd default(none) schedule(static) \
dt_omp_firstprivate(num_elem, in, out, exposure_boost, fulcrum, contrast_boost)\
dt_omp_firstprivate(num_elem, in, out, exposure_boost, fulcrum, contrast_boost, r_weight, g_weight, b_weight)\
reduction(min:min_lum) reduction(max:max_lum) \
aligned(in, out:64)" ) \
for(size_t k = 0; k < num_elem; k += 4) \
{ \
fn(in, out, k, exposure_boost, fulcrum, contrast_boost); \
const float lum = COMPUTE_LUM(fn); \
const float lum_boosted = linear_contrast(exposure_boost * lum, fulcrum, contrast_boost); \
min_lum = MIN(min_lum, lum_boosted); \
max_lum = MAX(max_lum, lum_boosted); \
out[k / 4] = lum_boosted; \
} \
break; \
}
Expand All @@ -234,7 +255,11 @@ static void pixel_rgb_geomean(const float *const restrict image,
{ \
for(size_t k = 0; k < num_elem; k += 4) \
{ \
fn(in, out, k, exposure_boost, fulcrum, contrast_boost); \
const float lum = COMPUTE_LUM(fn); \
const float lum_boosted = linear_contrast(exposure_boost * lum, fulcrum, contrast_boost); \
min_lum = MIN(min_lum, lum_boosted); \
max_lum = MAX(max_lum, lum_boosted); \
out[k / 4] = lum_boosted; \
} \
break; \
}
Expand All @@ -249,11 +274,20 @@ static inline void luminance_mask(const float *const restrict in,
const dt_iop_luminance_mask_method_t method,
const float exposure_boost,
const float fulcrum,
const float contrast_boost)
const float contrast_boost,
const float r_weight,
const float g_weight,
const float b_weight,
float *image_min_ev,
float *image_max_ev)
{
const size_t num_elem = width * height * 4;
float min_lum = INFINITY;
float max_lum = -INFINITY;

switch(method)
{
#define COMPUTE_LUM(fn) fn(in, out, k)
case DT_TONEEQ_MEAN:
LOOP(pixel_rgb_mean);

Expand All @@ -275,11 +309,21 @@ static inline void luminance_mask(const float *const restrict in,
case DT_TONEEQ_GEOMEAN:
LOOP(pixel_rgb_geomean);

case DT_TONEEQ_REC709W:
LOOP(pixel_rgb_r709w);

#undef COMPUTE_LUM
#define COMPUTE_LUM(fn) fn(in, out, k, r_weight, g_weight, b_weight)
case DT_TONEEQ_CUSTOM:
LOOP(pixel_rgb_custom);

default:
break;
}
}

*image_min_ev = log2f(min_lum);
*image_max_ev = log2f(max_lum);
}

// clang-format off
// modelines: These editor modelines have been set for all relevant files by tools/update_modelines.py
Expand Down
22 changes: 22 additions & 0 deletions src/dtgtk/paint.c
Original file line number Diff line number Diff line change
Expand Up @@ -1174,6 +1174,28 @@ void dtgtk_cairo_paint_linear_scale(cairo_t *cr, const gint x, const gint y, con
FINISH
}

void dtgtk_cairo_paint_linear_scale_ignore_border(cairo_t *cr, const gint x, const gint y, const gint w, const gint h, const gint flags, void *data)
{
PREAMBLE(1, 1, 0, 0)

// Main line with reduced slope
cairo_move_to(cr, 0.0, 0.7);
cairo_line_to(cr, 1.0, 0.3);
cairo_stroke(cr);

// Small marks at edges to indicate "ignore border"
cairo_save(cr);
cairo_set_line_width(cr, 0.15);
cairo_move_to(cr, 0.0, 0.85);
cairo_line_to(cr, 0.0, 0.55);
cairo_move_to(cr, 1.0, 0.45);
cairo_line_to(cr, 1.0, 0.15);
cairo_stroke(cr);
cairo_restore(cr);

FINISH
}

void dtgtk_cairo_paint_logarithmic_scale(cairo_t *cr, const gint x, const gint y, const gint w, const gint h, gint flags, void *data)
{
PREAMBLE(1, 1, 0, 0)
Expand Down
2 changes: 2 additions & 0 deletions src/dtgtk/paint.h
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,8 @@ void dtgtk_cairo_paint_waveform_scope(cairo_t *cr, gint x, gint y, gint w, gint
void dtgtk_cairo_paint_vectorscope(cairo_t *cr, gint x, gint y, gint w, gint h, gint flags, void *data);
/** paint linear scale icon */
void dtgtk_cairo_paint_linear_scale(cairo_t *cr, gint x, gint y, gint w, gint h, gint flags, void *data);
/** paint linear scale ignore border icon */
void dtgtk_cairo_paint_linear_scale_ignore_border(cairo_t *cr, gint x, gint y, gint w, gint h, gint flags, void *data);
/** paint logarithmic scale icon */
void dtgtk_cairo_paint_logarithmic_scale(cairo_t *cr, gint x, gint y, gint w, gint h, gint flags, void *data);
/** paint waveform overlaid icon */
Expand Down
44 changes: 44 additions & 0 deletions src/gui/draw.h
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,50 @@ static inline void dt_draw_grid(cairo_t *cr,
}
}

static inline void dt_draw_grid_xy(cairo_t *cr,
const int num_x,
const int num_y,
const int left,
const int top,
const int right,
const int bottom,
const gboolean border_left,
const gboolean border_top,
const gboolean border_right,
const gboolean border_bottom)
{
float width = right - left;
float height = bottom - top;

// Draw vertical lines (num_x divisions)
const int k_min_x = border_left ? 0 : 1;
const int k_max_x = border_right ? num_x : num_x - 1;

for(int k = k_min_x; k <= k_max_x; k++)
{
dt_draw_line(cr,
left + k / (float)num_x * width,
top,
left + k / (float)num_x * width,
bottom);
cairo_stroke(cr);
}

// Draw horizontal lines (num_y divisions)
const int k_min_y = border_top ? 0 : 1;
const int k_max_y = border_bottom ? num_y : num_y - 1;

for(int k = k_min_y; k <= k_max_y; k++)
{
dt_draw_line(cr,
left,
top + k / (float)num_y * height,
right,
top + k / (float)num_y * height);
cairo_stroke(cr);
}
}

static inline float dt_curve_to_mouse(const float x,
const float zoom_factor,
const float offset)
Expand Down
Loading
Loading