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",
1070 pipe->type,
TRUE, &cache_data,
1077 if(created_luminance_entry)
1084 if(created_luminance_entry)
1127 if(preview_output && self->dev->gui_attached && !
IS_NULL_PTR(
g)
1131 gboolean keep_process_ref =
FALSE;
1138 if(
g->thumb_preview_entry != luminance_entry ||
g->thumb_preview_hash != luminance_hash)
1140 old_entry =
g->thumb_preview_entry;
1141 g->thumb_preview_entry = luminance_entry;
1142 g->thumb_preview_hash = luminance_hash;
1144 g->thumb_preview_buf_width =
width;
1145 g->thumb_preview_buf_height =
height;
1146 g->luminance_valid =
TRUE;
1147 g->histogram_valid =
FALSE;
1148 keep_process_ref =
TRUE;
1155 if(!keep_process_ref)
1158 else if(luminance_entry)
1171 const void *
const restrict ivoid,
void *
const restrict
ovoid)
1175 return toneeq_process(self, pipe, piece, ivoid,
ovoid, roi_in, roi_out);
1191 const int radius = (int)((diameter - 1.0f) / ( 2.0f));
1238 const float gauss_denom = gaussian_denom(
sigma);
1239 const int min_ev = -8;
1246 float result = 0.0f;
1248 result += gaussian_func(exposure - centers_ops[
i], gauss_denom) * factors[
i];
1259 factors[0] =
p->noise;
1260 factors[1] =
p->ultra_deep_blacks;
1261 factors[2] =
p->deep_blacks;
1262 factors[3] =
p->blacks;
1263 factors[4] =
p->shadows;
1264 factors[5] =
p->midtones;
1265 factors[6] =
p->highlights;
1266 factors[7] =
p->whites;
1267 factors[8] =
p->speculars;
1281 factors[c] = exp2f(factors[c]);
1300 if(isnan(
out[
i]) ||
out[
i] < 0.25f ||
out[
i] > 4.0f) valid = 0;
1312 const int valid = 1;
1315 out[
i] = log2f(in[
i]);
1323 p->noise = factors[0];
1324 p->ultra_deep_blacks = factors[1];
1325 p->deep_blacks = factors[2];
1326 p->blacks = factors[3];
1327 p->shadows = factors[4];
1328 p->midtones = factors[5];
1329 p->highlights = factors[6];
1330 p->whites = factors[7];
1331 p->speculars = factors[8];
1350 g->max_histogram = 1;
1352 g->sigma = sqrtf(2.0f);
1355 g->interpolation_valid =
FALSE;
1356 g->luminance_valid =
FALSE;
1357 g->histogram_valid =
FALSE;
1360 g->user_param_valid =
FALSE;
1361 g->factors_valid =
TRUE;
1363 g->valid_nodes_x =
FALSE;
1364 g->valid_nodes_y =
FALSE;
1365 g->area_cursor_valid =
FALSE;
1366 g->area_dragging =
FALSE;
1369 g->thumb_preview_entry = NULL;
1370 g->thumb_preview_buf_width = 0;
1371 g->thumb_preview_buf_height = 0;
1396 static const char cache_tag[] =
"toneequal:luminance";
1407 const float gauss_denom = gaussian_denom(
sigma);
1408 __OMP_SIMD__(aligned(
A, centers_ops, centers_params:64) collapse(2))
1411 A[
i *
PIXEL_CHAN + j] = gaussian_func(centers_params[
i] - centers_ops[j], gauss_denom);
1418 const size_t num_elem,
1420 float *first_decile,
float *last_decile)
1423 memset(histogram, 0,
sizeof(
int) *
UI_SAMPLES);
1426 #define TEMP_SAMPLES 2 * UI_SAMPLES
1432 for(
size_t k = 0;
k < num_elem;
k++)
1436 temp_hist[index] += 1;
1439 const int first = (int)((
float)num_elem * 0.05f);
1440 const int last = (int)((
float)num_elem * (1.0f - 0.95f));
1449 const size_t prev_population = population;
1450 population += temp_hist[
k];
1451 if(prev_population < first && first <= population)
1460 const size_t prev_population = population;
1461 population += temp_hist[
k];
1462 if(prev_population < last && last <= population)
1470 *first_decile = 16.0 * (float)first_pos / (
float)(
TEMP_SAMPLES - 1) - 10.0;
1471 *last_decile = 16.0 * (float)last_pos / (
float)(
TEMP_SAMPLES - 1) - 10.0;
1477 float EV = 16.0 * (float)
k / (
float)(
TEMP_SAMPLES - 1) - 10.0;
1479 histogram[
i] += temp_hist[
k];
1482 *max_histogram = histogram[
i] > *max_histogram ? histogram[
i] : *max_histogram;
1495 gboolean needs_histogram =
FALSE;
1501 if(!
g->histogram_valid &&
g->luminance_valid &&
g->thumb_preview_entry)
1503 preview_entry =
g->thumb_preview_entry;
1504 width =
g->thumb_preview_buf_width;
1505 height =
g->thumb_preview_buf_height;
1506 preview_hash =
g->thumb_preview_hash;
1508 needs_histogram =
TRUE;
1520 int max_histogram = 1;
1521 float first_decile = 0.0f;
1522 float last_decile = 0.0f;
1538 if(
g->thumb_preview_entry == preview_entry &&
g->thumb_preview_hash == preview_hash && !
g->histogram_valid)
1540 memcpy(
g->histogram, histogram,
sizeof(histogram));
1541 g->max_histogram = max_histogram;
1542 g->histogram_first_decile = first_decile;
1543 g->histogram_last_decile = last_decile;
1544 g->histogram_average = (first_decile + last_decile) / 2.0f;
1545 g->histogram_valid =
TRUE;
1561 float *
const restrict LUT =
g->gui_lut;
1562 const float *
const restrict factors =
g->factors;
1563 const float sigma =
g->sigma;
1569 const float x = (8.0f * (((float)
k) / ((float)(
UI_SAMPLES - 1)))) - 8.0f;
1583 gboolean valid =
TRUE;
1585 if(!
g->interpolation_valid)
1588 g->interpolation_valid =
TRUE;
1589 g->factors_valid =
FALSE;
1592 if(!
g->user_param_valid)
1597 g->user_param_valid =
TRUE;
1598 g->factors_valid =
FALSE;
1601 if(!
g->factors_valid &&
g->user_param_valid)
1612 g->factors_valid =
TRUE;
1617 if(!
g->lut_valid &&
g->factors_valid)
1620 g->lut_valid =
TRUE;
1650 d->method =
p->method;
1651 d->details =
p->details;
1652 d->iterations =
p->iterations;
1653 d->smoothing =
p->smoothing;
1654 d->quantization =
p->quantization;
1657 d->blending =
p->blending / 100.0f;
1661 d->feathering = 1.f / (
p->feathering);
1664 d->contrast_boost = exp2f(
p->contrast_boost);
1665 d->exposure_boost = exp2f(
p->exposure_boost);
1675 if(
g->sigma !=
p->smoothing)
g->interpolation_valid =
FALSE;
1676 g->sigma =
p->smoothing;
1677 g->user_param_valid =
FALSE;
1724 gtk_widget_set_visible(
g->blending,
FALSE);
1725 gtk_widget_set_visible(
g->feathering,
FALSE);
1726 gtk_widget_set_visible(
g->iterations,
FALSE);
1727 gtk_widget_set_visible(
g->contrast_boost,
FALSE);
1728 gtk_widget_set_visible(
g->quantization,
FALSE);
1735 gtk_widget_set_visible(
g->blending,
TRUE);
1736 gtk_widget_set_visible(
g->feathering,
TRUE);
1737 gtk_widget_set_visible(
g->iterations,
TRUE);
1738 gtk_widget_set_visible(
g->contrast_boost,
FALSE);
1739 gtk_widget_set_visible(
g->quantization,
TRUE);
1746 gtk_widget_set_visible(
g->blending,
TRUE);
1747 gtk_widget_set_visible(
g->feathering,
TRUE);
1748 gtk_widget_set_visible(
g->iterations,
TRUE);
1749 gtk_widget_set_visible(
g->contrast_boost,
TRUE);
1750 gtk_widget_set_visible(
g->quantization,
TRUE);
1782 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(
g->show_luminance_mask),
g->mask_display);
1788 if(w ==
g->method ||
1790 w ==
g->feathering ||
1791 w ==
g->iterations ||
1792 w ==
g->quantization)
1796 else if (w ==
g->details)
1801 else if (w ==
g->contrast_boost || w ==
g->exposure_boost)
1822 if(!valid)
dt_control_log(_(
"the interpolation is unstable, decrease the curve smoothing"));
1826 gtk_widget_queue_draw(GTK_WIDGET(
g->area));
1838 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(self->
gui->
off),
TRUE);
1846 g->mask_display = !
g->mask_display;
1852 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(
g->show_luminance_mask),
g->mask_display);
1905 _(
"scroll over image to change tone exposure\n"
1906 "shift+scroll for large steps; "
1907 "ctrl+scroll for small steps"));
1930 const int fail = !sanity_check(self);
1936 g->area_active_node = -1;
1938 gtk_widget_queue_draw(GTK_WIDGET(
g->area));
1946 float pzxpy[2] = { (float)
x, (
float)y };
1949 const float x_pointer = pzxpy[0];
1950 const float y_pointer = pzxpy[1];
1953 if(x_pointer >= 0.f && x_pointer < 1.f && y_pointer >= 0.f && y_pointer < 1.f)
1955 g->cursor_valid =
TRUE;
1956 g->cursor_pos_x = x_pointer;
1957 g->cursor_pos_y = y_pointer;
1962 g->cursor_pos_x = 0.f;
1963 g->cursor_pos_y = 0.f;
1970 size_t preview_width = 0;
1971 size_t preview_height = 0;
1976 if(
g->luminance_valid && !
IS_NULL_PTR(
g->thumb_preview_entry))
1978 preview_entry =
g->thumb_preview_entry;
1979 preview_width =
g->thumb_preview_buf_width;
1980 preview_height =
g->thumb_preview_buf_height;
1985 if(!
IS_NULL_PTR(preview_entry) && preview_width > 0 && preview_height > 0)
1989 const float cursor_exposure
1991 x_pointer, y_pointer));
1994 if(!isnan(cursor_exposure))
1996 g->cursor_exposure = cursor_exposure;
2017 g->area_active_node = -1;
2022 gtk_widget_queue_draw(GTK_WIDGET(
g->area));
2037 const float std = gaussian_denom(blending_sigma);
2038 if(
g->user_param_valid)
2041 g->temp_user_params[
i] *= exp2f(gaussian_func(centers_params[
i] - control_exposure, std) * exposure_offset);
2047 if(
g->user_param_valid)
2049 if(!
g->user_param_valid)
dt_control_log(_(
"the interpolation is unstable, decrease the curve smoothing"));
2052 if(
g->user_param_valid)
2054 if(!
g->user_param_valid)
dt_control_log(_(
"some parameters are out-of-bounds"));
2056 const int commit =
g->user_param_valid;
2074 g->user_param_valid = 1;
2087 if(!sanity_check(self))
return 0;
2095 if(self->
gui->
off) gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(self->
gui->
off), 1);
2098 const int fail = (!
g->cursor_valid || !
g->interpolation_valid || !
g->user_param_valid || dev->
pipe->
processing || !self->
gui->
expanded);
2103 size_t preview_width = 0;
2104 size_t preview_height = 0;
2105 float cursor_x = 0.f;
2106 float cursor_y = 0.f;
2111 if(
g->luminance_valid && !
IS_NULL_PTR(
g->thumb_preview_entry))
2113 preview_entry =
g->thumb_preview_entry;
2114 preview_width =
g->thumb_preview_buf_width;
2115 preview_height =
g->thumb_preview_buf_height;
2116 cursor_x =
g->cursor_pos_x;
2117 cursor_y =
g->cursor_pos_y;
2122 if(
IS_NULL_PTR(preview_entry) || preview_width == 0 || preview_height == 0)
2129 if(!
IS_NULL_PTR(preview_entry) && preview_width > 0 && preview_height > 0)
2133 const float cursor_exposure
2135 cursor_x, cursor_y));
2138 if(!isnan(cursor_exposure))
2140 g->cursor_exposure = cursor_exposure;
2148 const float increment = (up) ? +1.0f : -1.0f;
2158 const float offset = step * ((float)increment);
2163 gtk_widget_queue_draw(GTK_WIDGET(
g->area));
2184void cairo_draw_hatches(cairo_t *cr,
double center[2],
double span[2],
int instances,
double line_width,
double shade)
2190 double C0[2] = { center[0] - span[0], center[1] - span[1] };
2191 double C2[2] = { center[0] + span[0], center[1] + span[1] };
2193 double delta[2] = { 2.0 * span[0] / (
double)instances,
2194 2.0 * span[1] / (
double)instances };
2196 cairo_set_line_width(cr, line_width);
2197 cairo_set_source_rgb(cr, shade, shade, shade);
2199 for(
int i = -instances / 2 - 1;
i <= instances / 2 + 1;
i++)
2201 cairo_move_to(cr, C0[0] + (
double)
i *
delta[0], C0[1]);
2202 cairo_line_to(cr, C2[0] + (
double)
i *
delta[0], C2[1]);
2210 const float gamma = 1.0f / 2.2f;
2211 const float shade = powf(
luminance, gamma);
2212 cairo_set_source_rgba(cr, shade, shade, shade, alpha);
2216static 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)
2221 const double radius_z = radius / zoom_scale;
2224 cairo_arc(cr, pointerx, pointery, radius_z, 0, 2 *
M_PI);
2225 cairo_fill_preserve(cr);
2232 double pointer_coord[2] = { pointerx, pointery };
2233 double span[2] = { radius_z, radius_z };
2244 const float contrast = 1.0f;
2246 if(exposure > -2.5f)
2247 shade = (fminf(exposure * contrast, 0.0f) - 2.5f);
2249 shade = (fmaxf(exposure / contrast, -5.0f) + 2.5f);
2256 int32_t pointerx, int32_t pointery)
2267 const int fail = (!
g->cursor_valid || !
g->interpolation_valid || dev->
pipe->
processing
2277 const float norm_x =
g->cursor_pos_x;
2278 const float norm_y =
g->cursor_pos_y;
2282 size_t preview_width = 0;
2283 size_t preview_height = 0;
2287 float exposure_in = 0.0f;
2288 float luminance_in = 0.0f;
2289 float correction = 0.0f;
2290 float exposure_out = 0.0f;
2291 float luminance_out = 0.0f;
2297 if(
g->luminance_valid && !
IS_NULL_PTR(
g->thumb_preview_entry))
2299 preview_entry =
g->thumb_preview_entry;
2300 preview_width =
g->thumb_preview_buf_width;
2301 preview_height =
g->thumb_preview_buf_height;
2309 if(!
IS_NULL_PTR(preview_entry) && preview_width > 0 && preview_height > 0)
2317 luminance_in = exp2f(exposure_in);
2319 exposure_out = exposure_in + correction;
2320 luminance_out = exp2f(exposure_out);
2329 if(!isnan(exposure_in))
2331 g->cursor_exposure = exposure_in;
2338 if(isnan(correction) || isnan(exposure_in))
return;
2345 const double outer_radius = 16.;
2346 const double inner_radius = outer_radius / 2.0;
2347 const double setting_offset_x = (outer_radius + 4. *
g->inner_padding) / zoom_scale;
2352 cairo_set_line_width(cr, 2.0 * fill_width);
2353 cairo_move_to(cr, x_pointer - setting_offset_x, y_pointer);
2355 if(correction > 0.0f)
2356 cairo_arc(cr, x_pointer, y_pointer, setting_offset_x,
M_PI,
M_PI + correction *
M_PI / 4.0);
2358 cairo_arc_negative(cr, x_pointer, y_pointer, setting_offset_x,
M_PI,
M_PI + correction *
M_PI / 4.0);
2364 cairo_move_to(cr, x_pointer + (outer_radius + 2. *
g->inner_padding) / zoom_scale, y_pointer);
2365 cairo_line_to(cr, x_pointer + outer_radius / zoom_scale, y_pointer);
2366 cairo_move_to(cr, x_pointer - outer_radius / zoom_scale, y_pointer);
2367 cairo_line_to(cr, x_pointer - setting_offset_x - 4.0 *
g->inner_padding / zoom_scale, y_pointer);
2372 cairo_move_to(cr, x_pointer, y_pointer + setting_offset_x + fill_width);
2373 cairo_line_to(cr, x_pointer, y_pointer + outer_radius / zoom_scale);
2374 cairo_move_to(cr, x_pointer, y_pointer - outer_radius / zoom_scale);
2375 cairo_line_to(cr, x_pointer, y_pointer - setting_offset_x - fill_width);
2380 draw_exposure_cursor(cr, x_pointer, y_pointer, inner_radius, luminance_out, zoom_scale, 3, .9);
2384 PangoLayout *layout;
2386 PangoFontDescription *desc = pango_font_description_copy_static(
dt_bauhaus_get_global()->pango_font_desc);
2389 const int old_size = pango_font_description_get_size(desc);
2390 pango_font_description_set_size (desc, (
int)(old_size / zoom_scale));
2391 layout = pango_cairo_create_layout(cr);
2392 pango_layout_set_font_description(layout, desc);
2396 if(preview_entry && self->
enabled)
2397 snprintf(text,
sizeof(text), _(
"%+.1f EV"), exposure_in);
2399 snprintf(text,
sizeof(text),
"? EV");
2400 pango_layout_set_text(layout, text, -1);
2401 pango_layout_get_pixel_extents(layout, &ink, NULL);
2405 cairo_rectangle(cr, x_pointer + (outer_radius + 2. *
g->inner_padding) / zoom_scale,
2406 y_pointer - ink.y - ink.height / 2.0 -
g->inner_padding / zoom_scale,
2407 ink.width + 2.0 * ink.x + 4. *
g->inner_padding / zoom_scale,
2408 ink.height + 2.0 * ink.y + 2. *
g->inner_padding / zoom_scale);
2413 cairo_move_to(cr, x_pointer + (outer_radius + 4. *
g->inner_padding) / zoom_scale,
2414 y_pointer - ink.y - ink.height / 2.);
2415 pango_cairo_show_layout(cr, layout);
2419 pango_font_description_free(desc);
2420 g_object_unref(layout);
2422 if(preview_entry && self->
enabled)
2425 const float radius_threshold = 0.45f;
2426 g->area_active_node = -1;
2430 const float delta_x = fabsf(
g->cursor_exposure - centers_params[
i]);
2431 if(delta_x < radius_threshold)
2432 g->area_active_node =
i;
2435 gtk_widget_queue_draw(GTK_WIDGET(
g->area));
2447 const gboolean was_mask =
g->mask_display;
2452 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(
g->show_luminance_mask),
FALSE);
2458 gboolean needs_preview_update =
FALSE;
2474 size_t preview_width = 0;
2475 size_t preview_height = 0;
2480 void *preview_buf = NULL;
2484 &preview_buf, &preview_entry);
2490 preview_ready =
FALSE;
2496 gboolean keep_new_entry =
FALSE;
2498 if(
g->thumb_preview_entry != preview_entry ||
g->thumb_preview_hash != preview_hash
2499 ||
g->thumb_preview_buf_width != preview_width
2500 ||
g->thumb_preview_buf_height != preview_height || !
g->luminance_valid)
2502 old_entry =
g->thumb_preview_entry;
2503 g->thumb_preview_entry = preview_entry;
2504 g->thumb_preview_hash = preview_hash;
2506 g->thumb_preview_buf_width = preview_width;
2507 g->thumb_preview_buf_height = preview_height;
2508 g->luminance_valid =
TRUE;
2509 g->histogram_valid =
FALSE;
2510 keep_new_entry =
TRUE;
2522 g->pending_preview_hash = preview_hash;
2524 needs_preview_update =
TRUE;
2528 needs_preview_update =
TRUE;
2531 if(needs_preview_update)
2535 _(
"scroll over image to change tone exposure\n"
2536 "shift+scroll for large steps; "
2537 "ctrl+scroll for small steps"));
2546 gtk_widget_get_allocation(widget, &
g->allocation);
2548 if(
g->cst) cairo_surface_destroy(
g->cst);
2551 if(
g->cr) cairo_destroy(
g->cr);
2552 g->cr = cairo_create(
g->cst);
2554 if(
g->layout) g_object_unref(
g->layout);
2555 g->layout = pango_cairo_create_layout(
g->cr);
2557 if(
g->desc) pango_font_description_free(
g->desc);
2560 pango_layout_set_font_description(
g->layout,
g->desc);
2562 g->context = gtk_widget_get_style_context(widget);
2567 snprintf(text,
sizeof(text),
"X");
2568 pango_layout_set_text(
g->layout, text, -1);
2569 pango_layout_get_pixel_extents(
g->layout, &
g->ink, NULL);
2570 g->line_height =
g->ink.height;
2573 snprintf(text,
sizeof(text),
"-");
2574 pango_layout_set_text(
g->layout, text, -1);
2575 pango_layout_get_pixel_extents(
g->layout, &
g->ink, NULL);
2576 g->sign_width =
g->ink.width / 2.0;
2581 g->graph_left_space =
g->line_height +
g->inner_padding;
2582 g->graph_width =
g->allocation.width -
g->inset - 2.0 *
g->line_height;
2583 g->graph_height =
g->allocation.height -
g->inset - 2.0 *
g->line_height;
2584 g->gradient_left_limit = 0.0;
2585 g->gradient_right_limit =
g->graph_width;
2586 g->gradient_top_limit =
g->graph_height + 2 *
g->inner_padding;
2587 g->gradient_width =
g->gradient_right_limit -
g->gradient_left_limit;
2588 g->legend_top_limit = -0.5 *
g->line_height - 2.0 *
g->inner_padding;
2589 g->x_label =
g->graph_width +
g->sign_width + 3.0 *
g->inner_padding;
2591 gtk_render_background(
g->context,
g->cr, 0, 0,
g->allocation.width,
g->allocation.height);
2594 cairo_translate(
g->cr,
g->line_height + 2 *
g->inner_padding,
g->line_height + 3 *
g->inner_padding);
2599 float value = -8.0f;
2603 const float xn = (((float)
k) / ((float)(
CHANNELS - 1))) *
g->graph_width -
g->sign_width;
2604 snprintf(text,
sizeof(text),
"%+.0f",
value);
2605 pango_layout_set_text(
g->layout, text, -1);
2606 pango_layout_get_pixel_extents(
g->layout, &
g->ink, NULL);
2607 cairo_move_to(
g->cr, xn - 0.5 *
g->ink.width -
g->ink.x,
2608 g->legend_top_limit - 0.5 *
g->ink.height -
g->ink.y);
2609 pango_cairo_show_layout(
g->cr,
g->layout);
2610 cairo_stroke(
g->cr);
2617 for(
int k = 0;
k < 5;
k++)
2619 const float yn = (
k / 4.0f) *
g->graph_height;
2620 snprintf(text,
sizeof(text),
"%+.0f",
value);
2621 pango_layout_set_text(
g->layout, text, -1);
2622 pango_layout_get_pixel_extents(
g->layout, &
g->ink, NULL);
2623 cairo_move_to(
g->cr,
g->x_label - 0.5 *
g->ink.width -
g->ink.x,
2624 yn - 0.5 *
g->ink.height -
g->ink.y);
2625 pango_cairo_show_layout(
g->cr,
g->layout);
2626 cairo_stroke(
g->cr);
2633 cairo_pattern_t *grad;
2634 grad = cairo_pattern_create_linear(
g->gradient_left_limit, 0.0,
g->gradient_right_limit, 0.0);
2636 cairo_set_line_width(
g->cr, 0.0);
2637 cairo_rectangle(
g->cr,
g->gradient_left_limit,
g->gradient_top_limit,
g->gradient_width,
g->line_height);
2638 cairo_set_source(
g->cr, grad);
2640 cairo_pattern_destroy(grad);
2644 grad = cairo_pattern_create_linear(0.0,
g->graph_height, 0.0, 0.0);
2646 cairo_set_line_width(
g->cr, 0.0);
2647 cairo_rectangle(
g->cr, -
g->line_height - 2 *
g->inner_padding, 0.0,
g->line_height,
g->graph_height);
2648 cairo_set_source(
g->cr, grad);
2651 cairo_pattern_destroy(grad);
2656 cairo_rectangle(
g->cr, 0, 0,
g->graph_width,
g->graph_height);
2657 cairo_stroke_preserve(
g->cr);
2672 if(!
g->valid_nodes_x &&
g->graph_width > 0)
2675 g->nodes_x[
i] = (((
float)
i) / ((
float)(
CHANNELS - 1))) *
g->graph_width;
2676 g->valid_nodes_x =
TRUE;
2686 if(
g->user_param_valid &&
g->graph_height > 0)
2689 g->nodes_y[
i] = (0.5 - log2f(
g->temp_user_params[
i]) / 4.0) *
g->graph_height;
2690 g->valid_nodes_y =
TRUE;
2708 g->valid_nodes_x =
FALSE;
2709 g->valid_nodes_y =
FALSE;
2717 cairo_rectangle(
g->cr, 0, 0,
g->graph_width,
g->graph_height);
2729 cairo_move_to(
g->cr, 0, 0.5 *
g->graph_height);
2730 cairo_line_to(
g->cr,
g->graph_width, 0.5 *
g->graph_height);
2731 cairo_stroke(
g->cr);
2733 if(
g->histogram_valid && self->
enabled)
2738 cairo_move_to(
g->cr, 0,
g->graph_height);
2743 const float x_temp = (8.0 * (float)
k / (
float)(
UI_SAMPLES - 1)) - 8.0;
2744 const float y_temp = (float)(
g->histogram[
k]) / (float)(
g->max_histogram) * 0.96;
2745 cairo_line_to(
g->cr, (x_temp + 8.0) *
g->graph_width / 8.0,
2746 (1.0 - y_temp) *
g->graph_height );
2748 cairo_line_to(
g->cr,
g->graph_width,
g->graph_height);
2749 cairo_close_path(
g->cr);
2752 if(
g->histogram_last_decile > -0.1f)
2756 cairo_set_source_rgb(
g->cr, 0.75, 0.50, 0.);
2758 2.0 *
g->line_height, 2.0 *
g->line_height, 0, NULL);
2759 cairo_restore(
g->cr);
2762 if(
g->histogram_first_decile < -7.9f)
2766 cairo_set_source_rgb(
g->cr, 0.75, 0.50, 0.);
2768 2.0 *
g->line_height, 2.0 *
g->line_height, 0, NULL);
2769 cairo_restore(
g->cr);
2777 cairo_move_to(
g->cr, 0,
g->gui_lut[0] *
g->graph_height);
2783 const float x_temp = (8.0f * (((float)
k) / ((float)(
UI_SAMPLES - 1)))) - 8.0f;
2784 const float y_temp =
g->gui_lut[
k];
2786 cairo_line_to(
g->cr, (x_temp + 8.0f) *
g->graph_width / 8.0f,
2787 y_temp *
g->graph_height );
2789 cairo_stroke(
g->cr);
2795 if(
g->user_param_valid)
2800 const float xn =
g->nodes_x[
k];
2801 const float yn =
g->nodes_y[
k];
2806 cairo_move_to(
g->cr, xn, 0.5 *
g->graph_height);
2807 cairo_line_to(
g->cr, xn, yn);
2808 cairo_stroke(
g->cr);
2814 cairo_stroke_preserve(
g->cr);
2816 if(
g->area_active_node ==
k)
2827 if(
g->area_cursor_valid)
2829 const float radius =
g->sigma *
g->graph_width / 8.0f / sqrtf(2.0f);
2832 cairo_arc(
g->cr,
g->area_x, y *
g->graph_height, radius, 0, 2. *
M_PI);
2834 cairo_stroke(
g->cr);
2840 float x_pos = (
g->cursor_exposure + 8.0f) / 8.0f *
g->graph_width;
2842 if(x_pos >
g->graph_width || x_pos < 0.0f)
2846 cairo_set_source_rgb(
g->cr, 0.75, 0.50, 0.);
2848 x_pos = (x_pos < 0.0f) ? 0.0f :
g->graph_width;
2856 cairo_move_to(
g->cr, x_pos, 0.0);
2857 cairo_line_to(
g->cr, x_pos,
g->graph_height);
2858 cairo_stroke(
g->cr);
2863 cairo_set_source_surface(cr,
g->cst, 0, 0);
2876 g->area_x = (
event->x -
g->inset);
2877 g->area_y = (
event->y -
g->inset);
2878 g->area_dragging =
FALSE;
2879 g->area_active_node = -1;
2880 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);
2882 gtk_widget_queue_draw(GTK_WIDGET(
g->area));
2896 if(
g->area_dragging)
2903 g->area_x = (
event->x -
g->inset);
2904 g->area_y = (
event->y -
g->inset);
2905 g->area_dragging =
FALSE;
2906 g->area_active_node = -1;
2907 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);
2909 gtk_widget_queue_draw(GTK_WIDGET(
g->area));
2923 if(event->button == 1 && event->type == GDK_2BUTTON_PRESS)
2929 p->noise =
d->noise;
2930 p->ultra_deep_blacks =
d->ultra_deep_blacks;
2931 p->deep_blacks =
d->deep_blacks;
2932 p->blacks =
d->blacks;
2933 p->shadows =
d->shadows;
2934 p->midtones =
d->midtones;
2935 p->highlights =
d->highlights;
2936 p->whites =
d->whites;
2937 p->speculars =
d->speculars;
2943 gtk_widget_queue_draw(self->
gui->
widget);
2947 else if(event->button == 1)
2951 g->area_dragging = 1;
2952 gtk_widget_queue_draw(GTK_WIDGET(
g->area));
2977 if(
g->area_dragging)
2980 const float offset = (-
event->y +
g->area_y) /
g->graph_height * 4.0f;
2981 const float cursor_exposure =
g->area_x /
g->graph_width * 8.0f - 8.0f;
2987 g->area_x = (
event->x -
g->graph_left_space);
2988 g->area_y =
event->y;
2989 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);
2990 g->area_active_node = -1;
2993 if(
g->valid_nodes_x)
2995 const float radius_threshold = fabsf(
g->nodes_x[1] -
g->nodes_x[0]) * 0.45f;
2998 const float delta_x = fabsf(
g->area_x -
g->nodes_x[
i]);
2999 if(delta_x < radius_threshold)
3001 g->area_active_node =
i;
3002 g->area_cursor_valid = 1;
3007 gtk_widget_queue_draw(GTK_WIDGET(
g->area));
3023 if(event->button == 1)
3027 if(
g->area_dragging)
3032 g->area_dragging= 0;
3069 g->mask_display = 0;
3073 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(
g->show_luminance_mask),
g->mask_display);
3089 size_t preview_width = 0;
3090 size_t preview_height = 0;
3098 gtk_widget_queue_draw(GTK_WIDGET(
g->area));
3102 gboolean already_attached =
FALSE;
3104 if(!
IS_NULL_PTR(
g->thumb_preview_entry) &&
g->thumb_preview_hash == preview_hash &&
g->luminance_valid)
3107 already_attached =
TRUE;
3111 if(!already_attached)
3113 void *preview_buf = NULL;
3116 &preview_buf, &preview_entry);
3122 preview_ready =
FALSE;
3128 gboolean keep_new_entry =
FALSE;
3130 if(
g->thumb_preview_entry != preview_entry ||
g->thumb_preview_hash != preview_hash
3131 ||
g->thumb_preview_buf_width != preview_width ||
g->thumb_preview_buf_height != preview_height
3132 || !
g->luminance_valid)
3134 old_entry =
g->thumb_preview_entry;
3135 g->thumb_preview_entry = preview_entry;
3136 g->thumb_preview_hash = preview_hash;
3138 g->thumb_preview_buf_width = preview_width;
3139 g->thumb_preview_buf_height = preview_height;
3140 g->luminance_valid =
TRUE;
3141 g->histogram_valid =
FALSE;
3142 keep_new_entry =
TRUE;
3156 g->pending_preview_hash = preview_hash;
3162 gtk_widget_queue_draw(GTK_WIDGET(
g->area));
3166 const guint64 producer_node_key, gpointer user_data)
3169 (
void)producer_node_key;
3175 const gboolean matched = (
g->pending_preview_hash == hash);
3177 if(!matched)
return;
3179 size_t preview_width = 0;
3180 size_t preview_height = 0;
3182 if(preview_hash != hash)
return;
3184 void *preview_buf = NULL;
3187 &preview_buf, &preview_entry);
3197 gboolean keep_new_entry =
FALSE;
3199 if(
g->thumb_preview_entry != preview_entry ||
g->thumb_preview_hash != preview_hash
3200 ||
g->thumb_preview_buf_width != preview_width ||
g->thumb_preview_buf_height != preview_height
3201 || !
g->luminance_valid)
3203 old_entry =
g->thumb_preview_entry;
3204 g->thumb_preview_entry = preview_entry;
3205 g->thumb_preview_hash = preview_hash;
3206 g->thumb_preview_buf_width = preview_width;
3207 g->thumb_preview_buf_height = preview_height;
3208 g->luminance_valid =
TRUE;
3209 g->histogram_valid =
FALSE;
3210 keep_new_entry =
TRUE;
3221 gtk_widget_queue_draw(GTK_WIDGET(
g->area));
3244 gtk_widget_queue_draw(self->
gui->
widget);
3249 float *
const picked,
float *
const picked_min,
float *
const picked_max)
3256 const size_t box[4] = {
3262 const size_t x0 =
MIN(box[0],
width - 1);
3263 const size_t y0 =
MIN(box[1],
height - 1);
3264 const size_t x1 = CLAMP(
MAX(box[2], x0 + 1), 1,
width);
3265 const size_t y1 = CLAMP(
MAX(box[3], y0 + 1), 1,
height);
3268 float minimum = INFINITY;
3269 float maximum = -INFINITY;
3274 for(
size_t y = y0; y < y1; ++y)
3277 for(
size_t x = x0;
x < x1; ++
x)
3281 minimum = fminf(minimum,
value);
3282 maximum = fmaxf(maximum,
value);
3287 if(count == 0)
return FALSE;
3288 *picked = mean / (float)count;
3289 *picked_min = minimum;
3290 *picked_max = maximum;
3291 return isfinite(*picked) && isfinite(*picked_min) && isfinite(*picked_max);
3294 const size_t x = CLAMP((
size_t)roundf(sample->
point[0] *
width), 0,
width - 1);
3295 const size_t y = CLAMP((
size_t)roundf(sample->
point[1] *
height), 0,
height - 1);
3298 *picked_min =
value;
3299 *picked_max =
value;
3300 return isfinite(
value);
3329 size_t preview_width = 0;
3330 size_t preview_height = 0;
3332 if(
IS_NULL_PTR(
g) || (picker !=
g->exposure_boost && picker !=
g->contrast_boost))
3335 (
void *)picker, (
void *)pipe, piece ? piece->
global_hash : 0);
3340 g->area_active_node = -1;
3345 preview_entry =
g->thumb_preview_entry;
3346 preview_width =
g->thumb_preview_buf_width;
3347 preview_height =
g->thumb_preview_buf_height;
3352 if(
IS_NULL_PTR(preview_entry) || preview_width < 1 || preview_height < 1)
3356 dt_print(
DT_DEBUG_DEV,
"[picker/toneequal] no preview mask picker=%p pipe=%p hash=%" PRIu64
"\n",
3357 (
void *)picker, (
void *)pipe, piece ? piece->
global_hash : 0);
3365 float picked_min = NAN;
3366 float picked_max = NAN;
3368 &picked, &picked_min, &picked_max);
3374 dt_print(
DT_DEBUG_DEV,
"[picker/toneequal] mask sample failed picker=%p pipe=%p hash=%" PRIu64
"\n",
3375 (
void *)picker, (
void *)pipe, piece ? piece->
global_hash : 0);
3380 g->cursor_valid = isfinite(picked) && picked > 0.0f;
3381 g->cursor_exposure =
g->cursor_valid ? log2f(picked) : 0.0f;
3383 if(picker ==
g->exposure_boost)
3385 if(isfinite(picked) && picked > 0.0f)
3394 "[picker/toneequal] exposure picker=%p luminance=%g set=%g pipe=%p hash=%" PRIu64
"\n",
3395 (
void *)picker, picked,
p->exposure_boost, (
void *)pipe, piece ? piece->
global_hash : 0);
3400 "[picker/toneequal] exposure picker=%p invalid luminance=%g pipe=%p hash=%" PRIu64
"\n",
3401 (
void *)picker, picked, (
void *)pipe, piece ? piece->
global_hash : 0);
3406 const float fd_old = fminf(picked_min, picked_max);
3407 const float ld_old = fmaxf(picked_min, picked_max);
3409 if(isfinite(fd_old) && isfinite(ld_old) && fd_old > 0.0f && ld_old > fd_old)
3413 const float mix = fd_old * s2 + ld_old * s1;
3421 const float correction = -0.0276f + 0.01823f *
p->feathering + (0.7566f - 1.0f) * contrast;
3422 if(
p->feathering < 5.0f)
3423 contrast += correction;
3424 else if(
p->feathering < 10.0f)
3425 contrast += correction * (2.0f -
p->feathering / 5.0f);
3429 contrast = 0.0235f + 1.1225f * contrast;
3432 p->contrast_boost = contrast;
3439 "[picker/toneequal] contrast picker=%p min=%g max=%g set=%g pipe=%p hash=%" PRIu64
"\n",
3440 (
void *)picker, fd_old, ld_old,
p->contrast_boost, (
void *)pipe,
3446 "[picker/toneequal] contrast picker=%p invalid min=%g max=%g pipe=%p hash=%" PRIu64
"\n",
3447 (
void *)picker, fd_old, ld_old, (
void *)pipe, piece ? piece->
global_hash : 0);
3452 gtk_widget_queue_draw(GTK_WIDGET(
g->area));
3477 for(
size_t k = 0;
k < num_elem; ++
k)
3480 if(!isfinite(
value) ||
value <= 0.0f)
continue;
3486 if(count == 0)
return;
3488 const float picked = mean / (float)count;
3505 g->area = GTK_DRAWING_AREA(gtk_drawing_area_new());
3506 gtk_widget_set_hexpand(GTK_WIDGET(
g->area),
TRUE);
3507 gtk_box_pack_start(GTK_BOX(self->
gui->
widget),
3509 "plugins/darkroom/toneequal/graphheight", 280, 120),
3512 | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK
3513 | GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK);
3514 gtk_widget_set_can_focus(GTK_WIDGET(
g->area),
TRUE);
3515 g_signal_connect(G_OBJECT(
g->area),
"draw", G_CALLBACK(
area_draw), self);
3516 g_signal_connect(G_OBJECT(
g->area),
"button-press-event", G_CALLBACK(
area_button_press), self);
3517 g_signal_connect(G_OBJECT(
g->area),
"button-release-event", G_CALLBACK(
area_button_release), self);
3518 g_signal_connect(G_OBJECT(
g->area),
"leave-notify-event", G_CALLBACK(
area_leave_notify), self);
3519 g_signal_connect(G_OBJECT(
g->area),
"enter-notify-event", G_CALLBACK(
area_enter_notify), self);
3520 g_signal_connect(G_OBJECT(
g->area),
"motion-notify-event", G_CALLBACK(
area_motion_notify), self);
3521 gtk_widget_set_tooltip_text(GTK_WIDGET(
g->area), _(
"double-click to reset the curve"));
3526 gtk_widget_set_tooltip_text(
g->smoothing, _(
"positive values will produce more progressive tone transitions\n"
3527 "but the curve might become oscillatory in some settings.\n"
3528 "negative values will avoid oscillations and behave more robustly\n"
3529 "but may produce brutal tone transitions and damage local contrast."));
3531 g_signal_connect(G_OBJECT(
g->smoothing),
"value-changed", G_CALLBACK(
smoothing_callback), self);
3537 gtk_widget_set_tooltip_text(
g->exposure_boost, _(
"use this to slide the mask average exposure along channels\n"
3538 "for a better control of the exposure correction with the available nodes.\n"
3539 "the color picker will map the sampled tone to -4 EV."));
3545 gtk_widget_set_tooltip_text(
g->contrast_boost, _(
"use this to counter the averaging effect of the guided filter\n"
3546 "and dilate the mask contrast around -4EV\n"
3547 "this allows to spread the exposure histogram over more channels\n"
3548 "for a better control of the exposure correction.\n"
3549 "the color picker will fit the sampled spread inside the control range."));
3598 gtk_widget_set_tooltip_text(
g->method, _(
"preview the mask and chose the estimator that gives you the\n"
3599 "higher contrast between areas to dodge and areas to burn"));
3603 gtk_widget_set_tooltip_text(
g->details, _(
"'no' affects global and local contrast (safe if you only add contrast)\n"
3604 "'guided filter' only affects global contrast and tries to preserve local contrast\n"
3605 "'averaged guided filter' is a geometric mean of 'no' and 'guided filter' methods\n"
3606 "'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"
3607 "'averaged eigf' is a geometric mean of 'no' and 'exposure-independent guided filter' methods"));
3611 gtk_widget_set_tooltip_text(
g->iterations, _(
"number of passes of guided filter to apply\n"
3612 "helps diffusing the edges of the filter at the expense of speed"));
3617 gtk_widget_set_tooltip_text(
g->blending, _(
"diameter of the blur in percent of the largest image size\n"
3618 "warning: big values of this parameter can make the darkroom\n"
3619 "preview much slower if denoise profiled is used."));
3623 gtk_widget_set_tooltip_text(
g->feathering, _(
"precision of the feathering:\n"
3624 "higher values force the mask to follow edges more closely\n"
3625 "but may void the effect of the smoothing\n"
3626 "lower values give smoother gradients and better smoothing\n"
3627 "but may lead to inaccurate edges taping and halos"));
3631 gtk_widget_set_tooltip_text(
g->quantization, _(
"0 disables the quantization.\n"
3632 "higher values posterize the luminance mask to help the guiding\n"
3633 "produce piece-wise smooth areas when using high feathering values"));
3638 const int active_page =
dt_conf_get_int(
"plugins/darkroom/toneequal/gui_page");
3639 gtk_widget_show(gtk_notebook_get_nth_page(
g->notebook, active_page));
3640 gtk_notebook_set_current_page(
g->notebook, active_page);
3672 dt_conf_set_int(
"plugins/darkroom/toneequal/gui_page", gtk_notebook_get_current_page (
g->notebook));
3681 preview_entry =
g->thumb_preview_entry;
3682 g->thumb_preview_entry = NULL;
3685 g->thumb_preview_buf_width = 0;
3686 g->thumb_preview_buf_height = 0;
3687 g->luminance_valid =
FALSE;
3691 if(
g->desc) pango_font_description_free(
g->desc);
3692 if(
g->layout) g_object_unref(
g->layout);
3693 if(
g->cr) cairo_destroy(
g->cr);
3694 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 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)
void dt_dev_pixelpipe_cache_ref_count_entry(gboolean lock, dt_pixel_cache_entry_t *cache_entry)
Increase/Decrease the reference count on the cache line as to prevent LRU item removal....
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.
int dt_dev_pixelpipe_cache_remove(const gboolean force, dt_pixel_cache_entry_t *cache_entry)
Arbitrarily remove the cache entry matching hash. Entries having a reference count > 0 (inter-thread ...
size_t dt_pixel_cache_entry_get_size(dt_pixel_cache_entry_t *entry)
Peek the size (in bytes) reserved for the host buffer of a cache 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.
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_iop_buffer_dsc_t dsc_in
struct dt_iop_module_t *void * data
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)