87#define INVERSE_SQRT_3 0.5773502691896258f
90#define ILLUM_X_MAX 360.0
91#define ILLUM_Y_MAX 300.0
92#define LIGHTNESS_MAX 100.0
94#define CHROMA_MAX 128.0
96#define TEMP_MAX 25000.
97#define DT_CHANNELMIXERRGB_SIMPLE_MODE_CONF "plugins/darkroom/channelmixerrgb/mixer_mode"
98#define DT_CHANNELMIXERRGB_SIMPLE_TAN_SCALE DT_IOP_CHANNELMIXER_SHARED_SIMPLE_TAN_SCALE
99#define DT_CHANNELMIXERRGB_SIMPLE_EPS DT_IOP_CHANNELMIXER_SHARED_SIMPLE_EPS
100#define DT_CHANNELMIXERRGB_SIMPLE_CHROMA_PROBE DT_IOP_CHANNELMIXER_SHARED_SIMPLE_CHROMA_PROBE
168#define DT_CHANNELMIXERRGB_SIMPLE_PROBE_ROTATION DT_IOP_CHANNELMIXER_SHARED_SIMPLE_PROBE_ROTATION
169#define DT_CHANNELMIXERRGB_SIMPLE_PROBE_AXIS_1 DT_IOP_CHANNELMIXER_SHARED_SIMPLE_PROBE_AXIS_1
170#define DT_CHANNELMIXERRGB_SIMPLE_PROBE_AXIS_2 DT_IOP_CHANNELMIXER_SHARED_SIMPLE_PROBE_AXIS_2
171#define DT_CHANNELMIXERRGB_PRIMARIES_BASIS_RGB DT_IOP_CHANNELMIXER_SHARED_PRIMARIES_BASIS_RGB
172#define DT_CHANNELMIXERRGB_PRIMARIES_BASIS_XYZ DT_IOP_CHANNELMIXER_SHARED_PRIMARIES_BASIS_XYZ
173#define DT_CHANNELMIXERRGB_PRIMARIES_BASIS_BRADFORD DT_IOP_CHANNELMIXER_SHARED_PRIMARIES_BASIS_BRADFORD
174#define DT_CHANNELMIXERRGB_PRIMARIES_BASIS_CAT16 DT_IOP_CHANNELMIXER_SHARED_PRIMARIES_BASIS_CAT16
273 return _(
"color _calibration");
278 return _(
"channel mixer|white balance|monochrome");
284 "such as white balance, channels mixing\n"
285 "and conversions to monochrome emulating film"),
286 _(
"corrective or creative"),
287 _(
"linear, RGB, scene-referred"),
288 _(
"linear, RGB or XYZ"),
289 _(
"linear, RGB, scene-referred"));
316 const int new_version)
318 if(old_version == 1 && new_version == 3)
329 const float R =
n->saturation[0];
330 const float B =
n->saturation[2];
331 n->saturation[0] =
B;
332 n->saturation[2] =
R;
339 if(old_version == 2 && new_version == 3)
341 typedef struct dt_iop_channelmixer_rgb_params_v2_t
349 gboolean normalize_R, normalize_G, normalize_B, normalize_sat, normalize_light, normalize_grey;
358 } dt_iop_channelmixer_rgb_params_v2_t;
360 memcpy(new_params, old_params,
sizeof(dt_iop_channelmixer_rgb_params_v2_t));
365 const float B =
n->saturation[2];
366 n->saturation[0] =
B;
367 n->saturation[2] =
R;
380 memset(&
p, 0,
sizeof(
p));
393 p.temperature = 5003.f;
406 p.saturation[0] = 0.f;
407 p.saturation[1] = 0.f;
408 p.saturation[2] = 0.f;
409 p.lightness[0] = 0.f;
410 p.lightness[1] = 0.f;
411 p.lightness[2] = 0.f;
416 p.normalize_R =
TRUE;
417 p.normalize_G =
TRUE;
418 p.normalize_B =
TRUE;
420 p.normalize_light =
FALSE;
421 p.normalize_grey =
TRUE;
454 p.grey[0] = 0.25304098f;
455 p.grey[1] = 0.25958747f;
456 p.grey[2] = 0.48737156f;
463 p.grey[0] = 0.24552374f;
464 p.grey[1] = 0.25366007f;
465 p.grey[2] = 0.50081619f;
473 p.grey[0] = 0.24376712f;
474 p.grey[1] = 0.23613559f;
475 p.grey[2] = 0.52009729f;
482 p.grey[0] = 0.24149085f;
483 p.grey[1] = 0.22149272f;
484 p.grey[2] = 0.53701643f;
507 p.normalize_R =
TRUE;
508 p.normalize_G =
TRUE;
509 p.normalize_B =
TRUE;
566 for(
size_t k = 0;
k < 4;
k++) custom_wb[
k] = 1.f;
570 dt_iop_fmt_log(self,
"get_white_balance_coeff: class=%s matrix_supported=0 -> custom_wb=no-op (CAT uses identity)",
585 const double green = bwb[1];
601 if(wb_coeffs[0] != 0.f)
603 for(
size_t k = 0;
k < 4;
k++)
607 const float denom = wb_coeffs[
k];
608 custom_wb[
k] = (
k == 3 && !isnormal(denom)) ? custom_wb[1] : bwb[
k] / denom;
625gamut_mapping(
const dt_aligned_pixel_simd_t input,
const float compression,
const int clip)
628 const float sum = input[0] + input[1] + input[2];
629 const float Y = input[1];
631 if(sum > 0.f && Y > 0.f)
634 float x = input[0] / sum;
635 float y = input[1] / sum;
638 const float uv_denominator = -2.f *
x + 12.f * y + 3.f;
639 float u = 4.f *
x / uv_denominator;
640 float v = 9.f * y / uv_denominator;
643 const float D50[2]
DT_ALIGNED_PIXEL = { 0.20915914598542354f, 0.488075320769787f };
645 const float Delta = Y * (sqf(
delta[0]) + sqf(
delta[1]));
648 const float correction = (compression == 0.0f) ? 0.f : powf(Delta, compression);
651 const float tmp_u =
DT_FMA(correction,
delta[0], u);
653 u = (u > D50[0]) ? fmaxf(tmp_u, D50[0]) : fminf(tmp_u, D50[0]);
654 v = (
v > D50[1]) ? fmaxf(tmp_v, D50[1]) : fminf(tmp_v, D50[1]);
657 const float xy_denominator = 6.f * u - 16.f *
v + 12.f;
658 x = 9.f * u / xy_denominator;
659 y = 4.f *
v / xy_denominator;
674 const float scale =
x + y;
675 const int sanitize = (scale >= 1.f);
682 return (dt_aligned_pixel_simd_t){ Y *
x / y, Y, Y * (1.f -
x - y) / y, 0.f };
687 return (dt_aligned_pixel_simd_t){ 0.f };
697 float norm = euclidean_norm(input);
698 const float avg = fmaxf((input[0] + input[1] + input[2]) / 3.0f,
NORM_MIN);
700 if(norm > 0.f && avg > 0.f)
703 const float mix = scalar_product(input, lightness);
709 for(
size_t c = 0; c < 3; c++) output[c] = input[c] / norm;
712 float coeff_ratio = 0.f;
716 for(
size_t c = 0; c < 3; c++)
717 coeff_ratio += sqf(1.0f - output[c]) * saturation[c];
720 coeff_ratio = scalar_product(output, saturation) / 3.f;
723 for(
size_t c = 0; c < 3; c++)
727 const float min_ratio = (output[c] < 0.0f) ? output[c] : 0.0f;
728 const float output_inverse = 1.0f - output[c];
729 output[c] = fmaxf(
DT_FMA(output_inverse, coeff_ratio, output[c]),
738 norm *= fmaxf(1.f +
mix / avg, 0.f);
739 for(
size_t c = 0; c < 3; c++) output[c] *= norm;
744 for(
size_t c = 0; c < 3; c++) output[c] = input[c];
749static inline void loop_switch(
const float *
const restrict in,
float *
const restrict
out,
764 const dt_aligned_pixel_simd_t rgb_to_xyz0 = dt_colormatrix_row_to_simd(RGB_to_XYZ_t, 0);
765 const dt_aligned_pixel_simd_t rgb_to_xyz1 = dt_colormatrix_row_to_simd(RGB_to_XYZ_t, 1);
766 const dt_aligned_pixel_simd_t rgb_to_xyz2 = dt_colormatrix_row_to_simd(RGB_to_XYZ_t, 2);
767 const dt_aligned_pixel_simd_t xyz_to_rgb0 = dt_colormatrix_row_to_simd(XYZ_to_RGB_t, 0);
768 const dt_aligned_pixel_simd_t xyz_to_rgb1 = dt_colormatrix_row_to_simd(XYZ_to_RGB_t, 1);
769 const dt_aligned_pixel_simd_t xyz_to_rgb2 = dt_colormatrix_row_to_simd(XYZ_to_RGB_t, 2);
770 const dt_aligned_pixel_simd_t mix0 = dt_colormatrix_row_to_simd(MIX_t, 0);
771 const dt_aligned_pixel_simd_t mix1 = dt_colormatrix_row_to_simd(MIX_t, 1);
772 const dt_aligned_pixel_simd_t mix2 = dt_colormatrix_row_to_simd(MIX_t, 2);
773 const dt_aligned_pixel_simd_t illuminant_v = dt_load_simd_aligned(
illuminant);
777 const dt_aligned_pixel_simd_t in_v = dt_load_simd_aligned(in +
k);
778 dt_aligned_pixel_simd_t temp_one_v = { 0.f };
779 dt_aligned_pixel_simd_t temp_two_v = clip ? dt_simd_max_zero(in_v) : in_v;
788 temp_one_v = dt_mat3x4_mul_vec4(temp_two_v, rgb_to_xyz0, rgb_to_xyz1, rgb_to_xyz2);
789 const float Y = temp_one_v[1];
796 temp_two_v = dt_mat3x4_mul_vec4(temp_one_v, mix0, mix1, mix2);
804 temp_one_v = dt_mat3x4_mul_vec4(temp_two_v, rgb_to_xyz0, rgb_to_xyz1, rgb_to_xyz2);
805 const float Y = temp_one_v[1];
812 temp_two_v = dt_mat3x4_mul_vec4(temp_one_v, mix0, mix1, mix2);
820 temp_one_v = dt_mat3x4_mul_vec4(temp_two_v, rgb_to_xyz0, rgb_to_xyz1, rgb_to_xyz2);
821 const float Y = temp_one_v[1];
826 temp_one_v = _upscale_vector_simd(
CAT16_adapt_D50(temp_two_v, illuminant_v, 1.0f,
TRUE), Y);
828 temp_two_v = dt_mat3x4_mul_vec4(temp_one_v, mix0, mix1, mix2);
836 temp_one_v = dt_mat3x4_mul_vec4(temp_two_v, rgb_to_xyz0, rgb_to_xyz1, rgb_to_xyz2);
837 const float Y = temp_one_v[1];
840 temp_two_v = _upscale_vector_simd(
XYZ_adapt_D50(_downscale_vector_simd(temp_one_v, Y), illuminant_v), Y);
841 temp_one_v = dt_mat3x4_mul_vec4(temp_two_v, mix0, mix1, mix2);
852 temp_one_v = dt_mat3x4_mul_vec4(temp_two_v, mix0, mix1, mix2);
853 temp_one_v = dt_mat3x4_mul_vec4(temp_one_v, rgb_to_xyz0, rgb_to_xyz1, rgb_to_xyz2);
861 temp_two_v = gamut_mapping(temp_one_v, gamut, clip);
871 temp_one_v = convert_any_XYZ_to_LMS(temp_two_v,
kind);
879 temp_one_v = dt_mat3x4_mul_vec4(temp_two_v, xyz_to_rgb0, xyz_to_rgb1, xyz_to_rgb2);
887 if(clip) temp_one_v = dt_simd_max_zero(temp_one_v);
893 luma_chroma(luma_input, saturation, lightness, luma_output, version);
894 temp_two_v = dt_load_simd_aligned(luma_output);
897 if(clip) temp_two_v = dt_simd_max_zero(temp_two_v);
903 const float grey_mix = fmaxf(temp_two_v[0] * grey[0] + temp_two_v[1] * grey[1] + temp_two_v[2] * grey[2], 0.0f);
904 dt_store_simd_nontemporal(
out +
k, (dt_aligned_pixel_simd_t){ grey_mix, grey_mix, grey_mix, in_v[3] });
916 temp_one_v = convert_any_LMS_to_XYZ(temp_two_v,
kind);
924 temp_one_v = dt_mat3x4_mul_vec4(temp_two_v, rgb_to_xyz0, rgb_to_xyz1, rgb_to_xyz2);
932 if(clip) temp_one_v = dt_simd_max_zero(temp_one_v);
935 temp_two_v = dt_mat3x4_mul_vec4(temp_one_v, xyz_to_rgb0, xyz_to_rgb1, xyz_to_rgb2);
936 if(clip) temp_two_v = dt_simd_max_zero(temp_two_v);
938 dt_store_simd_nontemporal(
out +
k, (dt_aligned_pixel_simd_t){ temp_two_v[0], temp_two_v[1], temp_two_v[2], in_v[3] });
945#define SHF(ii, jj, c) ((i + ii) * width + j + jj) * ch + c
972 for(
size_t j = 0; j <
width; j++)
974 const size_t index = (
i *
width + j) *
ch;
980 RGB[c] = fmaxf(in[index + c], 0.0f);
983 dot_product(
RGB, RGB_to_XYZ,
XYZ);
992 const float D50[2] = { 0.34567f, 0.35850f };
993 const float norm = dt_fast_hypotf(D50[0], D50[1]);
995 temp[index ] = (
XYZ[0] - D50[0]) / norm;
996 temp[index + 1] = (
XYZ[1] - D50[1]) / norm;
997 temp[index + 2] =
XYZ[2];
1000 float elements = 0.f;
1011 for(
size_t c = 0; c < 2; c++)
1015 2.f * temp[
SHF( 0, -
OFF, c)] + 4.f * temp[
SHF( 0, 0, c)] + 2.f * temp[
SHF( 0, +
OFF, c)] +
1017 central_average[c] = fmaxf(central_average[c], 0.0f);
1024 for(
size_t c = 0; c < 2; c++)
1027 sqf(temp[
SHF(-
OFF, -
OFF, c)] - central_average[c]) +
1028 sqf(temp[
SHF(-
OFF, 0, c)] - central_average[c]) +
1029 sqf(temp[
SHF(-
OFF, +
OFF, c)] - central_average[c]) +
1030 sqf(temp[
SHF(0, -
OFF, c)] - central_average[c]) +
1031 sqf(temp[
SHF(0, 0, c)] - central_average[c]) +
1032 sqf(temp[
SHF(0, +
OFF, c)] - central_average[c]) +
1033 sqf(temp[
SHF(+
OFF, -
OFF, c)] - central_average[c]) +
1034 sqf(temp[
SHF(+
OFF, 0, c)] - central_average[c]) +
1035 sqf(temp[
SHF(+
OFF, +
OFF, c)] - central_average[c])
1043 (temp[
SHF(-
OFF, -
OFF, 0)] - central_average[0]) * (temp[
SHF(-
OFF, -
OFF, 1)] - central_average[1]) +
1044 (temp[
SHF(-
OFF, 0, 0)] - central_average[0]) * (temp[
SHF(-
OFF, 0, 1)] - central_average[1]) +
1045 (temp[
SHF(-
OFF, +
OFF, 0)] - central_average[0]) * (temp[
SHF(-
OFF, +
OFF, 1)] - central_average[1]) +
1046 (temp[
SHF( 0, -
OFF, 0)] - central_average[0]) * (temp[
SHF( 0, -
OFF, 1)] - central_average[1]) +
1047 (temp[
SHF( 0, 0, 0)] - central_average[0]) * (temp[
SHF( 0, 0, 1)] - central_average[1]) +
1048 (temp[
SHF( 0, +
OFF, 0)] - central_average[0]) * (temp[
SHF( 0, +
OFF, 1)] - central_average[1]) +
1049 (temp[
SHF(+
OFF, -
OFF, 0)] - central_average[0]) * (temp[
SHF(+
OFF, -
OFF, 1)] - central_average[1]) +
1050 (temp[
SHF(+
OFF, 0, 0)] - central_average[0]) * (temp[
SHF(+
OFF, 0, 1)] - central_average[1]) +
1051 (temp[
SHF(+
OFF, +
OFF, 0)] - central_average[0]) * (temp[
SHF(+
OFF, +
OFF, 1)] - central_average[1])
1055 const float p = 8.f;
1057 = powf(powf(fabsf(central_average[0]),
p) + powf(fabsf(central_average[1]),
p), 1.f /
p) +
NORM_MIN;
1058 const float weight = var[0] * var[1] * var[2];
1060 for(
size_t c = 0; c < 2; c++)
xyY[c] += central_average[c] *
weight / p_norm;
1061 elements +=
weight / p_norm;
1073 for(
size_t c = 0; c < 2; c++)
1077 2.f * temp[
SHF( 0, -
OFF, c)] + 4.f * temp[
SHF( 0, 0, c)] + 2.f * temp[
SHF( 0, +
OFF, c)] +
1081 dd[c] = temp[
SHF(0, 0, c)] - central_average[c];
1085 const float p = 8.f;
1086 const float p_norm = powf(powf(fabsf(dd[0]),
p) + powf(fabsf(dd[1]),
p), 1.f /
p) +
NORM_MIN;
1088 for(
size_t c = 0; c < 2; c++)
xyY[c] -= dd[c] / p_norm;
1093 const float D50[2] = { 0.34567f, 0.35850 };
1094 const float norm_D50 = dt_fast_hypotf(D50[0], D50[1]);
1096 for(
size_t c = 0; c < 2; c++)
1097 xyz[c] = norm_D50 * (
xyY[c] / elements) + D50[c];
1154static inline __attribute__((always_inline))
void check_if_close_to_daylight(
const float x,
const float y,
float *temperature,
1169 if(t < 3000.f && t > 1667.f)
1176 float xy_ref[2] = {
x, y };
1180 float xy_test[2] = { 0.f };
1188 const float delta_daylight = dt_fast_hypotf((uv_test[0] - uv_ref[0]), (uv_test[1] - uv_ref[1]));
1195 const float delta_bb = dt_fast_hypotf((uv_test[0] - uv_ref[0]), (uv_test[1] - uv_ref[1]));
1198 if(delta_bb < 0.005f || delta_daylight < 0.005f)
1202 if(delta_bb < delta_daylight)
1218#define DEG_TO_RAD(x) (x * M_PI / 180.f)
1219#define RAD_TO_DEG(x) (x * 180.f / M_PI)
1223 float *
const restrict delta_E,
float *
const restrict avg_delta_E,
float *
const restrict max_delta_E)
1237 for(
size_t c = 0; c < 4; c++) XYZ_test[c] = patches[
k * 4 + c];
1240 const float *
const restrict Lab_ref = checker->
values[
k].
Lab;
1245 const float DL = Lab_ref[0] - Lab_test[0];
1246 const float L_avg = (Lab_ref[0] + Lab_test[0]) / 2.f;
1247 const float C_ref = dt_fast_hypotf(Lab_ref[1], Lab_ref[2]);
1248 const float C_test = dt_fast_hypotf(Lab_test[1], Lab_test[2]);
1249 const float C_avg = (C_ref + C_test) / 2.f;
1250 float C_avg_7 = C_avg * C_avg;
1254 const float C_avg_7_ratio_sqrt = sqrtf(C_avg_7 / (C_avg_7 + 6103515625.f));
1255 const float a_ref_prime = Lab_ref[1] * (1.f + 0.5f * (1.f - C_avg_7_ratio_sqrt));
1256 const float a_test_prime = Lab_test[1] * (1.f + 0.5f * (1.f - C_avg_7_ratio_sqrt));
1257 const float C_ref_prime = dt_fast_hypotf(a_ref_prime, Lab_ref[2]);
1258 const float C_test_prime = dt_fast_hypotf(a_test_prime, Lab_test[2]);
1259 const float DC_prime = C_ref_prime - C_test_prime;
1260 const float C_avg_prime = (C_ref_prime + C_test_prime) / 2.f;
1261 float h_ref_prime = atan2f(Lab_ref[2], a_ref_prime);
1262 float h_test_prime = atan2f(Lab_test[2], a_test_prime);
1265 if(C_ref_prime == 0.f) h_ref_prime = 0.f;
1266 if(C_test_prime == 0.f) h_test_prime = 0.f;
1270 if(h_ref_prime < 0.f) h_ref_prime = 2.f *
M_PI - h_ref_prime;
1271 if(h_test_prime < 0.f) h_test_prime = 2.f *
M_PI - h_test_prime;
1277 float Dh_prime = h_test_prime - h_ref_prime;
1278 float Dh_prime_abs = fabsf(Dh_prime);
1279 if(C_test_prime == 0.f || C_ref_prime == 0.f)
1281 else if(Dh_prime_abs <= 180.f)
1283 else if(Dh_prime_abs > 180.f && (h_test_prime <= h_ref_prime))
1285 else if(Dh_prime_abs > 180.f && (h_test_prime > h_ref_prime))
1289 Dh_prime_abs = fabsf(Dh_prime);
1291 const float DH_prime = 2.f * sqrtf(C_test_prime * C_ref_prime) * sinf(
DEG_TO_RAD(Dh_prime) / 2.f);
1292 float H_avg_prime = h_ref_prime + h_test_prime;
1293 if(C_test_prime == 0.f || C_ref_prime == 0.f)
1295 else if(Dh_prime_abs <= 180.f)
1297 else if(Dh_prime_abs > 180.f && (H_avg_prime < 360.f))
1298 H_avg_prime = (H_avg_prime + 360.f) / 2.f;
1299 else if(Dh_prime_abs > 180.f && (H_avg_prime >= 360.f))
1300 H_avg_prime = (H_avg_prime - 360.f) / 2.f;
1304 + 0.24f * cosf(2.f *
DEG_TO_RAD(H_avg_prime))
1308 const float S_L = 1.f + (0.015f * sqf(L_avg - 50.f)) / sqrtf(20.f + sqf(L_avg - 50.f));
1309 const float S_C = 1.f + 0.045f * C_avg_prime;
1310 const float S_H = 1.f + 0.015f * C_avg_prime * T;
1311 const float R_T = -2.f * C_avg_7_ratio_sqrt
1312 * sinf(
DEG_TO_RAD(60.f) * expf(-sqf((H_avg_prime - 275.f) / 25.f)));
1315 const float DE = sqrtf(sqf(DL / S_L) + sqf(DC_prime / S_C) + sqf(DH_prime / S_H)
1316 + R_T * (DC_prime / S_C) * (DH_prime / S_H));
1326 dE += DE / (float)checker->
patches;
1327 if(DE > max_dE) max_dE = DE;
1331 *max_delta_E = max_dE;
1335 float hue = atan2f(reference[2], reference[1]); \
1336 const float chroma = hypotf(reference[2], reference[1]); \
1337 float delta_hue = hue - ref_hue; \
1340 else if(fabsf(delta_hue) <= M_PI) \
1342 else if(fabsf(delta_hue) > M_PI && (hue <= ref_hue)) \
1343 delta_hue += 2.f * M_PI; \
1344 else if(fabsf(delta_hue) > M_PI && (hue > ref_hue)) \
1345 delta_hue -= 2.f * M_PI; \
1346 w = sqrtf(expf(-sqf(delta_hue) / 2.f));
1358 float *
const restrict patches,
1359 const gboolean normalize_exposure,
1364 const float radius_x =
g->checker->radius * hypotf(1.f,
g->checker->ratio) *
g->safety_margin;
1365 const float radius_y = radius_x /
g->checker->ratio;
1374 for(
size_t k = 0;
k <
g->checker->patches;
k++)
1377 const point_t center = {
g->checker->values[
k].
x,
g->checker->values[
k].y };
1380 const point_t corners[4] = { {center.
x - radius_x, center.
y - radius_y},
1381 {center.
x + radius_x, center.
y - radius_y},
1382 {center.
x + radius_x, center.
y + radius_y},
1383 {center.
x - radius_x, center.
y + radius_y} };
1388 size_t x_min =
width - 1;
1390 size_t y_min =
height - 1;
1392 for(
size_t c = 0; c < 4; c++) {
1394 x_min = fminf(new_corners[c].
x, x_min);
1395 x_max = fmaxf(new_corners[c].
x, x_max);
1396 y_min = fminf(new_corners[c].y, y_min);
1397 y_max = fmaxf(new_corners[c].y, y_max);
1400 x_min = CLAMP((
size_t)floorf(x_min), 0,
width - 1);
1401 x_max = CLAMP((
size_t)ceilf(x_max), 0,
width - 1);
1402 y_min = CLAMP((
size_t)floorf(y_min), 0,
height - 1);
1403 y_max = CLAMP((
size_t)ceilf(y_max), 0,
height - 1);
1406 patches[
k * 4] = patches[
k * 4 + 1] = patches[
k * 4 + 2] = patches[
k * 4 + 3] = 0.f;
1407 size_t num_elem = 0;
1410 for(
size_t j = y_min; j < y_max; j++)
1411 for(
size_t i = x_min;
i < x_max;
i++)
1414 point_t current_point = {
i + 0.5f, j + 0.5f };
1416 current_point.
x -= center.
x;
1417 current_point.
y -= center.
y;
1419 if(current_point.
x < radius_x && current_point.
x > -radius_x &&
1420 current_point.
y < radius_y && current_point.
y > -radius_y)
1422 for(
size_t c = 0; c < 3; c++)
1424 patches[
k * 4 + c] += in[(j *
width +
i) * 4 + c];
1434 for(
size_t c = 0; c < 3; c++) patches[
k * 4 + c] /= (
float)num_elem;
1438 dot_product(patches +
k * 4, RGB_to_XYZ,
XYZ);
1439 for(
size_t c = 0; c < 3; c++) patches[
k * 4 + c] =
XYZ[c];
1444 dt_Lab_to_XYZ(
g->checker->values[
g->checker->white].Lab, XYZ_white_ref);
1445 const float white_ref_norm = euclidean_norm(XYZ_white_ref);
1449 for(
size_t c = 0; c < 3; c++) XYZ_white_test[c] = patches[
g->checker->white * 4 + c];
1450 const float white_test_norm = euclidean_norm(XYZ_white_test);
1455 float exposure = white_ref_norm / white_test_norm;
1461 if(normalize_exposure)
1463 for(
size_t k = 0;
k <
g->checker->patches;
k++)
1465 float *
const sample = patches +
k * 4;
1470 const float sample_norm = euclidean_norm(sample);
1471 const float ref_norm = euclidean_norm(XYZ_ref);
1473 const float relative_luminance_test = sample_norm / white_test_norm;
1474 const float relative_luminance_ref = ref_norm / white_ref_norm;
1476 const float luma_correction = relative_luminance_ref / relative_luminance_test;
1477 for(
size_t c = 0; c < 3; ++c) sample[c] *= luma_correction * exposure;
1489 float mean_ref = 0.f;
1490 float mean_test = 0.f;
1492 for(
size_t k = 0;
k <
g->checker->patches;
k++)
1497 for(
size_t c = 0; c < 3; c++) XYZ_test[c] = patches[
k * 4 + c];
1500 dot_product(XYZ_test, XYZ_to_CAM, RGB_test);
1501 dot_product(XYZ_ref, XYZ_to_CAM, RGB_ref);
1506 for(
int c = 0; c < 3; c++)
1508 mean_test += RGB_test[c];
1509 mean_ref += RGB_ref[c];
1512 mean_test /= 3.f *
g->checker->patches;
1513 mean_ref /= 3.f *
g->checker->patches;
1515 float variance = 0.f;
1516 float covariance = 0.f;
1518 for(
size_t k = 0;
k <
g->checker->patches;
k++)
1523 for(
size_t c = 0; c < 3; c++) XYZ_test[c] = patches[
k * 4 + c];
1526 dot_product(XYZ_test, XYZ_to_CAM, RGB_test);
1527 dot_product(XYZ_ref, XYZ_to_CAM, RGB_ref);
1529 for(
int c = 0; c < 3; c++)
1531 variance += sqf(RGB_test[c] - mean_test);
1532 covariance += (RGB_ref[c] - mean_ref) * (RGB_test[c] - mean_ref);
1535 variance /= 3.f *
g->checker->patches;
1536 covariance /= 3.f *
g->checker->patches;
1542 exposure = covariance / variance;
1543 black = mean_ref - exposure * mean_test;
1551 result->
black = black;
1576 float pre_wb_delta_E = 0.f;
1577 float pre_wb_max_delta_E = 0.f;
1584 dt_Lab_to_XYZ(
g->checker->values[
g->checker->middle_grey].Lab, XYZ_grey_ref);
1588 for(
size_t c = 0; c < 3; c++) XYZ_grey_test[c] = patches[
g->checker->middle_grey * 4 + c];
1595 const float Y_test = XYZ_grey_test[1];
1596 const float Y_ref = XYZ_grey_ref[1];
1597 for(
size_t c = 0; c < 3; c++)
1599 XYZ_grey_ref[c] /= Y_ref;
1600 XYZ_grey_test[c] /= Y_test;
1611 for(
size_t c = 0; c < 3; c++)
illuminant[c] = D50_LMS[c] * LMS_grey_test[c] / LMS_grey_ref[c];
1616 const float Y_illu = illuminant_XYZ[1];
1617 for(
size_t c = 0; c < 3; c++) illuminant_XYZ[c] /= Y_illu;
1621 g->xy[0] = illuminant_xyY[0];
1622 g->xy[1] = illuminant_xyY[1];
1627 const float p = powf(0.818155f /
illuminant[2], 0.0834f);
1630 for(
size_t k = 0;
k <
g->checker->patches;
k++)
1633 float *
const sample = patches +
k * 4;
1634 const float Y = sample[1];
1635 downscale_vector(sample, Y);
1641 const dt_aligned_pixel_simd_t LMS_v = dt_load_simd_aligned(
LMS);
1642 const dt_aligned_pixel_simd_t illuminant_v = dt_load_simd_aligned(
illuminant);
1671 for(
size_t c = 0; c < 3; ++c) temp[c] =
LMS[c];
1677 upscale_vector(sample, Y);
1681 float post_wb_delta_E = 0.f;
1682 float post_wb_max_delta_E = 0.f;
1686 double *
const restrict Y =
dt_alloc_align(
g->checker->patches * 3 *
sizeof(
double));
1687 double *
const restrict
A =
dt_alloc_align(
g->checker->patches * 3 * 9 *
sizeof(
double));
1696 for(
size_t k = 0;
k <
g->checker->patches;
k++)
1698 float *
const sample = patches +
k * 4;
1702 const float *
const reference =
g->checker->values[
k].Lab;
1710 w = sqrtf(1.f / (
float)
g->checker->patches);
1712 w = sqrtf(hypotf(reference[1] / 128.f, reference[2] / 128.f));
1714 w = sqrtf(1.f - hypotf(reference[1] / 128.f, reference[2] / 128.f));
1718 const float ref_hue = 1.f;
1724 const float ref_hue = 2.23f;
1730 const float ref_hue = -1.93f;
1734 w = sqrtf(sqrtf(1.f /
g->delta_E_in[
k]));
1736 w = sqrtf(sqrtf(
g->delta_E_in[
k]));
1739 for(
size_t c = 0; c < 3; c++) Y[
k * 3 + c] = w * LMS_ref[c];
1742 A[
k * 3 * 9 + 0] = w * LMS_test[0];
1743 A[
k * 3 * 9 + 1] = w * LMS_test[1];
1744 A[
k * 3 * 9 + 2] = w * LMS_test[2];
1745 A[
k * 3 * 9 + 3] = 0.f;
1746 A[
k * 3 * 9 + 4] = 0.f;
1747 A[
k * 3 * 9 + 5] = 0.f;
1748 A[
k * 3 * 9 + 6] = 0.f;
1749 A[
k * 3 * 9 + 7] = 0.f;
1750 A[
k * 3 * 9 + 8] = 0.f;
1753 A[
k * 3 * 9 + 9 + 0] = 0.f;
1754 A[
k * 3 * 9 + 9 + 1] = 0.f;
1755 A[
k * 3 * 9 + 9 + 2] = 0.f;
1756 A[
k * 3 * 9 + 9 + 3] = w * LMS_test[0];
1757 A[
k * 3 * 9 + 9 + 4] = w * LMS_test[1];
1758 A[
k * 3 * 9 + 9 + 5] = w * LMS_test[2];
1759 A[
k * 3 * 9 + 9 + 6] = 0.f;
1760 A[
k * 3 * 9 + 9 + 7] = 0.f;
1761 A[
k * 3 * 9 + 9 + 8] = 0.f;
1764 A[
k * 3 * 9 + 18 + 0] = 0.f;
1765 A[
k * 3 * 9 + 18 + 1] = 0.f;
1766 A[
k * 3 * 9 + 18 + 2] = 0.f;
1767 A[
k * 3 * 9 + 18 + 3] = 0.f;
1768 A[
k * 3 * 9 + 18 + 4] = 0.f;
1769 A[
k * 3 * 9 + 18 + 5] = 0.f;
1770 A[
k * 3 * 9 + 18 + 6] = w * LMS_test[0];
1771 A[
k * 3 * 9 + 18 + 7] = w * LMS_test[1];
1772 A[
k * 3 * 9 + 18 + 8] = w * LMS_test[2];
1790 for(
size_t k = 0;
k <
g->checker->patches;
k++)
1792 float *
const sample = patches +
k * 4;
1797 for(
size_t c = 0; c < 3; c++) temp[c] = sample[c];
1800 dot_product(LMS_test,
g->mix, temp);
1805 float post_mix_delta_E = 0.f;
1806 float post_mix_max_delta_E = 0.f;
1812 float x = illuminant_xyY[0];
1813 float y = illuminant_xyY[1];
1814 check_if_close_to_daylight(
x, y, &temperature, &test_illuminant, NULL);
1817 string = _(
"(daylight)");
1819 string = _(
"(black body)");
1821 string = _(
"(invalid)");
1824 if(post_mix_delta_E <= 1.2f)
1825 diagnostic = _(
"very good");
1826 else if(post_mix_delta_E <= 2.3f)
1827 diagnostic = _(
"good");
1828 else if(post_mix_delta_E <= 3.4f)
1829 diagnostic = _(
"passable");
1831 diagnostic = _(
"bad");
1833 g->profile_ready =
TRUE;
1837 g->delta_E_label_text
1838 = g_strdup_printf(_(
"\n<b>Profile quality report: %s</b>\n"
1839 "input \316\224E: \tavg. %.2f ; \tmax. %.2f\n"
1840 "WB \316\224E: \tavg. %.2f; \tmax. %.2f\n"
1841 "output \316\224E: \tavg. %.2f; \tmax. %.2f\n\n"
1842 "<b>Profile data</b>\n"
1843 "illuminant: \t%.0f K \t%s\n"
1844 "matrix in adaptation space:\n"
1845 "<tt>%+.4f \t%+.4f \t%+.4f\n"
1846 "%+.4f \t%+.4f \t%+.4f\n"
1847 "%+.4f \t%+.4f \t%+.4f</tt>\n\n"
1848 "<b>Normalization values</b>\n"
1849 "exposure compensation: \t%+.2f EV\n"
1850 "black offset: \t%+.4f"
1852 diagnostic, pre_wb_delta_E, pre_wb_max_delta_E, post_wb_delta_E, post_wb_max_delta_E,
1853 post_mix_delta_E, post_mix_max_delta_E, temperature,
string,
g->mix[0][0],
g->mix[0][1],
1854 g->mix[0][2],
g->mix[1][0],
g->mix[1][1],
g->mix[1][2],
g->mix[2][0],
g->mix[2][1],
1855 g->mix[2][2], log2f(extraction_result.
exposure), extraction_result.
black);
1875 float pre_wb_delta_E = 0.f;
1876 float pre_wb_max_delta_E = 0.f;
1880 if(pre_wb_delta_E <= 1.2f)
1881 diagnostic = _(
"very good");
1882 else if(pre_wb_delta_E <= 2.3f)
1883 diagnostic = _(
"good");
1884 else if(pre_wb_delta_E <= 3.4f)
1885 diagnostic = _(
"passable");
1887 diagnostic = _(
"bad");
1891 g->delta_E_label_text = g_strdup_printf(_(
"\n<b>Profile quality report: %s</b>\n"
1892 "output \316\224E: \tavg. %.2f; \tmax. %.2f\n\n"
1893 "<b>Normalization values</b>\n"
1894 "exposure compensation: \t%+.2f EV\n"
1895 "black offset: \t%+.4f"),
1896 diagnostic, pre_wb_delta_E, pre_wb_max_delta_E, log2f(extraction_result.
exposure),
1897 extraction_result.
black);
1904 const void *
const restrict ivoid,
void *
const restrict
ovoid)
1923 memcpy(RGB_to_XYZ, work_profile->
matrix_in,
sizeof(RGB_to_XYZ));
1924 memcpy(XYZ_to_RGB, work_profile->
matrix_out,
sizeof(XYZ_to_RGB));
1925 memcpy(XYZ_to_CAM, input_profile->
matrix_out,
sizeof(XYZ_to_CAM));
1928 const size_t ch = 4;
1930 const float *
const restrict in = (
const float *
const restrict)ivoid;
1931 float *
const restrict
out = (
float *
const restrict)
ovoid;
1936 gboolean exit =
FALSE;
2006 XYZ_to_RGB, RGB_to_XYZ, data->
MIX,
2014 XYZ_to_RGB, RGB_to_XYZ, data->
MIX,
2022 XYZ_to_RGB, RGB_to_XYZ, data->
MIX,
2030 XYZ_to_RGB, RGB_to_XYZ, data->
MIX,
2038 XYZ_to_RGB, RGB_to_XYZ, data->
MIX,
2055 g->run_validation =
FALSE;
2095 d->illuminant[3] = 0.f;
2101 const int devid = pipe->
devid;
2107 cl_mem input_matrix_cl = NULL;
2108 cl_mem output_matrix_cl = NULL;
2110 float input_matrix_3x4[12];
2111 float output_matrix_3x4[12];
2112 float mix_matrix_3x4[12];
2124 switch(
d->adaptation)
2172 if(err != CL_SUCCESS)
goto error;
2189 const int program = 32;
2216 const float x_increment,
const float y_increment)
2219 for(
size_t k = 0;
k < 4;
k++)
2221 if(
g->active_node[
k])
2223 g->box[
k].x += x_increment;
2224 g->box[
k].y += y_increment;
2235 if(
g->checker && !
g->checker_ready)
2238 g->box[0].x =
g->box[0].y = 10.;
2241 g->box[1].x = (
width - 10.);
2242 g->box[1].y =
g->box[0].y;
2245 g->box[2].x =
g->box[1].x;
2246 g->box[2].y = (
width - 10.) *
g->checker->ratio;
2249 g->box[3].x =
g->box[0].x;
2250 g->box[3].y =
g->box[2].y;
2252 g->checker_ready =
TRUE;
2255 g->center_box.x = 0.5f;
2256 g->center_box.y = 0.5f;
2258 g->ideal_box[0].x = 0.f;
2259 g->ideal_box[0].y = 0.f;
2260 g->ideal_box[1].x = 1.f;
2261 g->ideal_box[1].y = 0.f;
2262 g->ideal_box[2].x = 1.f;
2263 g->ideal_box[2].y = 1.f;
2264 g->ideal_box[3].x = 0.f;
2265 g->ideal_box[3].y = 1.f;
2278 if(
g->box[0].x == -1.0f ||
g->box[1].y == -1.0f)
return 0;
2283 if(wd == 0.f || ht == 0.f)
return 0;
2285 float pzxpy[2] = { (float)
x, (
float)y };
2288 const float pzx = pzxpy[0];
2289 const float pzy = pzxpy[1];
2297 g->click_end.x = pzx;
2298 g->click_end.y = pzy;
2302 g->click_start.x = pzx;
2303 g->click_start.y = pzy;
2312 g->is_cursor_close =
FALSE;
2314 for(
size_t k = 0;
k < 4;
k++)
2316 if(hypotf(pzx -
g->box[
k].x, pzy -
g->box[
k].y) < 15.f)
2318 g->active_node[
k] =
TRUE;
2319 g->is_cursor_close =
TRUE;
2327 if(
g->is_cursor_close)
2354 if(wd == 0.f || ht == 0.f)
return 0;
2357 if(
type == GDK_DOUBLE_BUTTON_PRESS)
2360 g->checker_ready =
FALSE;
2361 g->profile_ready =
FALSE;
2370 if(
g->box[0].x == -1.0f ||
g->box[1].y == -1.0f)
return 0;
2373 if(!
g->is_cursor_close)
return 0;
2375 float pzxpy[2] = { (float)
x, (
float)y };
2378 const float pzx = pzxpy[0];
2379 const float pzy = pzxpy[1];
2382 g->drag_drop =
TRUE;
2383 g->click_start.x = pzx;
2384 g->click_start.y = pzy;
2398 if(
g->box[0].x == -1.0f ||
g->box[1].y == -1.0f)
return 0;
2399 if(!
g->is_cursor_close || !
g->drag_drop)
return 0;
2404 if(wd == 0.f || ht == 0.f)
return 0;
2406 float pzxpy[2] = { (float)
x, (
float)y };
2409 const float pzx = pzxpy[0];
2410 const float pzy = pzxpy[1];
2414 g->click_end.x = pzx;
2415 g->click_end.y = pzy;
2425 int32_t pointerx, int32_t pointery)
2438 cairo_set_line_width(cr, 2.0 / zoom_scale);
2439 const double origin = 9. / zoom_scale;
2440 const double destination = 18. / zoom_scale;
2442 for(
size_t k = 0;
k < 4;
k++)
2444 if(
g->active_node[
k])
2447 cairo_set_source_rgba(cr, 1., 1., 1., 1.);
2449 cairo_move_to(cr,
g->box[
k].x - origin,
g->box[
k].y);
2450 cairo_line_to(cr,
g->box[
k].x - destination,
g->box[
k].y);
2452 cairo_move_to(cr,
g->box[
k].x + origin,
g->box[
k].y);
2453 cairo_line_to(cr,
g->box[
k].x + destination,
g->box[
k].y);
2455 cairo_move_to(cr,
g->box[
k].x,
g->box[
k].y - origin);
2456 cairo_line_to(cr,
g->box[
k].x,
g->box[
k].y - destination);
2458 cairo_move_to(cr,
g->box[
k].x,
g->box[
k].y + origin);
2459 cairo_line_to(cr,
g->box[
k].x,
g->box[
k].y + destination);
2465 cairo_set_source_rgba(cr, 1., 1., 1., 1.);
2466 cairo_arc(cr,
g->box[
k].x,
g->box[
k].y, 8. / zoom_scale, 0, 2. *
M_PI);
2470 cairo_set_source_rgba(cr, 0., 0., 0., 1.);
2471 cairo_arc(cr,
g->box[
k].x,
g->box[
k].y, 1.5 / zoom_scale, 0, 2. *
M_PI);
2476 cairo_set_line_width(cr, 1.5 / zoom_scale);
2477 cairo_set_source_rgba(cr, 1., 1., 1., 1.);
2478 const point_t top_ideal = { 0.5f, 1.f };
2480 const point_t bottom_ideal = { 0.5f, 0.f };
2482 cairo_move_to(cr,
top.x,
top.y);
2483 cairo_line_to(cr, bottom.
x, bottom.
y);
2486 const point_t left_ideal = { 0.f, 0.5f };
2488 const point_t right_ideal = { 1.f, 0.5f };
2490 cairo_move_to(cr, left.
x, left.
y);
2491 cairo_line_to(cr, right.
x, right.
y);
2504 if(!
g->checker || !
g->checker->finished || !
g->checker->values)
2508 const point_t target_center = { 0.5f, 0.5f };
2511 const char *msg = _(
"Error: No color data available for the selected chart.");
2512 cairo_set_font_size(cr, 20.0 / zoom_scale);
2513 cairo_text_extents_t extents;
2514 cairo_text_extents(cr, msg, &extents);
2517 cairo_set_source_rgba(cr, 1., 1., 1., 1.);
2519 new_target_center.
x - extents.width / 2.0 - 5.0 / zoom_scale,
2520 new_target_center.
y - extents.height / 2.0 - 5.0 / zoom_scale,
2521 extents.width + 10.0 / zoom_scale,
2522 extents.height + 10.0 / zoom_scale);
2526 cairo_set_source_rgba(cr, 1., 0., 0., 1.);
2527 cairo_select_font_face(cr,
"roboto", CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_NORMAL);
2528 cairo_move_to(cr, new_target_center.
x - extents.width / 2.0, new_target_center.
y + extents.height / 2.0);
2529 cairo_show_text(cr, msg);
2533 const float radius_x =
g->checker->radius * hypotf(1.f,
g->checker->ratio) *
g->safety_margin;
2534 const float radius_y = radius_x /
g->checker->ratio;
2536 for(
size_t k = 0;
k <
g->checker->patches;
k++)
2539 const point_t center = {
g->checker->values[
k].
x,
g->checker->values[
k].y };
2542 const point_t corners[4] = { {center.
x - radius_x, center.
y - radius_y},
2543 {center.
x + radius_x, center.
y - radius_y},
2544 {center.
x + radius_x, center.
y + radius_y},
2545 {center.
x - radius_x, center.
y + radius_y} };
2553 for(
size_t c = 0; c < 4; c++) new_corners[c] =
apply_homography(corners[c],
g->homography);
2555 cairo_set_line_cap(cr, CAIRO_LINE_CAP_SQUARE);
2556 cairo_set_source_rgba(cr, 0., 0., 0., 1.);
2557 cairo_move_to(cr, new_corners[0].
x, new_corners[0].y);
2558 cairo_line_to(cr, new_corners[1].
x, new_corners[1].y);
2559 cairo_line_to(cr, new_corners[2].
x, new_corners[2].y);
2560 cairo_line_to(cr, new_corners[3].
x, new_corners[3].y);
2561 cairo_line_to(cr, new_corners[0].
x, new_corners[0].y);
2566 if(
g->delta_E_in[
k] > 2.3f)
2569 cairo_move_to(cr, new_corners[0].
x, new_corners[0].y);
2570 cairo_line_to(cr, new_corners[2].
x, new_corners[2].y);
2572 if(
g->delta_E_in[
k] > 4.6f)
2575 cairo_move_to(cr, new_corners[1].
x, new_corners[1].y);
2576 cairo_line_to(cr, new_corners[3].
x, new_corners[3].y);
2580 cairo_set_line_width(cr, 5.0 / zoom_scale);
2581 cairo_stroke_preserve(cr);
2582 cairo_set_line_width(cr, 2.0 / zoom_scale);
2583 cairo_set_source_rgba(cr, 1., 1., 1., 1.);
2586 cairo_set_line_cap(cr, CAIRO_LINE_CAP_BUTT);
2590 work_profile->unbounded_coeffs_out, work_profile->
lutsize,
2593 cairo_set_source_rgba(cr,
RGB[0],
RGB[1],
RGB[2], 1.);
2594 cairo_arc(cr, new_center.
x, new_center.
y, 0.25 * (radius_x + radius_y) *
scaling, 0, 2. *
M_PI);
2606 gtk_widget_show(GTK_WIDGET(
g->checkers_color_list));
2607 gtk_widget_hide(GTK_WIDGET(
g->checker_msg));
2611 gtk_widget_hide(GTK_WIDGET(
g->checkers_color_list));
2612 gtk_widget_show_now(GTK_WIDGET(
g->checker_msg));
2617 gtk_widget_hide(GTK_WIDGET(
g->checkers_color_list));
2618 gtk_widget_hide(GTK_WIDGET(
g->checker_msg));
2632 if(!
g->colorcheckers)
return;
2634 const int selected_checker =
dt_conf_get_int(
"darkroom/modules/channelmixerrgb/colorchecker");
2642 if(
g->n_color == 0)
return;
2646 g_list_free(
g->colorcheckers_color);
2647 g->colorcheckers_color = NULL;
2650 const int checker_patch_nb = checker_label ? checker_label->
patch_nb : 0;
2652 if(checker_patch_nb > 0)
2654 for(GList *l = g_list_first(
g->colorcheckers_all_color); l; l = g_list_next(l))
2659 if(cht_label->
patch_nb == checker_patch_nb)
2662 g->colorcheckers_color = g_list_append(
g->colorcheckers_color, cht_label);
2676 g->n_colorcheckers = 0;
2678 g->n_colorcheckers = pos;
2682 gboolean has_builtin_checker =
FALSE;
2683 gboolean user_checker_separator_added =
FALSE;
2684 for(GList *l = g_list_first(
g->colorcheckers); l; l = g_list_next(l))
2690 has_builtin_checker =
TRUE;
2692 else if(has_builtin_checker && !user_checker_separator_added)
2695 user_checker_separator_added =
TRUE;
2712 g->optimization =
i;
2723 dt_conf_set_int(
"darkroom/modules/channelmixerrgb/colorchecker_color", selected_cmbbx_index);
2730 const char *color_path = color_label ? color_label->
path : NULL;
2738 if(wd == 0.f || ht == 0.f)
return;
2741 g->profile_ready =
FALSE;
2755 dt_conf_set_int(
"darkroom/modules/channelmixerrgb/colorchecker", checker_cmbbx_index);
2763 const char *color_path = color_label ? color_label->
path : NULL;
2771 if(wd == 0.f || ht == 0.f)
return;
2774 g->profile_ready =
FALSE;
2800 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(self->
off),
TRUE);
2805 if(wd == 0.f || ht == 0.f)
return;
2808 g->is_profiling_started = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(
g->cs.toggle));
2825 g->run_profile =
TRUE;
2838 g->run_validation =
TRUE;
2851 if(!
g->profile_ready)
return;
2858 check_if_close_to_daylight(
p->x,
p->y, &
p->temperature, NULL, NULL);
2860 p->red[0] =
g->mix[0][0];
2861 p->red[1] =
g->mix[0][1];
2862 p->red[2] =
g->mix[0][2];
2864 p->green[0] =
g->mix[1][0];
2865 p->green[1] =
g->mix[1][1];
2866 p->green[2] =
g->mix[1][2];
2868 p->blue[0] =
g->mix[2][0];
2869 p->blue[1] =
g->mix[2][1];
2870 p->blue[2] =
g->mix[2][2];
2919 check_if_close_to_daylight(
p->x,
p->y, &
p->temperature, &
p->illuminant, &
p->adaptation);
2952 gtk_label_set_markup(GTK_LABEL(
g->label_delta_E),
g->delta_E_label_text);
2963 d->version =
p->version;
2965 float norm_R = 1.0f;
2966 if(
p->normalize_R) norm_R =
p->red[0] +
p->red[1] +
p->red[2];
2968 float norm_G = 1.0f;
2969 if(
p->normalize_G) norm_G =
p->green[0] +
p->green[1] +
p->green[2];
2971 float norm_B = 1.0f;
2972 if(
p->normalize_B) norm_B =
p->blue[0] +
p->blue[1] +
p->blue[2];
2974 float norm_sat = 0.0f;
2975 if(
p->normalize_sat) norm_sat = (
p->saturation[0] +
p->saturation[1] +
p->saturation[2]) / 3.f;
2977 float norm_light = 0.0f;
2978 if(
p->normalize_light) norm_light = (
p->lightness[0] +
p->lightness[1] +
p->lightness[2]) / 3.f;
2980 float norm_grey =
p->grey[0] +
p->grey[1] +
p->grey[2];
2981 d->apply_grey = (
p->grey[0] != 0.f) || (
p->grey[1] != 0.f) || (
p->grey[2] != 0.f);
2982 if(!
p->normalize_grey || norm_grey == 0.f) norm_grey = 1.f;
2984 for(
int i = 0;
i < 3;
i++)
2986 d->MIX[0][
i] =
p->red[
i] / norm_R;
2987 d->MIX[1][
i] =
p->green[
i] / norm_G;
2988 d->MIX[2][
i] =
p->blue[
i] / norm_B;
2989 d->saturation[
i] = -
p->saturation[
i] + norm_sat;
2990 d->lightness[
i] =
p->lightness[
i] - norm_light;
2991 d->grey[
i] =
p->grey[
i] / norm_grey;
2997 d->saturation[0] = -
p->saturation[2] + norm_sat;
2998 d->saturation[2] = -
p->saturation[0] + norm_sat;
3006 d->adaptation =
p->adaptation;
3008 d->gamut = (
p->gamut == 0.f) ?
p->gamut : 1.f /
p->gamut;
3021 check_if_close_to_daylight(
x, y, NULL, NULL, &(
d->adaptation));
3023 d->illuminant_type =
p->illuminant;
3031 d->illuminant[3] = 0.f;
3033 dt_iop_fmt_log(self,
"commit: class=%s matrix_supported=%d illuminant=%d adaptation=%d custom_wb=[%.4f %.4f %.4f %.4f] xy=[%.4f %.4f]",
3036 p->illuminant,
d->adaptation, custom_wb[0], custom_wb[1], custom_wb[2], custom_wb[3],
x, y);
3046 d->p = powf(0.818155f /
d->illuminant[2], 0.0834f);
3071 gtk_widget_set_visible(
g->illuminant,
FALSE);
3072 gtk_widget_set_visible(
g->illum_color,
FALSE);
3073 gtk_widget_set_visible(
g->approx_cct,
FALSE);
3074 gtk_widget_set_visible(
g->color_picker,
FALSE);
3075 gtk_widget_set_visible(
g->temperature,
FALSE);
3076 gtk_widget_set_visible(
g->illum_fluo,
FALSE);
3077 gtk_widget_set_visible(
g->illum_led,
FALSE);
3078 gtk_widget_set_visible(
g->illum_x,
FALSE);
3079 gtk_widget_set_visible(
g->illum_y,
FALSE);
3085 gtk_widget_set_visible(
g->illuminant,
TRUE);
3086 gtk_widget_set_visible(
g->illum_color,
TRUE);
3087 gtk_widget_set_visible(
g->approx_cct,
TRUE);
3088 gtk_widget_set_visible(
g->color_picker,
TRUE);
3089 gtk_widget_set_visible(
g->temperature,
TRUE);
3090 gtk_widget_set_visible(
g->illum_fluo,
TRUE);
3091 gtk_widget_set_visible(
g->illum_led,
TRUE);
3092 gtk_widget_set_visible(
g->illum_x,
TRUE);
3096 switch(
p->illuminant)
3102 gtk_widget_set_visible(
g->adaptation,
TRUE);
3103 gtk_widget_set_visible(
g->temperature,
FALSE);
3104 gtk_widget_set_visible(
g->illum_fluo,
FALSE);
3105 gtk_widget_set_visible(
g->illum_led,
FALSE);
3106 gtk_widget_set_visible(
g->illum_x,
FALSE);
3107 gtk_widget_set_visible(
g->illum_y,
FALSE);
3113 gtk_widget_set_visible(
g->adaptation,
TRUE);
3114 gtk_widget_set_visible(
g->temperature,
TRUE);
3115 gtk_widget_set_visible(
g->illum_fluo,
FALSE);
3116 gtk_widget_set_visible(
g->illum_led,
FALSE);
3117 gtk_widget_set_visible(
g->illum_x,
FALSE);
3118 gtk_widget_set_visible(
g->illum_y,
FALSE);
3123 gtk_widget_set_visible(
g->adaptation,
TRUE);
3124 gtk_widget_set_visible(
g->temperature,
FALSE);
3125 gtk_widget_set_visible(
g->illum_fluo,
TRUE);
3126 gtk_widget_set_visible(
g->illum_led,
FALSE);
3127 gtk_widget_set_visible(
g->illum_x,
FALSE);
3128 gtk_widget_set_visible(
g->illum_y,
FALSE);
3133 gtk_widget_set_visible(
g->adaptation,
TRUE);
3134 gtk_widget_set_visible(
g->temperature,
FALSE);
3135 gtk_widget_set_visible(
g->illum_fluo,
FALSE);
3136 gtk_widget_set_visible(
g->illum_led,
TRUE);
3137 gtk_widget_set_visible(
g->illum_x,
FALSE);
3138 gtk_widget_set_visible(
g->illum_y,
FALSE);
3143 gtk_widget_set_visible(
g->adaptation,
TRUE);
3144 gtk_widget_set_visible(
g->temperature,
FALSE);
3145 gtk_widget_set_visible(
g->illum_fluo,
FALSE);
3146 gtk_widget_set_visible(
g->illum_led,
FALSE);
3147 gtk_widget_set_visible(
g->illum_x,
TRUE);
3148 gtk_widget_set_visible(
g->illum_y,
TRUE);
3153 gtk_widget_set_visible(
g->adaptation,
TRUE);
3154 gtk_widget_set_visible(
g->temperature,
FALSE);
3155 gtk_widget_set_visible(
g->illum_fluo,
FALSE);
3156 gtk_widget_set_visible(
g->illum_led,
FALSE);
3157 gtk_widget_set_visible(
g->illum_x,
FALSE);
3158 gtk_widget_set_visible(
g->illum_y,
FALSE);
3164 gtk_widget_set_visible(
g->adaptation,
FALSE);
3165 gtk_widget_set_visible(
g->temperature,
FALSE);
3166 gtk_widget_set_visible(
g->illum_fluo,
FALSE);
3167 gtk_widget_set_visible(
g->illum_led,
FALSE);
3168 gtk_widget_set_visible(
g->illum_x,
FALSE);
3169 gtk_widget_set_visible(
g->illum_y,
FALSE);
3232 dt_xyY_to_Lch(xyY2,
Lch);
3244 gtk_widget_queue_draw(
g->illum_x);
3245 gtk_widget_queue_draw(
g->illum_y);
3262 dt_LCH_2_Lab(Lch_hue,
Lab);
3270 dt_LCH_2_Lab(Lch_lightness,
Lab);
3278 dt_LCH_2_Lab(Lch_chroma,
Lab);
3285 gtk_widget_queue_draw(
g->hue_spot);
3286 gtk_widget_queue_draw(
g->lightness_spot);
3287 gtk_widget_queue_draw(
g->chroma_spot);
3288 gtk_widget_queue_draw(
g->target_spot);
3304 = {
g->primaries_achromatic_hue,
g->primaries_achromatic_purity,
g->primaries_red_hue,
3305 g->primaries_red_purity,
g->primaries_green_hue,
g->primaries_green_purity,
3306 g->primaries_blue_hue,
g->primaries_blue_purity,
g->primaries_gain };
3310 const float rows[3][3] = { {
p->red[0],
p->red[1],
p->red[2] },
3311 {
p->green[0],
p->green[1],
p->green[2] },
3312 {
p->blue[0],
p->blue[1],
p->blue[2] } };
3313 const gboolean
normalize[3] = {
p->normalize_R,
p->normalize_G,
p->normalize_B };
3314 float M[3][3] = { { 0.f } };
3315 float roundtrip[3][3] = { { 0.f } };
3338 = {
g->primaries_achromatic_hue,
g->primaries_achromatic_purity,
g->primaries_red_hue,
3339 g->primaries_red_purity,
g->primaries_green_hue,
g->primaries_green_purity,
3340 g->primaries_blue_hue,
g->primaries_blue_purity,
g->primaries_gain };
3359 convert_any_LMS_to_RGB(
LMS, work_rgb,
p->adaptation);
3363 for(
size_t c = 0; c < 3; c++) work_rgb[c] =
LMS[c];
3372 GtkWidget *w,
float stop,
float c,
float r,
float g,
float b)
3375 0.5f * (c *
g + 1 -
g),
3376 0.5f * (c * b + 1 - b)};
3395 const float sum =
RGB[0] +
RGB[1] +
RGB[2];
3396 if(sum != 0.f)
for(
int c = 0; c < 3; c++)
RGB[c] /= sum;
3412 gtk_widget_queue_draw(w_r);
3413 gtk_widget_queue_draw(w_b);
3414 gtk_widget_queue_draw(w_g);
3429 = {
g->simple_theta,
g->simple_psi,
g->simple_stretch_1,
g->simple_stretch_2,
g->simple_coupling_1,
3430 g->simple_coupling_2 };
3431 const float rows[3][3] = { {
p->red[0],
p->red[1],
p->red[2] },
3432 {
p->green[0],
p->green[1],
p->green[2] },
3433 {
p->blue[0],
p->blue[1],
p->blue[2] } };
3434 const gboolean
normalize[3] = {
p->normalize_R,
p->normalize_G,
p->normalize_B };
3435 float M[3][3] = { { 0.f } };
3436 float roundtrip[3][3] = { { 0.f } };
3474 = {
g->simple_theta,
g->simple_psi,
g->simple_stretch_1,
g->simple_stretch_2,
g->simple_coupling_1,
3475 g->simple_coupling_2 };
3489 gtk_widget_queue_draw(
g->illum_color);
3500 GtkAllocation allocation;
3501 gtk_widget_get_allocation(widget, &allocation);
3502 int width = allocation.width,
height = allocation.height;
3504 cairo_t *cr = cairo_create(cst);
3518 &
x, &y,
p->temperature,
p->illum_fluo,
p->illum_led);
3520 cairo_set_source_rgb(cr,
RGB[0],
RGB[1],
RGB[2]);
3527 cairo_set_source_surface(crf, cst, 0, 0);
3529 cairo_surface_destroy(cst);
3539 GtkAllocation allocation;
3540 gtk_widget_get_allocation(widget, &allocation);
3541 int width = allocation.width,
height = allocation.height;
3543 cairo_t *cr = cairo_create(cst);
3562 cairo_set_source_rgb(cr,
RGB[0],
RGB[1],
RGB[2]);
3569 cairo_set_source_surface(crf, cst, 0, 0);
3571 cairo_surface_destroy(cst);
3581 GtkAllocation allocation;
3582 gtk_widget_get_allocation(widget, &allocation);
3583 int width = allocation.width,
height = allocation.height;
3585 cairo_t *cr = cairo_create(cst);
3592 cairo_set_source_rgb(cr,
g->spot_RGB[0],
g->spot_RGB[1],
g->spot_RGB[2]);
3599 cairo_set_source_surface(crf, cst, 0, 0);
3601 cairo_surface_destroy(cst);
3618 check_if_close_to_daylight(
x, y, &
t, &test_illuminant, NULL);
3621 if(
t > 1667.f &&
t < 25000.f)
3625 str = g_strdup_printf(_(
"CCT: %.0f K (daylight)"),
t);
3626 gtk_widget_set_tooltip_text(GTK_WIDGET(
g->approx_cct),
3627 _(
"approximated correlated color temperature.\n"
3628 "this illuminant can be accurately modeled by a daylight spectrum,\n"
3629 "so its temperature is relevant and meaningful with a D illuminant."));
3633 str = g_strdup_printf(_(
"CCT: %.0f K (black body)"),
t);
3634 gtk_widget_set_tooltip_text(GTK_WIDGET(
g->approx_cct),
3635 _(
"approximated correlated color temperature.\n"
3636 "this illuminant can be accurately modeled by a black body spectrum,\n"
3637 "so its temperature is relevant and meaningful with a Planckian illuminant."));
3641 str = g_strdup_printf(_(
"CCT: %.0f K (invalid)"),
t);
3642 gtk_widget_set_tooltip_text(GTK_WIDGET(
g->approx_cct),
3643 _(
"approximated correlated color temperature.\n"
3644 "this illuminant cannot be accurately modeled by a daylight or black body spectrum,\n"
3645 "so its temperature is not relevant and meaningful and you need to use a custom illuminant."));
3650 str = g_strdup_printf(_(
"CCT: undefined"));
3651 gtk_widget_set_tooltip_text(GTK_WIDGET(
g->approx_cct),
3652 _(
"the approximated correlated color temperature\n"
3653 "cannot be computed at all so you need to use a custom illuminant."));
3655 gtk_label_set_text(GTK_LABEL(
g->approx_cct), str);
3689 dt_print(
DT_DEBUG_DEV,
"[picker/channelmixerrgb] history commit source=illum_xy_callback slider=%p\n",
3710 g->is_profiling_started =
FALSE;
3720 float simple_error = INFINITY;
3721 float primaries_error = INFINITY;
3737 gboolean use_mixing =
TRUE;
3739 use_mixing =
dt_conf_get_bool(
"darkroom/modules/channelmixerrgb/use_mixing");
3740 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(
g->use_mixing), use_mixing);
3742 float lightness = 50.f;
3759 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(
g->clip),
p->clip);
3760 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(
g->normalize_R),
p->normalize_R);
3761 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(
g->normalize_G),
p->normalize_G);
3762 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(
g->normalize_B),
p->normalize_B);
3782 gtk_widget_hide(GTK_WIDGET(
g->saturation_version));
3784 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(
g->normalize_sat),
p->normalize_sat);
3785 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(
g->normalize_light),
p->normalize_light);
3786 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(
g->normalize_grey),
p->normalize_grey);
3793 const int selected_checker =
dt_conf_get_int(
"darkroom/modules/channelmixerrgb/colorchecker");
3796 const int selected_color =
dt_conf_get_int(
"darkroom/modules/channelmixerrgb/colorchecker_color");
3800 const char *color_path = label ? label->
path : NULL;
3807 const int j =
dt_conf_get_int(
"darkroom/modules/channelmixerrgb/optimization");
3809 g->optimization = j;
3817 g->is_profiling_started =
FALSE;
3822 g->spot_RGB[0] = 0.f;
3823 g->spot_RGB[1] = 0.f;
3824 g->spot_RGB[2] = 0.f;
3825 g->spot_RGB[3] = 0.f;
3843 if(
g->delta_E_label_text)
3846 g->delta_E_label_text = NULL;
3869 d->red[0] =
d->green[1] =
d->blue[2] = 1.0;
3870 d->normalize_R =
TRUE;
3871 d->normalize_G =
TRUE;
3872 d->normalize_B =
TRUE;
3890 fprintf(stderr,
"[channelmixerrgb] BUG: temperature (iop_order %d) must run before channelmixerrgb "
3891 "(iop_order %d); white-balance-derived defaults will be wrong on a fresh history\n",
3895 d->normalize_R =
TRUE;
3896 d->normalize_G =
TRUE;
3897 d->normalize_B =
TRUE;
3899 d->x =
module->get_f("x")->Float.Default;
3900 d->y =
module->get_f("y")->Float.Default;
3901 d->temperature =
module->get_f("temperature")->Float.Default;
3902 d->illuminant =
module->get_f("illuminant")->Enum.Default;
3903 d->adaptation =
module->get_f("adaptation")->Enum.Default;
3908 const gboolean is_modern =
3915 const dt_image_t *img = &
module->dev->image_storage;
3918 gboolean CAT_already_applied =
3923 module->default_enabled = FALSE;
3926 if(!CAT_already_applied
3935 check_if_close_to_daylight(
d->x,
d->y, &(
d->temperature), &(
d->illuminant), &(
d->adaptation));
3936 module->workflow_enabled = dt_image_needs_rawprepare(img);
3964 const gboolean use_mixing = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(
g->use_mixing));
3970 dt_conf_set_float(
"darkroom/modules/channelmixerrgb/hue", Lch_target[2] * 360.f);
3971 dt_conf_set_bool(
"darkroom/modules/channelmixerrgb/use_mixing", use_mixing);
3988 gtk_stack_set_visible_child_name(GTK_STACK(
g->mixer_stack),
4013 float error = INFINITY;
4016 dt_control_log(_(
"simple mixer mode requires all three output rows to be normalized with non-zero sums."));
4032 const float rows[3][3] = { {
p->red[0],
p->red[1],
p->red[2] },
4033 {
p->green[0],
p->green[1],
p->green[2] },
4034 {
p->blue[0],
p->blue[1],
p->blue[2] } };
4035 const gboolean
normalize[3] = {
p->normalize_R,
p->normalize_G,
p->normalize_B };
4036 float M[3][3] = { { 0.f } };
4037 float error = INFINITY;
4041 dt_control_log(_(
"primaries mixer mode requires a non-singular 3x3 matrix with non-zero affine sums."));
4050 gboolean changed =
p->normalize_R ||
p->normalize_G ||
p->normalize_B;
4051 for(
int col = 0; col < 3; col++)
4053 changed = changed ||
p->red[col] !=
M[0][col] ||
p->green[col] !=
M[1][col] ||
p->blue[col] !=
M[2][col];
4054 p->red[col] =
M[0][col];
4055 p->green[col] =
M[1][col];
4056 p->blue[col] =
M[2][col];
4064 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(
g->normalize_R),
FALSE);
4065 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(
g->normalize_G),
FALSE);
4066 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(
g->normalize_B),
FALSE);
4097 = {
g->simple_theta,
g->simple_psi,
g->simple_stretch_1,
g->simple_stretch_2,
g->simple_coupling_1,
4098 g->simple_coupling_2 };
4100 float M[3][3] = { { 0.f } };
4105 for(
int col = 0; col < 3; col++)
4107 p->red[col] =
M[0][col];
4108 p->green[col] =
M[1][col];
4109 p->blue[col] =
M[2][col];
4113 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(
g->normalize_R),
p->normalize_R);
4114 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(
g->normalize_G),
p->normalize_G);
4115 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(
g->normalize_B),
p->normalize_B);
4130 dt_print(
DT_DEBUG_DEV,
"[channelmixerrgb] history commit source=simple_slider slider=%p\n", (
void *)slider);
4142 = {
g->primaries_achromatic_hue,
g->primaries_achromatic_purity,
g->primaries_red_hue,
4143 g->primaries_red_purity,
g->primaries_green_hue,
g->primaries_green_purity,
4144 g->primaries_blue_hue,
g->primaries_blue_purity,
g->primaries_gain };
4148 float M[3][3] = { { 0.f } };
4153 dt_control_log(_(
"primaries mixer mode requires a non-singular 3x3 matrix with non-zero affine sums."));
4157 for(
int col = 0; col < 3; col++)
4159 p->red[col] =
M[0][col];
4160 p->green[col] =
M[1][col];
4161 p->blue[col] =
M[2][col];
4168 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(
g->normalize_R),
FALSE);
4169 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(
g->normalize_G),
FALSE);
4170 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(
g->normalize_B),
FALSE);
4185 dt_print(
DT_DEBUG_DEV,
"[channelmixerrgb] history commit source=primaries_slider slider=%p\n", (
void *)slider);
4194 const gboolean simple_widget
4195 = w ==
g->simple_theta || w ==
g->simple_psi || w ==
g->simple_stretch_1 || w ==
g->simple_stretch_2
4196 || w ==
g->simple_coupling_1 || w ==
g->simple_coupling_2;
4197 const gboolean primaries_widget
4198 = w ==
g->primaries_achromatic_hue || w ==
g->primaries_achromatic_purity || w ==
g->primaries_red_hue
4199 || w ==
g->primaries_red_purity || w ==
g->primaries_green_hue || w ==
g->primaries_green_purity
4200 || w ==
g->primaries_blue_hue || w ==
g->primaries_blue_purity || w ==
g->primaries_gain;
4201 const gboolean
normalize[3] = {
p->normalize_R,
p->normalize_G,
p->normalize_B };
4203 const gboolean complete_widget
4204 = w ==
g->scale_red_R || w ==
g->scale_red_G || w ==
g->scale_red_B || w ==
g->scale_green_R
4205 || w ==
g->scale_green_G || w ==
g->scale_green_B || w ==
g->scale_blue_R || w ==
g->scale_blue_G
4206 || w ==
g->scale_blue_B || w ==
g->normalize_R || w ==
g->normalize_G || w ==
g->normalize_B;
4209 const gboolean illuminant_widgets_ready
4229 check_if_close_to_daylight(
p->x,
p->y, &(
p->temperature), NULL, &(
p->adaptation));
4238 check_if_close_to_daylight(
p->x,
p->y, &(
p->temperature), NULL, &(
p->adaptation));
4241 dt_control_log(_(
"white balance successfully extracted from raw image"));
4251 if(!
IS_NULL_PTR(w) && (w ==
g->illuminant || w ==
g->illum_fluo || w ==
g->illum_led || w ==
g->temperature))
4258 illuminant_to_xy(
p->illuminant, NULL, NULL, &(
p->x), &(
p->y),
p->temperature,
p->illum_fluo,
p->illum_led);
4264 check_if_close_to_daylight(
p->x,
p->y, &(
p->temperature), NULL, NULL);
4270 if(
IS_NULL_PTR(w) || w ==
g->hue_spot || w ==
g->chroma_spot || w ==
g->lightness_spot || w ==
g->spot_settings)
4275 if((
IS_NULL_PTR(w) || w ==
g->illuminant || w ==
g->illum_fluo || w ==
g->illum_led || w ==
g->temperature)
4276 && illuminant_widgets_ready)
4300 if(w ==
g->adaptation)
4303 if(
IS_NULL_PTR(w) || w ==
g->adaptation || primaries_widget || w ==
g->scale_red_R || w ==
g->scale_red_G || w ==
g->scale_red_B || w ==
g->normalize_R)
4305 if(
IS_NULL_PTR(w) || w ==
g->adaptation || primaries_widget || w ==
g->scale_green_R || w ==
g->scale_green_G || w ==
g->scale_green_B || w ==
g->normalize_G)
4306 _update_RGB_colors(self, 0, 1, 0,
p->normalize_G,
p->green,
g->scale_green_R,
g->scale_green_G,
g->scale_green_B);
4307 if(
IS_NULL_PTR(w) || w ==
g->adaptation || primaries_widget || w ==
g->scale_blue_R || w ==
g->scale_blue_G || w ==
g->scale_blue_B || w ==
g->normalize_B)
4308 _update_RGB_colors(self, 0, 0, 1,
p->normalize_B,
p->blue,
g->scale_blue_R,
g->scale_blue_G,
g->scale_blue_B);
4310 if(rows_are_normalized && !simple_widget && (
IS_NULL_PTR(w) || complete_widget))
4312 float error = INFINITY;
4318 error,
p->normalize_R,
p->normalize_G,
p->normalize_B);
4319 dt_control_log(_(
"simple mixer mode requires all three output rows to be normalized with non-zero sums."));
4329 if(!primaries_widget && (
IS_NULL_PTR(w) || complete_widget || simple_widget || w ==
g->adaptation))
4331 float error = INFINITY;
4344 if(
IS_NULL_PTR(w) || w ==
g->adaptation || complete_widget || simple_widget || primaries_widget)
4347 if(
IS_NULL_PTR(w) || w ==
g->adaptation || complete_widget || simple_widget || primaries_widget)
4352 if((
p->grey[0] != 0.f) || (
p->grey[1] != 0.f) || (
p->grey[2] != 0.f))
4353 if((
p->grey[0] +
p->grey[1] +
p->grey[2] == 0.f) &&
p->normalize_grey)
4354 dt_control_log(_(
"color calibration: the sum of the gray channel parameters is zero, normalization will be disabled."));
4359 gtk_widget_queue_draw(
g->adaptation);
4380 if(!isfinite(
RGB[0]) || !isfinite(
RGB[1]) || !isfinite(
RGB[2]))
4402 if(!isfinite(
XYZ[0]) || !isfinite(
XYZ[1]) || !isfinite(
XYZ[2]))
return;
4413 gtk_label_set_text(GTK_LABEL(
g->Lch_origin),
4414 g_strdup_printf(_(
"L: \t%.1f %%\nh: \t%.1f \302\260\nc: \t%.1f"),
4416 gtk_widget_queue_draw(
g->origin_spot);
4420 const gboolean use_mixing = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(
g->use_mixing));
4425 float norm_R = 1.0f;
4426 if(
p->normalize_R) norm_R =
p->red[0] +
p->red[1] +
p->red[2];
4428 float norm_G = 1.0f;
4429 if(
p->normalize_G) norm_G =
p->green[0] +
p->green[1] +
p->green[2];
4431 float norm_B = 1.0f;
4432 if(
p->normalize_B) norm_B =
p->blue[0] +
p->blue[1] +
p->blue[2];
4434 for(
int i = 0;
i < 3;
i++)
4436 MIX[0][
i] =
p->red[
i] / norm_R;
4437 MIX[1][
i] =
p->green[
i] / norm_G;
4438 MIX[2][
i] =
p->blue[
i] / norm_B;
4465 const float pp = powf(0.818155f / LMS_illuminant[2], 0.0834f);
4475 dt_load_simd_aligned(LMS_illuminant),
4484 dot_product(LMS_output, MIX, temp);
4492 dt_Lab_2_LCH(Lab_output, Lch_output);
4504 dt_conf_set_float(
"darkroom/modules/channelmixerrgb/hue", Lch_output[2] * 360.f);
4505 dt_conf_set_bool(
"darkroom/modules/channelmixerrgb/use_mixing", use_mixing);
4521 dt_LCH_2_Lab(Lch_target, Lab_target);
4523 const float Y_target = XYZ_target[1];
4524 for(
int c = 0; c < 3; c++) XYZ_target[c] /= Y_target;
4525 dt_store_simd_aligned(LMS_target, convert_any_XYZ_to_LMS(dt_load_simd_aligned(XYZ_target),
p->adaptation));
4543 float MIX_INV_3x3[9];
4560 dot_product(LMS_target, MIX_INV, temp);
4568 const float Y_mix = XYZ_target[1];
4569 for(
int c = 0; c < 3; c++) XYZ_target[c] /= Y_mix;
4570 dt_store_simd_aligned(LMS_target, convert_any_XYZ_to_LMS(dt_load_simd_aligned(XYZ_target),
p->adaptation));
4580 const float Y =
XYZ[1];
4581 for(
int c = 0; c < 3; c++)
XYZ[c] /= Y;
4592 for(
int c = 0; c < 3; c++) illuminant_LMS[c] = D50[c] *
LMS[c] / LMS_target[c];
4593 dt_store_simd_aligned(illuminant_XYZ, convert_any_LMS_to_XYZ(dt_load_simd_aligned(illuminant_LMS),
p->adaptation));
4596 const float sum = fmaxf(illuminant_XYZ[0] + illuminant_XYZ[1] + illuminant_XYZ[2],
NORM_MIN);
4597 illuminant_XYZ[0] /= sum;
4598 illuminant_XYZ[2] = illuminant_XYZ[1];
4599 illuminant_XYZ[1] /= sum;
4601 p->x = illuminant_XYZ[0];
4602 p->y = illuminant_XYZ[1];
4610 check_if_close_to_daylight(
p->x,
p->y, &
p->temperature, NULL, NULL);
4618 dt_xyY_to_Lch(
xyY, Lch_illuminant);
4627 gtk_widget_queue_draw(
g->origin_spot);
4631 if(memcmp(&previous,
p,
sizeof(previous)) != 0)
4637 dt_print(
DT_DEBUG_DEV,
"[picker/channelmixerrgb] history commit source=auto_set_illuminant\n");
4647 dt_print(
DT_DEBUG_DEV,
"[picker/channelmixerrgb] apply picker=%p pipe=%p\n", (
void *)picker, (
void *)pipe);
4665 float norm_R = 1.0f;
4666 if(
p->normalize_R) norm_R =
p->red[0] +
p->red[1] +
p->red[2];
4668 float norm_G = 1.0f;
4669 if(
p->normalize_G) norm_G =
p->green[0] +
p->green[1] +
p->green[2];
4671 float norm_B = 1.0f;
4672 if(
p->normalize_B) norm_B =
p->blue[0] +
p->blue[1] +
p->blue[2];
4674 for(
int c = 0; c < 3; c++)
4676 MIX[0][c] =
p->red[c] / norm_R;
4677 MIX[1][c] =
p->green[c] / norm_G;
4678 MIX[2][c] =
p->blue[c] / norm_B;
4681 float lightness = 50.f;
4697 dt_LCH_2_Lab(Lch_target, Lab_target);
4701 const float Y_target = XYZ_target[1];
4702 for(
int c = 0; c < 3; c++) XYZ_target[c] /= Y_target;
4703 dt_store_simd_aligned(LMS_target, convert_any_XYZ_to_LMS(dt_load_simd_aligned(XYZ_target),
p->adaptation));
4708 float MIX_INV_3x3[9];
4715 dot_product(LMS_target, MIX_INV, temp);
4718 const float Y_mix = XYZ_target[1];
4720 for(
int c = 0; c < 3; c++) XYZ_target[c] /= Y_mix;
4721 dt_store_simd_aligned(LMS_target, convert_any_XYZ_to_LMS(dt_load_simd_aligned(XYZ_target),
p->adaptation));
4723 const float *
const restrict in = (
float *)
i;
4724 float average_L = 0.f;
4725 float average_M = 0.f;
4726 float average_S = 0.f;
4727 size_t valid_pixels = 0;
4731 for(
size_t k = 0;
k < roi_out->
width * roi_out->
height * 4;
k += 4)
4737 current_profile->unbounded_coeffs_in, current_profile->
lutsize,
4741 if(!isfinite(
LMS[0]) || !isfinite(
LMS[1]) || !isfinite(
LMS[2]))
continue;
4743 average_L +=
LMS[0];
4744 average_M +=
LMS[1];
4745 average_S +=
LMS[2];
4749 if(valid_pixels == 0)
return;
4751 const float norm = 1.f / (float)valid_pixels;
4752 dt_aligned_pixel_t average_LMS = { average_L * norm, average_M * norm, average_S * norm, 0.f };
4756 dt_store_simd_aligned(average_XYZ, convert_any_LMS_to_XYZ(dt_load_simd_aligned(average_LMS),
p->adaptation));
4757 const float Y_average_lms = average_XYZ[1];
4758 for(
int c = 0; c < 3; c++) average_XYZ[c] /= Y_average_lms;
4759 dt_store_simd_aligned(average_LMS, convert_any_XYZ_to_LMS(dt_load_simd_aligned(average_XYZ),
p->adaptation));
4766 for(
int c = 0; c < 3; c++)
4768 const float target = copysignf(fmaxf(fabsf(LMS_target[c]),
NORM_MIN), LMS_target[c]);
4769 illuminant_LMS[c] = D50[c] * average_LMS[c] / target;
4772 dt_store_simd_aligned(illuminant_XYZ, convert_any_LMS_to_XYZ(dt_load_simd_aligned(illuminant_LMS),
p->adaptation));
4774 const float sum = fmaxf(illuminant_XYZ[0] + illuminant_XYZ[1] + illuminant_XYZ[2],
NORM_MIN);
4775 p->x = illuminant_XYZ[0] / sum;
4776 p->y = illuminant_XYZ[1] / sum;
4778 check_if_close_to_daylight(
p->x,
p->y, &
p->temperature, NULL, NULL);
4787 for(
size_t k = 0;
k < 4;
k++)
4789 g->box[
k].x =
g->box[
k].y = -1.;
4792 g->is_cursor_close =
FALSE;
4794 g->is_profiling_started =
FALSE;
4796 g->run_validation =
FALSE;
4797 g->profile_ready =
FALSE;
4798 g->checker_ready =
FALSE;
4799 g->delta_E_in = NULL;
4800 g->delta_E_label_text = NULL;
4801 g->colorcheckers = NULL;
4820 gtk_widget_set_tooltip_text(GTK_WIDGET(
g->adaptation),
4821 _(
"choose the method to adapt the illuminant\n"
4822 "and the colorspace in which the module works: \n"
4823 "- Linear Bradford (1985) is consistent with ICC v4 toolchain.\n"
4824 "- CAT16 (2016) is more robust and accurate.\n"
4825 "- Non-linear Bradford (1985) is the original Bradford,\n"
4826 " it can produce better results than the linear version, but is unreliable.\n"
4827 "- XYZ is a simple scaling in XYZ space. It is not recommended in general.\n"
4828 "- none disables any adaptation and uses pipeline working RGB."));
4833 gtk_box_pack_start(GTK_BOX(hbox),
g->approx_cct,
FALSE,
FALSE, 0);
4835 g->illum_color = GTK_WIDGET(gtk_drawing_area_new());
4838 gtk_widget_set_tooltip_text(GTK_WIDGET(
g->illum_color),
4839 _(
"this is the color of the scene illuminant before chromatic adaptation\n"
4840 "this color will be turned into pure white by the adaptation."));
4843 gtk_box_pack_start(GTK_BOX(hbox),
g->illum_color,
TRUE,
TRUE, 0);
4846 gtk_widget_set_tooltip_text(
g->color_picker, _(
"set white balance to detected from area"));
4848 gtk_box_pack_start(GTK_BOX(self->
widget), GTK_WIDGET(hbox),
FALSE,
FALSE, 0);
4861 g_signal_connect(G_OBJECT(
g->illum_x),
"value-changed", G_CALLBACK(
illum_xy_callback), self);
4862 gtk_box_pack_start(GTK_BOX(self->
widget), GTK_WIDGET(
g->illum_x),
FALSE,
FALSE, 0);
4868 g_signal_connect(G_OBJECT(
g->illum_y),
"value-changed", G_CALLBACK(
illum_xy_callback), self);
4869 gtk_box_pack_start(GTK_BOX(self->
widget), GTK_WIDGET(
g->illum_y),
FALSE,
FALSE, 0);
4884 "plugins/darkroom/channelmixerrgb/expand_picker_mapping",
4885 _(
"spot color mapping"),
4886 GTK_BOX(self->
widget), GTK_PACK_END);
4888 gtk_widget_set_tooltip_text(
g->csspot.expander, _(
"use a color checker target to autoset CAT and channels"));
4891 _(
"\"correction\" automatically adjust the illuminant\n"
4892 "such that the input color is mapped to the target.\n"
4893 "\"measure\" simply shows how an input color is mapped by the CAT\n"
4894 "and can be used to sample a target."),
4898 gtk_box_pack_start(GTK_BOX(
g->csspot.container), GTK_WIDGET(
g->spot_mode),
TRUE,
TRUE, 0);
4901 gchar *label = N_(
"take channel mixing into account");
4902 g->use_mixing = gtk_check_button_new_with_label(_(label));
4903 gtk_label_set_ellipsize(GTK_LABEL(gtk_bin_get_child(GTK_BIN(
g->use_mixing))), PANGO_ELLIPSIZE_END);
4904 gtk_widget_set_tooltip_text(
g->use_mixing,
4905 _(
"compute the target by taking the channel mixing into account.\n"
4906 "if disabled, only the CAT is considered."));
4907 gtk_box_pack_start(GTK_BOX(
g->csspot.container), GTK_WIDGET(
g->use_mixing),
TRUE,
TRUE, 0);
4915 g->origin_spot = GTK_WIDGET(gtk_drawing_area_new());
4918 gtk_widget_set_tooltip_text(GTK_WIDGET(
g->origin_spot),
4919 _(
"the input color that should be mapped to the target"));
4921 g_signal_connect(G_OBJECT(
g->origin_spot),
"draw", G_CALLBACK(
origin_color_draw), self);
4922 gtk_box_pack_start(GTK_BOX(vvbox),
g->origin_spot,
TRUE,
TRUE, 0);
4924 g->Lch_origin = gtk_label_new(_(
"L: \tN/A\nh: \tN/A\nc: \tN/A"));
4925 gtk_widget_set_tooltip_text(GTK_WIDGET(
g->Lch_origin),
4926 _(
"these LCh coordinates are computed from CIE Lab 1976 coordinates"));
4927 gtk_box_pack_start(GTK_BOX(vvbox), GTK_WIDGET(
g->Lch_origin),
FALSE,
FALSE, 0);
4935 g->target_spot = GTK_WIDGET(gtk_drawing_area_new());
4938 gtk_widget_set_tooltip_text(GTK_WIDGET(
g->target_spot),
4939 _(
"the desired target color after mapping"));
4941 g_signal_connect(G_OBJECT(
g->target_spot),
"draw", G_CALLBACK(
target_color_draw), self);
4942 gtk_box_pack_start(GTK_BOX(vvbox),
g->target_spot,
TRUE,
TRUE, 0);
4948 gtk_box_pack_start(GTK_BOX(vvbox), GTK_WIDGET(
g->lightness_spot),
TRUE,
TRUE, 0);
4955 gtk_box_pack_start(GTK_BOX(vvbox), GTK_WIDGET(
g->hue_spot),
TRUE,
TRUE, 0);
4961 gtk_box_pack_start(GTK_BOX(vvbox), GTK_WIDGET(
g->chroma_spot),
TRUE,
TRUE, 0);
4966 gtk_box_pack_start(GTK_BOX(
g->csspot.container), GTK_WIDGET(hhbox),
FALSE,
FALSE, 0);
4974 gtk_widget_set_tooltip_text(
g->mixer_mode,
4975 _(
"complete exposes the original nine mixer coefficients.\n"
4976 "simple rebuilds the normalized mixer as an exact chroma-plane rotation,\n"
4977 "two signed stretches and two neutral couplings.\n"
4978 "primaries rebuilds the mixer as a generalized primaries, white tint and gain model."));
4979 gtk_box_pack_start(GTK_BOX(mixer_page), GTK_WIDGET(
g->mixer_mode),
FALSE,
FALSE, 0);
4982 g->mixer_stack = gtk_stack_new();
4983 gtk_stack_set_transition_type(GTK_STACK(
g->mixer_stack), GTK_STACK_TRANSITION_TYPE_NONE);
4984 gtk_box_pack_start(GTK_BOX(mixer_page), GTK_WIDGET(
g->mixer_stack),
FALSE,
FALSE, 0);
4987 gtk_stack_add_named(GTK_STACK(
g->mixer_stack), mixer_complete,
"complete");
4990#define MIXER_ROW(var, short, section, swap) \
4991 gtk_box_pack_start(GTK_BOX(mixer_complete), dt_ui_section_label_new(section), FALSE, FALSE, 0); \
4992 self->widget = mixer_complete; \
4993 first = dt_bauhaus_slider_from_params(self, swap ? #var "[2]" : #var "[0]");\
4994 dt_bauhaus_slider_set_digits(first, 3); \
4995 dt_bauhaus_widget_set_label(first, N_("input R")); \
4996 second = dt_bauhaus_slider_from_params(self, #var "[1]"); \
4997 dt_bauhaus_slider_set_digits(second, 3); \
4998 dt_bauhaus_widget_set_label(second, N_("input G")); \
4999 third = dt_bauhaus_slider_from_params(self, swap ? #var "[0]" : #var "[2]");\
5000 dt_bauhaus_slider_set_digits(third, 3); \
5001 dt_bauhaus_widget_set_label(third, N_("input B")); \
5002 g->scale_##var##_R = swap ? third : first; \
5003 g->scale_##var##_G = second; \
5004 g->scale_##var##_B = swap ? first : third; \
5005 g->normalize_##short = dt_bauhaus_toggle_from_params(self, "normalize_" #short);
5012 gtk_stack_add_named(GTK_STACK(
g->mixer_stack), mixer_simple,
"simple");
5018 gtk_widget_set_tooltip_text(
g->simple_theta, _(
"global rotation of the normalized chroma plane."));
5019 gtk_box_pack_start(GTK_BOX(mixer_simple), GTK_WIDGET(
g->simple_theta),
FALSE,
FALSE, 0);
5028 gtk_widget_set_tooltip_text(
g->simple_psi, _(
"orientation of the principal stretch axes in the chroma plane."));
5029 gtk_box_pack_start(GTK_BOX(mixer_simple), GTK_WIDGET(
g->simple_psi),
FALSE,
FALSE, 0);
5034 gtk_widget_set_tooltip_text(
g->simple_stretch_1, _(
"stretch along the first principal chroma axis. 0 neutralizes chroma, 1 keeps identity, -1 reverses chroma and +/-1.5 add contrast."));
5035 gtk_box_pack_start(GTK_BOX(mixer_simple), GTK_WIDGET(
g->simple_stretch_1),
FALSE,
FALSE, 0);
5040 gtk_widget_set_tooltip_text(
g->simple_stretch_2, _(
"stretch along the second principal chroma axis. 0 neutralizes chroma, 1 keeps identity, -1 reverses chroma and +/-1.5 add contrast."));
5041 gtk_box_pack_start(GTK_BOX(mixer_simple), GTK_WIDGET(
g->simple_stretch_2),
FALSE,
FALSE, 0);
5050 gtk_widget_set_tooltip_text(
g->simple_coupling_2, _(
"chroma direction, in the fixed chroma basis, that is coupled into the achromatic axis."));
5051 gtk_box_pack_start(GTK_BOX(mixer_simple), GTK_WIDGET(
g->simple_coupling_2),
FALSE,
FALSE, 0);
5056 gtk_widget_set_tooltip_text(
g->simple_coupling_1, _(
"strength of the chroma-to-achromatic coupling in the fixed chroma basis."));
5057 gtk_box_pack_start(GTK_BOX(mixer_simple), GTK_WIDGET(
g->simple_coupling_1),
FALSE,
FALSE, 0);
5061 gtk_stack_add_named(GTK_STACK(
g->mixer_stack), mixer_primaries,
"primaries");
5069 gtk_widget_set_tooltip_text(
g->primaries_achromatic_hue,
5070 _(
"rotate the custom white vector around the D50 white of the current mixer basis."));
5071 gtk_box_pack_start(GTK_BOX(mixer_primaries), GTK_WIDGET(
g->primaries_achromatic_hue),
FALSE,
FALSE, 0);
5072 g_signal_connect(G_OBJECT(
g->primaries_achromatic_hue),
"value-changed",
5077 gtk_widget_set_tooltip_text(
g->primaries_achromatic_purity,
5078 _(
"distance of the custom white vector from the D50 white within the current mixer basis."));
5079 gtk_box_pack_start(GTK_BOX(mixer_primaries), GTK_WIDGET(
g->primaries_achromatic_purity),
FALSE,
FALSE, 0);
5080 g_signal_connect(G_OBJECT(
g->primaries_achromatic_purity),
"value-changed",
5089 gtk_widget_set_tooltip_text(
g->primaries_red_hue,
5090 _(
"rotate the first basis vector around the D50 white of the current mixer basis."));
5091 gtk_box_pack_start(GTK_BOX(mixer_primaries), GTK_WIDGET(
g->primaries_red_hue),
FALSE,
FALSE, 0);
5092 g_signal_connect(G_OBJECT(
g->primaries_red_hue),
"value-changed",
5097 gtk_widget_set_tooltip_text(
g->primaries_red_purity,
5098 _(
"radial scaling of the first basis vector inside the affine primaries footprint."));
5099 gtk_box_pack_start(GTK_BOX(mixer_primaries), GTK_WIDGET(
g->primaries_red_purity),
FALSE,
FALSE, 0);
5100 g_signal_connect(G_OBJECT(
g->primaries_red_purity),
"value-changed",
5109 gtk_widget_set_tooltip_text(
g->primaries_green_hue,
5110 _(
"rotate the second basis vector around the D50 white of the current mixer basis."));
5111 gtk_box_pack_start(GTK_BOX(mixer_primaries), GTK_WIDGET(
g->primaries_green_hue),
FALSE,
FALSE, 0);
5112 g_signal_connect(G_OBJECT(
g->primaries_green_hue),
"value-changed",
5117 gtk_widget_set_tooltip_text(
g->primaries_green_purity,
5118 _(
"radial scaling of the second basis vector inside the affine primaries footprint."));
5119 gtk_box_pack_start(GTK_BOX(mixer_primaries), GTK_WIDGET(
g->primaries_green_purity),
FALSE,
FALSE, 0);
5120 g_signal_connect(G_OBJECT(
g->primaries_green_purity),
"value-changed",
5129 gtk_widget_set_tooltip_text(
g->primaries_blue_hue,
5130 _(
"rotate the third basis vector around the D50 white of the current mixer basis."));
5131 gtk_box_pack_start(GTK_BOX(mixer_primaries), GTK_WIDGET(
g->primaries_blue_hue),
FALSE,
FALSE, 0);
5132 g_signal_connect(G_OBJECT(
g->primaries_blue_hue),
"value-changed",
5137 gtk_widget_set_tooltip_text(
g->primaries_blue_purity,
5138 _(
"radial scaling of the third basis vector inside the affine primaries footprint."));
5139 gtk_box_pack_start(GTK_BOX(mixer_primaries), GTK_WIDGET(
g->primaries_blue_purity),
FALSE,
FALSE, 0);
5140 g_signal_connect(G_OBJECT(
g->primaries_blue_purity),
"value-changed",
5147 gtk_widget_set_tooltip_text(
g->primaries_gain,
5148 _(
"global gain multiplying the custom white vector after the affine primaries transform."));
5149 gtk_box_pack_start(GTK_BOX(mixer_primaries), GTK_WIDGET(
g->primaries_gain),
FALSE,
FALSE, 0);
5150 g_signal_connect(G_OBJECT(
g->primaries_gain),
"value-changed",
5154 _(
"output colorfulness, brightness and B&W mixing"));
5155 self->
widget = outputs_page;
5157#define OUTPUT_SECTION(var, short, section, swap) \
5158 gtk_box_pack_start(GTK_BOX(outputs_page), dt_ui_section_label_new(section), FALSE, FALSE, 0); \
5160 first = dt_bauhaus_slider_from_params(self, swap ? #var "[2]" : #var "[0]");\
5161 dt_bauhaus_slider_set_digits(first, 3); \
5162 dt_bauhaus_widget_set_label(first, N_("input R")); \
5164 second = dt_bauhaus_slider_from_params(self, #var "[1]"); \
5165 dt_bauhaus_slider_set_digits(second, 3); \
5166 dt_bauhaus_widget_set_label(second, N_("input G")); \
5168 third = dt_bauhaus_slider_from_params(self, swap ? #var "[0]" : #var "[2]");\
5169 dt_bauhaus_slider_set_digits(third, 3); \
5170 dt_bauhaus_widget_set_label(third, N_("input B")); \
5172 g->scale_##var##_R = swap ? third : first; \
5173 g->scale_##var##_G = second; \
5174 g->scale_##var##_B = swap ? first : third; \
5176 g->normalize_##short = dt_bauhaus_toggle_from_params(self, "normalize_" #short);
5186 gtk_box_pack_start(GTK_BOX(self->
widget), GTK_WIDGET(
g->notebook),
FALSE,
FALSE, 0);
5187 const int saved_page =
dt_conf_get_int(
"plugins/darkroom/channelmixerrgb/gui_page");
5188 const int active_page = saved_page > 2 ? 2 : CLAMP(saved_page, 0, 2);
5189 gtk_widget_show(gtk_notebook_get_nth_page(
g->notebook, active_page));
5190 gtk_notebook_set_current_page(
g->notebook, active_page);
5196 "plugins/darkroom/channelmixerrgb/expand_values",
5197 _(
"calibrate with a color checker"),
5198 GTK_BOX(self->
widget), GTK_PACK_END);
5200 gtk_widget_set_tooltip_text(
g->cs.toggle,
5201 _(
"use a color checker target to autoset CAT and channels"));
5204 GtkWidget *collapsible = GTK_WIDGET(
g->cs.container);
5206 gchar *tip_files_loc = NULL;
5211 gchar *user_CGATS_dir = g_build_filename(confdir,
"color",
"checker", NULL);
5212 tip_files_loc = g_strdup_printf(_(
"'%s'"), user_CGATS_dir);
5218 gtk_box_pack_start(GTK_BOX(collapsible), GTK_WIDGET(
g->checkers_list),
TRUE,
TRUE, 0);
5220 gchar *
tooltip = g_strdup_printf(_(
"Choose the vendor and the type of your chart.\n"
5221 ".cht files in %s."), tip_files_loc);
5222 gtk_widget_set_tooltip_text(
g->checkers_list,
tooltip);
5228 gtk_box_pack_start(GTK_BOX(collapsible), GTK_WIDGET(
g->checkers_color_list),
TRUE,
TRUE, 0);
5231 tooltip = g_strdup_printf(_(
"Choose the definition of your chart.\n"
5232 "CGATS.17 files in %s."), tip_files_loc);
5233 gtk_widget_set_tooltip_text(
g->checkers_color_list,
tooltip);
5237 tooltip = g_markup_printf_escaped(_(
"<i>No CGATS.17 color file matches the selected chart.\n"
5238 "Add a compatible CGATS.17 file to <b>%s</b>.</i>"), tip_files_loc);
5239 g->checker_msg = gtk_label_new(NULL);
5240 gtk_label_set_markup(GTK_LABEL(
g->checker_msg),
tooltip);
5242 gtk_label_set_line_wrap(GTK_LABEL(
g->checker_msg),
TRUE);
5243 gtk_label_set_line_wrap_mode(GTK_LABEL(
g->checker_msg), PANGO_WRAP_WORD);
5244 gtk_box_pack_start(GTK_BOX(collapsible), GTK_WIDGET(
g->checker_msg),
FALSE,
TRUE, 0);
5245 gtk_widget_set_visible(
g->checker_msg,
FALSE);
5251 _(
"choose the colors that will be optimized with higher priority.\n"
5252 "neutral colors gives the lowest average delta E but a high maximum delta E\n"
5253 "saturated colors gives the lowest maximum delta E but a high average delta E\n"
5254 "none is a trade-off between both\n"
5255 "the others are special behaviours to protect some hues"),
5258 N_(
"neutral colors"),
5259 N_(
"saturated colors"),
5260 N_(
"skin and soil colors"),
5261 N_(
"foliage colors"),
5262 N_(
"sky and water colors"),
5263 N_(
"average delta E"),
5264 N_(
"maximum delta E"));
5265 gtk_box_pack_start(GTK_BOX(collapsible), GTK_WIDGET(
g->optimize),
TRUE,
TRUE, 0);
5269 gtk_widget_set_tooltip_text(
g->safety, _(
"reduce the radius of the patches to select the more or less central part.\n"
5270 "useful when the perspective correction is sloppy or\n"
5271 "the patches frame cast a shadows on the edges of the patch." ));
5273 gtk_box_pack_start(GTK_BOX(collapsible), GTK_WIDGET(
g->safety),
TRUE,
TRUE, 0);
5276 gtk_box_pack_start(GTK_BOX(collapsible), GTK_WIDGET(
g->label_delta_E),
TRUE,
TRUE, 0);
5277 gtk_widget_set_tooltip_text(
g->label_delta_E, _(
"the delta E is using the CIE 2000 formula"));
5282 gtk_box_pack_end(GTK_BOX(toolbar), GTK_WIDGET(
g->button_commit),
FALSE,
FALSE, 0);
5283 gtk_widget_set_tooltip_text(
g->button_commit, _(
"accept the computed profile and set it in the module"));
5284 g_signal_connect(G_OBJECT(
g->button_commit),
"button-press-event", G_CALLBACK(
commit_profile_callback), (gpointer)self);
5287 g_signal_connect(G_OBJECT(
g->button_profile),
"button-press-event", G_CALLBACK(
run_profile_callback), (gpointer)self);
5288 gtk_widget_set_tooltip_text(
g->button_profile, _(
"recompute the profile"));
5289 gtk_box_pack_end(GTK_BOX(toolbar), GTK_WIDGET(
g->button_profile),
FALSE,
FALSE, 0);
5292 g_signal_connect(G_OBJECT(
g->button_validate),
"button-press-event", G_CALLBACK(
run_validation_callback), (gpointer)self);
5293 gtk_widget_set_tooltip_text(
g->button_validate, _(
"check the output delta E"));
5294 gtk_box_pack_end(GTK_BOX(toolbar), GTK_WIDGET(
g->button_validate),
FALSE,
FALSE, 0);
5296 gtk_box_pack_start(GTK_BOX(collapsible), GTK_WIDGET(toolbar),
FALSE,
FALSE, 0);
5307 dt_conf_set_int(
"plugins/darkroom/channelmixerrgb/gui_page", gtk_notebook_get_current_page (
g->notebook));
5312 g->delta_E_in = NULL;
5319 g_list_free(
g->colorcheckers_color);
static void error(char *msg)
void dt_bauhaus_slider_set_soft_range(GtkWidget *widget, float soft_min, float soft_max)
void dt_bauhaus_slider_set_digits(GtkWidget *widget, int val)
void dt_bauhaus_slider_set_hard_max(GtkWidget *widget, float val)
void dt_bauhaus_combobox_clear(GtkWidget *widget)
void dt_bauhaus_slider_set_default(GtkWidget *widget, float def)
const char * dt_bauhaus_combobox_get_entry(GtkWidget *widget, int pos)
void dt_bauhaus_slider_set_stop(GtkWidget *widget, float stop, float r, float g, float b)
float dt_bauhaus_slider_get(GtkWidget *widget)
GtkWidget * dt_bauhaus_slider_new_with_range_and_feedback(dt_bauhaus_t *bh, dt_gui_module_t *self, float min, float max, float step, float defval, int digits, int feedback)
int dt_bauhaus_combobox_get(GtkWidget *widget)
void dt_bauhaus_slider_set_soft_max(GtkWidget *widget, float val)
int dt_bauhaus_combobox_length(GtkWidget *widget)
void dt_bauhaus_combobox_set_default(GtkWidget *widget, int def)
void dt_bauhaus_combobox_add_full(GtkWidget *widget, const char *text, dt_bauhaus_combobox_alignment_t align, gpointer data, void(free_func)(void *data), gboolean sensitive)
void dt_bauhaus_combobox_add_separator(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)
void dt_bauhaus_combobox_remove_at(GtkWidget *widget, int pos)
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)
@ DT_BAUHAUS_COMBOBOX_ALIGN_RIGHT
#define DT_BAUHAUS_COMBOBOX_NEW_FULL(bauhaus, widget, action, label, tip, pos, callback, data,...)
#define DT_BAUHAUS_SLIDER_MAX_STOPS
@ DEVELOP_BLEND_CS_RGB_SCENE
static __DT_CLONE_TARGETS__ void normalize(float *const buffer, const size_t width, const size_t height, const float norm)
static void checker_color_changed_callback(GtkWidget *widget, gpointer user_data)
#define DT_CHANNELMIXERRGB_SIMPLE_MODE_CONF
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)
void init(dt_iop_module_t *module)
static __DT_CLONE_TARGETS__ void loop_switch(const float *const restrict in, float *const restrict out, const size_t width, const size_t height, const size_t ch, const dt_colormatrix_t XYZ_to_RGB, const dt_colormatrix_t RGB_to_XYZ, const dt_colormatrix_t MIX, const dt_aligned_pixel_t illuminant, const dt_aligned_pixel_t saturation, const dt_aligned_pixel_t lightness, const dt_aligned_pixel_t grey, const float p, const float gamut, const int clip, const int apply_grey, const dt_adaptation_t kind, const dt_iop_channelmixer_rgb_version_t version)
const char ** description(struct dt_iop_module_t *self)
static void _spot_settings_changed_callback(GtkWidget *slider, dt_iop_module_t *self)
static void optimize_changed_callback(GtkWidget *widget, gpointer user_data)
void gui_reset(dt_iop_module_t *self)
static void update_xy_color(dt_iop_module_t *self)
void _auto_set_illuminant(dt_iop_module_t *self, dt_dev_pixelpipe_t *pipe)
dt_iop_channelmixer_rgb_mixer_mode_t
@ DT_CHANNELMIXERRGB_MIXER_SIMPLE
@ DT_CHANNELMIXERRGB_MIXER_PRIMARIES
@ DT_CHANNELMIXERRGB_MIXER_COMPLETE
void reload_defaults(dt_iop_module_t *module)
static void update_bounding_box(dt_iop_channelmixer_rgb_gui_data_t *g, const float x_increment, const float y_increment)
static void update_illuminant_color(struct dt_iop_module_t *self)
static gboolean _channelmixerrgb_sync_primaries_from_params(dt_iop_module_t *self, float *error)
Synchronize the primaries-mode GUI from the effective mixer matrix.
static gboolean _is_another_module_cat_on_pipe(struct dt_iop_module_t *self)
static void _channelmixerrgb_set_mixer_mode(dt_iop_channelmixer_rgb_gui_data_t *g, dt_iop_channelmixer_rgb_mixer_mode_t mode)
static gboolean target_color_draw(GtkWidget *widget, cairo_t *crf, gpointer user_data)
static gboolean origin_color_draw(GtkWidget *widget, cairo_t *crf, gpointer user_data)
dt_iop_channelmixer_shared_primaries_params_t dt_iop_channelmixer_rgb_primaries_params_t
static void _channelmixerrgb_mixer_mode_callback(GtkWidget *combo, gpointer user_data)
void update_colorchecker_list(dt_iop_module_t *self)
dt_iop_channelmixer_shared_primaries_basis_t dt_iop_channelmixer_rgb_primaries_basis_t
static __DT_CLONE_TARGETS__ int auto_detect_WB(const float *const restrict in, dt_illuminant_t illuminant, const size_t width, const size_t height, const size_t ch, const dt_colormatrix_t RGB_to_XYZ, dt_aligned_pixel_t xyz)
static __DT_CLONE_TARGETS__ int _extract_patches(const float *const restrict in, const dt_iop_roi_t *const roi_in, dt_iop_channelmixer_rgb_gui_data_t *g, const dt_colormatrix_t RGB_to_XYZ, const dt_colormatrix_t XYZ_to_CAM, float *const restrict patches, const gboolean normalize_exposure, extraction_result_t *result)
void init_pipe(struct dt_iop_module_t *self, dt_dev_pixelpipe_t *pipe, dt_dev_pixelpipe_iop_t *piece)
static void start_profiling_callback(GtkWidget *togglebutton, dt_iop_module_t *self)
static __DT_CLONE_TARGETS__ int _custom_wb_from_coeffs(struct dt_iop_module_t *self, const dt_aligned_pixel_t wb_coeffs, dt_aligned_pixel_t custom_wb)
static void _update_RGB_slider_stop(dt_iop_channelmixer_rgb_params_t *p, const struct dt_iop_order_iccprofile_info_t *const work_profile, const struct dt_iop_order_iccprofile_info_t *const display_profile, GtkWidget *w, float stop, float c, float r, float g, float b)
static void _update_RGB_colors(dt_iop_module_t *self, float r, float g, float b, gboolean normalize, float *a, GtkWidget *w_r, GtkWidget *w_g, GtkWidget *w_b)
static void _channelmixerrgb_update_simple_colors(dt_iop_module_t *self)
Paint the simple mixer sliders from the exact chroma-basis model.
dt_iop_channelmixer_shared_simple_params_t dt_iop_channelmixer_rgb_simple_params_t
void gui_update(struct dt_iop_module_t *self)
__DT_CLONE_TARGETS__ int extract_color_checker(const float *const restrict in, float *const restrict out, const dt_iop_roi_t *const roi_in, dt_iop_channelmixer_rgb_gui_data_t *g, const dt_colormatrix_t RGB_to_XYZ, const dt_colormatrix_t XYZ_to_RGB, const dt_colormatrix_t XYZ_to_CAM, const dt_adaptation_t kind)
int validate_color_checker(const float *const restrict in, const dt_iop_roi_t *const roi_in, dt_iop_channelmixer_rgb_gui_data_t *g, const dt_colormatrix_t RGB_to_XYZ, const dt_colormatrix_t XYZ_to_RGB, const dt_colormatrix_t XYZ_to_CAM)
void gui_init(struct dt_iop_module_t *self)
int button_pressed(struct dt_iop_module_t *self, double x, double y, double pressure, int which, int type, uint32_t state)
void gui_changed(dt_iop_module_t *self, GtkWidget *w, void *previous)
int button_released(struct dt_iop_module_t *self, double x, double y, int which, uint32_t state)
#define MIXER_ROW(var, short, section, swap)
static __DT_CLONE_TARGETS__ int get_white_balance_coeff(struct dt_iop_module_t *self, dt_aligned_pixel_t custom_wb)
static void run_validation_callback(GtkWidget *widget, GdkEventButton *event, gpointer user_data)
static gboolean illuminant_color_draw(GtkWidget *widget, cairo_t *crf, gpointer user_data)
static void update_illuminants(struct dt_iop_module_t *self)
static void _channelmixerrgb_primaries_slider_callback(GtkWidget *slider, gpointer user_data)
static void _channelmixerrgb_simple_slider_callback(GtkWidget *slider, gpointer user_data)
void color_list_visibility(dt_iop_module_t *self, const int checker_cmbbx_index)
void cleanup_global(dt_iop_module_so_t *module)
static void run_profile_callback(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)
void checker_changed_callback(GtkWidget *widget, 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)
void gui_cleanup(struct dt_iop_module_t *self)
void update_colorchecker_color_list(dt_iop_module_t *self)
This function filters the list of all .cht files to only those that match the currently selected colo...
static void init_bounding_box(dt_iop_channelmixer_rgb_gui_data_t *g, const float width, const float height)
void init_presets(dt_iop_module_so_t *self)
static void _preview_pipe_finished_callback(gpointer instance, gpointer user_data)
static __DT_CLONE_TARGETS__ void declare_cat_on_pipe(struct dt_iop_module_t *self, gboolean preset)
static void _develop_ui_pipe_finished_callback(gpointer instance, gpointer user_data)
#define DT_CHANNELMIXERRGB_SIMPLE_EPS
static void illum_xy_callback(GtkWidget *slider, gpointer user_data)
static void update_approx_cct(struct dt_iop_module_t *self)
static void compute_patches_delta_E(const float *const restrict patches, const dt_color_checker_t *const checker, float *const restrict delta_E, float *const restrict avg_delta_E, float *const restrict max_delta_E)
void cleanup_pipe(struct dt_iop_module_t *self, dt_dev_pixelpipe_t *pipe, dt_dev_pixelpipe_iop_t *piece)
#define OUTPUT_SECTION(var, short, section, swap)
void init_global(dt_iop_module_so_t *module)
dt_iop_channelmixer_shared_simple_probe_t dt_iop_channelmixer_rgb_simple_probe_t
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)
dt_iop_channelmixer_rgb_version_t
@ DT_SOLVE_OPTIMIZE_MAX_DELTA_E
@ DT_SOLVE_OPTIMIZE_LOW_SAT
@ DT_SOLVE_OPTIMIZE_AVG_DELTA_E
@ DT_SOLVE_OPTIMIZE_HIGH_SAT
@ DT_SOLVE_OPTIMIZE_FOLIAGE
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)
int mouse_moved(struct dt_iop_module_t *self, double x, double y, double pressure, int which)
static void commit_profile_callback(GtkWidget *widget, GdkEventButton *event, gpointer user_data)
static void safety_changed_callback(GtkWidget *widget, gpointer user_data)
static void paint_hue(dt_iop_module_t *self)
void color_picker_apply(dt_iop_module_t *self, GtkWidget *picker, dt_dev_pixelpipe_t *pipe, dt_dev_pixelpipe_iop_t *piece)
static void _convert_GUI_colors(dt_iop_channelmixer_rgb_params_t *p, const struct dt_iop_order_iccprofile_info_t *const work_profile, const struct dt_iop_order_iccprofile_info_t *const display_profile, const dt_aligned_pixel_t LMS, dt_aligned_pixel_t RGB)
int legacy_params(dt_iop_module_t *self, const void *const old_params, const int old_version, void *new_params, const int new_version)
static void _channelmixerrgb_update_primaries_colors(dt_iop_module_t *self)
static gboolean _channelmixerrgb_sync_simple_from_params(dt_iop_module_t *self, float *error)
Rebuild the simple mixer sliders from the current normalized 3x3 matrix.
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)
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_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_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])
void dt_iop_channelmixer_shared_simple_to_sliders(const dt_iop_channelmixer_shared_simple_params_t *const simple, GtkWidget *const widgets[6])
dt_iop_channelmixer_shared_primaries_basis_t dt_iop_channelmixer_shared_primaries_basis_from_adaptation(const dt_adaptation_t adaptation)
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)
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])
void dt_iop_channelmixer_shared_primaries_from_sliders(GtkWidget *const widgets[9], dt_iop_channelmixer_shared_primaries_params_t *primaries)
dt_iop_channelmixer_shared_simple_probe_t
dt_iop_channelmixer_shared_primaries_basis_t
static const dt_aligned_pixel_simd_t const dt_adaptation_t adaptation
static const dt_adaptation_t kind
static void convert_D50_to_LMS(const dt_adaptation_t adaptation, dt_aligned_pixel_t D50)
@ DT_ADAPTATION_FULL_BRADFORD
@ DT_ADAPTATION_LINEAR_BRADFORD
static const dt_aligned_pixel_simd_t const dt_adaptation_t const float p
static const float scaling
static const dt_aligned_pixel_simd_t illuminant
void dt_iop_color_picker_reset(dt_iop_module_t *module, gboolean keep)
gboolean dt_iop_color_picker_is_active_module(const dt_iop_module_t *module)
Tell whether one module currently owns the active darkroom picker.
GtkWidget * dt_color_picker_new(dt_iop_module_t *module, dt_iop_color_picker_kind_t kind, GtkWidget *w)
void dt_colorchecker_label_list_cleanup(GList **colorcheckers)
static dt_color_checker_t * dt_get_color_checker(const dt_color_checker_targets target_type, GList **colorchecker_label, const char *color_filename)
int dt_colorchecker_find(GList **colorcheckers_label)
Find all builtin and .cht colorcheckers.
void dt_colorchecker_cleanup(dt_color_checker_t *checker)
int dt_colorchecker_find_color(GList **color_label)
Find all builtin and CGATS colorcheckers.
static void CAT16_adapt_D50(float4 *lms_in, const float4 origin_illuminant, const float D, const int full)
static float4 convert_XYZ_to_bradford_LMS(const float4 XYZ)
static float4 convert_XYZ_to_CAT16_LMS(const float4 XYZ)
static void bradford_adapt_D50(float4 *lms_in, const float4 origin_illuminant, const float p, const int full)
static void XYZ_adapt_D50(float4 *lms_in, const float4 origin_illuminant)
static float4 dt_XYZ_to_xyY(const float4 XYZ)
static float4 convert_CAT16_LMS_to_XYZ(const float4 LMS)
static float4 convert_bradford_LMS_to_XYZ(const float4 LMS)
__DT_CLONE_TARGETS__ int dt_colorspaces_conversion_matrices_rgb(const float adobe_XYZ_to_CAM[4][3], double out_RGB_to_CAM[4][3], double out_CAM_to_RGB[3][4], const float *embedded_matrix, double mul[4])
static dt_aligned_pixel_t xyY
dt_XYZ_to_sRGB(XYZ, result)
static dt_aligned_pixel_t XYZ
static dt_aligned_pixel_t Lab
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)))
static dt_aligned_pixel_t RGB
static dt_aligned_pixel_t Lch
static const dt_colormatrix_t M
typedef void((*dt_cache_allocate_t)(void *userdata, dt_cache_entry_t *entry))
gboolean dt_image_is_matrix_correction_supported(const dt_image_t *img)
dt_image_pipe_class_t dt_image_pipe_class(const dt_image_t *img)
const char * dt_image_pipe_class_name(const dt_image_pipe_class_t klass)
gboolean dt_image_is_monochrome(const dt_image_t *img)
point_t apply_homography(point_t p, const float *h)
float apply_homography_scaling(point_t p, const float *h)
int get_homography(const point_t *source, const point_t *target, float *h)
void dt_conf_set_bool(const char *name, int val)
int dt_conf_get_bool(const char *name)
int dt_conf_key_exists(const char *key)
void dt_conf_set_float(const char *name, float val)
float dt_conf_get_float(const char *name)
void dt_conf_set_int(const char *name, int val)
int dt_conf_get_int(const char *name)
gboolean dt_conf_is_equal(const char *name, const char *value)
void dt_control_log(const char *msg,...)
void dt_control_queue_redraw_center()
request redraw of center window. This redraws the center view within a gdk critical section to preven...
void dt_control_queue_redraw_widget(GtkWidget *widget)
threadsafe request of redraw of specific widget. Use this function if you need to redraw a specific w...
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)
void * dt_alloc_align(size_t size)
void dt_print(dt_debug_thread_t thread, const char *msg,...)
#define dt_free_align(ptr)
static void * dt_calloc_align(size_t size)
#define dt_gui_freeze_begin()
#define dt_gui_freeze_end()
#define for_each_channel(_var,...)
#define dt_pixelpipe_cache_alloc_align_float_cache(pixels, id)
static float * dt_alloc_align_float(size_t pixels)
float dt_aligned_pixel_simd_t __attribute__((vector_size(16), aligned(16)))
Enable aggressive floating-point arithmetic optimizations, in denormals handling. Set through user pr...
void void void gboolean dt_gui_widgets_suppressed(void)
#define DT_MODULE_INTROSPECTION(MODVER, PARAMSTYPE)
#define __OMP_DECLARE_SIMD__(...)
#define dt_pixelpipe_cache_free_align(mem)
#define __DT_CLONE_TARGETS__
#define __OMP_PARALLEL_FOR__(...)
#define IS_NULL_PTR(p)
C is way too permissive with !=, == and if(var) checks, which can mean too many things depending on w...
#define dt_dev_add_history_item(dev, module, enable, redraw)
#define dt_dev_pixelpipe_resync_history_preview(dev)
float dt_dev_get_overlay_scale(dt_develop_t *dev)
Get the overlay scale factor in GUI logical coordinates.
void dt_dev_coordinates_image_norm_to_preview_abs(dt_develop_t *dev, float *points, size_t num_points)
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.
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.
void dtgtk_cairo_paint_refresh(cairo_t *cr, gint x, gint y, gint w, gint h, gint flags, void *data)
void dtgtk_cairo_paint_check_mark(cairo_t *cr, gint x, gint y, gint w, gint h, gint flags, void *data)
void dtgtk_cairo_paint_softproof(cairo_t *cr, gint x, gint y, gint w, gint h, gint flags, void *data)
static void weight(const float *c1, const float *c2, const float sharpen, dt_aligned_pixel_t weight)
void dt_loc_get_user_config_dir(char *configdir, size_t bufsize)
static int pseudo_solve_gaussian(double *const restrict A, double *const restrict y, const size_t m, const size_t n, const int checks)
void dt_gui_hide_collapsible_section(dt_gui_collapsible_section_t *cs)
void dt_gui_new_collapsible_section(dt_gui_collapsible_section_t *cs, const char *confname, const char *label, GtkBox *parent, GtkPackType pack)
Create a collapsible section and pack it into the parent box.
GtkWidget * dt_ui_notebook_page(GtkNotebook *notebook, const char *text, const char *tooltip)
GtkNotebook * dt_ui_notebook_new()
void dt_gui_update_collapsible_section(dt_gui_collapsible_section_t *cs)
void dt_ui_notebook_set_picker_owner(GtkNotebook *notebook, gpointer owner)
Register an opaque owner for a GtkNotebook's page switches, and relay every "switch_page" as DT_SIGNA...
static cairo_surface_t * dt_cairo_image_surface_create(cairo_format_t format, int width, int height)
static GtkWidget * dt_ui_section_label_new(const gchar *str)
#define DT_GUI_BOX_SPACING
#define DT_PIXEL_APPLY_DPI(value)
static GtkWidget * dt_ui_label_new(const gchar *str)
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, const dt_develop_blend_colorspace_t blend_cst)
void dt_gui_throttle_queue(gpointer source, dt_gui_throttle_callback_t callback, gpointer user_data)
static int illuminant_to_xy(const dt_illuminant_t illuminant, const dt_image_t *img, const dt_aligned_pixel_t custom_wb, float *x_out, float *y_out, const float t, const dt_illuminant_fluo_t fluo, const dt_illuminant_led_t iled)
@ DT_ILLUMINANT_DETECT_EDGES
@ DT_ILLUMINANT_DETECT_SURFACES
static void illuminant_xy_to_RGB(const float x, const float y, dt_aligned_pixel_t RGB)
static void illuminant_xy_to_XYZ(const float x, const float y, dt_aligned_pixel_t XYZ)
static float xy_to_CCT(const float x, const float y)
static float CCT_reverse_lookup(const float x, const float y)
static int find_temperature_from_raw_coeffs(const dt_image_t *img, const dt_aligned_pixel_t custom_wb, float *chroma_x, float *chroma_y)
static void matrice_pseudoinverse(float(*in)[3], float(*out)[3], int size)
@ DT_ILLUMINANT_FLUO_LAST
static void xy_to_uv(const float xy[2], float uv[2])
static __DT_CLONE_TARGETS__ void dt_simd_memcpy(const float *const __restrict__ in, float *const __restrict__ out, const size_t num_elem)
static void dt_iop_image_copy_by_size(float *const __restrict__ out, const float *const __restrict__ in, const size_t width, const size_t height, const size_t ch)
dt_iop_module_t * dt_iop_get_module_from_list(GList *iop_list, const char *op)
void dt_iop_throttled_history_update(gpointer data)
gboolean dt_iop_is_first_instance(GList *modules, dt_iop_module_t *module)
void dt_iop_default_init(dt_iop_module_t *module)
void dt_iop_request_focus(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)
void dt_iop_set_cache_bypass(dt_iop_module_t *module, gboolean state)
@ DT_REQUEST_COLORPICK_OFF
#define dt_omploop_sfence()
static void dt_iop_gui_enter_critical_section(dt_iop_module_t *const module) ACQUIRE(&module -> gui_lock)
#define dt_iop_fmt_log(module, fmt,...)
Debug helper to trace a module's input-format-driven decisions on the -d pipe channel (DT_DEBUG_PIPE)...
@ IOP_FLAGS_INCLUDE_IN_STYLES
@ IOP_FLAGS_SUPPORTS_BLENDING
static void dt_iop_gui_leave_critical_section(dt_iop_module_t *const module) RELEASE(&module -> gui_lock)
#define IOP_GUI_ALLOC(module)
GtkWidget * dt_bauhaus_toggle_from_params(dt_iop_module_t *self, const char *param)
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 float kernel(const float *x, const float *y)
struct dt_iop_tonecurve_params_t preset
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_iop_order_iccprofile_info_t * dt_ioppr_get_pipe_input_profile_info(const struct dt_dev_pixelpipe_t *pipe)
static float mix(const float a, const float b, const float t)
float *const restrict const size_t k
float *const restrict const size_t const size_t ch
static void transpose_3x3_to_3xSSE(const float input[9], dt_colormatrix_t output)
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])
static void repack_double3x3_to_3xSSE(const double input[9], dt_colormatrix_t output)
static void pack_3xSSE_to_3x3(const dt_colormatrix_t input, float output[9])
float dt_aligned_pixel_t[4]
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)
@ DT_DEV_PIXELPIPE_PREVIEW
#define DT_DEBUG_CONTROL_SIGNAL_DISCONNECT(ctlsig, cb, user_data)
@ 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)
struct _GtkWidget GtkWidget
const float uint32_t state[4]
struct dt_control_signal_t * signals
struct dt_bauhaus_t * bauhaus
struct dt_develop_t * develop
dt_color_checker_patch * values
dt_color_checker_targets type
dt_iop_buffer_dsc_t dsc_in
struct dt_iop_module_t *void * data
dt_dev_pixelpipe_type_t type
struct dt_develop_t * dev
dt_aligned_pixel_t wb_coeffs
struct dt_develop_t::@17 roi
struct dt_develop_t::@20 proxy
struct dt_iop_module_t * chroma_adaptation
struct dt_dev_pixelpipe_t * pipe
float d65_color_matrix[9]
float adobe_XYZ_to_CAM[4][3]
struct dt_iop_buffer_dsc_t::@33 temperature
dt_aligned_pixel_t coeffs
dt_iop_buffer_type_t datatype
dt_adaptation_t adaptation
dt_illuminant_t illuminant_type
dt_aligned_pixel_t illuminant
dt_iop_channelmixer_rgb_version_t version
float DT_ALIGNED_PIXEL lightness[4]
float DT_ALIGNED_PIXEL grey[4]
float DT_ALIGNED_PIXEL saturation[4]
int kernel_channelmixer_rgb_bradford_linear
int kernel_channelmixer_rgb_cat16
int kernel_channelmixer_rgb_rgb
int kernel_channelmixer_rgb_bradford_full
int kernel_channelmixer_rgb_xyz
GtkWidget * primaries_blue_purity
gboolean is_profiling_started
GtkWidget * button_commit
GtkWidget * primaries_achromatic_hue
GtkWidget * scale_green_G
GtkWidget * lightness_spot
dt_solving_strategy_t optimization
GtkWidget * button_profile
GtkWidget * checkers_color_list
GtkWidget * label_delta_E
GtkWidget * scale_saturation_B
GtkWidget * primaries_green_hue
GtkWidget * primaries_gain
GtkWidget * scale_green_B
gchar * delta_E_label_text
GList * colorcheckers_all_color
GtkWidget * primaries_blue_hue
dt_aligned_pixel_t spot_RGB
GtkWidget * scale_green_R
GtkWidget * simple_coupling_1
GtkWidget * button_validate
GtkWidget * primaries_achromatic_purity
GtkWidget * scale_saturation_R
GtkWidget * simple_stretch_1
GtkWidget * normalize_sat
GtkWidget * scale_lightness_R
GtkWidget * scale_lightness_B
GList * colorcheckers_color
GtkWidget * spot_settings
dt_gui_collapsible_section_t csspot
GtkWidget * scale_lightness_G
GtkWidget * primaries_red_purity
GtkWidget * saturation_version
GtkWidget * simple_stretch_2
GtkWidget * primaries_red_hue
GtkWidget * normalize_grey
GtkWidget * primaries_green_purity
GtkWidget * scale_saturation_G
GtkWidget * normalize_light
float inverse_homography[9]
dt_gui_collapsible_section_t cs
GtkWidget * simple_coupling_2
dt_color_checker_t * checker
GtkWidget * checkers_list
dt_illuminant_led_t illum_led
dt_illuminant_fluo_t illum_fluo
dt_iop_channelmixer_rgb_version_t version
dt_adaptation_t adaptation
dt_illuminant_t illuminant
GModule *dt_dev_operation_t op
dt_iop_global_data_t * data
dt_dev_request_colorpick_flags_t request_color_pick
GtkDarktableToggleButton * off
dt_iop_params_t * default_params
struct dt_develop_t * dev
dt_iop_gui_data_t * gui_data
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
dt_colormatrix_t matrix_out_transposed
dt_colormatrix_t matrix_out
dt_colormatrix_t matrix_in_transposed
dt_colormatrix_t matrix_in
Region of interest passed through the pixelpipe.