62#define DT_SPLITTONING_RGB_POINT_COUNT 2
63#define DT_SPLITTONING_RGB_ROW_COUNT 3
64#define DT_SPLITTONING_RGB_COMPLETE_COUNT 3
65#define DT_SPLITTONING_RGB_PREVIEW_HEIGHT 60
148 "plugins/darkroom/splittoningrgb/dark_mixer_mode",
149 "plugins/darkroom/splittoningrgb/bright_mixer_mode"
161 return _(
"split-toning");
166 return _(
"split toning|split tone RGB");
172 _(
"blend two CAT16 plus RGB mixer corrections across brightness keyframes"),
173 _(
"creative or corrective"), _(
"linear, RGB, scene-referred"),
174 _(
"linear, RGB"), _(
"linear, RGB, scene-referred"));
212 if(temperature > 4000.f)
221 for(
int col = 0; col < 3; col++)
223 rows[0][col] =
p->red[
point][col];
224 rows[1][col] =
p->green[
point][col];
225 rows[2][col] =
p->blue[
point][col];
233 for(
int col = 0; col < 3; col++)
235 p->red[
point][col] =
M[0][col];
236 p->green[
point][col] =
M[1][col];
237 p->blue[
point][col] =
M[2][col];
249 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(
g->point[
point].normalize[
row]),
p->normalize[
point][
row]);
256 gtk_stack_set_visible_child_name(GTK_STACK(
g->point[
point].mixer_stack),
261 gtk_widget_queue_resize(
g->point[
point].mixer_stack);
262 gtk_widget_queue_resize(
g->point[
point].page);
263 gtk_widget_queue_resize(GTK_WIDGET(
g->tabs));
270 for(
int col = 0; col < 3; col++)
271 CAT[
row][col] =
row == col ? 1.f : 0.f;
286 for(
int col = 0; col < 3; col++)
296 chroma_adapt_pixel(dt_load_simd_aligned(XYZ_in), dt_load_simd_aligned(illuminant_LMS),
307 float rows[3][3] = { { 0.f } };
309 float mixer[3][3] = { { 0.f } };
310 float CAT[3][3] = { { 0.f } };
311 float combined[3][3] = { { 0.f } };
321 for(
int col = 0; col < 3; col++)
322 point_matrix[col][
row] = combined[
row][col];
344 for(
int col = 0; col < 3; col++)
345 interpolated[
row][col] = from[
row][col] + alpha * (to[
row][col] - from[
row][col]);
347 interpolated[0][3] = 0.f;
348 interpolated[1][3] = 0.f;
349 interpolated[2][3] = 0.f;
368 dt_colormatrix_t identity = { { 1.f, 0.f, 0.f, 0.f }, { 0.f, 1.f, 0.f, 0.f }, { 0.f, 0.f, 1.f, 0.f } };
369 const float segment = fmaxf(
d->bright_luminance -
d->dark_luminance,
NORM_MIN);
371 if(luminance <= d->dark_luminance)
373 const float alpha = CLAMP(1.f - (
d->dark_luminance - fmaxf(
luminance, 0.f)) / segment, 0.f, 1.f);
380 const float alpha = CLAMP(1.f - (
luminance -
d->bright_luminance) / segment, 0.f, 1.f);
385 const float alpha = CLAMP((
luminance -
d->dark_luminance) / segment, 0.f, 1.f);
395 = {
g->point[
point].simple_theta,
g->point[
point].simple_psi,
g->point[
point].simple_stretch_1,
396 g->point[
point].simple_stretch_2,
g->point[
point].simple_coupling_1,
397 g->point[
point].simple_coupling_2 };
398 float rows[3][3] = { { 0.f } };
400 float M[3][3] = { { 0.f } };
401 float roundtrip[3][3] = { { 0.f } };
428 = {
g->point[
point].primaries_achromatic_hue,
g->point[
point].primaries_achromatic_purity,
429 g->point[
point].primaries_red_hue,
g->point[
point].primaries_red_purity,
430 g->point[
point].primaries_green_hue,
g->point[
point].primaries_green_purity,
431 g->point[
point].primaries_blue_hue,
g->point[
point].primaries_blue_purity,
432 g->point[
point].primaries_gain };
433 float rows[3][3] = { { 0.f } };
435 float M[3][3] = { { 0.f } };
436 float roundtrip[3][3] = { { 0.f } };
471 widgets[0] =
g->point[
point].white_preserving_red_rotation;
472 widgets[1] =
g->point[
point].white_preserving_red_saturation;
473 widgets[2] =
g->point[
point].white_preserving_green_rotation;
474 widgets[3] =
g->point[
point].white_preserving_green_saturation;
475 widgets[4] =
g->point[
point].white_preserving_blue_rotation;
476 widgets[5] =
g->point[
point].white_preserving_blue_saturation;
478 for(
int widget = 0; widget < 6; widget++)
497 float rows[3][3] = { { 0.f } };
499 float M[3][3] = { { 0.f } };
500 float roundtrip[3][3] = { { 0.f } };
513 &white_preserving, roundtrip))
529 if(
g &&
g->preview) gtk_widget_queue_draw(
g->preview);
552 if(
g->preview_surface)
554 cairo_surface_destroy(
g->preview_surface);
555 g->preview_surface = NULL;
557 g->preview_width = 0;
558 g->preview_height = 0;
584 const float basis[3][3] = { { 1.f, 0.f, 0.f }, { 0.f, 1.f, 0.f }, { 0.f, 0.f, 1.f } };
588 = {
g->point[
point].simple_theta,
g->point[
point].simple_psi,
g->point[
point].simple_stretch_1,
589 g->point[
point].simple_stretch_2,
g->point[
point].simple_coupling_1,
g->point[
point].simple_coupling_2 };
591 = {
g->point[
point].primaries_achromatic_hue,
g->point[
point].primaries_achromatic_purity,
592 g->point[
point].primaries_red_hue,
g->point[
point].primaries_red_purity,
593 g->point[
point].primaries_green_hue,
g->point[
point].primaries_green_purity,
594 g->point[
point].primaries_blue_hue,
g->point[
point].primaries_blue_purity,
595 g->point[
point].primaries_gain };
617 GtkWidget *white_preserving_widgets[6] = { NULL };
625 &white_preserving, white_preserving_widgets);
635 gboolean complete_widget =
FALSE;
639 complete_widget = complete_widget || changed ==
g->point[
point].normalize[
row];
640 for(
int col = 0; col < 3; col++) complete_widget = complete_widget || changed ==
g->point[
point].complete[
row][col];
645 float simple_error = INFINITY;
646 float primaries_error = INFINITY;
647 float white_preserving_error = INFINITY;
650 GtkWidget *white_preserving_widgets[6] = { NULL };
663 dt_control_log(_(
"simple mixer mode requires normalized rows with non-zero sums."));
672 dt_control_log(_(
"primaries mixer mode requires a non-singular 3x3 matrix with non-zero affine sums."));
675 && !white_preserving_ok)
682 dt_control_log(_(
"white-preserving mixer mode requires a matrix that leaves white unchanged."));
695 const int point = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(changed),
"split-point"));
698 if(
g->preview_surface)
700 cairo_surface_destroy(
g->preview_surface);
701 g->preview_surface = NULL;
703 g->preview_width = 0;
704 g->preview_height = 0;
730 cairo_t *cr = cairo_create(surface);
748 for(
int i = 0;
i < 3;
i++)
state.rgb_to_xyz_transposed[
i][
i] = 1.f;
753 if(
state.bright_luminance <=
state.dark_luminance)
754 state.bright_luminance =
state.dark_luminance + fmaxf(
state.dark_luminance * 0.01f, 1e-4f);
757 const float grey =
width > 1 ? (float)
x / (
float)(
width - 1) : 0.f;
763 const dt_aligned_pixel_simd_t input_v = dt_load_simd_aligned(input);
766 _get_split_matrix(fmaxf(
XYZ[1], 0.f), &
state, combined_matrix);
769 dt_colormatrix_row_to_simd(combined_matrix, 0),
770 dt_colormatrix_row_to_simd(combined_matrix, 1),
771 dt_colormatrix_row_to_simd(combined_matrix, 2)));
774 cairo_set_source_rgb(cr, display[0], display[1], display[2]);
775 cairo_rectangle(cr,
x, 0, 1,
height);
780 cairo_surface_mark_dirty(surface);
787 GtkAllocation allocation;
789 gtk_widget_get_allocation(widget, &allocation);
790 if(allocation.width <= 0 || allocation.height <= 0)
return TRUE;
792 if(
IS_NULL_PTR(
g->preview_surface) ||
g->preview_width != allocation.width ||
g->preview_height != allocation.height)
794 if(
g->preview_surface) cairo_surface_destroy(
g->preview_surface);
797 g->preview_width = allocation.width;
798 g->preview_height = allocation.height;
802 cairo_set_source_surface(cr,
g->preview_surface, 0., 0.);
815 const int point = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(widget),
"split-point"));
816 gboolean changed_complete =
FALSE;
818 if(widget ==
g->point[
point].ev)
820 else if(widget ==
g->point[
point].temperature)
825 if(widget ==
g->point[
point].normalize[
row])
827 p->normalize[
point][
row] = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget));
828 changed_complete =
TRUE;
831 for(
int col = 0; col < 3; col++)
833 if(widget ==
g->point[
point].complete[
row][col])
837 changed_complete =
TRUE;
853 const int point = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(widget),
"split-point"));
860 float error = INFINITY;
863 dt_control_log(_(
"simple mixer mode requires normalized rows with non-zero sums."));
874 float error = INFINITY;
877 dt_control_log(_(
"primaries mixer mode requires a non-singular 3x3 matrix with non-zero affine sums."));
888 float error = INFINITY;
891 dt_control_log(_(
"white-preserving mixer mode requires a matrix that leaves white unchanged."));
918 const int point = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(widget),
"split-point"));
920 = {
g->point[
point].simple_theta,
g->point[
point].simple_psi,
g->point[
point].simple_stretch_1,
921 g->point[
point].simple_stretch_2,
g->point[
point].simple_coupling_1,
922 g->point[
point].simple_coupling_2 };
924 float M[3][3] = { { 0.f } };
948 const int point = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(widget),
"split-point"));
950 = {
g->point[
point].primaries_achromatic_hue,
g->point[
point].primaries_achromatic_purity,
951 g->point[
point].primaries_red_hue,
g->point[
point].primaries_red_purity,
952 g->point[
point].primaries_green_hue,
g->point[
point].primaries_green_purity,
953 g->point[
point].primaries_blue_hue,
g->point[
point].primaries_blue_purity,
954 g->point[
point].primaries_gain };
956 float M[3][3] = { { 0.f } };
961 dt_control_log(_(
"primaries mixer mode requires a non-singular 3x3 matrix with non-zero affine sums."));
983 const int point = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(widget),
"split-point"));
986 float M[3][3] = { { 0.f } };
992 &white_preserving,
M))
997 dt_control_log(_(
"white-preserving mixer mode requires non-degenerate primaries."));
1034 for(
int i = 0;
i < 3;
i++)
d->rgb_to_xyz_transposed[
i][
i] = 1.f;
1048 if(
d->bright_luminance <=
d->dark_luminance)
1049 d->bright_luminance =
d->dark_luminance + fmaxf(
d->dark_luminance * 0.01f, 1e-4f);
1066 const void *
const restrict ivoid,
void *
const restrict
ovoid)
1069 const float *
const restrict in = (
const float *
const restrict)ivoid;
1070 float *
const restrict
out = (
float *
const restrict)
ovoid;
1075 for(
size_t k = 0;
k < pixels * 4;
k += 4)
1080 const dt_aligned_pixel_simd_t input_v = dt_load_simd_aligned(input);
1083 dt_mat3x4_mul_vec4(input_v, dt_colormatrix_row_to_simd(
d->rgb_to_xyz_transposed, 0),
1084 dt_colormatrix_row_to_simd(
d->rgb_to_xyz_transposed, 1),
1085 dt_colormatrix_row_to_simd(
d->rgb_to_xyz_transposed, 2)));
1087 _get_split_matrix(fmaxf(
XYZ[1], 0.f),
d, combined_matrix);
1090 dt_colormatrix_row_to_simd(combined_matrix, 0),
1091 dt_colormatrix_row_to_simd(combined_matrix, 1),
1092 dt_colormatrix_row_to_simd(combined_matrix, 2)));
1093 out[
k + 3] = input[3];
1102 cl_mem dev_in, cl_mem dev_out)
1109 const int devid = pipe->
devid;
1114 cl_mem rgb_to_xyz_cl = NULL;
1115 cl_mem dark_matrix_cl = NULL;
1116 cl_mem bright_matrix_cl = NULL;
1137 if(err != CL_SUCCESS)
goto error;
1154 const int program = 32;
1188 const dt_iop_module_t *
const sampled_module = piece && piece->module ? piece->module : self;
1201 if(
point < 0)
return;
1206 work_profile->unbounded_coeffs_in,
1232 float simple_error = INFINITY;
1233 float primaries_error = INFINITY;
1234 float white_preserving_error = INFINITY;
1256 if(
g->preview_surface)
1258 cairo_surface_destroy(
g->preview_surface);
1259 g->preview_surface = NULL;
1261 g->preview_width = 0;
1262 g->preview_height = 0;
1268 g_object_set_data(G_OBJECT(widget),
"split-point", GINT_TO_POINTER(
point));
1274 static const char *
const row_label[3] = { N_(
"output red"), N_(
"output green"), N_(
"output blue") };
1275 static const char *
const input_label[3] = { N_(
"input R"), N_(
"input G"), N_(
"input B") };
1282 for(
int col = 0; col < 3; col++)
1293 g->point[
point].normalize[
row] = gtk_check_button_new_with_label(_(
"normalize"));
1303 g->point[
point].simple_theta
1309 g->point[
point].simple_psi
1315 g->point[
point].simple_stretch_1
1319 g->point[
point].simple_stretch_2
1323 g->point[
point].simple_coupling_2
1329 g->point[
point].simple_coupling_1
1334 g->point[
point].simple_theta,
1336 g->point[
point].simple_psi,
1337 g->point[
point].simple_stretch_1,
1338 g->point[
point].simple_stretch_2,
1340 g->point[
point].simple_coupling_2,
1341 g->point[
point].simple_coupling_1,
1344 for(
size_t i = 0;
i < G_N_ELEMENTS(widgets);
i++)
1346 if(
i != 1 &&
i != 5)
1358 g->point[
point].primaries_achromatic_hue
1364 g->point[
point].primaries_achromatic_purity
1368 g->point[
point].primaries_red_hue
1374 g->point[
point].primaries_red_purity
1378 g->point[
point].primaries_green_hue
1384 g->point[
point].primaries_green_purity
1388 g->point[
point].primaries_blue_hue
1394 g->point[
point].primaries_blue_purity
1398 g->point[
point].primaries_gain
1404 g->point[
point].primaries_achromatic_hue,
1405 g->point[
point].primaries_achromatic_purity,
1407 g->point[
point].primaries_red_hue,
1408 g->point[
point].primaries_red_purity,
1410 g->point[
point].primaries_green_hue,
1411 g->point[
point].primaries_green_purity,
1413 g->point[
point].primaries_blue_hue,
1414 g->point[
point].primaries_blue_purity,
1416 g->point[
point].primaries_gain,
1419 for(
size_t i = 0;
i < G_N_ELEMENTS(widgets);
i++)
1433#define SPLITTONING_WHITE_PRESERVING_PRIMARY(var, rotation_label, saturation_label) \
1434 g->point[point].white_preserving_##var##_rotation \
1435 = dt_bauhaus_slider_new_with_range(dt_bauhaus_get_global(), DT_GUI_MODULE(self), -1.f, 1.f, 0, 0.f, 3); \
1436 dt_bauhaus_widget_set_label(g->point[point].white_preserving_##var##_rotation, rotation_label); \
1437 dt_bauhaus_slider_set_factor(g->point[point].white_preserving_##var##_rotation, 90.f); \
1438 dt_bauhaus_slider_set_format(g->point[point].white_preserving_##var##_rotation, "\302\260"); \
1441 g->point[point].white_preserving_##var##_saturation \
1442 = dt_bauhaus_slider_new_with_range(dt_bauhaus_get_global(), DT_GUI_MODULE(self), -4.f, 4.f, 0, 0.f, 3); \
1443 dt_bauhaus_slider_set_soft_range(g->point[point].white_preserving_##var##_saturation, -1.f, 1.f); \
1444 dt_bauhaus_widget_set_label(g->point[point].white_preserving_##var##_saturation, saturation_label); \
1446 dt_bauhaus_slider_set_format(g->point[point].white_preserving_##var##_saturation, "%");
1452#undef SPLITTONING_WHITE_PRESERVING_PRIMARY
1456 g->point[
point].white_preserving_red_rotation,
1457 g->point[
point].white_preserving_red_saturation,
1459 g->point[
point].white_preserving_green_rotation,
1460 g->point[
point].white_preserving_green_saturation,
1462 g->point[
point].white_preserving_blue_rotation,
1463 g->point[
point].white_preserving_blue_saturation,
1466 for(
size_t i = 0;
i < G_N_ELEMENTS(widgets);
i++)
1481 g->preview_surface = NULL;
1482 g->preview_width = 0;
1483 g->preview_height = 0;
1485 g->preview = GTK_WIDGET(gtk_drawing_area_new());
1487 g_signal_connect(G_OBJECT(
g->preview),
"draw", G_CALLBACK(
_preview_draw), self);
1497 g->point[
point].page = page;
1502 point == 0 ? -16.f : 0.f, 2));
1505 gtk_widget_set_tooltip_text(
g->point[
point].ev, _(
"sample average luminance from an area to set this keyframe"));
1510 g->point[
point].temperature
1516 g_signal_connect(G_OBJECT(
g->point[
point].temperature),
"value-changed", G_CALLBACK(
_general_callback), self);
1517 gtk_box_pack_start(GTK_BOX(page),
g->point[
point].temperature,
FALSE,
FALSE, 0);
1527 gtk_box_pack_start(GTK_BOX(page),
g->point[
point].mixer_mode,
FALSE,
FALSE, 0);
1529 g->point[
point].mixer_stack = gtk_stack_new();
1530 gtk_stack_set_hhomogeneous(GTK_STACK(
g->point[
point].mixer_stack),
FALSE);
1531 gtk_stack_set_vhomogeneous(GTK_STACK(
g->point[
point].mixer_stack),
FALSE);
1532 gtk_stack_set_transition_type(GTK_STACK(
g->point[
point].mixer_stack), GTK_STACK_TRANSITION_TYPE_NONE);
1533 gtk_box_pack_start(GTK_BOX(page),
g->point[
point].mixer_stack,
FALSE,
FALSE, 0);
1539 gtk_stack_add_named(GTK_STACK(
g->point[
point].mixer_stack), complete,
"complete");
1540 gtk_stack_add_named(GTK_STACK(
g->point[
point].mixer_stack), simple,
"simple");
1541 gtk_stack_add_named(GTK_STACK(
g->point[
point].mixer_stack), primaries,
"primaries");
1542 gtk_stack_add_named(GTK_STACK(
g->point[
point].mixer_stack), white_preserving,
"white-preserving");
1562 if(
g->preview_surface) cairo_surface_destroy(
g->preview_surface);
1571 memset(
d, 0,
sizeof(*
d));
1587 module->default_enabled = FALSE;
static void error(char *msg)
uint32_t container(dt_lib_module_t *self)
void dt_bauhaus_slider_set_soft_range(GtkWidget *widget, float soft_min, float soft_max)
float dt_bauhaus_slider_get(GtkWidget *widget)
int dt_bauhaus_combobox_get(GtkWidget *widget)
void dt_bauhaus_slider_set(GtkWidget *widget, float pos)
void dt_bauhaus_combobox_set(GtkWidget *widget, const int 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)
GtkWidget * dt_bauhaus_combobox_new(dt_bauhaus_t *bh, dt_gui_module_t *self)
void dt_bauhaus_slider_set_format(GtkWidget *widget, const char *format)
void dt_bauhaus_combobox_add(GtkWidget *widget, const char *text)
void dt_bauhaus_slider_set_factor(GtkWidget *widget, float factor)
static __DT_CLONE_TARGETS__ void normalize(float *const buffer, const size_t width, const size_t height, const float norm)
void dt_iop_channelmixer_shared_paint_row_sliders(dt_adaptation_t adaptation, const dt_iop_order_iccprofile_info_t *const work_profile, const dt_iop_order_iccprofile_info_t *const display_profile, const float r, const float g, const float b, const gboolean normalize, const float row[3], GtkWidget *const widgets[3])
void dt_iop_channelmixer_shared_paint_primaries_sliders(const dt_adaptation_t adaptation, const dt_iop_order_iccprofile_info_t *const work_profile, const dt_iop_order_iccprofile_info_t *const display_profile, const dt_iop_channelmixer_shared_primaries_basis_t basis, const dt_iop_channelmixer_shared_primaries_params_t *const primaries, GtkWidget *const widgets[9])
void dt_iop_channelmixer_shared_paint_white_preserving_sliders(const dt_adaptation_t adaptation, const dt_iop_order_iccprofile_info_t *const work_profile, const dt_iop_order_iccprofile_info_t *const display_profile, const dt_iop_channelmixer_shared_primaries_basis_t basis, const dt_iop_channelmixer_shared_white_preserving_params_t *const white_preserving, GtkWidget *const widgets[6])
void dt_iop_channelmixer_shared_white_preserving_from_sliders(GtkWidget *const widgets[6], dt_iop_channelmixer_shared_white_preserving_params_t *white_preserving)
void dt_iop_channelmixer_shared_simple_from_sliders(GtkWidget *const widgets[6], dt_iop_channelmixer_shared_simple_params_t *simple)
void dt_iop_channelmixer_shared_paint_temperature_slider(GtkWidget *const widget, const float temperature_min, const float temperature_max)
void dt_iop_channelmixer_shared_white_preserving_to_sliders(const dt_iop_channelmixer_shared_white_preserving_params_t *const white_preserving, GtkWidget *const widgets[6])
void dt_iop_channelmixer_shared_primaries_to_sliders(const dt_iop_channelmixer_shared_primaries_params_t *const primaries, GtkWidget *const widgets[9])
void dt_iop_channelmixer_shared_simple_to_matrix(const dt_iop_channelmixer_shared_simple_params_t *const simple, float M[3][3])
gboolean dt_iop_channelmixer_shared_white_preserving_from_matrix(const dt_iop_channelmixer_shared_primaries_basis_t basis, const float M[3][3], dt_iop_channelmixer_shared_white_preserving_params_t *white_preserving)
Read a white-preserving primaries state back from a mixer matrix.
gboolean dt_iop_channelmixer_shared_white_preserving_to_matrix(const dt_iop_channelmixer_shared_primaries_basis_t basis, const dt_iop_channelmixer_shared_white_preserving_params_t *const white_preserving, float M[3][3])
Build the mixer matrix of a white-preserving primaries state.
void dt_iop_channelmixer_shared_simple_to_sliders(const dt_iop_channelmixer_shared_simple_params_t *const simple, GtkWidget *const widgets[6])
void dt_iop_channelmixer_shared_work_rgb_to_display(const dt_aligned_pixel_t work_rgb, const dt_iop_order_iccprofile_info_t *const work_profile, const dt_iop_order_iccprofile_info_t *const display_profile, dt_aligned_pixel_t display_rgb)
void dt_iop_channelmixer_shared_paint_simple_sliders(const dt_adaptation_t adaptation, const dt_iop_order_iccprofile_info_t *const work_profile, const dt_iop_order_iccprofile_info_t *const display_profile, const dt_iop_channelmixer_shared_simple_params_t *const simple, GtkWidget *const widgets[6])
gboolean dt_iop_channelmixer_shared_primaries_from_matrix(const dt_iop_channelmixer_shared_primaries_basis_t basis, const float M[3][3], dt_iop_channelmixer_shared_primaries_params_t *primaries)
void dt_iop_channelmixer_shared_simple_from_matrix(const float M[3][3], dt_iop_channelmixer_shared_simple_params_t *simple)
void dt_iop_channelmixer_shared_mul3x3(const float A[3][3], const float B[3][3], float C[3][3])
gboolean dt_iop_channelmixer_shared_rows_are_normalized(const gboolean normalize[3])
float dt_iop_channelmixer_shared_roundtrip_error(const float M[3][3], const float roundtrip[3][3])
gboolean dt_iop_channelmixer_shared_get_matrix(const float rows[3][3], const gboolean normalize[3], const gboolean force_normalize, float M[3][3])
gboolean dt_iop_channelmixer_shared_primaries_to_matrix(const dt_iop_channelmixer_shared_primaries_basis_t basis, const dt_iop_channelmixer_shared_primaries_params_t *primaries, float M[3][3])
float dt_iop_channelmixer_shared_roundtrip_error_relative(const float M[3][3], const float roundtrip[3][3])
Roundtrip error, measured against the magnitude of the matrix itself.
void dt_iop_channelmixer_shared_primaries_from_sliders(GtkWidget *const widgets[9], dt_iop_channelmixer_shared_primaries_params_t *primaries)
#define DT_IOP_CHANNELMIXER_SHARED_SIMPLE_EPS
@ DT_IOP_CHANNELMIXER_SHARED_PRIMARIES_BASIS_RGB
static float dt_camera_rgb_luminance(const float4 rgb)
GtkWidget * dt_color_picker_new(dt_iop_module_t *module, dt_iop_color_picker_kind_t kind, GtkWidget *w)
struct _GtkWidget GtkWidget
GtkWidget, opaque, spelled exactly as GTK spells it.
dt_apply_transposed_color_matrix(XYZ, xyz_to_srgb_matrix_transposed, sRGB)
static dt_aligned_pixel_t XYZ
const dt_colormatrix_t dt_aligned_pixel_t out
dt_store_simd_aligned(out, dt_mat3x4_mul_vec4(vin, dt_colormatrix_row_to_simd(matrix, 0), dt_colormatrix_row_to_simd(matrix, 1), dt_colormatrix_row_to_simd(matrix, 2)))
const dt_colormatrix_t matrix
static const dt_colormatrix_t M
int dt_conf_key_exists(const char *key)
Does key have a value?
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,...)
struct dt_bauhaus_t * dt_bauhaus_get_global(void)
#define dt_dev_add_history_item(dev, module, enable, redraw)
dt_iop_order_iccprofile_info_t * dt_ioppr_get_pipe_output_profile_info(const struct dt_dev_pixelpipe_t *pipe)
dt_iop_order_iccprofile_info_t * dt_ioppr_get_pipe_work_profile_info(const struct dt_dev_pixelpipe_t *pipe)
dt_iop_order_iccprofile_info_t * dt_ioppr_get_pipe_current_profile_info(dt_iop_module_t *module, const struct dt_dev_pixelpipe_t *pipe)
__DT_CLONE_TARGETS__ dt_iop_order_iccprofile_info_t * dt_ioppr_get_iop_work_profile_info(struct dt_iop_module_t *module, GList *iop_list)
static void illuminant_xy_to_XYZ(const float x, const float y, dt_aligned_pixel_t XYZ)
static void CCT_to_xy_daylight(const float t, float *x, float *y)
static void CCT_to_xy_blackbody(const float t, float *x, float *y)
void dt_iop_default_init(dt_iop_module_t *module)
const char ** dt_iop_set_description(dt_iop_module_t *module, const char *main_text, const char *purpose, const char *input, const char *process, const char *output)
@ IOP_FLAGS_INCLUDE_IN_STYLES
@ IOP_FLAGS_SUPPORTS_BLENDING
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)
GtkWidget * dt_ui_section_label_new(const gchar *str)
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 luminance
float *const restrict const size_t k
#define IS_NULL_PTR(p)
C is way too permissive with !=, == and if(var) checks, which can mean too many things depending on w...
float DT_ALIGNED_ARRAY dt_colormatrix_t[4][4]
static void transpose_3xSSE(const dt_colormatrix_t input, dt_colormatrix_t output)
static void pack_3xSSE_to_3x4(const dt_colormatrix_t input, float output[12])
#define dt_free_align(ptr)
Release memory from dt_alloc_align() and set ptr to NULL.
static void * dt_calloc_align(size_t size)
dt_alloc_align() followed by a zero fill.
#define dt_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...
int dt_opencl_enqueue_kernel_2d(const int dev, const int kernel, const size_t *sizes)
int dt_opencl_create_kernel(const int prog, const char *name)
void * dt_opencl_copy_host_to_device_constant(const int devid, const size_t size, void *host)
void dt_opencl_free_kernel(const int kernel)
int dt_opencl_set_kernel_arg(const int dev, const int kernel, const int num, const size_t size, const void *arg)
void dt_opencl_release_mem_object(cl_mem mem)
#define dt_omploop_sfence()
#define __OMP_PARALLEL_FOR__(...)
static cairo_surface_t * dt_cairo_image_surface_create(cairo_format_t format, int width, int height)
static int dt_cairo_image_surface_get_width(cairo_surface_t *surface)
static int dt_cairo_image_surface_get_height(cairo_surface_t *surface)
#define DT_DEBUG_CONTROL_SIGNAL_DISCONNECT(ctlsig, cb, user_data)
struct dt_control_signal_t * dt_control_signal_get_global(void)
@ DT_SIGNAL_DEVELOP_PREVIEW_PIPE_FINISHED
This signal is raised when develop preview pipe process is finished no param, no returned value.
@ 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)
DT_ALIGNED_PIXEL float dt_aligned_pixel_t[4]
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...
void init(dt_iop_module_t *module)
__DT_CLONE_TARGETS__ int process(struct dt_iop_module_t *self, const dt_dev_pixelpipe_t *pipe, const dt_dev_pixelpipe_iop_t *piece, const void *const restrict ivoid, void *const restrict ovoid)
static void _set_point_complete_widgets(dt_iop_splittoning_rgb_gui_data_t *g, const dt_iop_splittoning_rgb_params_t *p, const int point)
static void _build_white_preserving_ui(dt_iop_module_t *self, dt_iop_splittoning_rgb_gui_data_t *g, const int point, GtkWidget *container)
const char ** description(struct dt_iop_module_t *self)
static const char *const _mode_conf[2]
#define DT_SPLITTONING_RGB_PREVIEW_HEIGHT
static void _build_cat16_rgb_matrix(const dt_iop_order_iccprofile_info_t *work_profile, const float temperature, float CAT[3][3])
static void _set_point_rows(dt_iop_splittoning_rgb_params_t *p, const int point, const float M[3][3])
static void _queue_preview_redraw(dt_iop_module_t *self)
static gboolean _sync_simple_from_params(dt_iop_module_t *self, const int point, float *error)
static void _simple_slider_callback(GtkWidget *widget, gpointer user_data)
void commit_params(struct dt_iop_module_t *self, dt_iop_params_t *params, dt_dev_pixelpipe_t *pipe, dt_dev_pixelpipe_iop_t *piece)
static void _primaries_slider_callback(GtkWidget *widget, gpointer user_data)
static void _pipe_finished_callback(gpointer instance, gpointer user_data)
Refresh cached GUI colors after the darkroom pipe has converged.
#define SPLITTONING_WHITE_PRESERVING_PRIMARY(var, rotation_label, saturation_label)
static void _mixer_mode_callback(GtkWidget *widget, gpointer user_data)
static void _general_callback(GtkWidget *widget, gpointer user_data)
void init_pipe(struct dt_iop_module_t *self, dt_dev_pixelpipe_t *pipe, dt_dev_pixelpipe_iop_t *piece)
dt_iop_splittoning_rgb_point_t
@ DT_SPLITTONING_RGB_POINT_BRIGHT
@ DT_SPLITTONING_RGB_POINT_DARK
static void _update_point_slider_colors(dt_iop_module_t *self, int point)
Repaint every slider background for one split-toning keyframe.
static void _commit_gui_change(dt_iop_module_t *self, GtkWidget *changed)
static void _white_preserving_slider_callback(GtkWidget *widget, gpointer user_data)
static void _tag_widget(GtkWidget *widget, const int point)
void gui_update(struct dt_iop_module_t *self)
Refresh GUI controls from current params and configuration.
#define DT_SPLITTONING_RGB_ROW_COUNT
dt_iop_splittoning_rgb_mixer_mode_t
@ DT_SPLITTONING_RGB_MIXER_PRIMARIES
@ DT_SPLITTONING_RGB_MIXER_WHITE_PRESERVING
@ DT_SPLITTONING_RGB_MIXER_COMPLETE
@ DT_SPLITTONING_RGB_MIXER_SIMPLE
static gboolean _sync_primaries_from_params(dt_iop_module_t *self, const int point, float *error)
void gui_init(struct dt_iop_module_t *self)
static float _ev_to_grey(const float ev)
static float _ev_to_luminance(const float ev)
static int _build_point_transform(const dt_iop_splittoning_rgb_params_t *p, const int point, const dt_iop_order_iccprofile_info_t *work_profile, dt_colormatrix_t point_matrix)
#define DT_SPLITTONING_RGB_COMPLETE_COUNT
void cleanup_global(dt_iop_module_so_t *module)
static void _render_preview_surface(dt_iop_module_t *self, cairo_surface_t *surface)
Render the cached split-toning preview as a simple keyed gradient.
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 _preview_draw(GtkWidget *widget, cairo_t *cr, gpointer user_data)
void gui_cleanup(struct dt_iop_module_t *self)
static void _temperature_to_xy(const float temperature, float *x, float *y)
static gboolean _white_preserving_widgets(const dt_iop_splittoning_rgb_gui_data_t *const g, const int point, GtkWidget *widgets[6])
Collect the six white-preserving widgets of one keyframe.
static void _get_point_rows(const dt_iop_splittoning_rgb_params_t *p, const int point, float rows[3][3], gboolean normalize[3])
static void _build_complete_ui(dt_iop_module_t *self, dt_iop_splittoning_rgb_gui_data_t *g, const int point, GtkWidget *container)
void cleanup_pipe(struct dt_iop_module_t *self, dt_dev_pixelpipe_t *pipe, dt_dev_pixelpipe_iop_t *piece)
static gboolean _sync_white_preserving_from_params(dt_iop_module_t *self, const int point, float *error)
Synchronize the white-preserving GUI of one keyframe from its effective mixer matrix.
void init_global(dt_iop_module_so_t *module)
static void _set_point_mixer_mode(dt_iop_splittoning_rgb_gui_data_t *g, const int point, const dt_iop_splittoning_rgb_mixer_mode_t mode)
int process_cl(struct dt_iop_module_t *self, const dt_dev_pixelpipe_t *pipe, const dt_dev_pixelpipe_iop_t *piece, cl_mem dev_in, cl_mem dev_out)
static void _update_point_gui(dt_iop_module_t *self, const int point, GtkWidget *changed)
static void _build_simple_ui(dt_iop_module_t *self, dt_iop_splittoning_rgb_gui_data_t *g, const int point, GtkWidget *container)
#define DT_SPLITTONING_RGB_POINT_COUNT
void color_picker_apply(dt_iop_module_t *self, GtkWidget *picker, dt_dev_pixelpipe_t *pipe, dt_dev_pixelpipe_iop_t *piece)
Set a brightness keyframe from the average luminance of a picked area.
static const char *const _point_label[2]
static void _build_primaries_ui(dt_iop_module_t *self, dt_iop_splittoning_rgb_gui_data_t *g, const int point, GtkWidget *container)
const float uint32_t state[4]
struct dt_iop_module_t *void * data
struct dt_dev_pixelpipe_t * preview_pipe
struct dt_dev_pixelpipe_t * pipe
dt_iop_buffer_type_t datatype
White-preserving primaries model : per-primary rotation and saturation, no white freedom.
dt_iop_global_data_t * data
dt_iop_params_t * default_params
struct dt_iop_module_gui_t * gui
struct dt_develop_t * dev
dt_iop_global_data_t * global_data
dt_aligned_pixel_t picked_color_min
dt_aligned_pixel_t picked_color_max
dt_aligned_pixel_t picked_color
A profile reduced to the arithmetic the pixel loop can run: two matrices and six tone-curve LUTs,...
int nonlinearlut
Non-zero when the profile has tone curves at all; tested as a boolean everywhere, but it is really th...
int lutsize
Entry count of each of the six LUTs. Always 65536 in practice: both callers of dt_ioppr_init_profile_...
dt_colormatrix_t matrix_out_transposed
float * lut_in[3]
Per-channel encoded -> linear tone curve, lutsize entries each, sampled over [0,1]....
dt_colormatrix_t matrix_in_transposed
dt_colormatrix_t matrix_in
RGB -> XYZ (D50), row-major. matrix_in[1][*] is the luminance row. NaN in [0][0] marks the whole prof...
Region of interest passed through the pixelpipe.
dt_colormatrix_t rgb_to_xyz_transposed
float point_matrix_cl[2][12]
dt_colormatrix_t point_matrix[2]
int kernel_splittoningrgb
cairo_surface_t * preview_surface
GtkWidget * white_preserving_green_saturation
GtkWidget * simple_stretch_2
GtkWidget * primaries_achromatic_hue
GtkWidget * white_preserving_blue_saturation
GtkWidget * white_preserving_red_saturation
GtkWidget * white_preserving_green_rotation
GtkWidget * primaries_blue_hue
GtkWidget * primaries_red_hue
GtkWidget * simple_stretch_1
GtkWidget * simple_coupling_2
GtkWidget * primaries_red_purity
GtkWidget * complete[3][3]
GtkWidget * primaries_green_purity
GtkWidget * white_preserving_blue_rotation
GtkWidget * simple_coupling_1
GtkWidget * primaries_gain
GtkWidget * white_preserving_red_rotation
GtkWidget * primaries_achromatic_purity
GtkWidget * primaries_blue_purity
GtkWidget * primaries_green_hue
#define __DT_CLONE_TARGETS__
Telling the user something happened.