36#define DT_IOP_CHANNELMIXER_SHARED_INV_SQRT_3 0.5773502691896258f
40 while(angle <= -
M_PI) angle += 2.f * (float)
M_PI;
41 while(angle >
M_PI) angle -= 2.f * (float)
M_PI;
47 while(angle <= -(
float)M_PI_2) angle += (float)
M_PI;
48 while(angle > (
float)M_PI_2) angle -= (
float)
M_PI;
54 if(stretch >= 1.f)
return fminf(0.5f * (stretch + 1.f), 1.5f);
55 if(stretch >= -1.f)
return stretch;
56 return fmaxf(0.5f * (stretch - 1.f), -1.5f);
61 if(slider >= 1.f)
return 2.f * slider - 1.f;
62 if(slider >= -1.f)
return slider;
63 return 2.f * slider + 1.f;
73 const float bounded = CLAMP(slider, 0.f, 0.999f);
133 const gboolean force_normalize,
float M[3][3])
140 sum = rows[
row][0] + rows[
row][1] + rows[
row][2];
141 if(sum == 0.f)
return FALSE;
144 for(
int col = 0; col < 3; col++)
M[
row][col] = rows[
row][col] / sum;
153 for(
int col = 0; col < 3; col++)
160 for(
int col = 0; col < 3; col++)
169 static const float P[3][3]
173 float PTM[3][3] = { { 0.f } };
174 float PT[3][3] = { { 0.f } };
177 for(
int col = 0; col < 3; col++)
186 static const float P[3][3]
190 float temp[3][3] = { { 0.f } };
191 float PT[3][3] = { { 0.f } };
194 for(
int col = 0; col < 3; col++)
204 float B[3][3] = { { 0.f } };
207 const float a =
B[0][0];
208 const float b =
B[0][1];
209 const float c =
B[1][0];
210 const float d =
B[1][1];
212 const float ctheta = cosf(theta);
213 const float stheta = sinf(theta);
215 const float s00 = ctheta * a + stheta * c;
216 const float s01 = ctheta * b + stheta *
d;
217 const float s11 = -stheta * b + ctheta *
d;
218 const float diff = s00 - s11;
220 const float radius = hypotf(0.5f * diff, s01);
221 const float trace = s00 + s11;
223 simple->
theta = theta;
225 simple->
stretch_1 = 0.5f * trace + radius;
226 simple->
stretch_2 = 0.5f * trace - radius;
236 const float ctheta = cosf(simple->
theta);
237 const float stheta = sinf(simple->
theta);
238 const float cpsi = cosf(simple->
psi);
239 const float spsi = sinf(simple->
psi);
247 = { { ctheta * s00 - stheta * s01, ctheta * s01 - stheta * s11, 0.f },
248 { stheta * s00 + ctheta * s01, stheta * s01 + ctheta * s11, 0.f },
249 { coupling_0, coupling_1, 1.f } };
256 float max_error = 0.f;
258 for(
int col = 0; col < 3; col++)
259 max_error = fmaxf(max_error, fabsf(
M[
row][col] - roundtrip[
row][col]));
304 for(
int c = 0; c < 3; c++) white[c] = D50[c];
309 return vector[0] + vector[1] + vector[2];
317 for(
int c = 0; c < 3; c++) normalized[c] = vector[c] / sum;
323 uv[0] = 0.7071067811865475f * (difference[0] - difference[1]);
324 uv[1] = 0.4082482904638631f * (difference[0] + difference[1]) - 0.8164965809277261f * difference[2];
329 difference[0] = 0.7071067811865475f * uv[0] + 0.4082482904638631f * uv[1];
330 difference[1] = -0.7071067811865475f * uv[0] + 0.4082482904638631f * uv[1];
331 difference[2] = -0.8164965809277261f * uv[1];
334static void _rotate_2d(
const float vector[2],
const float angle,
float rotated[2])
336 const float cosine = cosf(angle);
337 const float sine = sinf(angle);
338 rotated[0] = cosine * vector[0] - sine * vector[1];
339 rotated[1] = sine * vector[0] + cosine * vector[1];
344 const float edge_x = second[0] - first[0];
345 const float edge_y = second[1] - first[1];
346 const float denominator = direction[0] * edge_y - direction[1] * edge_x;
349 const float t = (first[0] * edge_y - first[1] * edge_x) / denominator;
350 const float u = (first[0] * direction[1] - first[1] * direction[0]) / denominator;
351 if(
t >= 0.f && u >= 0.f && u <= 1.f)
return t;
357 float distance_to_edge = FLT_MAX;
359 for(
int i = 0;
i < 3;
i++)
361 const int next =
i == 2 ? 0 :
i + 1;
363 reference_primaries[next]);
364 if(distance < distance_to_edge) distance_to_edge = distance;
367 return distance_to_edge;
371 float white_normalized[3],
float reference_primaries[3][2])
373 const float identity[3][3] = { { 1.f, 0.f, 0.f },
376 float white[3] = { 0.f };
381 for(
int i = 0;
i < 3;
i++)
383 float difference[3] = { identity[
i][0] - white_normalized[0],
384 identity[
i][1] - white_normalized[1],
385 identity[
i][2] - white_normalized[2] };
393 const int reference_index,
const float hue,
const float purity,
394 float point_normalized[3])
396 const float *
const reference = reference_primaries[reference_index];
397 const float radius = hypotf(reference[0], reference[1]);
400 const float direction_reference[2] = { reference[0] / radius, reference[1] / radius };
401 float direction[2] = { 0.f };
402 float difference[3] = { 0.f };
404 _rotate_2d(direction_reference, hue, direction);
407 if(distance_to_edge == FLT_MAX)
return FALSE;
409 const float uv[2] = { purity * distance_to_edge * direction[0],
410 purity * distance_to_edge * direction[1] };
413 for(
int c = 0; c < 3; c++) point_normalized[c] = white_normalized[c] + difference[c];
418 const int reference_index,
const float point_normalized[3],
float *hue,
421 const float *
const reference = reference_primaries[reference_index];
422 const float reference_angle = atan2f(reference[1], reference[0]);
423 float difference[3] = { point_normalized[0] - white_normalized[0],
424 point_normalized[1] - white_normalized[1],
425 point_normalized[2] - white_normalized[2] };
426 float uv[2] = { 0.f };
430 const float radius = hypotf(uv[0], uv[1]);
438 const float direction[2] = { uv[0] / radius, uv[1] / radius };
443 *purity = radius / distance_to_edge;
451 float white_reference[3] = { 0.f };
452 float reference_primaries[3][2] = { { 0.f } };
453 float white_reference_normalized[3] = { 0.f };
454 float custom_white_normalized[3] = { 0.f };
455 float custom_primaries[3][3] = { { 0.f } };
460 const float white_reference_sum =
_affine_sum3(white_reference);
480 for(
int col = 0; col < 3; col++)
481 normalized_primaries[
row][col] = custom_primaries[col][
row];
485 const float white_gain = primaries->
gain * white_reference_sum;
487 white_gain * custom_white_normalized[1],
488 white_gain * custom_white_normalized[2],
493 for(
int col = 0; col < 3; col++)
495 M[
row][col] = column_scales[col] * custom_primaries[col][
row];
506 float white_reference[3] = { 0.f };
507 float white_reference_normalized[3] = { 0.f };
508 float reference_primaries[3][2] = { { 0.f } };
509 float custom_white[3] = { 0.f };
510 float custom_white_normalized[3] = { 0.f };
511 float custom_primary_normalized[3] = { 0.f };
514 for(
int col = 0; col < 3; col++)
515 padded[
row][col] =
M[
row][col];
519 const float white_reference_sum =
_affine_sum3(white_reference);
532 for(
int primary = 0; primary < 3; primary++)
534 const float column[3] = {
M[0][primary],
M[1][primary],
M[2][primary] };
538 float *purity = primary == 0 ? &primaries->
red_purity
543 custom_primary_normalized, hue, purity))
553 static const float P[3][3]
571 for(
int col = 0; col < 3; col++) source[
row] +=
P[
row][col] * basis[col];
577 const float max_RGB = fmaxf(fmaxf(linear_display_rgb[0], linear_display_rgb[1]), linear_display_rgb[2]);
579 for(
int c = 0; c < 3; c++) linear_display_rgb[c] = fmaxf(linear_display_rgb[c] / max_RGB, 0.f);
581 for(
int c = 0; c < 3; c++) linear_display_rgb[c] = fmaxf(linear_display_rgb[c], 0.f);
595 work_profile->unbounded_coeffs_in, work_profile->
lutsize,
601 _apply_trc(linear_display_rgb, display_rgb, display_profile->
lut_out, display_profile->unbounded_coeffs_out,
604 for(
int c = 0; c < 4; c++) display_rgb[c] = linear_display_rgb[c];
608 for(
int c = 0; c < 4; c++) display_rgb[c] = work_rgb[c];
612 for(
int c = 0; c < 3; c++) display_rgb[c] = CLAMP(display_rgb[c], 0.f, 1.f);
618 float display_rgb[3])
620 dt_aligned_pixel_t work_rgb = { module_color[0], module_color[1], module_color[2], 0.f };
630 for(
int c = 0; c < 3; c++) display_rgb[c] = display[c];
634 const float temperature_max)
638 const float temp_range = temperature_max - temperature_min;
642 const float temperature = temperature_min + stop * temp_range;
649 gtk_widget_queue_draw(widget);
655 GtkWidget *
const widget,
const float stop,
const float c,
656 const float r,
const float g,
const float b)
658 const float module_color[3]
659 = { 0.5f * (c *
r + 1.f -
r), 0.5f * (c *
g + 1.f -
g), 0.5f * (c * b + 1.f - b) };
660 float display_rgb[3] = { 0.f };
670 const float r,
const float g,
const float b,
678 const float sum =
RGB[0] +
RGB[1] +
RGB[2];
680 for(
int c = 0; c < 3; c++)
RGB[c] /= sum;
703 for(
int widget = 0; widget < 3; widget++) gtk_widget_queue_draw(widgets[widget]);
713 for(
int col = 0; col < 3; col++)
716 dot_product(padded_source,
mix, padded_color);
717 for(
int c = 0; c < 3; c++) module_color[c] = padded_color[c];
735 static const float P[3][3]
745 module_color[
row] = 0.f;
746 for(
int col = 0; col < 3; col++) module_color[
row] +=
P[
row][col] * basis[col];
768 const float slider = 2.f * stop - 1.f;
769 const float stretch_slider = 3.f * stop - 1.5f;
771 for(
int widget = 0; widget < 6; widget++)
774 float source[3] = { 0.f };
775 float M[3][3] = { { 0.f } };
776 float module_color[3] = { 0.f };
777 float display_rgb[3] = { 0.f };
783 probe_simple.
theta = slider * (float)
M_PI;
786 probe_simple.
psi = slider * (float)M_PI_2;
810 static const float P[3][3]
821 for(
int col = 0; col < 3; col++) source[
row] +=
P[
row][col] * basis[col];
829 if((widget == 0 || widget == 5)
830 && fabsf(fmaxf(fmaxf(display_rgb[0], display_rgb[1]), display_rgb[2])
831 - fminf(fminf(display_rgb[0], display_rgb[1]), display_rgb[2])) < 0.05f)
833 const float hue = widget == 0 ? probe_simple.
theta : coupling_hue;
843 for(
int widget = 0; widget < 6; widget++) gtk_widget_queue_draw(widgets[widget]);
848 if(widget_index <= 1 || widget_index == 8)
855 const int column = widget_index <= 3 ? 0 : widget_index <= 5 ? 1 : 2;
871 for(
int widget = 0; widget < 9; widget++)
874 float M[3][3] = { { 0.f } };
875 float module_color[3] = { 0.f };
876 float display_rgb[3] = { 0.f };
879 const float value = hard_min + stop * (hard_max - hard_min);
923 for(
int widget = 0; widget < 9; widget++) gtk_widget_queue_draw(widgets[widget]);
void dt_bauhaus_slider_clear_stops(GtkWidget *widget)
void dt_bauhaus_slider_set_stop(GtkWidget *widget, float stop, float r, float g, float b)
float dt_bauhaus_slider_get(GtkWidget *widget)
float dt_bauhaus_slider_get_hard_min(GtkWidget *widget)
void dt_bauhaus_slider_set(GtkWidget *widget, float pos)
float dt_bauhaus_slider_get_hard_max(GtkWidget *widget)
#define DT_BAUHAUS_SLIDER_MAX_STOPS
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_module_color_to_display(const float module_color[3], 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, float display_rgb[3])
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])
static void _shared_paint_probe_matrix(const float source[3], const float M[3][3], float module_color[3])
static void _shared_primaries_probe_color(const float M[3][3], const int widget_index, float module_color[3])
static void _affine_project_difference(const float difference[3], float uv[2])
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)
static float _affine_distance_to_edge(const float direction[2], const float reference_primaries[3][2])
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_probe_source(const dt_iop_channelmixer_shared_simple_probe_t probe, float source[3])
float dt_iop_channelmixer_shared_decode_simple_stretch(const float slider)
void dt_iop_channelmixer_shared_simple_to_matrix(const dt_iop_channelmixer_shared_simple_params_t *const simple, float M[3][3])
static gboolean _affine_point_from_polar(const float white_normalized[3], const float reference_primaries[3][2], const int reference_index, const float hue, const float purity, float point_normalized[3])
static void _mixer_from_chroma_basis(const float B[3][3], float M[3][3])
static void _mixer_to_chroma_basis(const float M[3][3], float B[3][3])
static void _paint_RGB_slider_stop(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, GtkWidget *const widget, const float stop, const float c, const float r, const float g, const float b)
float dt_iop_channelmixer_shared_encode_simple_coupling_amount(const float amount)
void dt_iop_channelmixer_shared_simple_to_sliders(const dt_iop_channelmixer_shared_simple_params_t *const simple, GtkWidget *const widgets[6])
float dt_iop_channelmixer_shared_decode_simple_coupling_amount(const float slider)
dt_iop_channelmixer_shared_primaries_basis_t dt_iop_channelmixer_shared_primaries_basis_from_adaptation(const dt_adaptation_t adaptation)
float dt_iop_channelmixer_shared_encode_simple_stretch(const float stretch)
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)
static void _shared_simple_hue_probe(const float hue, float module_color[3])
Build a stable hue cue in the fixed chroma basis for hue-like sliders.
void dt_iop_channelmixer_shared_set_matrix(float rows[3][3], const float M[3][3])
static void _normalize_linear_display_rgb(dt_aligned_pixel_t linear_display_rgb)
static float _intersect_affine_ray_segment(const float direction[2], const float first[2], const float second[2])
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)
static void _primaries_reference_white(const dt_iop_channelmixer_shared_primaries_basis_t basis, float white[3])
static void _rotate_2d(const float vector[2], const float angle, float rotated[2])
static void _affine_unproject_difference(const float uv[2], float difference[3])
#define DT_IOP_CHANNELMIXER_SHARED_INV_SQRT_3
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_wrap_pi(float angle)
static gboolean _affine_normalize(const float vector[3], float normalized[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])
void dt_iop_channelmixer_shared_primaries_from_sliders(GtkWidget *const widgets[9], dt_iop_channelmixer_shared_primaries_params_t *primaries)
static float _affine_sum3(const float vector[3])
static gboolean _affine_polar_from_point(const float white_normalized[3], const float reference_primaries[3][2], const int reference_index, const float point_normalized[3], float *hue, float *purity)
float dt_iop_channelmixer_shared_wrap_half_pi(float angle)
static gboolean _build_affine_simplex(const dt_iop_channelmixer_shared_primaries_basis_t basis, float white_normalized[3], float reference_primaries[3][2])
dt_iop_channelmixer_shared_simple_probe_t
@ DT_IOP_CHANNELMIXER_SHARED_SIMPLE_PROBE_AXIS_2
@ DT_IOP_CHANNELMIXER_SHARED_SIMPLE_PROBE_AXIS_1
@ DT_IOP_CHANNELMIXER_SHARED_SIMPLE_PROBE_ROTATION
#define DT_IOP_CHANNELMIXER_SHARED_SIMPLE_CHROMA_PROBE
#define DT_IOP_CHANNELMIXER_SHARED_SIMPLE_TAN_SCALE
#define DT_IOP_CHANNELMIXER_SHARED_SIMPLE_EPS
dt_iop_channelmixer_shared_primaries_basis_t
@ DT_IOP_CHANNELMIXER_SHARED_PRIMARIES_BASIS_CAT16
@ DT_IOP_CHANNELMIXER_SHARED_PRIMARIES_BASIS_RGB
@ DT_IOP_CHANNELMIXER_SHARED_PRIMARIES_BASIS_BRADFORD
@ DT_IOP_CHANNELMIXER_SHARED_PRIMARIES_BASIS_XYZ
static const dt_aligned_pixel_simd_t const dt_adaptation_t adaptation
static void convert_D50_to_LMS(const dt_adaptation_t adaptation, dt_aligned_pixel_t D50)
@ DT_ADAPTATION_FULL_BRADFORD
@ DT_ADAPTATION_LINEAR_BRADFORD
dt_apply_transposed_color_matrix(XYZ, xyz_to_srgb_matrix_transposed, sRGB)
static dt_aligned_pixel_t XYZ
static const float const float C
static dt_aligned_pixel_t RGB
static const dt_colormatrix_t M
static const dt_aligned_pixel_simd_t value
#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 void illuminant_CCT_to_RGB(const float t, dt_aligned_pixel_t RGB)
static float mix(const float a, const float b, const float t)
float *const restrict const size_t k
float DT_ALIGNED_ARRAY dt_colormatrix_t[4][4]
static int mat3SSEinv(dt_colormatrix_t dst, const dt_colormatrix_t src)
float dt_aligned_pixel_t[4]
struct _GtkWidget GtkWidget
dt_colormatrix_t matrix_out_transposed
dt_colormatrix_t matrix_in_transposed