33#define DT_IOP_CHANNELMIXER_SHARED_INV_SQRT_3 0.5773502691896258f
37 while(angle <= -
M_PI) angle += 2.f * (float)
M_PI;
38 while(angle >
M_PI) angle -= 2.f * (float)
M_PI;
44 while(angle <= -(
float)M_PI_2) angle += (float)
M_PI;
45 while(angle > (
float)M_PI_2) angle -= (
float)
M_PI;
51 if(stretch >= 1.f)
return fminf(0.5f * (stretch + 1.f), 1.5f);
52 if(stretch >= -1.f)
return stretch;
53 return fmaxf(0.5f * (stretch - 1.f), -1.5f);
58 if(slider >= 1.f)
return 2.f * slider - 1.f;
59 if(slider >= -1.f)
return slider;
60 return 2.f * slider + 1.f;
70 const float bounded = CLAMP(slider, 0.f, 0.999f);
153 const gboolean force_normalize,
float M[3][3])
160 sum = rows[
row][0] + rows[
row][1] + rows[
row][2];
161 if(sum == 0.f)
return FALSE;
164 for(
int col = 0; col < 3; col++)
M[
row][col] = rows[
row][col] / sum;
173 for(
int col = 0; col < 3; col++)
180 for(
int col = 0; col < 3; col++)
189 static const float P[3][3]
193 float PTM[3][3] = { { 0.f } };
194 float PT[3][3] = { { 0.f } };
197 for(
int col = 0; col < 3; col++)
206 static const float P[3][3]
210 float temp[3][3] = { { 0.f } };
211 float PT[3][3] = { { 0.f } };
214 for(
int col = 0; col < 3; col++)
224 float B[3][3] = { { 0.f } };
227 const float a =
B[0][0];
228 const float b =
B[0][1];
229 const float c =
B[1][0];
230 const float d =
B[1][1];
232 const float ctheta = cosf(theta);
233 const float stheta = sinf(theta);
235 const float s00 = ctheta * a + stheta * c;
236 const float s01 = ctheta * b + stheta *
d;
237 const float s11 = -stheta * b + ctheta *
d;
238 const float diff = s00 - s11;
240 const float radius = hypotf(0.5f * diff, s01);
241 const float trace = s00 + s11;
243 simple->
theta = theta;
245 simple->
stretch_1 = 0.5f * trace + radius;
246 simple->
stretch_2 = 0.5f * trace - radius;
256 const float ctheta = cosf(simple->
theta);
257 const float stheta = sinf(simple->
theta);
258 const float cpsi = cosf(simple->
psi);
259 const float spsi = sinf(simple->
psi);
267 = { { ctheta * s00 - stheta * s01, ctheta * s01 - stheta * s11, 0.f },
268 { stheta * s00 + ctheta * s01, stheta * s01 + ctheta * s11, 0.f },
269 { coupling_0, coupling_1, 1.f } };
278 for(
int col = 0; col < 3; col++)
301 float max_magnitude = 1.f;
303 for(
int col = 0; col < 3; col++)
304 max_magnitude = fmaxf(max_magnitude, fabsf(
M[
row][col]));
349 for(
int c = 0; c < 3; c++) white[c] = D50[c];
354 return vector[0] + vector[1] + vector[2];
362 for(
int c = 0; c < 3; c++) normalized[c] = vector[c] / sum;
368 uv[0] = 0.7071067811865475f * (difference[0] - difference[1]);
369 uv[1] = 0.4082482904638631f * (difference[0] + difference[1]) - 0.8164965809277261f * difference[2];
374 difference[0] = 0.7071067811865475f * uv[0] + 0.4082482904638631f * uv[1];
375 difference[1] = -0.7071067811865475f * uv[0] + 0.4082482904638631f * uv[1];
376 difference[2] = -0.8164965809277261f * uv[1];
379static void _rotate_2d(
const float vector[2],
const float angle,
float rotated[2])
381 const float cosine = cosf(angle);
382 const float sine = sinf(angle);
383 rotated[0] = cosine * vector[0] - sine * vector[1];
384 rotated[1] = sine * vector[0] + cosine * vector[1];
389 const float edge_x = second[0] - first[0];
390 const float edge_y = second[1] - first[1];
391 const float denominator = direction[0] * edge_y - direction[1] * edge_x;
394 const float t = (first[0] * edge_y - first[1] * edge_x) / denominator;
395 const float u = (first[0] * direction[1] - first[1] * direction[0]) / denominator;
396 if(
t >= 0.f && u >= 0.f && u <= 1.f)
return t;
402 float distance_to_edge = FLT_MAX;
404 for(
int i = 0;
i < 3;
i++)
406 const int next =
i == 2 ? 0 :
i + 1;
408 reference_primaries[next]);
409 if(distance < distance_to_edge) distance_to_edge = distance;
412 return distance_to_edge;
416 float white_normalized[3],
float reference_primaries[3][2])
418 const float identity[3][3] = { { 1.f, 0.f, 0.f },
421 float white[3] = { 0.f };
426 for(
int i = 0;
i < 3;
i++)
428 float difference[3] = { identity[
i][0] - white_normalized[0],
429 identity[
i][1] - white_normalized[1],
430 identity[
i][2] - white_normalized[2] };
438 const int reference_index,
const float hue,
const float purity,
439 float point_normalized[3])
441 const float *
const reference = reference_primaries[reference_index];
445 const float direction_reference[2] = {
reference[0] / radius,
reference[1] / radius };
446 float direction[2] = { 0.f };
447 float difference[3] = { 0.f };
449 _rotate_2d(direction_reference, hue, direction);
452 if(distance_to_edge == FLT_MAX)
return FALSE;
454 const float uv[2] = { purity * distance_to_edge * direction[0],
455 purity * distance_to_edge * direction[1] };
458 for(
int c = 0; c < 3; c++) point_normalized[c] = white_normalized[c] + difference[c];
463 const int reference_index,
const float point_normalized[3],
float *hue,
466 const float *
const reference = reference_primaries[reference_index];
468 float difference[3] = { point_normalized[0] - white_normalized[0],
469 point_normalized[1] - white_normalized[1],
470 point_normalized[2] - white_normalized[2] };
471 float uv[2] = { 0.f };
475 const float radius = hypotf(uv[0], uv[1]);
483 const float direction[2] = { uv[0] / radius, uv[1] / radius };
488 *purity = radius / distance_to_edge;
508 const float reference_primaries[3][2],
509 const int reference_index,
const float rotation,
510 const float saturation,
float point_normalized[3])
512 const float *
const reference = reference_primaries[reference_index];
516 const float scale = 1.f + saturation;
517 float rotated[2] = { 0.f };
518 float difference[3] = { 0.f };
522 const float uv[2] = { scale * rotated[0], scale * rotated[1] };
525 for(
int c = 0; c < 3; c++) point_normalized[c] = white_normalized[c] + difference[c];
541 const float reference_primaries[3][2],
542 const int reference_index,
543 const float point_normalized[3],
float *rotation,
546 const float *
const reference = reference_primaries[reference_index];
551 const float difference[3] = { point_normalized[0] - white_normalized[0],
552 point_normalized[1] - white_normalized[1],
553 point_normalized[2] - white_normalized[2] };
554 float uv[2] = { 0.f };
558 const float radius = hypotf(uv[0], uv[1]);
568 *saturation = radius / reference_radius - 1.f;
576 float white_reference[3] = { 0.f };
577 float reference_primaries[3][2] = { { 0.f } };
578 float white_reference_normalized[3] = { 0.f };
579 float custom_white_normalized[3] = { 0.f };
580 float custom_primaries[3][3] = { { 0.f } };
585 const float white_reference_sum =
_affine_sum3(white_reference);
605 for(
int col = 0; col < 3; col++)
606 normalized_primaries[
row][col] = custom_primaries[col][
row];
610 const float white_gain = primaries->
gain * white_reference_sum;
612 white_gain * custom_white_normalized[1],
613 white_gain * custom_white_normalized[2],
621 dot_product(custom_white, normalized_inverse, column_scales);
623 for(
int col = 0; col < 3; col++)
625 M[
row][col] = column_scales[col] * custom_primaries[col][
row];
636 float white_reference[3] = { 0.f };
637 float white_reference_normalized[3] = { 0.f };
638 float reference_primaries[3][2] = { { 0.f } };
639 float custom_white[3] = { 0.f };
640 float custom_white_normalized[3] = { 0.f };
641 float custom_primary_normalized[3] = { 0.f };
644 for(
int col = 0; col < 3; col++)
645 padded[
row][col] =
M[
row][col];
649 const float white_reference_sum =
_affine_sum3(white_reference);
662 for(
int primary = 0; primary < 3; primary++)
664 const float column[3] = {
M[0][primary],
M[1][primary],
M[2][primary] };
668 float *purity = primary == 0 ? &primaries->
red_purity
673 custom_primary_normalized, hue, purity))
688 const float first[2] = { vertices[1][0] - vertices[0][0], vertices[1][1] - vertices[0][1] };
689 const float second[2] = { vertices[2][0] - vertices[0][0], vertices[2][1] - vertices[0][1] };
691 return first[0] * second[1] - first[1] * second[0];
715 float white_reference[3] = { 0.f };
716 float white_reference_normalized[3] = { 0.f };
717 float reference_primaries[3][2] = { { 0.f } };
718 float custom_primaries[3][3] = { { 0.f } };
725 float custom_footprint[3][2] = { { 0.f } };
727 for(
int primary = 0; primary < 3; primary++)
730 rotations[primary], saturations[primary], custom_primaries[primary]))
733 const float difference[3] = { custom_primaries[primary][0] - white_reference_normalized[0],
734 custom_primaries[primary][1] - white_reference_normalized[1],
735 custom_primaries[primary][2] - white_reference_normalized[2] };
749 for(
int col = 0; col < 3; col++)
750 normalized_primaries[
row][col] = custom_primaries[col][
row];
755 = { white_reference[0], white_reference[1], white_reference[2], 0.f };
757 dot_product(padded_white, normalized_inverse, white_weights);
759 for(
int col = 0; col < 3; col++)
763 const float column_scale = white_weights[col] / white_reference[col];
764 for(
int row = 0;
row < 3;
row++)
M[
row][col] = column_scale * custom_primaries[col][
row];
788 float white_reference_normalized[3] = { 0.f };
789 float reference_primaries[3][2] = { { 0.f } };
790 float custom_primary_normalized[3] = { 0.f };
794 for(
int primary = 0; primary < 3; primary++)
796 const float column[3] = {
M[0][primary],
M[1][primary],
M[2][primary] };
799 float *rotation = primary == 0 ? &white_preserving->
red_rotation
802 float *saturation = primary == 0 ? &white_preserving->
red_saturation
807 custom_primary_normalized, rotation, saturation))
817 static const float P[3][3]
835 for(
int col = 0; col < 3; col++) source[
row] +=
P[
row][col] * basis[col];
841 const float max_RGB = fmaxf(fmaxf(linear_display_rgb[0], linear_display_rgb[1]), linear_display_rgb[2]);
843 for(
int c = 0; c < 3; c++) linear_display_rgb[c] = fmaxf(linear_display_rgb[c] / max_RGB, 0.f);
845 for(
int c = 0; c < 3; c++) linear_display_rgb[c] = fmaxf(linear_display_rgb[c], 0.f);
859 work_profile->unbounded_coeffs_in, work_profile->
lutsize,
865 _apply_trc(linear_display_rgb, display_rgb, display_profile->
lut_out, display_profile->unbounded_coeffs_out,
868 for(
int c = 0; c < 4; c++) display_rgb[c] = linear_display_rgb[c];
872 for(
int c = 0; c < 4; c++) display_rgb[c] = work_rgb[c];
876 for(
int c = 0; c < 3; c++) display_rgb[c] = CLAMP(display_rgb[c], 0.f, 1.f);
882 float display_rgb[3])
884 dt_aligned_pixel_t work_rgb = { module_color[0], module_color[1], module_color[2], 0.f };
894 for(
int c = 0; c < 3; c++) display_rgb[c] = display[c];
898 const float temperature_max)
902 const float temp_range = temperature_max - temperature_min;
906 const float temperature = temperature_min + stop * temp_range;
913 gtk_widget_queue_draw(widget);
919 GtkWidget *
const widget,
const float stop,
const float c,
920 const float r,
const float g,
const float b)
922 const float module_color[3]
923 = { 0.5f * (c *
r + 1.f -
r), 0.5f * (c *
g + 1.f -
g), 0.5f * (c * b + 1.f - b) };
924 float display_rgb[3] = { 0.f };
934 const float r,
const float g,
const float b,
942 const float sum =
RGB[0] +
RGB[1] +
RGB[2];
944 for(
int c = 0; c < 3; c++)
RGB[c] /= sum;
967 for(
int widget = 0; widget < 3; widget++) gtk_widget_queue_draw(widgets[widget]);
977 for(
int col = 0; col < 3; col++)
980 dot_product(padded_source,
mix, padded_color);
981 for(
int c = 0; c < 3; c++) module_color[c] = padded_color[c];
999 static const float P[3][3]
1003 const float basis[3]
1009 module_color[
row] = 0.f;
1010 for(
int col = 0; col < 3; col++) module_color[
row] +=
P[
row][col] * basis[col];
1032 const float slider = 2.f * stop - 1.f;
1033 const float stretch_slider = 3.f * stop - 1.5f;
1035 for(
int widget = 0; widget < 6; widget++)
1038 float source[3] = { 0.f };
1039 float M[3][3] = { { 0.f } };
1040 float module_color[3] = { 0.f };
1041 float display_rgb[3] = { 0.f };
1047 probe_simple.
theta = slider * (float)
M_PI;
1050 probe_simple.
psi = slider * (float)M_PI_2;
1074 static const float P[3][3]
1078 const float basis[3]
1085 for(
int col = 0; col < 3; col++) source[
row] +=
P[
row][col] * basis[col];
1093 if((widget == 0 || widget == 5)
1094 && fabsf(fmaxf(fmaxf(display_rgb[0], display_rgb[1]), display_rgb[2])
1095 - fminf(fminf(display_rgb[0], display_rgb[1]), display_rgb[2])) < 0.05f)
1097 const float hue = widget == 0 ? probe_simple.
theta : coupling_hue;
1107 for(
int widget = 0; widget < 6; widget++) gtk_widget_queue_draw(widgets[widget]);
1112 if(widget_index <= 1 || widget_index == 8)
1119 const int column = widget_index <= 3 ? 0 : widget_index <= 5 ? 1 : 2;
1135 for(
int widget = 0; widget < 9; widget++)
1138 float M[3][3] = { { 0.f } };
1139 float module_color[3] = { 0.f };
1140 float display_rgb[3] = { 0.f };
1143 const float value = hard_min + stop * (hard_max - hard_min);
1187 for(
int widget = 0; widget < 9; widget++) gtk_widget_queue_draw(widgets[widget]);
1203 for(
int widget = 0; widget < 6; widget++)
1206 float M[3][3] = { { 0.f } };
1207 float module_color[3] = { 0.f };
1208 float display_rgb[3] = { 0.f };
1211 const float value = hard_min + stop * (hard_max - hard_min);
1240 const int column = widget / 2;
1249 for(
int widget = 0; widget < 6; 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 gboolean _white_preserving_point_from_polar(const float white_normalized[3], const float reference_primaries[3][2], const int reference_index, const float rotation, const float saturation, float point_normalized[3])
Place one white-preserving primary from its rotation and saturation.
static void _shared_paint_probe_matrix(const float source[3], const float M[3][3], float module_color[3])
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])
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_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)
static gboolean _white_preserving_polar_from_point(const float white_normalized[3], const float reference_primaries[3][2], const int reference_index, const float point_normalized[3], float *rotation, float *saturation)
Read back the rotation and saturation of one white-preserving primary.
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])
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])
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.
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)
static float _affine_triangle_area(const float vertices[3][2])
Twice the signed area of a triangle given in affine plane coordinates.
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])
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)
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_MIN_FOOTPRINT
#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
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
static const float const float C
static dt_aligned_pixel_t RGB
static const dt_colormatrix_t M
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
#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 int mat3SSEinv(dt_colormatrix_t dst, const dt_colormatrix_t src)
static double reference(const op_t op, const double x)
static double max_error(const float *const got, const float *const in, const int n, const op_t op)
DT_ALIGNED_PIXEL float dt_aligned_pixel_t[4]
static const dt_aligned_pixel_simd_t value
White-preserving primaries model : per-primary rotation and saturation, no white freedom.
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_out[3]
Per-channel linear -> encoded tone curve, same convention as lut_in.
float * lut_in[3]
Per-channel encoded -> linear tone curve, lutsize entries each, sampled over [0,1]....
dt_colormatrix_t matrix_in_transposed