159#define UI_SAMPLES 256
160#define CONTRAST_FULCRUM exp2f(-4.0f)
161#define MIN_FLOAT exp2f(-16.0f)
170#define LUT_RESOLUTION 10000
183 -4.0f, -3.0f, -2.0f, -1.0f, 0.0f};
346 return _(
"tone e_qualizer");
351 return _(
"tone curve|tone mapping|relight|background light|shadows highlights");
357 return dt_iop_set_description(self, _(
"relight the scene as if the lighting was done directly on the scene"),
358 _(
"corrective and creative"),
359 _(
"linear, RGB, scene-referred"),
360 _(
"quasi-linear, RGB"),
361 _(
"quasi-linear, RGB, scene-referred"));
388 const int new_version)
390 if(old_version == 1 && new_version == 2)
392 typedef struct dt_iop_toneequalizer_params_v1_t
394 float noise, ultra_deep_blacks, deep_blacks, blacks, shadows, midtones, highlights, whites, speculars;
399 } dt_iop_toneequalizer_params_v1_t;
401 dt_iop_toneequalizer_params_v1_t *
o = (dt_iop_toneequalizer_params_v1_t *)old_params;
409 n->ultra_deep_blacks =
o->ultra_deep_blacks;
410 n->deep_blacks =
o->deep_blacks;
411 n->blacks =
o->blacks;
412 n->shadows =
o->shadows;
413 n->midtones =
o->midtones;
414 n->highlights =
o->highlights;
415 n->whites =
o->whites;
416 n->speculars =
o->speculars;
418 n->blending =
o->blending;
419 n->feathering =
o->feathering;
420 n->contrast_boost =
o->contrast_boost;
421 n->exposure_boost =
o->exposure_boost;
423 n->details =
o->details;
424 n->iterations =
o->iterations;
425 n->method =
o->method;
428 n->quantization = 0.01f;
429 n->smoothing = sqrtf(2.0f);
441 p->ultra_deep_blacks = 5.f / 3.f * step;
442 p->deep_blacks = 5.f / 3.f * step;
446 p->highlights = -5.f / 3.f * step;
447 p->whites = -5.f / 3.f * step;
448 p->speculars = -step;
456 p->noise = -15.f / 9.f * step;
457 p->ultra_deep_blacks = -14.f / 9.f * step;
458 p->deep_blacks = -12.f / 9.f * step;
459 p->blacks = -8.f / 9.f * step;
461 p->midtones = 8.f / 9.f * step;
462 p->highlights = 12.f / 9.f * step;
463 p->whites = 14.f / 9.f * step;
464 p->speculars = 15.f / 9.f * step;
470 memset(&
p, 0,
sizeof(
p));
473 p.contrast_boost = 0.0f;
475 p.exposure_boost = -0.5f;
478 p.smoothing = sqrtf(2.0f);
479 p.quantization = 0.0f;
482 p.noise =
p.ultra_deep_blacks =
p.deep_blacks =
p.blacks =
p.shadows =
p.midtones =
p.highlights =
p.whites =
p. speculars = 0.0f;
486 self->version(), &
p,
sizeof(
p), 1);
495 p.quantization = 0.0f;
496 p.exposure_boost = 0.0f;
497 p.contrast_boost = 0.0f;
499 self->version(), &
p,
sizeof(
p), 1);
502 p.feathering = 10.0f;
505 self->version(), &
p,
sizeof(
p), 1);
509 p.exposure_boost = -1.57f;
510 p.contrast_boost = 0.0f;
512 p.feathering = 50.0f;
514 p.quantization = 0.0f;
518 p.feathering = 20.0f;
521 self->version(), &
p,
sizeof(
p), 1);
523 p.feathering = 500.0f;
525 self->version(), &
p,
sizeof(
p), 1);
533 self->version(), &
p,
sizeof(
p), 1);
535 p.feathering = 500.0f;
537 self->version(), &
p,
sizeof(
p), 1);
545 self->version(), &
p,
sizeof(
p), 1);
547 p.feathering = 500.0f;
549 self->version(), &
p,
sizeof(
p), 1);
555 self->version(), &
p,
sizeof(
p), 1);
559 self->version(), &
p,
sizeof(
p), 1);
563 self->version(), &
p,
sizeof(
p), 1);
570 p.quantization = 0.0f;
571 p.exposure_boost = -0.5f;
572 p.contrast_boost = 0.0f;
575 p.ultra_deep_blacks = 0.15f;
576 p.deep_blacks = 0.6f;
585 self->version(), &
p,
sizeof(
p), 1);
612 g->max_histogram = 1;
613 g->luminance_valid =
FALSE;
614 g->histogram_valid = 0;
617 g->thumb_preview_buf_width = 0;
618 g->thumb_preview_buf_height = 0;
619 preview_entry =
g->thumb_preview_entry;
620 g->thumb_preview_entry = NULL;
634 const double position_self = self->
iop_order;
637 if(position_self < position_min && self->
enabled)
639 dt_control_log(_(
"tone equalizer needs to be after distortion modules in the pipeline - disabled"));
640 fprintf(stdout,
"tone equalizer needs to be after distortion modules in the pipeline - disabled\n");
650 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(self->
gui->
off), self->
enabled);
662 { { 0.076555024f, 0.124401914f, 0.076555024f },
663 { 0.124401914f, 0.196172249f, 0.124401914f },
664 { 0.076555024f, 0.124401914f, 0.076555024f } };
668 const size_t x,
const size_t y)
686 for(
int i = 0;
i < 3; ++
i)
688 const size_t y_i = y_abs[
i];
702 for(
int i = 0;
i < 3; ++
i)
704 const size_t y_i = y_abs[
i];
723 const float norm_x,
const float norm_y)
726 if(!(norm_x >= 0.f) || !(norm_y >= 0.f) || norm_x >= 1.f || norm_y >= 1.f)
return NAN;
773static inline
__attribute__((always_inline))
float gaussian_func(const
float radius, const
float denominator)
779 return expf(- radius * radius / denominator);
782#define DT_TONEEQ_USE_LUT TRUE
790 float *
const restrict
out,
795 const size_t num_elem = (size_t)roi_in->
width * roi_in->
height;
796 const int min_ev = -8;
797 const int max_ev = 0;
798 const float* restrict
lut =
d->correction_lut;
800 for(
size_t k = 0;
k < num_elem; ++
k)
804 const float correction =
lut[(unsigned)roundf((exposure - min_ev) *
LUT_RESOLUTION)];
805 const size_t idx =
k *
ch;
806 const dt_aligned_pixel_simd_t pix_in = dt_load_simd_aligned(in + idx);
807 const dt_aligned_pixel_simd_t correction_v = { correction, correction, correction, 1.0f };
808 dt_store_simd_nontemporal(
out + idx, pix_in * correction_v);
820 float *
const restrict
out,
825 const size_t num_elem = roi_in->
width * roi_in->
height;
826 const float *
const restrict factors =
d->factors;
827 const float sigma =
d->smoothing;
828 const float gauss_denom = gaussian_denom(
sigma);
830 for(
size_t k = 0;
k < num_elem; ++
k)
840 result += gaussian_func(exposure - centers_ops[
i], gauss_denom) * factors[
i];
843 const float correction =
fast_clamp(result, 0.25f, 4.0f);
844 const size_t idx =
k *
ch;
845 const dt_aligned_pixel_simd_t pix_in = dt_load_simd_aligned(in + idx);
846 const dt_aligned_pixel_simd_t correction_v = { correction, correction, correction, 1.0f };
847 dt_store_simd_nontemporal(
out + idx, pix_in * correction_v);
854 const float *
const restrict factors,
860 const float gauss_denom = gaussian_denom(
sigma);
861 const float expo =
fast_clamp(exposure, -8.0f, 0.0f);
864 result += gaussian_func(expo - centers_ops[
i], gauss_denom) * factors[
i];
947 float *
const restrict
out,
952 const size_t offset_x = (roi_in->
x < roi_out->
x) ? -roi_in->
x + roi_out->
x : 0;
953 const size_t offset_y = (roi_in->
y < roi_out->
y) ? -roi_in->
y + roi_out->
y : 0;
958 const size_t in_width = roi_in->
width;
962 for(
size_t i = 0 ;
i < out_height; ++
i)
963 for(
size_t j = 0; j < out_width; ++j)
967 const float intensity = sqrtf(fminf(fmaxf(
luminance[(
i + offset_y) * in_width + (j + offset_x)] - 0.00390625f, 0.f) / 0.99609375f, 1.f));
968 const size_t index = (
i * out_width + j) *
ch;
969 dt_aligned_pixel_simd_t intensity_v =
dt_simd_set1(intensity);
974 const size_t in_index = ((
i + offset_y) * in_width + (j + offset_x)) *
ch;
975 intensity_v[3] = in[in_index + 3];
978 dt_store_simd_nontemporal(
out + index, intensity_v);
996 gboolean created_luminance_entry =
FALSE;
1002 dt_control_log(_(
"tone equalizer in/out buffer are ill-aligned, please report the bug to the developers"));
1003 fprintf(stdout,
"tone equalizer in/out buffer are ill-aligned, please report the bug to the developers\n");
1007 const size_t width = roi_in->width;
1008 const size_t height = roi_in->height;
1010 const size_t ch = 4;
1013 const int position = self->iop_order;
1018 if(roi_in->width < roi_out->width || roi_in->height < roi_out->height)
return 0;
1019 if(!sanity_check(self))
1026 if(self->dev->gui_attached)
1036 g->thumb_preview_buf_width = 0;
1037 g->thumb_preview_buf_height = 0;
1038 g->luminance_valid =
FALSE;
1039 g->histogram_valid =
FALSE;
1040 preview_entry =
g->thumb_preview_entry;
1041 g->thumb_preview_entry = NULL;
1049 if(self->dev->gui_attached)
1064 void *cache_data = NULL;
1065 static const char cache_tag[] =
"toneequal:luminance";
1066 luminance_hash =
dt_hash(
piece->global_hash, cache_tag,
sizeof(cache_tag));
1069 num_elem *
sizeof(
float),
"toneequal luminance",
1079 if(created_luminance_entry)
1089 if(created_luminance_entry)
1133 if(preview_output && self->dev->gui_attached && !
IS_NULL_PTR(
g)
1137 gboolean keep_process_ref =
FALSE;
1144 if(
g->thumb_preview_entry != luminance_entry ||
g->thumb_preview_hash != luminance_hash)
1146 old_entry =
g->thumb_preview_entry;
1147 g->thumb_preview_entry = luminance_entry;
1148 g->thumb_preview_hash = luminance_hash;
1150 g->thumb_preview_buf_width =
width;
1151 g->thumb_preview_buf_height =
height;
1152 g->luminance_valid =
TRUE;
1153 g->histogram_valid =
FALSE;
1154 keep_process_ref =
TRUE;
1161 if(!keep_process_ref)
1164 else if(luminance_entry)
1177 const void *
const restrict ivoid,
void *
const restrict
ovoid)
1181 return toneeq_process(self,
pipe,
piece, ivoid,
ovoid, roi_in, roi_out);
1197 const int radius = (int)((diameter - 1.0f) / ( 2.0f));
1244 const float gauss_denom = gaussian_denom(
sigma);
1245 const int min_ev = -8;
1252 float result = 0.0f;
1254 result += gaussian_func(exposure - centers_ops[
i], gauss_denom) * factors[
i];
1265 factors[0] =
p->noise;
1266 factors[1] =
p->ultra_deep_blacks;
1267 factors[2] =
p->deep_blacks;
1268 factors[3] =
p->blacks;
1269 factors[4] =
p->shadows;
1270 factors[5] =
p->midtones;
1271 factors[6] =
p->highlights;
1272 factors[7] =
p->whites;
1273 factors[8] =
p->speculars;
1287 factors[c] = exp2f(factors[c]);
1306 if(isnan(
out[
i]) ||
out[
i] < 0.25f ||
out[
i] > 4.0f) valid = 0;
1318 const int valid = 1;
1321 out[
i] = log2f(in[
i]);
1329 p->noise = factors[0];
1330 p->ultra_deep_blacks = factors[1];
1331 p->deep_blacks = factors[2];
1332 p->blacks = factors[3];
1333 p->shadows = factors[4];
1334 p->midtones = factors[5];
1335 p->highlights = factors[6];
1336 p->whites = factors[7];
1337 p->speculars = factors[8];
1356 g->max_histogram = 1;
1358 g->sigma = sqrtf(2.0f);
1361 g->interpolation_valid =
FALSE;
1362 g->luminance_valid =
FALSE;
1363 g->histogram_valid =
FALSE;
1366 g->user_param_valid =
FALSE;
1367 g->factors_valid =
TRUE;
1369 g->valid_nodes_x =
FALSE;
1370 g->valid_nodes_y =
FALSE;
1371 g->area_cursor_valid =
FALSE;
1372 g->area_dragging =
FALSE;
1375 g->thumb_preview_entry = NULL;
1376 g->thumb_preview_buf_width = 0;
1377 g->thumb_preview_buf_height = 0;
1402 static const char cache_tag[] =
"toneequal:luminance";
1403 return dt_hash(
piece->global_hash, cache_tag,
sizeof(cache_tag));
1413 const float gauss_denom = gaussian_denom(
sigma);
1414 __OMP_SIMD__(aligned(
A, centers_ops, centers_params:64) collapse(2))
1417 A[
i *
PIXEL_CHAN + j] = gaussian_func(centers_params[
i] - centers_ops[j], gauss_denom);
1424 const size_t num_elem,
1426 float *first_decile,
float *last_decile)
1429 memset(histogram, 0,
sizeof(
int) *
UI_SAMPLES);
1432 #define TEMP_SAMPLES 2 * UI_SAMPLES
1438 for(
size_t k = 0;
k < num_elem;
k++)
1442 temp_hist[index] += 1;
1445 const int first = (int)((
float)num_elem * 0.05f);
1446 const int last = (int)((
float)num_elem * (1.0f - 0.95f));
1455 const size_t prev_population = population;
1456 population += temp_hist[
k];
1457 if(prev_population < first && first <= population)
1466 const size_t prev_population = population;
1467 population += temp_hist[
k];
1468 if(prev_population < last && last <= population)
1476 *first_decile = 16.0 * (float)first_pos / (
float)(
TEMP_SAMPLES - 1) - 10.0;
1477 *last_decile = 16.0 * (float)last_pos / (
float)(
TEMP_SAMPLES - 1) - 10.0;
1483 float EV = 16.0 * (float)
k / (
float)(
TEMP_SAMPLES - 1) - 10.0;
1485 histogram[
i] += temp_hist[
k];
1488 *max_histogram = histogram[
i] > *max_histogram ? histogram[
i] : *max_histogram;
1501 gboolean needs_histogram =
FALSE;
1507 if(!
g->histogram_valid &&
g->luminance_valid &&
g->thumb_preview_entry)
1509 preview_entry =
g->thumb_preview_entry;
1510 width =
g->thumb_preview_buf_width;
1511 height =
g->thumb_preview_buf_height;
1512 preview_hash =
g->thumb_preview_hash;
1514 needs_histogram =
TRUE;
1526 int max_histogram = 1;
1527 float first_decile = 0.0f;
1528 float last_decile = 0.0f;
1544 if(
g->thumb_preview_entry == preview_entry &&
g->thumb_preview_hash == preview_hash && !
g->histogram_valid)
1546 memcpy(
g->histogram, histogram,
sizeof(histogram));
1547 g->max_histogram = max_histogram;
1548 g->histogram_first_decile = first_decile;
1549 g->histogram_last_decile = last_decile;
1550 g->histogram_average = (first_decile + last_decile) / 2.0f;
1551 g->histogram_valid =
TRUE;
1567 float *
const restrict LUT =
g->gui_lut;
1568 const float *
const restrict factors =
g->factors;
1569 const float sigma =
g->sigma;
1575 const float x = (8.0f * (((float)
k) / ((float)(
UI_SAMPLES - 1)))) - 8.0f;
1589 gboolean valid =
TRUE;
1591 if(!
g->interpolation_valid)
1594 g->interpolation_valid =
TRUE;
1595 g->factors_valid =
FALSE;
1598 if(!
g->user_param_valid)
1603 g->user_param_valid =
TRUE;
1604 g->factors_valid =
FALSE;
1607 if(!
g->factors_valid &&
g->user_param_valid)
1618 g->factors_valid =
TRUE;
1623 if(!
g->lut_valid &&
g->factors_valid)
1626 g->lut_valid =
TRUE;
1656 d->method =
p->method;
1657 d->details =
p->details;
1658 d->iterations =
p->iterations;
1659 d->smoothing =
p->smoothing;
1660 d->quantization =
p->quantization;
1663 d->blending =
p->blending / 100.0f;
1667 d->feathering = 1.f / (
p->feathering);
1670 d->contrast_boost = exp2f(
p->contrast_boost);
1671 d->exposure_boost = exp2f(
p->exposure_boost);
1681 if(
g->sigma !=
p->smoothing)
g->interpolation_valid =
FALSE;
1682 g->sigma =
p->smoothing;
1683 g->user_param_valid =
FALSE;
1730 gtk_widget_set_visible(
g->blending,
FALSE);
1731 gtk_widget_set_visible(
g->feathering,
FALSE);
1732 gtk_widget_set_visible(
g->iterations,
FALSE);
1733 gtk_widget_set_visible(
g->contrast_boost,
FALSE);
1734 gtk_widget_set_visible(
g->quantization,
FALSE);
1741 gtk_widget_set_visible(
g->blending,
TRUE);
1742 gtk_widget_set_visible(
g->feathering,
TRUE);
1743 gtk_widget_set_visible(
g->iterations,
TRUE);
1744 gtk_widget_set_visible(
g->contrast_boost,
FALSE);
1745 gtk_widget_set_visible(
g->quantization,
TRUE);
1752 gtk_widget_set_visible(
g->blending,
TRUE);
1753 gtk_widget_set_visible(
g->feathering,
TRUE);
1754 gtk_widget_set_visible(
g->iterations,
TRUE);
1755 gtk_widget_set_visible(
g->contrast_boost,
TRUE);
1756 gtk_widget_set_visible(
g->quantization,
TRUE);
1788 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(
g->show_luminance_mask),
g->mask_display);
1794 if(w ==
g->method ||
1796 w ==
g->feathering ||
1797 w ==
g->iterations ||
1798 w ==
g->quantization)
1802 else if (w ==
g->details)
1807 else if (w ==
g->contrast_boost || w ==
g->exposure_boost)
1828 if(!valid)
dt_control_log(_(
"the interpolation is unstable, decrease the curve smoothing"));
1832 gtk_widget_queue_draw(GTK_WIDGET(
g->area));
1844 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(self->
gui->
off),
TRUE);
1852 g->mask_display = !
g->mask_display;
1858 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(
g->show_luminance_mask),
g->mask_display);
1911 _(
"scroll over image to change tone exposure\n"
1912 "shift+scroll for large steps; "
1913 "ctrl+scroll for small steps"));
1936 const int fail = !sanity_check(self);
1942 g->area_active_node = -1;
1944 gtk_widget_queue_draw(GTK_WIDGET(
g->area));
1952 float pzxpy[2] = { (float)
x, (
float)y };
1955 const float x_pointer = pzxpy[0];
1956 const float y_pointer = pzxpy[1];
1959 if(x_pointer >= 0.f && x_pointer < 1.f && y_pointer >= 0.f && y_pointer < 1.f)
1961 g->cursor_valid =
TRUE;
1962 g->cursor_pos_x = x_pointer;
1963 g->cursor_pos_y = y_pointer;
1968 g->cursor_pos_x = 0.f;
1969 g->cursor_pos_y = 0.f;
1976 size_t preview_width = 0;
1977 size_t preview_height = 0;
1982 if(
g->luminance_valid && !
IS_NULL_PTR(
g->thumb_preview_entry))
1984 preview_entry =
g->thumb_preview_entry;
1985 preview_width =
g->thumb_preview_buf_width;
1986 preview_height =
g->thumb_preview_buf_height;
1991 if(!
IS_NULL_PTR(preview_entry) && preview_width > 0 && preview_height > 0)
1995 const float cursor_exposure
1997 x_pointer, y_pointer));
2000 if(!isnan(cursor_exposure))
2002 g->cursor_exposure = cursor_exposure;
2023 g->area_active_node = -1;
2028 gtk_widget_queue_draw(GTK_WIDGET(
g->area));
2043 const float std = gaussian_denom(blending_sigma);
2044 if(
g->user_param_valid)
2047 g->temp_user_params[
i] *= exp2f(gaussian_func(centers_params[
i] - control_exposure, std) * exposure_offset);
2053 if(
g->user_param_valid)
2055 if(!
g->user_param_valid)
dt_control_log(_(
"the interpolation is unstable, decrease the curve smoothing"));
2058 if(
g->user_param_valid)
2060 if(!
g->user_param_valid)
dt_control_log(_(
"some parameters are out-of-bounds"));
2062 const int commit =
g->user_param_valid;
2080 g->user_param_valid = 1;
2093 if(!sanity_check(self))
return 0;
2101 if(self->
gui->
off) gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(self->
gui->
off), 1);
2104 const int fail = (!
g->cursor_valid || !
g->interpolation_valid || !
g->user_param_valid || dev->
pipe->
processing || !self->
gui->
expanded);
2109 size_t preview_width = 0;
2110 size_t preview_height = 0;
2111 float cursor_x = 0.f;
2112 float cursor_y = 0.f;
2117 if(
g->luminance_valid && !
IS_NULL_PTR(
g->thumb_preview_entry))
2119 preview_entry =
g->thumb_preview_entry;
2120 preview_width =
g->thumb_preview_buf_width;
2121 preview_height =
g->thumb_preview_buf_height;
2122 cursor_x =
g->cursor_pos_x;
2123 cursor_y =
g->cursor_pos_y;
2128 if(
IS_NULL_PTR(preview_entry) || preview_width == 0 || preview_height == 0)
2135 if(!
IS_NULL_PTR(preview_entry) && preview_width > 0 && preview_height > 0)
2139 const float cursor_exposure
2141 cursor_x, cursor_y));
2144 if(!isnan(cursor_exposure))
2146 g->cursor_exposure = cursor_exposure;
2154 const float increment = (up) ? +1.0f : -1.0f;
2164 const float offset = step * ((float)increment);
2169 gtk_widget_queue_draw(GTK_WIDGET(
g->area));
2190void cairo_draw_hatches(cairo_t *cr,
double center[2],
double span[2],
int instances,
double line_width,
double shade)
2196 double C0[2] = { center[0] - span[0], center[1] - span[1] };
2197 double C2[2] = { center[0] + span[0], center[1] + span[1] };
2199 double delta[2] = { 2.0 * span[0] / (
double)instances,
2200 2.0 * span[1] / (
double)instances };
2202 cairo_set_line_width(cr, line_width);
2203 cairo_set_source_rgb(cr, shade, shade, shade);
2205 for(
int i = -instances / 2 - 1;
i <= instances / 2 + 1;
i++)
2207 cairo_move_to(cr, C0[0] + (
double)
i *
delta[0], C0[1]);
2208 cairo_line_to(cr, C2[0] + (
double)
i *
delta[0], C2[1]);
2216 const float gamma = 1.0f / 2.2f;
2217 const float shade = powf(
luminance, gamma);
2218 cairo_set_source_rgba(cr, shade, shade, shade, alpha);
2222static void draw_exposure_cursor(cairo_t *cr,
const double pointerx,
const double pointery,
const double radius,
const float luminance,
const float zoom_scale,
const int instances,
const float alpha)
2227 const double radius_z = radius / zoom_scale;
2230 cairo_arc(cr, pointerx, pointery, radius_z, 0, 2 *
M_PI);
2231 cairo_fill_preserve(cr);
2238 double pointer_coord[2] = { pointerx, pointery };
2239 double span[2] = { radius_z, radius_z };
2250 const float contrast = 1.0f;
2252 if(exposure > -2.5f)
2253 shade = (fminf(exposure * contrast, 0.0f) - 2.5f);
2255 shade = (fmaxf(exposure / contrast, -5.0f) + 2.5f);
2262 int32_t pointerx, int32_t pointery)
2273 const int fail = (!
g->cursor_valid || !
g->interpolation_valid || dev->
pipe->
processing
2283 const float norm_x =
g->cursor_pos_x;
2284 const float norm_y =
g->cursor_pos_y;
2288 size_t preview_width = 0;
2289 size_t preview_height = 0;
2293 float exposure_in = 0.0f;
2294 float luminance_in = 0.0f;
2295 float correction = 0.0f;
2296 float exposure_out = 0.0f;
2297 float luminance_out = 0.0f;
2303 if(
g->luminance_valid && !
IS_NULL_PTR(
g->thumb_preview_entry))
2305 preview_entry =
g->thumb_preview_entry;
2306 preview_width =
g->thumb_preview_buf_width;
2307 preview_height =
g->thumb_preview_buf_height;
2315 if(!
IS_NULL_PTR(preview_entry) && preview_width > 0 && preview_height > 0)
2323 luminance_in = exp2f(exposure_in);
2325 exposure_out = exposure_in + correction;
2326 luminance_out = exp2f(exposure_out);
2335 if(!isnan(exposure_in))
2337 g->cursor_exposure = exposure_in;
2344 if(isnan(correction) || isnan(exposure_in))
return;
2351 const double outer_radius = 16.;
2352 const double inner_radius = outer_radius / 2.0;
2353 const double setting_offset_x = (outer_radius + 4. *
g->inner_padding) / zoom_scale;
2358 cairo_set_line_width(cr, 2.0 * fill_width);
2359 cairo_move_to(cr, x_pointer - setting_offset_x, y_pointer);
2361 if(correction > 0.0f)
2362 cairo_arc(cr, x_pointer, y_pointer, setting_offset_x,
M_PI,
M_PI + correction *
M_PI / 4.0);
2364 cairo_arc_negative(cr, x_pointer, y_pointer, setting_offset_x,
M_PI,
M_PI + correction *
M_PI / 4.0);
2370 cairo_move_to(cr, x_pointer + (outer_radius + 2. *
g->inner_padding) / zoom_scale, y_pointer);
2371 cairo_line_to(cr, x_pointer + outer_radius / zoom_scale, y_pointer);
2372 cairo_move_to(cr, x_pointer - outer_radius / zoom_scale, y_pointer);
2373 cairo_line_to(cr, x_pointer - setting_offset_x - 4.0 *
g->inner_padding / zoom_scale, y_pointer);
2378 cairo_move_to(cr, x_pointer, y_pointer + setting_offset_x + fill_width);
2379 cairo_line_to(cr, x_pointer, y_pointer + outer_radius / zoom_scale);
2380 cairo_move_to(cr, x_pointer, y_pointer - outer_radius / zoom_scale);
2381 cairo_line_to(cr, x_pointer, y_pointer - setting_offset_x - fill_width);
2386 draw_exposure_cursor(cr, x_pointer, y_pointer, inner_radius, luminance_out, zoom_scale, 3, .9);
2390 PangoLayout *layout;
2392 PangoFontDescription *desc = pango_font_description_copy_static(
dt_bauhaus_get_global()->pango_font_desc);
2395 const int old_size = pango_font_description_get_size(desc);
2396 pango_font_description_set_size (desc, (
int)(old_size / zoom_scale));
2397 layout = pango_cairo_create_layout(cr);
2398 pango_layout_set_font_description(layout, desc);
2402 if(preview_entry && self->
enabled)
2403 snprintf(text,
sizeof(text), _(
"%+.1f EV"), exposure_in);
2405 snprintf(text,
sizeof(text),
"? EV");
2406 pango_layout_set_text(layout, text, -1);
2407 pango_layout_get_pixel_extents(layout, &ink, NULL);
2411 cairo_rectangle(cr, x_pointer + (outer_radius + 2. *
g->inner_padding) / zoom_scale,
2412 y_pointer - ink.y - ink.height / 2.0 -
g->inner_padding / zoom_scale,
2413 ink.width + 2.0 * ink.x + 4. *
g->inner_padding / zoom_scale,
2414 ink.height + 2.0 * ink.y + 2. *
g->inner_padding / zoom_scale);
2419 cairo_move_to(cr, x_pointer + (outer_radius + 4. *
g->inner_padding) / zoom_scale,
2420 y_pointer - ink.y - ink.height / 2.);
2421 pango_cairo_show_layout(cr, layout);
2425 pango_font_description_free(desc);
2426 g_object_unref(layout);
2428 if(preview_entry && self->
enabled)
2431 const float radius_threshold = 0.45f;
2432 g->area_active_node = -1;
2436 const float delta_x = fabsf(
g->cursor_exposure - centers_params[
i]);
2437 if(delta_x < radius_threshold)
2438 g->area_active_node =
i;
2441 gtk_widget_queue_draw(GTK_WIDGET(
g->area));
2453 const gboolean was_mask =
g->mask_display;
2458 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(
g->show_luminance_mask),
FALSE);
2464 gboolean needs_preview_update =
FALSE;
2480 size_t preview_width = 0;
2481 size_t preview_height = 0;
2486 void *preview_buf = NULL;
2490 &preview_buf, &preview_entry);
2496 preview_ready =
FALSE;
2502 gboolean keep_new_entry =
FALSE;
2504 if(
g->thumb_preview_entry != preview_entry ||
g->thumb_preview_hash != preview_hash
2505 ||
g->thumb_preview_buf_width != preview_width
2506 ||
g->thumb_preview_buf_height != preview_height || !
g->luminance_valid)
2508 old_entry =
g->thumb_preview_entry;
2509 g->thumb_preview_entry = preview_entry;
2510 g->thumb_preview_hash = preview_hash;
2512 g->thumb_preview_buf_width = preview_width;
2513 g->thumb_preview_buf_height = preview_height;
2514 g->luminance_valid =
TRUE;
2515 g->histogram_valid =
FALSE;
2516 keep_new_entry =
TRUE;
2528 g->pending_preview_hash = preview_hash;
2530 needs_preview_update =
TRUE;
2534 needs_preview_update =
TRUE;
2537 if(needs_preview_update)
2541 _(
"scroll over image to change tone exposure\n"
2542 "shift+scroll for large steps; "
2543 "ctrl+scroll for small steps"));
2552 gtk_widget_get_allocation(widget, &
g->allocation);
2554 if(
g->cst) cairo_surface_destroy(
g->cst);
2557 if(
g->cr) cairo_destroy(
g->cr);
2558 g->cr = cairo_create(
g->cst);
2560 if(
g->layout) g_object_unref(
g->layout);
2561 g->layout = pango_cairo_create_layout(
g->cr);
2563 if(
g->desc) pango_font_description_free(
g->desc);
2566 pango_layout_set_font_description(
g->layout,
g->desc);
2568 g->context = gtk_widget_get_style_context(widget);
2573 snprintf(text,
sizeof(text),
"X");
2574 pango_layout_set_text(
g->layout, text, -1);
2575 pango_layout_get_pixel_extents(
g->layout, &
g->ink, NULL);
2576 g->line_height =
g->ink.height;
2579 snprintf(text,
sizeof(text),
"-");
2580 pango_layout_set_text(
g->layout, text, -1);
2581 pango_layout_get_pixel_extents(
g->layout, &
g->ink, NULL);
2582 g->sign_width =
g->ink.width / 2.0;
2587 g->graph_left_space =
g->line_height +
g->inner_padding;
2588 g->graph_width =
g->allocation.width -
g->inset - 2.0 *
g->line_height;
2589 g->graph_height =
g->allocation.height -
g->inset - 2.0 *
g->line_height;
2590 g->gradient_left_limit = 0.0;
2591 g->gradient_right_limit =
g->graph_width;
2592 g->gradient_top_limit =
g->graph_height + 2 *
g->inner_padding;
2593 g->gradient_width =
g->gradient_right_limit -
g->gradient_left_limit;
2594 g->legend_top_limit = -0.5 *
g->line_height - 2.0 *
g->inner_padding;
2595 g->x_label =
g->graph_width +
g->sign_width + 3.0 *
g->inner_padding;
2597 gtk_render_background(
g->context,
g->cr, 0, 0,
g->allocation.width,
g->allocation.height);
2600 cairo_translate(
g->cr,
g->line_height + 2 *
g->inner_padding,
g->line_height + 3 *
g->inner_padding);
2605 float value = -8.0f;
2609 const float xn = (((float)
k) / ((float)(
CHANNELS - 1))) *
g->graph_width -
g->sign_width;
2610 snprintf(text,
sizeof(text),
"%+.0f",
value);
2611 pango_layout_set_text(
g->layout, text, -1);
2612 pango_layout_get_pixel_extents(
g->layout, &
g->ink, NULL);
2613 cairo_move_to(
g->cr, xn - 0.5 *
g->ink.width -
g->ink.x,
2614 g->legend_top_limit - 0.5 *
g->ink.height -
g->ink.y);
2615 pango_cairo_show_layout(
g->cr,
g->layout);
2616 cairo_stroke(
g->cr);
2623 for(
int k = 0;
k < 5;
k++)
2625 const float yn = (
k / 4.0f) *
g->graph_height;
2626 snprintf(text,
sizeof(text),
"%+.0f",
value);
2627 pango_layout_set_text(
g->layout, text, -1);
2628 pango_layout_get_pixel_extents(
g->layout, &
g->ink, NULL);
2629 cairo_move_to(
g->cr,
g->x_label - 0.5 *
g->ink.width -
g->ink.x,
2630 yn - 0.5 *
g->ink.height -
g->ink.y);
2631 pango_cairo_show_layout(
g->cr,
g->layout);
2632 cairo_stroke(
g->cr);
2639 cairo_pattern_t *grad;
2640 grad = cairo_pattern_create_linear(
g->gradient_left_limit, 0.0,
g->gradient_right_limit, 0.0);
2642 cairo_set_line_width(
g->cr, 0.0);
2643 cairo_rectangle(
g->cr,
g->gradient_left_limit,
g->gradient_top_limit,
g->gradient_width,
g->line_height);
2644 cairo_set_source(
g->cr, grad);
2646 cairo_pattern_destroy(grad);
2650 grad = cairo_pattern_create_linear(0.0,
g->graph_height, 0.0, 0.0);
2652 cairo_set_line_width(
g->cr, 0.0);
2653 cairo_rectangle(
g->cr, -
g->line_height - 2 *
g->inner_padding, 0.0,
g->line_height,
g->graph_height);
2654 cairo_set_source(
g->cr, grad);
2657 cairo_pattern_destroy(grad);
2662 cairo_rectangle(
g->cr, 0, 0,
g->graph_width,
g->graph_height);
2663 cairo_stroke_preserve(
g->cr);
2678 if(!
g->valid_nodes_x &&
g->graph_width > 0)
2681 g->nodes_x[
i] = (((
float)
i) / ((
float)(
CHANNELS - 1))) *
g->graph_width;
2682 g->valid_nodes_x =
TRUE;
2692 if(
g->user_param_valid &&
g->graph_height > 0)
2695 g->nodes_y[
i] = (0.5 - log2f(
g->temp_user_params[
i]) / 4.0) *
g->graph_height;
2696 g->valid_nodes_y =
TRUE;
2714 g->valid_nodes_x =
FALSE;
2715 g->valid_nodes_y =
FALSE;
2723 cairo_rectangle(
g->cr, 0, 0,
g->graph_width,
g->graph_height);
2735 cairo_move_to(
g->cr, 0, 0.5 *
g->graph_height);
2736 cairo_line_to(
g->cr,
g->graph_width, 0.5 *
g->graph_height);
2737 cairo_stroke(
g->cr);
2739 if(
g->histogram_valid && self->
enabled)
2744 cairo_move_to(
g->cr, 0,
g->graph_height);
2749 const float x_temp = (8.0 * (float)
k / (
float)(
UI_SAMPLES - 1)) - 8.0;
2750 const float y_temp = (float)(
g->histogram[
k]) / (float)(
g->max_histogram) * 0.96;
2751 cairo_line_to(
g->cr, (x_temp + 8.0) *
g->graph_width / 8.0,
2752 (1.0 - y_temp) *
g->graph_height );
2754 cairo_line_to(
g->cr,
g->graph_width,
g->graph_height);
2755 cairo_close_path(
g->cr);
2758 if(
g->histogram_last_decile > -0.1f)
2762 cairo_set_source_rgb(
g->cr, 0.75, 0.50, 0.);
2764 2.0 *
g->line_height, 2.0 *
g->line_height, 0, NULL);
2765 cairo_restore(
g->cr);
2768 if(
g->histogram_first_decile < -7.9f)
2772 cairo_set_source_rgb(
g->cr, 0.75, 0.50, 0.);
2774 2.0 *
g->line_height, 2.0 *
g->line_height, 0, NULL);
2775 cairo_restore(
g->cr);
2783 cairo_move_to(
g->cr, 0,
g->gui_lut[0] *
g->graph_height);
2789 const float x_temp = (8.0f * (((float)
k) / ((float)(
UI_SAMPLES - 1)))) - 8.0f;
2790 const float y_temp =
g->gui_lut[
k];
2792 cairo_line_to(
g->cr, (x_temp + 8.0f) *
g->graph_width / 8.0f,
2793 y_temp *
g->graph_height );
2795 cairo_stroke(
g->cr);
2801 if(
g->user_param_valid)
2806 const float xn =
g->nodes_x[
k];
2807 const float yn =
g->nodes_y[
k];
2812 cairo_move_to(
g->cr, xn, 0.5 *
g->graph_height);
2813 cairo_line_to(
g->cr, xn, yn);
2814 cairo_stroke(
g->cr);
2820 cairo_stroke_preserve(
g->cr);
2822 if(
g->area_active_node ==
k)
2833 if(
g->area_cursor_valid)
2835 const float radius =
g->sigma *
g->graph_width / 8.0f / sqrtf(2.0f);
2838 cairo_arc(
g->cr,
g->area_x, y *
g->graph_height, radius, 0, 2. *
M_PI);
2840 cairo_stroke(
g->cr);
2846 float x_pos = (
g->cursor_exposure + 8.0f) / 8.0f *
g->graph_width;
2848 if(x_pos >
g->graph_width || x_pos < 0.0f)
2852 cairo_set_source_rgb(
g->cr, 0.75, 0.50, 0.);
2854 x_pos = (x_pos < 0.0f) ? 0.0f :
g->graph_width;
2862 cairo_move_to(
g->cr, x_pos, 0.0);
2863 cairo_line_to(
g->cr, x_pos,
g->graph_height);
2864 cairo_stroke(
g->cr);
2869 cairo_set_source_surface(cr,
g->cst, 0, 0);
2882 g->area_x = (
event->x -
g->inset);
2883 g->area_y = (
event->y -
g->inset);
2884 g->area_dragging =
FALSE;
2885 g->area_active_node = -1;
2886 g->area_cursor_valid = (
g->area_x > 0.0f &&
g->area_x <
g->graph_width &&
g->area_y > 0.0f &&
g->area_y <
g->graph_height);
2888 gtk_widget_queue_draw(GTK_WIDGET(
g->area));
2902 if(
g->area_dragging)
2909 g->area_x = (
event->x -
g->inset);
2910 g->area_y = (
event->y -
g->inset);
2911 g->area_dragging =
FALSE;
2912 g->area_active_node = -1;
2913 g->area_cursor_valid = (
g->area_x > 0.0f &&
g->area_x <
g->graph_width &&
g->area_y > 0.0f &&
g->area_y <
g->graph_height);
2915 gtk_widget_queue_draw(GTK_WIDGET(
g->area));
2929 if(event->button == 1 && event->type == GDK_2BUTTON_PRESS)
2935 p->noise =
d->noise;
2936 p->ultra_deep_blacks =
d->ultra_deep_blacks;
2937 p->deep_blacks =
d->deep_blacks;
2938 p->blacks =
d->blacks;
2939 p->shadows =
d->shadows;
2940 p->midtones =
d->midtones;
2941 p->highlights =
d->highlights;
2942 p->whites =
d->whites;
2943 p->speculars =
d->speculars;
2949 gtk_widget_queue_draw(self->
gui->
widget);
2953 else if(event->button == 1)
2957 g->area_dragging = 1;
2958 gtk_widget_queue_draw(GTK_WIDGET(
g->area));
2983 if(
g->area_dragging)
2986 const float offset = (-
event->y +
g->area_y) /
g->graph_height * 4.0f;
2987 const float cursor_exposure =
g->area_x /
g->graph_width * 8.0f - 8.0f;
2993 g->area_x = (
event->x -
g->graph_left_space);
2994 g->area_y =
event->y;
2995 g->area_cursor_valid = (
g->area_x > 0.0f &&
g->area_x <
g->graph_width &&
g->area_y > 0.0f &&
g->area_y <
g->graph_height);
2996 g->area_active_node = -1;
2999 if(
g->valid_nodes_x)
3001 const float radius_threshold = fabsf(
g->nodes_x[1] -
g->nodes_x[0]) * 0.45f;
3004 const float delta_x = fabsf(
g->area_x -
g->nodes_x[
i]);
3005 if(delta_x < radius_threshold)
3007 g->area_active_node =
i;
3008 g->area_cursor_valid = 1;
3013 gtk_widget_queue_draw(GTK_WIDGET(
g->area));
3029 if(event->button == 1)
3033 if(
g->area_dragging)
3038 g->area_dragging= 0;
3075 g->mask_display = 0;
3079 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(
g->show_luminance_mask),
g->mask_display);
3095 size_t preview_width = 0;
3096 size_t preview_height = 0;
3104 gtk_widget_queue_draw(GTK_WIDGET(
g->area));
3108 gboolean already_attached =
FALSE;
3110 if(!
IS_NULL_PTR(
g->thumb_preview_entry) &&
g->thumb_preview_hash == preview_hash &&
g->luminance_valid)
3113 already_attached =
TRUE;
3117 if(!already_attached)
3119 void *preview_buf = NULL;
3122 &preview_buf, &preview_entry);
3128 preview_ready =
FALSE;
3134 gboolean keep_new_entry =
FALSE;
3136 if(
g->thumb_preview_entry != preview_entry ||
g->thumb_preview_hash != preview_hash
3137 ||
g->thumb_preview_buf_width != preview_width ||
g->thumb_preview_buf_height != preview_height
3138 || !
g->luminance_valid)
3140 old_entry =
g->thumb_preview_entry;
3141 g->thumb_preview_entry = preview_entry;
3142 g->thumb_preview_hash = preview_hash;
3144 g->thumb_preview_buf_width = preview_width;
3145 g->thumb_preview_buf_height = preview_height;
3146 g->luminance_valid =
TRUE;
3147 g->histogram_valid =
FALSE;
3148 keep_new_entry =
TRUE;
3162 g->pending_preview_hash = preview_hash;
3168 gtk_widget_queue_draw(GTK_WIDGET(
g->area));
3172 const guint64 producer_node_key, gpointer user_data)
3175 (
void)producer_node_key;
3181 const gboolean matched = (
g->pending_preview_hash ==
hash);
3183 if(!matched)
return;
3185 size_t preview_width = 0;
3186 size_t preview_height = 0;
3188 if(preview_hash !=
hash)
return;
3190 void *preview_buf = NULL;
3193 &preview_buf, &preview_entry);
3203 gboolean keep_new_entry =
FALSE;
3205 if(
g->thumb_preview_entry != preview_entry ||
g->thumb_preview_hash != preview_hash
3206 ||
g->thumb_preview_buf_width != preview_width ||
g->thumb_preview_buf_height != preview_height
3207 || !
g->luminance_valid)
3209 old_entry =
g->thumb_preview_entry;
3210 g->thumb_preview_entry = preview_entry;
3211 g->thumb_preview_hash = preview_hash;
3212 g->thumb_preview_buf_width = preview_width;
3213 g->thumb_preview_buf_height = preview_height;
3214 g->luminance_valid =
TRUE;
3215 g->histogram_valid =
FALSE;
3216 keep_new_entry =
TRUE;
3227 gtk_widget_queue_draw(GTK_WIDGET(
g->area));
3250 gtk_widget_queue_draw(self->
gui->
widget);
3255 float *
const picked,
float *
const picked_min,
float *
const picked_max)
3262 const size_t box[4] = {
3268 const size_t x0 =
MIN(box[0],
width - 1);
3269 const size_t y0 =
MIN(box[1],
height - 1);
3270 const size_t x1 = CLAMP(
MAX(box[2], x0 + 1), 1,
width);
3271 const size_t y1 = CLAMP(
MAX(box[3], y0 + 1), 1,
height);
3274 float minimum = INFINITY;
3275 float maximum = -INFINITY;
3280 for(
size_t y = y0; y < y1; ++y)
3283 for(
size_t x = x0;
x < x1; ++
x)
3287 minimum = fminf(minimum,
value);
3288 maximum = fmaxf(maximum,
value);
3293 if(count == 0)
return FALSE;
3294 *picked = mean / (float)count;
3295 *picked_min = minimum;
3296 *picked_max = maximum;
3297 return isfinite(*picked) && isfinite(*picked_min) && isfinite(*picked_max);
3300 const size_t x = CLAMP((
size_t)roundf(sample->
point[0] *
width), 0,
width - 1);
3301 const size_t y = CLAMP((
size_t)roundf(sample->
point[1] *
height), 0,
height - 1);
3304 *picked_min =
value;
3305 *picked_max =
value;
3306 return isfinite(
value);
3335 size_t preview_width = 0;
3336 size_t preview_height = 0;
3338 if(
IS_NULL_PTR(
g) || (picker !=
g->exposure_boost && picker !=
g->contrast_boost))
3346 g->area_active_node = -1;
3351 preview_entry =
g->thumb_preview_entry;
3352 preview_width =
g->thumb_preview_buf_width;
3353 preview_height =
g->thumb_preview_buf_height;
3358 if(
IS_NULL_PTR(preview_entry) || preview_width < 1 || preview_height < 1)
3362 dt_print(
DT_DEBUG_DEV,
"[picker/toneequal] no preview mask picker=%p pipe=%p hash=%" PRIu64
"\n",
3371 float picked_min = NAN;
3372 float picked_max = NAN;
3374 &picked, &picked_min, &picked_max);
3380 dt_print(
DT_DEBUG_DEV,
"[picker/toneequal] mask sample failed picker=%p pipe=%p hash=%" PRIu64
"\n",
3386 g->cursor_valid = isfinite(picked) && picked > 0.0f;
3387 g->cursor_exposure =
g->cursor_valid ? log2f(picked) : 0.0f;
3389 if(picker ==
g->exposure_boost)
3391 if(isfinite(picked) && picked > 0.0f)
3400 "[picker/toneequal] exposure picker=%p luminance=%g set=%g pipe=%p hash=%" PRIu64
"\n",
3401 (
void *)picker, picked,
p->exposure_boost, (
void *)
pipe,
piece ?
piece->global_hash : 0);
3406 "[picker/toneequal] exposure picker=%p invalid luminance=%g pipe=%p hash=%" PRIu64
"\n",
3407 (
void *)picker, picked, (
void *)
pipe,
piece ?
piece->global_hash : 0);
3412 const float fd_old = fminf(picked_min, picked_max);
3413 const float ld_old = fmaxf(picked_min, picked_max);
3415 if(isfinite(fd_old) && isfinite(ld_old) && fd_old > 0.0f && ld_old > fd_old)
3419 const float mix = fd_old * s2 + ld_old * s1;
3427 const float correction = -0.0276f + 0.01823f *
p->feathering + (0.7566f - 1.0f) * contrast;
3428 if(
p->feathering < 5.0f)
3429 contrast += correction;
3430 else if(
p->feathering < 10.0f)
3431 contrast += correction * (2.0f -
p->feathering / 5.0f);
3435 contrast = 0.0235f + 1.1225f * contrast;
3438 p->contrast_boost = contrast;
3445 "[picker/toneequal] contrast picker=%p min=%g max=%g set=%g pipe=%p hash=%" PRIu64
"\n",
3446 (
void *)picker, fd_old, ld_old,
p->contrast_boost, (
void *)
pipe,
3452 "[picker/toneequal] contrast picker=%p invalid min=%g max=%g pipe=%p hash=%" PRIu64
"\n",
3453 (
void *)picker, fd_old, ld_old, (
void *)
pipe,
piece ?
piece->global_hash : 0);
3458 gtk_widget_queue_draw(GTK_WIDGET(
g->area));
3466 if(
piece->dsc_in.channels != 4)
return;
3483 for(
size_t k = 0;
k < num_elem; ++
k)
3486 if(!isfinite(
value) ||
value <= 0.0f)
continue;
3492 if(count == 0)
return;
3494 const float picked = mean / (float)count;
3511 g->area = GTK_DRAWING_AREA(gtk_drawing_area_new());
3512 gtk_widget_set_hexpand(GTK_WIDGET(
g->area),
TRUE);
3513 gtk_box_pack_start(GTK_BOX(self->
gui->
widget),
3515 "plugins/darkroom/toneequal/graphheight", 280, 120),
3518 | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK
3519 | GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK);
3520 gtk_widget_set_can_focus(GTK_WIDGET(
g->area),
TRUE);
3521 g_signal_connect(G_OBJECT(
g->area),
"draw", G_CALLBACK(
area_draw), self);
3522 g_signal_connect(G_OBJECT(
g->area),
"button-press-event", G_CALLBACK(
area_button_press), self);
3523 g_signal_connect(G_OBJECT(
g->area),
"button-release-event", G_CALLBACK(
area_button_release), self);
3524 g_signal_connect(G_OBJECT(
g->area),
"leave-notify-event", G_CALLBACK(
area_leave_notify), self);
3525 g_signal_connect(G_OBJECT(
g->area),
"enter-notify-event", G_CALLBACK(
area_enter_notify), self);
3526 g_signal_connect(G_OBJECT(
g->area),
"motion-notify-event", G_CALLBACK(
area_motion_notify), self);
3527 gtk_widget_set_tooltip_text(GTK_WIDGET(
g->area), _(
"double-click to reset the curve"));
3532 gtk_widget_set_tooltip_text(
g->smoothing, _(
"positive values will produce more progressive tone transitions\n"
3533 "but the curve might become oscillatory in some settings.\n"
3534 "negative values will avoid oscillations and behave more robustly\n"
3535 "but may produce brutal tone transitions and damage local contrast."));
3537 g_signal_connect(G_OBJECT(
g->smoothing),
"value-changed", G_CALLBACK(
smoothing_callback), self);
3543 gtk_widget_set_tooltip_text(
g->exposure_boost, _(
"use this to slide the mask average exposure along channels\n"
3544 "for a better control of the exposure correction with the available nodes.\n"
3545 "the color picker will map the sampled tone to -4 EV."));
3551 gtk_widget_set_tooltip_text(
g->contrast_boost, _(
"use this to counter the averaging effect of the guided filter\n"
3552 "and dilate the mask contrast around -4EV\n"
3553 "this allows to spread the exposure histogram over more channels\n"
3554 "for a better control of the exposure correction.\n"
3555 "the color picker will fit the sampled spread inside the control range."));
3604 gtk_widget_set_tooltip_text(
g->method, _(
"preview the mask and chose the estimator that gives you the\n"
3605 "higher contrast between areas to dodge and areas to burn"));
3609 gtk_widget_set_tooltip_text(
g->details, _(
"'no' affects global and local contrast (safe if you only add contrast)\n"
3610 "'guided filter' only affects global contrast and tries to preserve local contrast\n"
3611 "'averaged guided filter' is a geometric mean of 'no' and 'guided filter' methods\n"
3612 "'eigf' (exposure-independent guided filter) is a guided filter that is exposure-independent, it smooths shadows and highlights the same way (contrary to guided filter which smooths less the highlights)\n"
3613 "'averaged eigf' is a geometric mean of 'no' and 'exposure-independent guided filter' methods"));
3617 gtk_widget_set_tooltip_text(
g->iterations, _(
"number of passes of guided filter to apply\n"
3618 "helps diffusing the edges of the filter at the expense of speed"));
3623 gtk_widget_set_tooltip_text(
g->blending, _(
"diameter of the blur in percent of the largest image size\n"
3624 "warning: big values of this parameter can make the darkroom\n"
3625 "preview much slower if denoise profiled is used."));
3629 gtk_widget_set_tooltip_text(
g->feathering, _(
"precision of the feathering:\n"
3630 "higher values force the mask to follow edges more closely\n"
3631 "but may void the effect of the smoothing\n"
3632 "lower values give smoother gradients and better smoothing\n"
3633 "but may lead to inaccurate edges taping and halos"));
3637 gtk_widget_set_tooltip_text(
g->quantization, _(
"0 disables the quantization.\n"
3638 "higher values posterize the luminance mask to help the guiding\n"
3639 "produce piece-wise smooth areas when using high feathering values"));
3644 const int active_page =
dt_conf_get_int(
"plugins/darkroom/toneequal/gui_page");
3645 gtk_widget_show(gtk_notebook_get_nth_page(
g->notebook, active_page));
3646 gtk_notebook_set_current_page(
g->notebook, active_page);
3678 dt_conf_set_int(
"plugins/darkroom/toneequal/gui_page", gtk_notebook_get_current_page (
g->notebook));
3687 preview_entry =
g->thumb_preview_entry;
3688 g->thumb_preview_entry = NULL;
3691 g->thumb_preview_buf_width = 0;
3692 g->thumb_preview_buf_height = 0;
3693 g->luminance_valid =
FALSE;
3697 if(
g->desc) pango_font_description_free(
g->desc);
3698 if(
g->layout) g_object_unref(
g->layout);
3699 if(
g->cr) cairo_destroy(
g->cr);
3700 if(
g->cst) cairo_surface_destroy(
g->cst);
Handle default and user-set shortcuts (accelerators)
void dt_gui_set_pango_resolution(PangoLayout *layout)
void dt_bauhaus_slider_set_soft_range(GtkWidget *widget, float soft_min, float soft_max)
float dt_bauhaus_slider_get(GtkWidget *widget)
void dt_bauhaus_slider_set_soft_max(GtkWidget *widget, float val)
void dt_bauhaus_widget_set_quad_active(GtkWidget *widget, int active)
void dt_bauhaus_slider_set(GtkWidget *widget, float pos)
void dt_bauhaus_widget_set_label(GtkWidget *widget, const char *label)
GtkWidget * dt_bauhaus_slider_new_with_range(dt_bauhaus_t *bh, dt_gui_module_t *self, float min, float max, float step, float defval, int digits)
void dt_bauhaus_combobox_remove_at(GtkWidget *widget, int pos)
void dt_bauhaus_slider_set_format(GtkWidget *widget, const char *format)
typedef void((*dt_cache_allocate_t)(void *userdata, dt_cache_entry_t *entry))
static int pseudo_solve(float *const restrict A, float *const restrict y, const size_t m, const size_t n, const int checks)
static const float scaling
return vector dt_simd_set1(valid ?(scaling+NORM_MIN) :NORM_MIN)
dt_collection_t * dt_collection_get_global(void)
void dt_collection_hint_message(const dt_collection_t *collection)
void dt_iop_color_picker_reset(dt_iop_module_t *module, gboolean keep)
GtkWidget * dt_color_picker_new(dt_iop_module_t *module, dt_iop_color_picker_kind_t kind, GtkWidget *w)
gboolean dt_iop_color_picker_is_visible(const dt_develop_t *dev)
@ DT_LIB_COLORPICKER_SIZE_BOX
struct _GtkWidget GtkWidget
GtkWidget, opaque, spelled exactly as GTK spells it.
const dt_colormatrix_t dt_aligned_pixel_t out
void dt_conf_set_int(const char *name, int val)
int dt_conf_get_int(const char *name)
Integer for name, clamped to the bounds declared in the XML.
void dt_control_log(const char *msg,...)
void dt_control_queue_redraw_center()
Request a redraw of the centre view.
void dt_control_hinter_message(const struct dt_control_t *s, const char *message)
void dt_control_queue_cursor_by_name(const char *curs_str)
Queue a GTK named cursor for the next cursor commit.
#define dt_control_set_cursor_visible(visible)
struct dt_control_t * dt_control_get_global(void)
struct dt_bauhaus_t * dt_bauhaus_get_global(void)
#define dt_dev_add_history_item(dev, module, enable, redraw)
#define dt_dev_pixelpipe_update_history_main(dev)
#define dt_dev_pixelpipe_update_history_preview(dev)
int32_t dt_dev_roi_request_preview_height(const dt_develop_t *dev)
int32_t dt_dev_roi_request_preview_width(const dt_develop_t *dev)
dt_dev_pixelpipe_iop_t * dt_dev_distort_get_iop_pipe(struct dt_dev_pixelpipe_t *pipe, struct dt_iop_module_t *module)
float dt_dev_get_overlay_scale(dt_develop_t *dev)
Get the overlay scale factor in GUI logical coordinates.
gboolean dt_dev_rescale_roi(dt_develop_t *dev, cairo_t *cr, int32_t width, int32_t height)
Scale the ROI to fit within given width/height, centered.
gboolean dt_dev_pixelpipe_has_preview_output(const dt_develop_t *dev, const dt_dev_pixelpipe_t *pipe, const dt_iop_roi_t *roi)
void dt_dev_coordinates_widget_to_image_norm(dt_develop_t *dev, float *points, size_t num_points)
Coordinate conversion helpers between widget, normalized image, and absolute image spaces.
@ DT_DEV_PIXELPIPE_DISPLAY_MASK
@ DT_DEV_PIXELPIPE_DISPLAY_PASSTHRU
@ DT_DEV_PIXELPIPE_DISPLAY_NONE
gboolean enabled
–doc was passed on the command line
static void set_color(cairo_t *cr, GdkRGBA color)
static void dt_draw_grid(cairo_t *cr, const int num, const int left, const int top, const int right, const int bottom)
static void dt_cairo_perceptual_gradient(cairo_pattern_t *grad, double alpha)
static __DT_CLONE_TARGETS__ int fast_eigf_surface_blur(float *const restrict image, const size_t width, const size_t height, const float sigma, float feathering, const int iterations, const dt_iop_guided_filter_blending_t filter, const float scale, const float quantization, const float quantize_min, const float quantize_max)
static __DT_CLONE_TARGETS__ int fast_surface_blur(float *const restrict image, const size_t width, const size_t height, const int radius, float feathering, const int iterations, const dt_iop_guided_filter_blending_t filter, const float scale, const float quantization, const float quantize_min, const float quantize_max)
static float fast_clamp(const float value, const float bottom, const float top)
void dt_gui_presets_add_generic(const char *name, dt_dev_operation_t op, const int32_t version, const void *params, const int32_t params_size, const int32_t enabled)
static uint64_t dt_hash(uint64_t hash, const char *str, size_t size)
static __DT_CLONE_TARGETS__ void dt_simd_memcpy(const float *const __restrict__ in, float *const __restrict__ out, const size_t num_elem)
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)
void dt_iop_set_cache_bypass(dt_iop_module_t *module, gboolean state)
@ DT_REQUEST_COLORPICK_OFF
void dt_iop_gui_leave_critical_section(dt_iop_module_t *const module)
Release what dt_iop_gui_enter_critical_section() took. Also a no-op headless.
void dt_iop_request_focus(dt_iop_module_t *module)
Move darkroom focus to module, or clear it with NULL.
@ IOP_FLAGS_INCLUDE_IN_STYLES
@ IOP_FLAGS_SUPPORTS_BLENDING
void dt_iop_gui_enter_critical_section(dt_iop_module_t *const module)
Take the module's GUI lock, serialising access to its dt_iop_gui_data_t.
GtkWidget * dt_iop_togglebutton_new(dt_iop_module_t *self, const char *section, const gchar *label, const gchar *ctrl_label, GCallback callback, gboolean local, guint accel_key, GdkModifierType mods, DTGTKCairoPaintIconFunc paint, GtkWidget *box)
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)
static dt_iop_gui_data_t * dt_iop_gui_data(const struct dt_iop_module_t *m)
The module's GUI data blob, NULL-safe for headless callers: IOP process() implementations read it for...
#define IOP_GUI_ALLOC(module)
int dt_ioppr_get_iop_order(GList *iop_order_list, const char *op_name, const int multi_priority)
Return the iop_order for a given operation/instance pair.
GtkWidget * dt_ui_label_new(const gchar *str)
static const dt_dev_pixelpipe_t * pipe
static const dt_dev_pixelpipe_t const dt_dev_pixelpipe_iop_t * piece
static float mix(const float a, const float b, const float t)
void dt_print(dt_debug_thread_t thread, const char *msg,...) __attribute__((format(printf
Print to stdout when thread is enabled, prefixed with seconds since startup.
float *const restrict const size_t const size_t const float exposure_boost
dt_iop_luminance_mask_method_t
float *const restrict luminance
float *const restrict const size_t const size_t const float const float const float contrast_boost
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...
static const int max_size
dt_masks_form_t * dt_masks_get_visible_form(const dt_develop_t *dev)
Return the currently visible form used by the masks GUI.
The interactive half of the masks subsystem: the editing state (dt_masks_form_gui_t),...
float DT_ALIGNED_ARRAY dt_colormatrix_t[4][4]
#define DT_ALIGNED_PIXEL
Align a 4-float pixel on 16 bytes, enough for SSE. Same struct-member caveat as DT_ALIGNED_ARRAY,...
#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.
static void * dt_check_sse_aligned(void *pointer)
Runtime-checked counterpart to DT_IS_ALIGNED().
#define dt_free(ptr)
g_free() ptr and set it to NULL, skipping both if it is already NULL.
#define DT_MODULE_INTROSPECTION(MODVER, PARAMSTYPE)
DT_MODULE() for a module whose params struct is introspected.
GtkWidget * dt_ui_notebook_page(GtkNotebook *notebook, const char *text, const char *tooltip)
GtkNotebook * dt_ui_notebook_new()
void dt_ui_notebook_set_picker_owner(GtkNotebook *notebook, gpointer owner)
Register an opaque owner for a GtkNotebook's page switches, and report every "switch_page" to the hos...
#define __OMP_SIMD__(...)
#define dt_omploop_sfence()
#define __OMP_DECLARE_SIMD__(...)
#define __OMP_PARALLEL_FOR__(...)
#define __OMP_FOR_SIMD__(...)
#define __OMP_PARALLEL_FOR_SIMD__(...)
void dt_dev_pixelpipe_cache_wrlock_entry(gboolean lock, dt_pixel_cache_entry_t *cache_entry)
Lock or release the write lock on the entry.
void * dt_pixel_cache_entry_get_data(dt_pixel_cache_entry_t *entry)
Peek the host pixels a cache entry holds for its current hash, without allocating.
void dt_dev_pixelpipe_cache_ref_count_entry(gboolean lock, dt_pixel_cache_entry_t *cache_entry)
Take or release one reference on a cache line. A referenced line is never evicted,...
void dt_dev_pixelpipe_cache_rdlock_entry(gboolean lock, dt_pixel_cache_entry_t *cache_entry)
Lock or release the read lock on the entry.
int dt_dev_pixelpipe_cache_get(const uint64_t hash, const size_t size, const char *name, const int id, const gboolean alloc, void **data, dt_pixel_cache_entry_t **entry)
Get a cache line from the cache.
size_t dt_pixel_cache_entry_get_size(const dt_pixel_cache_entry_t *entry)
gboolean dt_dev_pixelpipe_cache_ref_entry_by_hash(const uint64_t hash, void **data, dt_pixel_cache_entry_t **entry)
Resolve and retain an existing cache entry by hash.
void dt_dev_pixelpipe_cache_flag_auto_destroy(dt_pixel_cache_entry_t *cache_entry)
Flag a cache line as disposable (auto_destroy), for short-lived lines nothing will need again: the re...
Pixelpipe cache for storing intermediate results in the pixelpipe.
#define DT_PIXELPIPE_CACHE_HASH_INVALID
#define dt_pixelpipe_cache_free_align(mem)
#define dt_pixelpipe_cache_alloc_align_float(pixels, pipe)
static cairo_surface_t * dt_cairo_image_surface_create(cairo_format_t format, int width, int height)
#define DT_DEBUG_CONTROL_SIGNAL_DISCONNECT(ctlsig, cb, user_data)
struct dt_control_signal_t * dt_control_signal_get_global(void)
@ DT_SIGNAL_DEVELOP_HISTORY_CHANGE
This signal is raised when develop history is changed no param, no returned value.
@ DT_SIGNAL_HISTORY_RESYNC
This signal is raised once darkroom history has been resynchronized into all live pipelines....
@ DT_SIGNAL_CACHELINE_READY
This signal is raised when one cacheline write lock is released. 1 : uint64_t cacheline hash no retur...
@ DT_SIGNAL_DEVELOP_UI_PIPE_FINISHED
This signal is raised when pipe is finished and the gui is attached no param, no returned value.
#define DT_DEBUG_CONTROL_SIGNAL_CONNECT(ctlsig, signal, cb, user_data)
#define for_each_channel(_var,...)
float dt_aligned_pixel_simd_t __attribute__((vector_size(16), aligned(16)))
Apply one channel's tone curve to each of the three colour channels, or pass the channel through unto...
static const dt_aligned_pixel_simd_t value
const float uint32_t state[4]
unsigned __int64 uint64_t
dt_lib_colorpicker_size_t size
dt_dev_pixelpipe_type_t type
struct dt_colorpicker_sample_t * primary_sample
struct dt_develop_t::@13 color_picker
Authoritative darkroom color-picker state.
struct dt_dev_pixelpipe_t * preview_pipe
struct dt_masks_form_gui_t * form_gui
struct dt_dev_pixelpipe_t * pipe
dt_iop_buffer_type_t datatype
dt_iop_global_data_t * data
dt_dev_request_colorpick_flags_t request_color_pick
dt_iop_params_t * default_params
struct dt_iop_module_gui_t * gui
struct dt_develop_t * dev
Region of interest passed through the pixelpipe.
float factors[8] DT_ALIGNED_ARRAY
dt_iop_toneequalizer_filter_t details
float correction_lut[8 *10000+1] DT_ALIGNED_ARRAY
dt_iop_luminance_mask_method_t method
float gradient_left_limit
int histogram[256] DT_ALIGNED_ARRAY
float histogram_first_decile
dt_pixel_cache_entry_t * thumb_preview_entry
float histogram_last_decile
float interpolation_matrix[9 *8] DT_ALIGNED_ARRAY
GtkWidget * exposure_boost
float factors[8] DT_ALIGNED_ARRAY
size_t thumb_preview_buf_height
float gradient_right_limit
GtkWidget * show_luminance_mask
GtkWidget * contrast_boost
float temp_user_params[9] DT_ALIGNED_ARRAY
float gui_lut[256] DT_ALIGNED_ARRAY
GtkWidget * ultra_deep_blacks
size_t thumb_preview_buf_width
uint64_t thumb_preview_hash
uint64_t pending_preview_hash
PangoFontDescription * desc
GtkStyleContext * context
dt_iop_toneequalizer_filter_t details
dt_iop_luminance_mask_method_t method
#define __DT_CLONE_TARGETS__
typedef double((*spd)(unsigned long int wavelength, double TempK))
static void get_shade_from_luminance(cairo_t *cr, const float luminance, const float alpha)
static void draw_exposure_cursor(cairo_t *cr, const double pointerx, const double pointery, const double radius, const float luminance, const float zoom_scale, const int instances, const float alpha)
static void smoothing_callback(GtkWidget *slider, gpointer user_data)
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)
static gboolean area_draw(GtkWidget *widget, cairo_t *cr, gpointer user_data)
static gboolean area_button_press(GtkWidget *widget, GdkEventButton *event, gpointer user_data)
static int compute_luminance_mask(const float *const restrict in, float *const restrict luminance, const size_t width, const size_t height, const size_t ch, const dt_iop_toneequalizer_data_t *const d)
const char ** description(struct dt_iop_module_t *self)
void cairo_draw_hatches(cairo_t *cr, double center[2], double span[2], int instances, double line_width, double shade)
static gboolean area_leave_notify(GtkWidget *widget, GdkEventCrossing *event, gpointer user_data)
static float pixel_correction(const float exposure, const float *const restrict factors, const float sigma)
int scrolled(struct dt_iop_module_t *self, double x, double y, int up, uint32_t state)
static __DT_CLONE_TARGETS__ void compute_log_histogram_and_stats(const float *const restrict luminance, int histogram[256], const size_t num_elem, int *max_histogram, float *first_decile, float *last_decile)
static void get_channels_gains(float factors[9], const dt_iop_toneequalizer_params_t *p)
static void _develop_history_resync_callback(gpointer instance, gpointer user_data)
static void match_color_to_background(cairo_t *cr, const float exposure, const float alpha)
static void build_interpolation_matrix(float A[9 *8], const float sigma)
static gboolean in_mask_editing(dt_iop_module_t *self)
void show_guiding_controls(struct dt_iop_module_t *self)
static void init_nodes_y(dt_iop_toneequalizer_gui_data_t *g)
static gboolean update_curve_lut(struct dt_iop_module_t *self)
static void _switch_cursors(struct dt_iop_module_t *self)
static float get_luminance_at_norm(const float *const buffer, const size_t width, const size_t height, const float norm_x, const float norm_y)
Sample the luminance mask at a NORMALIZED image position.
void gui_focus(struct dt_iop_module_t *self, gboolean in)
void init_pipe(struct dt_iop_module_t *self, dt_dev_pixelpipe_t *pipe, dt_dev_pixelpipe_iop_t *piece)
static gboolean luminance_entry_fits(dt_pixel_cache_entry_t *entry, const size_t width, const size_t height)
Tell whether a cache entry can hold a width x height luminance mask.
static const float centers_ops[8] DT_ALIGNED_ARRAY
static __DT_CLONE_TARGETS__ void compute_lut_correction(struct dt_iop_toneequalizer_gui_data_t *g, const float offset, const float scaling)
static int set_new_params_interactive(const float control_exposure, const float exposure_offset, const float blending_sigma, dt_iop_toneequalizer_gui_data_t *g, dt_iop_toneequalizer_params_t *p)
void gui_reset(struct dt_iop_module_t *self)
void gui_update(struct dt_iop_module_t *self)
Refresh GUI controls from current params and configuration.
static void compress_shadows_highlight_preset_set_exposure_params(dt_iop_toneequalizer_params_t *p, const float step)
void gui_init(struct dt_iop_module_t *self)
static void dilate_shadows_highlight_preset_set_exposure_params(dt_iop_toneequalizer_params_t *p, const float step)
void gui_changed(dt_iop_module_t *self, GtkWidget *w, void *previous)
static void init_nodes_x(dt_iop_toneequalizer_gui_data_t *g)
static int compute_channels_gains(const float in[9], float out[9])
static void compute_correction_lut(float *restrict lut, const float sigma, const float *const restrict factors)
static const dt_colormatrix_t gauss_kernel
void cleanup_global(dt_iop_module_so_t *module)
static void gui_cache_init(struct dt_iop_module_t *self)
static void update_histogram(struct dt_iop_module_t *const self)
static uint64_t _current_preview_luminance_hash(dt_iop_module_t *self, size_t *width, size_t *height)
static void _develop_ui_pipe_started_callback(gpointer instance, gpointer user_data)
static gboolean area_enter_notify(GtkWidget *widget, GdkEventCrossing *event, gpointer user_data)
void update_exposure_sliders(dt_iop_toneequalizer_gui_data_t *g, dt_iop_toneequalizer_params_t *p)
static gboolean area_button_release(GtkWidget *widget, GdkEventButton *event, gpointer user_data)
int default_colorspace(dt_iop_module_t *self, dt_dev_pixelpipe_t *pipe, const dt_dev_pixelpipe_iop_t *piece)
void input_format(dt_iop_module_t *self, dt_dev_pixelpipe_t *pipe, dt_dev_pixelpipe_iop_t *piece, dt_iop_buffer_dsc_t *dsc)
static gboolean area_motion_notify(GtkWidget *widget, GdkEventMotion *event, gpointer user_data)
void gui_post_expose(struct dt_iop_module_t *self, cairo_t *cr, int32_t width, int32_t height, int32_t pointerx, int32_t pointery)
static void _develop_cacheline_ready_callback(gpointer instance, const guint64 hash, const guint64 producer_node_key, gpointer user_data)
void gui_cleanup(struct dt_iop_module_t *self)
dt_iop_toneequalizer_filter_t
void init_presets(dt_iop_module_so_t *self)
int mouse_leave(struct dt_iop_module_t *self)
static gboolean notebook_button_press(GtkWidget *widget, GdkEventButton *event, gpointer user_data)
static void get_channels_factors(float factors[9], const dt_iop_toneequalizer_params_t *p)
static void _develop_ui_pipe_finished_callback(gpointer instance, gpointer user_data)
static __DT_CLONE_TARGETS__ void display_luminance_mask(const float *const restrict in, const float *const restrict luminance, float *const restrict out, const dt_iop_roi_t *const roi_in, const dt_iop_roi_t *const roi_out, const dt_dev_pixelpipe_t *pipe, const size_t ch)
static gboolean _init_drawing(dt_iop_module_t *const restrict self, GtkWidget *widget, dt_iop_toneequalizer_gui_data_t *const restrict g)
void cleanup_pipe(struct dt_iop_module_t *self, dt_dev_pixelpipe_t *pipe, dt_dev_pixelpipe_iop_t *piece)
static float get_luminance_from_buffer(const float *const buffer, const size_t width, const size_t height, const size_t x, const size_t y)
void init_global(dt_iop_module_so_t *module)
void autoset(struct dt_iop_module_t *self, const struct dt_dev_pixelpipe_t *pipe, const struct dt_dev_pixelpipe_iop_t *piece, const void *i)
static int commit_channels_gains(const float factors[9], dt_iop_toneequalizer_params_t *p)
int mouse_moved(struct dt_iop_module_t *self, double x, double y, double pressure, int which)
static int compute_channels_factors(const float factors[8], float out[9], const float sigma)
void color_picker_apply(dt_iop_module_t *self, GtkWidget *picker, dt_dev_pixelpipe_t *pipe, dt_dev_pixelpipe_iop_t *piece)
Update tone equalizer sliders from one picker sample.
static gboolean _sample_picker_luminance_mask(const dt_develop_t *const dev, const float *const buffer, const size_t width, const size_t height, float *const picked, float *const picked_min, float *const picked_max)
static void show_luminance_mask_callback(GtkWidget *togglebutton, GdkEventButton *event, dt_iop_module_t *self)
void modify_roi_in(struct dt_iop_module_t *self, const struct dt_dev_pixelpipe_t *pipe, struct dt_dev_pixelpipe_iop_t *piece, const dt_iop_roi_t *roi_out, dt_iop_roi_t *roi_in)
int legacy_params(dt_iop_module_t *self, const void *const old_params, const int old_version, void *new_params, const int new_version)
int process(struct dt_iop_module_t *self, const dt_dev_pixelpipe_t *pipe, const dt_dev_pixelpipe_iop_t *piece, const void *const restrict ivoid, void *const restrict ovoid)
static void invalidate_luminance_cache(dt_iop_module_t *const self)
static __DT_CLONE_TARGETS__ void apply_toneequalizer(const float *const restrict in, const float *const restrict luminance, float *const restrict out, const dt_iop_roi_t *const roi_in, const dt_iop_roi_t *const roi_out, const size_t ch, const dt_iop_toneequalizer_data_t *const d)