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;
560 for(
size_t k = 0;
k < 4;
k++) custom_wb[
k] = 1.f;
564 dt_iop_fmt_log(self,
"get_white_balance_coeff: class=%s matrix_supported=0 -> custom_wb=no-op (CAT uses identity)",
579 const double green = bwb[1];
597 for(
size_t k = 0;
k < 4;
k++)
602 custom_wb[
k] = (
k == 3 && !isnormal(denom)) ? custom_wb[1] : bwb[
k] / denom;
611gamut_mapping(
const dt_aligned_pixel_simd_t input,
const float compression,
const int clip)
614 const float sum = input[0] + input[1] + input[2];
615 const float Y = input[1];
617 if(sum > 0.f && Y > 0.f)
620 float x = input[0] / sum;
621 float y = input[1] / sum;
624 const float uv_denominator = -2.f *
x + 12.f * y + 3.f;
625 float u = 4.f *
x / uv_denominator;
626 float v = 9.f * y / uv_denominator;
629 const float D50[2]
DT_ALIGNED_PIXEL = { 0.20915914598542354f, 0.488075320769787f };
631 const float Delta = Y * (sqf(
delta[0]) + sqf(
delta[1]));
634 const float correction = (compression == 0.0f) ? 0.f : powf(Delta, compression);
637 const float tmp_u =
DT_FMA(correction,
delta[0], u);
639 u = (u > D50[0]) ? fmaxf(tmp_u, D50[0]) : fminf(tmp_u, D50[0]);
640 v = (
v > D50[1]) ? fmaxf(tmp_v, D50[1]) : fminf(tmp_v, D50[1]);
643 const float xy_denominator = 6.f * u - 16.f *
v + 12.f;
644 x = 9.f * u / xy_denominator;
645 y = 4.f *
v / xy_denominator;
660 const float scale =
x + y;
661 const int sanitize = (scale >= 1.f);
668 return (dt_aligned_pixel_simd_t){ Y *
x / y, Y, Y * (1.f -
x - y) / y, 0.f };
673 return (dt_aligned_pixel_simd_t){ 0.f };
683 float norm = euclidean_norm(input);
684 const float avg = fmaxf((input[0] + input[1] + input[2]) / 3.0f,
NORM_MIN);
686 if(norm > 0.f && avg > 0.f)
689 const float mix = scalar_product(input, lightness);
695 for(
size_t c = 0; c < 3; c++) output[c] = input[c] / norm;
698 float coeff_ratio = 0.f;
702 for(
size_t c = 0; c < 3; c++)
703 coeff_ratio += sqf(1.0f - output[c]) * saturation[c];
706 coeff_ratio = scalar_product(output, saturation) / 3.f;
709 for(
size_t c = 0; c < 3; c++)
713 const float min_ratio = (output[c] < 0.0f) ? output[c] : 0.0f;
714 const float output_inverse = 1.0f - output[c];
715 output[c] = fmaxf(
DT_FMA(output_inverse, coeff_ratio, output[c]),
724 norm *= fmaxf(1.f +
mix / avg, 0.f);
725 for(
size_t c = 0; c < 3; c++) output[c] *= norm;
730 for(
size_t c = 0; c < 3; c++) output[c] = input[c];
735static inline void loop_switch(
const float *
const restrict in,
float *
const restrict
out,
750 const dt_aligned_pixel_simd_t rgb_to_xyz0 = dt_colormatrix_row_to_simd(RGB_to_XYZ_t, 0);
751 const dt_aligned_pixel_simd_t rgb_to_xyz1 = dt_colormatrix_row_to_simd(RGB_to_XYZ_t, 1);
752 const dt_aligned_pixel_simd_t rgb_to_xyz2 = dt_colormatrix_row_to_simd(RGB_to_XYZ_t, 2);
753 const dt_aligned_pixel_simd_t xyz_to_rgb0 = dt_colormatrix_row_to_simd(XYZ_to_RGB_t, 0);
754 const dt_aligned_pixel_simd_t xyz_to_rgb1 = dt_colormatrix_row_to_simd(XYZ_to_RGB_t, 1);
755 const dt_aligned_pixel_simd_t xyz_to_rgb2 = dt_colormatrix_row_to_simd(XYZ_to_RGB_t, 2);
756 const dt_aligned_pixel_simd_t mix0 = dt_colormatrix_row_to_simd(MIX_t, 0);
757 const dt_aligned_pixel_simd_t mix1 = dt_colormatrix_row_to_simd(MIX_t, 1);
758 const dt_aligned_pixel_simd_t mix2 = dt_colormatrix_row_to_simd(MIX_t, 2);
759 const dt_aligned_pixel_simd_t illuminant_v = dt_load_simd_aligned(
illuminant);
763 const dt_aligned_pixel_simd_t in_v = dt_load_simd_aligned(in +
k);
764 dt_aligned_pixel_simd_t temp_one_v = { 0.f };
765 dt_aligned_pixel_simd_t temp_two_v = clip ? dt_simd_max_zero(in_v) : in_v;
774 temp_one_v = dt_mat3x4_mul_vec4(temp_two_v, rgb_to_xyz0, rgb_to_xyz1, rgb_to_xyz2);
775 const float Y = temp_one_v[1];
782 temp_two_v = dt_mat3x4_mul_vec4(temp_one_v, mix0, mix1, mix2);
790 temp_one_v = dt_mat3x4_mul_vec4(temp_two_v, rgb_to_xyz0, rgb_to_xyz1, rgb_to_xyz2);
791 const float Y = temp_one_v[1];
798 temp_two_v = dt_mat3x4_mul_vec4(temp_one_v, mix0, mix1, mix2);
806 temp_one_v = dt_mat3x4_mul_vec4(temp_two_v, rgb_to_xyz0, rgb_to_xyz1, rgb_to_xyz2);
807 const float Y = temp_one_v[1];
812 temp_one_v = _upscale_vector_simd(
CAT16_adapt_D50(temp_two_v, illuminant_v, 1.0f,
TRUE), Y);
814 temp_two_v = dt_mat3x4_mul_vec4(temp_one_v, mix0, mix1, mix2);
822 temp_one_v = dt_mat3x4_mul_vec4(temp_two_v, rgb_to_xyz0, rgb_to_xyz1, rgb_to_xyz2);
823 const float Y = temp_one_v[1];
826 temp_two_v = _upscale_vector_simd(
XYZ_adapt_D50(_downscale_vector_simd(temp_one_v, Y), illuminant_v), Y);
827 temp_one_v = dt_mat3x4_mul_vec4(temp_two_v, mix0, mix1, mix2);
838 temp_one_v = dt_mat3x4_mul_vec4(temp_two_v, mix0, mix1, mix2);
839 temp_one_v = dt_mat3x4_mul_vec4(temp_one_v, rgb_to_xyz0, rgb_to_xyz1, rgb_to_xyz2);
847 temp_two_v = gamut_mapping(temp_one_v, gamut, clip);
857 temp_one_v = convert_any_XYZ_to_LMS(temp_two_v,
kind);
865 temp_one_v = dt_mat3x4_mul_vec4(temp_two_v, xyz_to_rgb0, xyz_to_rgb1, xyz_to_rgb2);
873 if(clip) temp_one_v = dt_simd_max_zero(temp_one_v);
879 luma_chroma(luma_input, saturation, lightness, luma_output, version);
880 temp_two_v = dt_load_simd_aligned(luma_output);
883 if(clip) temp_two_v = dt_simd_max_zero(temp_two_v);
889 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);
890 dt_store_simd_nontemporal(
out +
k, (dt_aligned_pixel_simd_t){ grey_mix, grey_mix, grey_mix, in_v[3] });
902 temp_one_v = convert_any_LMS_to_XYZ(temp_two_v,
kind);
910 temp_one_v = dt_mat3x4_mul_vec4(temp_two_v, rgb_to_xyz0, rgb_to_xyz1, rgb_to_xyz2);
918 if(clip) temp_one_v = dt_simd_max_zero(temp_one_v);
921 temp_two_v = dt_mat3x4_mul_vec4(temp_one_v, xyz_to_rgb0, xyz_to_rgb1, xyz_to_rgb2);
922 if(clip) temp_two_v = dt_simd_max_zero(temp_two_v);
924 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] });
931#define SHF(ii, jj, c) ((i + ii) * width + j + jj) * ch + c
958 for(
size_t j = 0; j <
width; j++)
960 const size_t index = (
i *
width + j) *
ch;
966 RGB[c] = fmaxf(in[index + c], 0.0f);
969 dot_product(
RGB, RGB_to_XYZ,
XYZ);
978 const float D50[2] = { 0.34567f, 0.35850f };
979 const float norm = dt_fast_hypotf(D50[0], D50[1]);
981 temp[index ] = (
XYZ[0] - D50[0]) / norm;
982 temp[index + 1] = (
XYZ[1] - D50[1]) / norm;
983 temp[index + 2] =
XYZ[2];
986 float elements = 0.f;
997 for(
size_t c = 0; c < 2; c++)
1001 2.f * temp[
SHF( 0, -
OFF, c)] + 4.f * temp[
SHF( 0, 0, c)] + 2.f * temp[
SHF( 0, +
OFF, c)] +
1003 central_average[c] = fmaxf(central_average[c], 0.0f);
1010 for(
size_t c = 0; c < 2; c++)
1013 sqf(temp[
SHF(-
OFF, -
OFF, c)] - central_average[c]) +
1014 sqf(temp[
SHF(-
OFF, 0, c)] - central_average[c]) +
1015 sqf(temp[
SHF(-
OFF, +
OFF, c)] - central_average[c]) +
1016 sqf(temp[
SHF(0, -
OFF, c)] - central_average[c]) +
1017 sqf(temp[
SHF(0, 0, c)] - central_average[c]) +
1018 sqf(temp[
SHF(0, +
OFF, c)] - central_average[c]) +
1019 sqf(temp[
SHF(+
OFF, -
OFF, c)] - central_average[c]) +
1020 sqf(temp[
SHF(+
OFF, 0, c)] - central_average[c]) +
1021 sqf(temp[
SHF(+
OFF, +
OFF, c)] - central_average[c])
1029 (temp[
SHF(-
OFF, -
OFF, 0)] - central_average[0]) * (temp[
SHF(-
OFF, -
OFF, 1)] - central_average[1]) +
1030 (temp[
SHF(-
OFF, 0, 0)] - central_average[0]) * (temp[
SHF(-
OFF, 0, 1)] - central_average[1]) +
1031 (temp[
SHF(-
OFF, +
OFF, 0)] - central_average[0]) * (temp[
SHF(-
OFF, +
OFF, 1)] - central_average[1]) +
1032 (temp[
SHF( 0, -
OFF, 0)] - central_average[0]) * (temp[
SHF( 0, -
OFF, 1)] - central_average[1]) +
1033 (temp[
SHF( 0, 0, 0)] - central_average[0]) * (temp[
SHF( 0, 0, 1)] - central_average[1]) +
1034 (temp[
SHF( 0, +
OFF, 0)] - central_average[0]) * (temp[
SHF( 0, +
OFF, 1)] - central_average[1]) +
1035 (temp[
SHF(+
OFF, -
OFF, 0)] - central_average[0]) * (temp[
SHF(+
OFF, -
OFF, 1)] - central_average[1]) +
1036 (temp[
SHF(+
OFF, 0, 0)] - central_average[0]) * (temp[
SHF(+
OFF, 0, 1)] - central_average[1]) +
1037 (temp[
SHF(+
OFF, +
OFF, 0)] - central_average[0]) * (temp[
SHF(+
OFF, +
OFF, 1)] - central_average[1])
1041 const float p = 8.f;
1043 = powf(powf(fabsf(central_average[0]),
p) + powf(fabsf(central_average[1]),
p), 1.f /
p) +
NORM_MIN;
1044 const float weight = var[0] * var[1] * var[2];
1046 for(
size_t c = 0; c < 2; c++)
xyY[c] += central_average[c] *
weight / p_norm;
1047 elements +=
weight / p_norm;
1059 for(
size_t c = 0; c < 2; c++)
1063 2.f * temp[
SHF( 0, -
OFF, c)] + 4.f * temp[
SHF( 0, 0, c)] + 2.f * temp[
SHF( 0, +
OFF, c)] +
1067 dd[c] = temp[
SHF(0, 0, c)] - central_average[c];
1071 const float p = 8.f;
1072 const float p_norm = powf(powf(fabsf(dd[0]),
p) + powf(fabsf(dd[1]),
p), 1.f /
p) +
NORM_MIN;
1074 for(
size_t c = 0; c < 2; c++)
xyY[c] -= dd[c] / p_norm;
1079 const float D50[2] = { 0.34567f, 0.35850 };
1080 const float norm_D50 = dt_fast_hypotf(D50[0], D50[1]);
1082 for(
size_t c = 0; c < 2; c++)
1083 xyz[c] = norm_D50 * (
xyY[c] / elements) + D50[c];
1140static inline __attribute__((always_inline))
void check_if_close_to_daylight(
const float x,
const float y,
float *temperature,
1155 if(t < 3000.f && t > 1667.f)
1162 float xy_ref[2] = {
x, y };
1166 float xy_test[2] = { 0.f };
1174 const float delta_daylight = dt_fast_hypotf((uv_test[0] - uv_ref[0]), (uv_test[1] - uv_ref[1]));
1181 const float delta_bb = dt_fast_hypotf((uv_test[0] - uv_ref[0]), (uv_test[1] - uv_ref[1]));
1184 if(delta_bb < 0.005f || delta_daylight < 0.005f)
1188 if(delta_bb < delta_daylight)
1204#define DEG_TO_RAD(x) (x * M_PI / 180.f)
1205#define RAD_TO_DEG(x) (x * 180.f / M_PI)
1209 float *
const restrict delta_E,
float *
const restrict avg_delta_E,
float *
const restrict max_delta_E)
1223 for(
size_t c = 0; c < 4; c++) XYZ_test[c] = patches[
k * 4 + c];
1226 const float *
const restrict Lab_ref = checker->
values[
k].
Lab;
1231 const float DL = Lab_ref[0] - Lab_test[0];
1232 const float L_avg = (Lab_ref[0] + Lab_test[0]) / 2.f;
1233 const float C_ref = dt_fast_hypotf(Lab_ref[1], Lab_ref[2]);
1234 const float C_test = dt_fast_hypotf(Lab_test[1], Lab_test[2]);
1235 const float C_avg = (C_ref + C_test) / 2.f;
1236 float C_avg_7 = C_avg * C_avg;
1240 const float C_avg_7_ratio_sqrt = sqrtf(C_avg_7 / (C_avg_7 + 6103515625.f));
1241 const float a_ref_prime = Lab_ref[1] * (1.f + 0.5f * (1.f - C_avg_7_ratio_sqrt));
1242 const float a_test_prime = Lab_test[1] * (1.f + 0.5f * (1.f - C_avg_7_ratio_sqrt));
1243 const float C_ref_prime = dt_fast_hypotf(a_ref_prime, Lab_ref[2]);
1244 const float C_test_prime = dt_fast_hypotf(a_test_prime, Lab_test[2]);
1245 const float DC_prime = C_ref_prime - C_test_prime;
1246 const float C_avg_prime = (C_ref_prime + C_test_prime) / 2.f;
1247 float h_ref_prime = atan2f(Lab_ref[2], a_ref_prime);
1248 float h_test_prime = atan2f(Lab_test[2], a_test_prime);
1251 if(C_ref_prime == 0.f) h_ref_prime = 0.f;
1252 if(C_test_prime == 0.f) h_test_prime = 0.f;
1256 if(h_ref_prime < 0.f) h_ref_prime = 2.f *
M_PI - h_ref_prime;
1257 if(h_test_prime < 0.f) h_test_prime = 2.f *
M_PI - h_test_prime;
1263 float Dh_prime = h_test_prime - h_ref_prime;
1264 float Dh_prime_abs = fabsf(Dh_prime);
1265 if(C_test_prime == 0.f || C_ref_prime == 0.f)
1267 else if(Dh_prime_abs <= 180.f)
1269 else if(Dh_prime_abs > 180.f && (h_test_prime <= h_ref_prime))
1271 else if(Dh_prime_abs > 180.f && (h_test_prime > h_ref_prime))
1275 Dh_prime_abs = fabsf(Dh_prime);
1277 const float DH_prime = 2.f * sqrtf(C_test_prime * C_ref_prime) * sinf(
DEG_TO_RAD(Dh_prime) / 2.f);
1278 float H_avg_prime = h_ref_prime + h_test_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_avg_prime < 360.f))
1284 H_avg_prime = (H_avg_prime + 360.f) / 2.f;
1285 else if(Dh_prime_abs > 180.f && (H_avg_prime >= 360.f))
1286 H_avg_prime = (H_avg_prime - 360.f) / 2.f;
1290 + 0.24f * cosf(2.f *
DEG_TO_RAD(H_avg_prime))
1294 const float S_L = 1.f + (0.015f * sqf(L_avg - 50.f)) / sqrtf(20.f + sqf(L_avg - 50.f));
1295 const float S_C = 1.f + 0.045f * C_avg_prime;
1296 const float S_H = 1.f + 0.015f * C_avg_prime * T;
1297 const float R_T = -2.f * C_avg_7_ratio_sqrt
1298 * sinf(
DEG_TO_RAD(60.f) * expf(-sqf((H_avg_prime - 275.f) / 25.f)));
1301 const float DE = sqrtf(sqf(DL / S_L) + sqf(DC_prime / S_C) + sqf(DH_prime / S_H)
1302 + R_T * (DC_prime / S_C) * (DH_prime / S_H));
1312 dE += DE / (float)checker->
patches;
1313 if(DE > max_dE) max_dE = DE;
1317 *max_delta_E = max_dE;
1321 float hue = atan2f(reference[2], reference[1]); \
1322 const float chroma = hypotf(reference[2], reference[1]); \
1323 float delta_hue = hue - ref_hue; \
1326 else if(fabsf(delta_hue) <= M_PI) \
1328 else if(fabsf(delta_hue) > M_PI && (hue <= ref_hue)) \
1329 delta_hue += 2.f * M_PI; \
1330 else if(fabsf(delta_hue) > M_PI && (hue > ref_hue)) \
1331 delta_hue -= 2.f * M_PI; \
1332 w = sqrtf(expf(-sqf(delta_hue) / 2.f));
1344 float *
const restrict patches,
1345 const gboolean normalize_exposure,
1350 const float radius_x =
g->checker->radius * hypotf(1.f,
g->checker->ratio) *
g->safety_margin;
1351 const float radius_y = radius_x /
g->checker->ratio;
1360 for(
size_t k = 0;
k <
g->checker->patches;
k++)
1363 const point_t center = {
g->checker->values[
k].
x,
g->checker->values[
k].y };
1366 const point_t corners[4] = { {center.
x - radius_x, center.
y - radius_y},
1367 {center.
x + radius_x, center.
y - radius_y},
1368 {center.
x + radius_x, center.
y + radius_y},
1369 {center.
x - radius_x, center.
y + radius_y} };
1374 size_t x_min =
width - 1;
1376 size_t y_min =
height - 1;
1378 for(
size_t c = 0; c < 4; c++) {
1380 x_min = fminf(new_corners[c].
x, x_min);
1381 x_max = fmaxf(new_corners[c].
x, x_max);
1382 y_min = fminf(new_corners[c].y, y_min);
1383 y_max = fmaxf(new_corners[c].y, y_max);
1386 x_min = CLAMP((
size_t)floorf(x_min), 0,
width - 1);
1387 x_max = CLAMP((
size_t)ceilf(x_max), 0,
width - 1);
1388 y_min = CLAMP((
size_t)floorf(y_min), 0,
height - 1);
1389 y_max = CLAMP((
size_t)ceilf(y_max), 0,
height - 1);
1392 patches[
k * 4] = patches[
k * 4 + 1] = patches[
k * 4 + 2] = patches[
k * 4 + 3] = 0.f;
1393 size_t num_elem = 0;
1396 for(
size_t j = y_min; j < y_max; j++)
1397 for(
size_t i = x_min;
i < x_max;
i++)
1400 point_t current_point = {
i + 0.5f, j + 0.5f };
1402 current_point.
x -= center.
x;
1403 current_point.
y -= center.
y;
1405 if(current_point.
x < radius_x && current_point.
x > -radius_x &&
1406 current_point.
y < radius_y && current_point.
y > -radius_y)
1408 for(
size_t c = 0; c < 3; c++)
1410 patches[
k * 4 + c] += in[(j *
width +
i) * 4 + c];
1420 for(
size_t c = 0; c < 3; c++) patches[
k * 4 + c] /= (
float)num_elem;
1424 dot_product(patches +
k * 4, RGB_to_XYZ,
XYZ);
1425 for(
size_t c = 0; c < 3; c++) patches[
k * 4 + c] =
XYZ[c];
1430 dt_Lab_to_XYZ(
g->checker->values[
g->checker->white].Lab, XYZ_white_ref);
1431 const float white_ref_norm = euclidean_norm(XYZ_white_ref);
1435 for(
size_t c = 0; c < 3; c++) XYZ_white_test[c] = patches[
g->checker->white * 4 + c];
1436 const float white_test_norm = euclidean_norm(XYZ_white_test);
1441 float exposure = white_ref_norm / white_test_norm;
1447 if(normalize_exposure)
1449 for(
size_t k = 0;
k <
g->checker->patches;
k++)
1451 float *
const sample = patches +
k * 4;
1456 const float sample_norm = euclidean_norm(sample);
1457 const float ref_norm = euclidean_norm(XYZ_ref);
1459 const float relative_luminance_test = sample_norm / white_test_norm;
1460 const float relative_luminance_ref = ref_norm / white_ref_norm;
1462 const float luma_correction = relative_luminance_ref / relative_luminance_test;
1463 for(
size_t c = 0; c < 3; ++c) sample[c] *= luma_correction * exposure;
1475 float mean_ref = 0.f;
1476 float mean_test = 0.f;
1478 for(
size_t k = 0;
k <
g->checker->patches;
k++)
1483 for(
size_t c = 0; c < 3; c++) XYZ_test[c] = patches[
k * 4 + c];
1486 dot_product(XYZ_test, XYZ_to_CAM, RGB_test);
1487 dot_product(XYZ_ref, XYZ_to_CAM, RGB_ref);
1492 for(
int c = 0; c < 3; c++)
1494 mean_test += RGB_test[c];
1495 mean_ref += RGB_ref[c];
1498 mean_test /= 3.f *
g->checker->patches;
1499 mean_ref /= 3.f *
g->checker->patches;
1501 float variance = 0.f;
1502 float covariance = 0.f;
1504 for(
size_t k = 0;
k <
g->checker->patches;
k++)
1509 for(
size_t c = 0; c < 3; c++) XYZ_test[c] = patches[
k * 4 + c];
1512 dot_product(XYZ_test, XYZ_to_CAM, RGB_test);
1513 dot_product(XYZ_ref, XYZ_to_CAM, RGB_ref);
1515 for(
int c = 0; c < 3; c++)
1517 variance += sqf(RGB_test[c] - mean_test);
1518 covariance += (RGB_ref[c] - mean_ref) * (RGB_test[c] - mean_ref);
1521 variance /= 3.f *
g->checker->patches;
1522 covariance /= 3.f *
g->checker->patches;
1528 exposure = covariance / variance;
1529 black = mean_ref - exposure * mean_test;
1537 result->
black = black;
1562 float pre_wb_delta_E = 0.f;
1563 float pre_wb_max_delta_E = 0.f;
1570 dt_Lab_to_XYZ(
g->checker->values[
g->checker->middle_grey].Lab, XYZ_grey_ref);
1574 for(
size_t c = 0; c < 3; c++) XYZ_grey_test[c] = patches[
g->checker->middle_grey * 4 + c];
1581 const float Y_test = XYZ_grey_test[1];
1582 const float Y_ref = XYZ_grey_ref[1];
1583 for(
size_t c = 0; c < 3; c++)
1585 XYZ_grey_ref[c] /= Y_ref;
1586 XYZ_grey_test[c] /= Y_test;
1597 for(
size_t c = 0; c < 3; c++)
illuminant[c] = D50_LMS[c] * LMS_grey_test[c] / LMS_grey_ref[c];
1602 const float Y_illu = illuminant_XYZ[1];
1603 for(
size_t c = 0; c < 3; c++) illuminant_XYZ[c] /= Y_illu;
1607 g->xy[0] = illuminant_xyY[0];
1608 g->xy[1] = illuminant_xyY[1];
1613 const float p = powf(0.818155f /
illuminant[2], 0.0834f);
1616 for(
size_t k = 0;
k <
g->checker->patches;
k++)
1619 float *
const sample = patches +
k * 4;
1620 const float Y = sample[1];
1621 downscale_vector(sample, Y);
1627 const dt_aligned_pixel_simd_t LMS_v = dt_load_simd_aligned(
LMS);
1628 const dt_aligned_pixel_simd_t illuminant_v = dt_load_simd_aligned(
illuminant);
1657 for(
size_t c = 0; c < 3; ++c) temp[c] =
LMS[c];
1663 upscale_vector(sample, Y);
1667 float post_wb_delta_E = 0.f;
1668 float post_wb_max_delta_E = 0.f;
1672 double *
const restrict Y =
dt_alloc_align(
g->checker->patches * 3 *
sizeof(
double));
1673 double *
const restrict
A =
dt_alloc_align(
g->checker->patches * 3 * 9 *
sizeof(
double));
1682 for(
size_t k = 0;
k <
g->checker->patches;
k++)
1684 float *
const sample = patches +
k * 4;
1688 const float *
const reference =
g->checker->values[
k].Lab;
1696 w = sqrtf(1.f / (
float)
g->checker->patches);
1698 w = sqrtf(hypotf(reference[1] / 128.f, reference[2] / 128.f));
1700 w = sqrtf(1.f - hypotf(reference[1] / 128.f, reference[2] / 128.f));
1704 const float ref_hue = 1.f;
1710 const float ref_hue = 2.23f;
1716 const float ref_hue = -1.93f;
1720 w = sqrtf(sqrtf(1.f /
g->delta_E_in[
k]));
1722 w = sqrtf(sqrtf(
g->delta_E_in[
k]));
1725 for(
size_t c = 0; c < 3; c++) Y[
k * 3 + c] = w * LMS_ref[c];
1728 A[
k * 3 * 9 + 0] = w * LMS_test[0];
1729 A[
k * 3 * 9 + 1] = w * LMS_test[1];
1730 A[
k * 3 * 9 + 2] = w * LMS_test[2];
1731 A[
k * 3 * 9 + 3] = 0.f;
1732 A[
k * 3 * 9 + 4] = 0.f;
1733 A[
k * 3 * 9 + 5] = 0.f;
1734 A[
k * 3 * 9 + 6] = 0.f;
1735 A[
k * 3 * 9 + 7] = 0.f;
1736 A[
k * 3 * 9 + 8] = 0.f;
1739 A[
k * 3 * 9 + 9 + 0] = 0.f;
1740 A[
k * 3 * 9 + 9 + 1] = 0.f;
1741 A[
k * 3 * 9 + 9 + 2] = 0.f;
1742 A[
k * 3 * 9 + 9 + 3] = w * LMS_test[0];
1743 A[
k * 3 * 9 + 9 + 4] = w * LMS_test[1];
1744 A[
k * 3 * 9 + 9 + 5] = w * LMS_test[2];
1745 A[
k * 3 * 9 + 9 + 6] = 0.f;
1746 A[
k * 3 * 9 + 9 + 7] = 0.f;
1747 A[
k * 3 * 9 + 9 + 8] = 0.f;
1750 A[
k * 3 * 9 + 18 + 0] = 0.f;
1751 A[
k * 3 * 9 + 18 + 1] = 0.f;
1752 A[
k * 3 * 9 + 18 + 2] = 0.f;
1753 A[
k * 3 * 9 + 18 + 3] = 0.f;
1754 A[
k * 3 * 9 + 18 + 4] = 0.f;
1755 A[
k * 3 * 9 + 18 + 5] = 0.f;
1756 A[
k * 3 * 9 + 18 + 6] = w * LMS_test[0];
1757 A[
k * 3 * 9 + 18 + 7] = w * LMS_test[1];
1758 A[
k * 3 * 9 + 18 + 8] = w * LMS_test[2];
1776 for(
size_t k = 0;
k <
g->checker->patches;
k++)
1778 float *
const sample = patches +
k * 4;
1783 for(
size_t c = 0; c < 3; c++) temp[c] = sample[c];
1786 dot_product(LMS_test,
g->mix, temp);
1791 float post_mix_delta_E = 0.f;
1792 float post_mix_max_delta_E = 0.f;
1798 float x = illuminant_xyY[0];
1799 float y = illuminant_xyY[1];
1800 check_if_close_to_daylight(
x, y, &temperature, &test_illuminant, NULL);
1803 string = _(
"(daylight)");
1805 string = _(
"(black body)");
1807 string = _(
"(invalid)");
1810 if(post_mix_delta_E <= 1.2f)
1811 diagnostic = _(
"very good");
1812 else if(post_mix_delta_E <= 2.3f)
1813 diagnostic = _(
"good");
1814 else if(post_mix_delta_E <= 3.4f)
1815 diagnostic = _(
"passable");
1817 diagnostic = _(
"bad");
1819 g->profile_ready =
TRUE;
1823 g->delta_E_label_text
1824 = g_strdup_printf(_(
"\n<b>Profile quality report: %s</b>\n"
1825 "input \316\224E: \tavg. %.2f ; \tmax. %.2f\n"
1826 "WB \316\224E: \tavg. %.2f; \tmax. %.2f\n"
1827 "output \316\224E: \tavg. %.2f; \tmax. %.2f\n\n"
1828 "<b>Profile data</b>\n"
1829 "illuminant: \t%.0f K \t%s\n"
1830 "matrix in adaptation space:\n"
1831 "<tt>%+.4f \t%+.4f \t%+.4f\n"
1832 "%+.4f \t%+.4f \t%+.4f\n"
1833 "%+.4f \t%+.4f \t%+.4f</tt>\n\n"
1834 "<b>Normalization values</b>\n"
1835 "exposure compensation: \t%+.2f EV\n"
1836 "black offset: \t%+.4f"
1838 diagnostic, pre_wb_delta_E, pre_wb_max_delta_E, post_wb_delta_E, post_wb_max_delta_E,
1839 post_mix_delta_E, post_mix_max_delta_E, temperature,
string,
g->mix[0][0],
g->mix[0][1],
1840 g->mix[0][2],
g->mix[1][0],
g->mix[1][1],
g->mix[1][2],
g->mix[2][0],
g->mix[2][1],
1841 g->mix[2][2], log2f(extraction_result.
exposure), extraction_result.
black);
1861 float pre_wb_delta_E = 0.f;
1862 float pre_wb_max_delta_E = 0.f;
1866 if(pre_wb_delta_E <= 1.2f)
1867 diagnostic = _(
"very good");
1868 else if(pre_wb_delta_E <= 2.3f)
1869 diagnostic = _(
"good");
1870 else if(pre_wb_delta_E <= 3.4f)
1871 diagnostic = _(
"passable");
1873 diagnostic = _(
"bad");
1877 g->delta_E_label_text = g_strdup_printf(_(
"\n<b>Profile quality report: %s</b>\n"
1878 "output \316\224E: \tavg. %.2f; \tmax. %.2f\n\n"
1879 "<b>Normalization values</b>\n"
1880 "exposure compensation: \t%+.2f EV\n"
1881 "black offset: \t%+.4f"),
1882 diagnostic, pre_wb_delta_E, pre_wb_max_delta_E, log2f(extraction_result.
exposure),
1883 extraction_result.
black);
1890 const void *
const restrict ivoid,
void *
const restrict
ovoid)
1909 memcpy(RGB_to_XYZ, work_profile->
matrix_in,
sizeof(RGB_to_XYZ));
1910 memcpy(XYZ_to_RGB, work_profile->
matrix_out,
sizeof(XYZ_to_RGB));
1911 memcpy(XYZ_to_CAM, input_profile->
matrix_out,
sizeof(XYZ_to_CAM));
1914 const size_t ch = 4;
1916 const float *
const restrict in = (
const float *
const restrict)ivoid;
1917 float *
const restrict
out = (
float *
const restrict)
ovoid;
1922 gboolean exit =
FALSE;
1990 XYZ_to_RGB, RGB_to_XYZ, data->
MIX,
1998 XYZ_to_RGB, RGB_to_XYZ, data->
MIX,
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,
2039 g->run_validation =
FALSE;
2077 d->illuminant[3] = 0.f;
2083 const int devid = pipe->
devid;
2089 cl_mem input_matrix_cl = NULL;
2090 cl_mem output_matrix_cl = NULL;
2092 float input_matrix_3x4[12];
2093 float output_matrix_3x4[12];
2094 float mix_matrix_3x4[12];
2106 switch(
d->adaptation)
2154 if(err != CL_SUCCESS)
goto error;
2171 const int program = 32;
2198 const float x_increment,
const float y_increment)
2201 for(
size_t k = 0;
k < 4;
k++)
2203 if(
g->active_node[
k])
2205 g->box[
k].x += x_increment;
2206 g->box[
k].y += y_increment;
2217 if(
g->checker && !
g->checker_ready)
2220 g->box[0].x =
g->box[0].y = 10.;
2223 g->box[1].x = (
width - 10.);
2224 g->box[1].y =
g->box[0].y;
2227 g->box[2].x =
g->box[1].x;
2228 g->box[2].y = (
width - 10.) *
g->checker->ratio;
2231 g->box[3].x =
g->box[0].x;
2232 g->box[3].y =
g->box[2].y;
2234 g->checker_ready =
TRUE;
2237 g->center_box.x = 0.5f;
2238 g->center_box.y = 0.5f;
2240 g->ideal_box[0].x = 0.f;
2241 g->ideal_box[0].y = 0.f;
2242 g->ideal_box[1].x = 1.f;
2243 g->ideal_box[1].y = 0.f;
2244 g->ideal_box[2].x = 1.f;
2245 g->ideal_box[2].y = 1.f;
2246 g->ideal_box[3].x = 0.f;
2247 g->ideal_box[3].y = 1.f;
2260 if(
g->box[0].x == -1.0f ||
g->box[1].y == -1.0f)
return 0;
2265 if(wd == 0.f || ht == 0.f)
return 0;
2267 float pzxpy[2] = { (float)
x, (
float)y };
2270 const float pzx = pzxpy[0];
2271 const float pzy = pzxpy[1];
2279 g->click_end.x = pzx;
2280 g->click_end.y = pzy;
2284 g->click_start.x = pzx;
2285 g->click_start.y = pzy;
2294 g->is_cursor_close =
FALSE;
2296 for(
size_t k = 0;
k < 4;
k++)
2298 if(hypotf(pzx -
g->box[
k].x, pzy -
g->box[
k].y) < 15.f)
2300 g->active_node[
k] =
TRUE;
2301 g->is_cursor_close =
TRUE;
2309 if(
g->is_cursor_close)
2336 if(wd == 0.f || ht == 0.f)
return 0;
2339 if(
type == GDK_DOUBLE_BUTTON_PRESS)
2342 g->checker_ready =
FALSE;
2343 g->profile_ready =
FALSE;
2352 if(
g->box[0].x == -1.0f ||
g->box[1].y == -1.0f)
return 0;
2355 if(!
g->is_cursor_close)
return 0;
2357 float pzxpy[2] = { (float)
x, (
float)y };
2360 const float pzx = pzxpy[0];
2361 const float pzy = pzxpy[1];
2364 g->drag_drop =
TRUE;
2365 g->click_start.x = pzx;
2366 g->click_start.y = pzy;
2380 if(
g->box[0].x == -1.0f ||
g->box[1].y == -1.0f)
return 0;
2381 if(!
g->is_cursor_close || !
g->drag_drop)
return 0;
2386 if(wd == 0.f || ht == 0.f)
return 0;
2388 float pzxpy[2] = { (float)
x, (
float)y };
2391 const float pzx = pzxpy[0];
2392 const float pzy = pzxpy[1];
2396 g->click_end.x = pzx;
2397 g->click_end.y = pzy;
2407 int32_t pointerx, int32_t pointery)
2420 cairo_set_line_width(cr, 2.0 / zoom_scale);
2421 const double origin = 9. / zoom_scale;
2422 const double destination = 18. / zoom_scale;
2424 for(
size_t k = 0;
k < 4;
k++)
2426 if(
g->active_node[
k])
2429 cairo_set_source_rgba(cr, 1., 1., 1., 1.);
2431 cairo_move_to(cr,
g->box[
k].x - origin,
g->box[
k].y);
2432 cairo_line_to(cr,
g->box[
k].x - destination,
g->box[
k].y);
2434 cairo_move_to(cr,
g->box[
k].x + origin,
g->box[
k].y);
2435 cairo_line_to(cr,
g->box[
k].x + destination,
g->box[
k].y);
2437 cairo_move_to(cr,
g->box[
k].x,
g->box[
k].y - origin);
2438 cairo_line_to(cr,
g->box[
k].x,
g->box[
k].y - destination);
2440 cairo_move_to(cr,
g->box[
k].x,
g->box[
k].y + origin);
2441 cairo_line_to(cr,
g->box[
k].x,
g->box[
k].y + destination);
2447 cairo_set_source_rgba(cr, 1., 1., 1., 1.);
2448 cairo_arc(cr,
g->box[
k].x,
g->box[
k].y, 8. / zoom_scale, 0, 2. *
M_PI);
2452 cairo_set_source_rgba(cr, 0., 0., 0., 1.);
2453 cairo_arc(cr,
g->box[
k].x,
g->box[
k].y, 1.5 / zoom_scale, 0, 2. *
M_PI);
2458 cairo_set_line_width(cr, 1.5 / zoom_scale);
2459 cairo_set_source_rgba(cr, 1., 1., 1., 1.);
2460 const point_t top_ideal = { 0.5f, 1.f };
2462 const point_t bottom_ideal = { 0.5f, 0.f };
2464 cairo_move_to(cr,
top.x,
top.y);
2465 cairo_line_to(cr, bottom.
x, bottom.
y);
2468 const point_t left_ideal = { 0.f, 0.5f };
2470 const point_t right_ideal = { 1.f, 0.5f };
2472 cairo_move_to(cr, left.
x, left.
y);
2473 cairo_line_to(cr, right.
x, right.
y);
2486 if(!
g->checker || !
g->checker->finished || !
g->checker->values)
2490 const point_t target_center = { 0.5f, 0.5f };
2493 const char *msg = _(
"Error: No color data available for the selected chart.");
2494 cairo_set_font_size(cr, 20.0 / zoom_scale);
2495 cairo_text_extents_t extents;
2496 cairo_text_extents(cr, msg, &extents);
2499 cairo_set_source_rgba(cr, 1., 1., 1., 1.);
2501 new_target_center.
x - extents.width / 2.0 - 5.0 / zoom_scale,
2502 new_target_center.
y - extents.height / 2.0 - 5.0 / zoom_scale,
2503 extents.width + 10.0 / zoom_scale,
2504 extents.height + 10.0 / zoom_scale);
2508 cairo_set_source_rgba(cr, 1., 0., 0., 1.);
2509 cairo_select_font_face(cr,
"roboto", CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_NORMAL);
2510 cairo_move_to(cr, new_target_center.
x - extents.width / 2.0, new_target_center.
y + extents.height / 2.0);
2511 cairo_show_text(cr, msg);
2515 const float radius_x =
g->checker->radius * hypotf(1.f,
g->checker->ratio) *
g->safety_margin;
2516 const float radius_y = radius_x /
g->checker->ratio;
2518 for(
size_t k = 0;
k <
g->checker->patches;
k++)
2521 const point_t center = {
g->checker->values[
k].
x,
g->checker->values[
k].y };
2524 const point_t corners[4] = { {center.
x - radius_x, center.
y - radius_y},
2525 {center.
x + radius_x, center.
y - radius_y},
2526 {center.
x + radius_x, center.
y + radius_y},
2527 {center.
x - radius_x, center.
y + radius_y} };
2535 for(
size_t c = 0; c < 4; c++) new_corners[c] =
apply_homography(corners[c],
g->homography);
2537 cairo_set_line_cap(cr, CAIRO_LINE_CAP_SQUARE);
2538 cairo_set_source_rgba(cr, 0., 0., 0., 1.);
2539 cairo_move_to(cr, new_corners[0].
x, new_corners[0].y);
2540 cairo_line_to(cr, new_corners[1].
x, new_corners[1].y);
2541 cairo_line_to(cr, new_corners[2].
x, new_corners[2].y);
2542 cairo_line_to(cr, new_corners[3].
x, new_corners[3].y);
2543 cairo_line_to(cr, new_corners[0].
x, new_corners[0].y);
2548 if(
g->delta_E_in[
k] > 2.3f)
2551 cairo_move_to(cr, new_corners[0].
x, new_corners[0].y);
2552 cairo_line_to(cr, new_corners[2].
x, new_corners[2].y);
2554 if(
g->delta_E_in[
k] > 4.6f)
2557 cairo_move_to(cr, new_corners[1].
x, new_corners[1].y);
2558 cairo_line_to(cr, new_corners[3].
x, new_corners[3].y);
2562 cairo_set_line_width(cr, 5.0 / zoom_scale);
2563 cairo_stroke_preserve(cr);
2564 cairo_set_line_width(cr, 2.0 / zoom_scale);
2565 cairo_set_source_rgba(cr, 1., 1., 1., 1.);
2568 cairo_set_line_cap(cr, CAIRO_LINE_CAP_BUTT);
2572 work_profile->unbounded_coeffs_out, work_profile->
lutsize,
2575 cairo_set_source_rgba(cr,
RGB[0],
RGB[1],
RGB[2], 1.);
2576 cairo_arc(cr, new_center.
x, new_center.
y, 0.25 * (radius_x + radius_y) *
scaling, 0, 2. *
M_PI);
2588 gtk_widget_show(GTK_WIDGET(
g->checkers_color_list));
2589 gtk_widget_hide(GTK_WIDGET(
g->checker_msg));
2593 gtk_widget_hide(GTK_WIDGET(
g->checkers_color_list));
2594 gtk_widget_show_now(GTK_WIDGET(
g->checker_msg));
2599 gtk_widget_hide(GTK_WIDGET(
g->checkers_color_list));
2600 gtk_widget_hide(GTK_WIDGET(
g->checker_msg));
2614 if(!
g->colorcheckers)
return;
2616 const int selected_checker =
dt_conf_get_int(
"darkroom/modules/channelmixerrgb/colorchecker");
2624 if(
g->n_color == 0)
return;
2628 g_list_free(
g->colorcheckers_color);
2629 g->colorcheckers_color = NULL;
2632 const int checker_patch_nb = checker_label ? checker_label->
patch_nb : 0;
2634 if(checker_patch_nb > 0)
2636 for(GList *l = g_list_first(
g->colorcheckers_all_color); l; l = g_list_next(l))
2641 if(cht_label->
patch_nb == checker_patch_nb)
2644 g->colorcheckers_color = g_list_append(
g->colorcheckers_color, cht_label);
2658 g->n_colorcheckers = 0;
2660 g->n_colorcheckers = pos;
2664 gboolean has_builtin_checker =
FALSE;
2665 gboolean user_checker_separator_added =
FALSE;
2666 for(GList *l = g_list_first(
g->colorcheckers); l; l = g_list_next(l))
2672 has_builtin_checker =
TRUE;
2674 else if(has_builtin_checker && !user_checker_separator_added)
2677 user_checker_separator_added =
TRUE;
2694 g->optimization =
i;
2705 dt_conf_set_int(
"darkroom/modules/channelmixerrgb/colorchecker_color", selected_cmbbx_index);
2712 const char *color_path = color_label ? color_label->
path : NULL;
2720 if(wd == 0.f || ht == 0.f)
return;
2723 g->profile_ready =
FALSE;
2737 dt_conf_set_int(
"darkroom/modules/channelmixerrgb/colorchecker", checker_cmbbx_index);
2745 const char *color_path = color_label ? color_label->
path : NULL;
2753 if(wd == 0.f || ht == 0.f)
return;
2756 g->profile_ready =
FALSE;
2782 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(self->
off),
TRUE);
2787 if(wd == 0.f || ht == 0.f)
return;
2790 g->is_profiling_started = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(
g->cs.toggle));
2807 g->run_profile =
TRUE;
2820 g->run_validation =
TRUE;
2833 if(!
g->profile_ready)
return;
2840 check_if_close_to_daylight(
p->x,
p->y, &
p->temperature, NULL, NULL);
2842 p->red[0] =
g->mix[0][0];
2843 p->red[1] =
g->mix[0][1];
2844 p->red[2] =
g->mix[0][2];
2846 p->green[0] =
g->mix[1][0];
2847 p->green[1] =
g->mix[1][1];
2848 p->green[2] =
g->mix[1][2];
2850 p->blue[0] =
g->mix[2][0];
2851 p->blue[1] =
g->mix[2][1];
2852 p->blue[2] =
g->mix[2][2];
2901 check_if_close_to_daylight(
p->x,
p->y, &
p->temperature, &
p->illuminant, &
p->adaptation);
2934 gtk_label_set_markup(GTK_LABEL(
g->label_delta_E),
g->delta_E_label_text);
2945 d->version =
p->version;
2947 float norm_R = 1.0f;
2948 if(
p->normalize_R) norm_R =
p->red[0] +
p->red[1] +
p->red[2];
2950 float norm_G = 1.0f;
2951 if(
p->normalize_G) norm_G =
p->green[0] +
p->green[1] +
p->green[2];
2953 float norm_B = 1.0f;
2954 if(
p->normalize_B) norm_B =
p->blue[0] +
p->blue[1] +
p->blue[2];
2956 float norm_sat = 0.0f;
2957 if(
p->normalize_sat) norm_sat = (
p->saturation[0] +
p->saturation[1] +
p->saturation[2]) / 3.f;
2959 float norm_light = 0.0f;
2960 if(
p->normalize_light) norm_light = (
p->lightness[0] +
p->lightness[1] +
p->lightness[2]) / 3.f;
2962 float norm_grey =
p->grey[0] +
p->grey[1] +
p->grey[2];
2963 d->apply_grey = (
p->grey[0] != 0.f) || (
p->grey[1] != 0.f) || (
p->grey[2] != 0.f);
2964 if(!
p->normalize_grey || norm_grey == 0.f) norm_grey = 1.f;
2966 for(
int i = 0;
i < 3;
i++)
2968 d->MIX[0][
i] =
p->red[
i] / norm_R;
2969 d->MIX[1][
i] =
p->green[
i] / norm_G;
2970 d->MIX[2][
i] =
p->blue[
i] / norm_B;
2971 d->saturation[
i] = -
p->saturation[
i] + norm_sat;
2972 d->lightness[
i] =
p->lightness[
i] - norm_light;
2973 d->grey[
i] =
p->grey[
i] / norm_grey;
2979 d->saturation[0] = -
p->saturation[2] + norm_sat;
2980 d->saturation[2] = -
p->saturation[0] + norm_sat;
2988 d->adaptation =
p->adaptation;
2990 d->gamut = (
p->gamut == 0.f) ?
p->gamut : 1.f /
p->gamut;
3001 check_if_close_to_daylight(
x, y, NULL, NULL, &(
d->adaptation));
3003 d->illuminant_type =
p->illuminant;
3011 d->illuminant[3] = 0.f;
3013 dt_iop_fmt_log(self,
"commit: class=%s matrix_supported=%d illuminant=%d adaptation=%d custom_wb=[%.4f %.4f %.4f %.4f] xy=[%.4f %.4f]",
3016 p->illuminant,
d->adaptation, custom_wb[0], custom_wb[1], custom_wb[2], custom_wb[3],
x, y);
3026 d->p = powf(0.818155f /
d->illuminant[2], 0.0834f);
3051 gtk_widget_set_visible(
g->illuminant,
FALSE);
3052 gtk_widget_set_visible(
g->illum_color,
FALSE);
3053 gtk_widget_set_visible(
g->approx_cct,
FALSE);
3054 gtk_widget_set_visible(
g->color_picker,
FALSE);
3055 gtk_widget_set_visible(
g->temperature,
FALSE);
3056 gtk_widget_set_visible(
g->illum_fluo,
FALSE);
3057 gtk_widget_set_visible(
g->illum_led,
FALSE);
3058 gtk_widget_set_visible(
g->illum_x,
FALSE);
3059 gtk_widget_set_visible(
g->illum_y,
FALSE);
3065 gtk_widget_set_visible(
g->illuminant,
TRUE);
3066 gtk_widget_set_visible(
g->illum_color,
TRUE);
3067 gtk_widget_set_visible(
g->approx_cct,
TRUE);
3068 gtk_widget_set_visible(
g->color_picker,
TRUE);
3069 gtk_widget_set_visible(
g->temperature,
TRUE);
3070 gtk_widget_set_visible(
g->illum_fluo,
TRUE);
3071 gtk_widget_set_visible(
g->illum_led,
TRUE);
3072 gtk_widget_set_visible(
g->illum_x,
TRUE);
3076 switch(
p->illuminant)
3082 gtk_widget_set_visible(
g->adaptation,
TRUE);
3083 gtk_widget_set_visible(
g->temperature,
FALSE);
3084 gtk_widget_set_visible(
g->illum_fluo,
FALSE);
3085 gtk_widget_set_visible(
g->illum_led,
FALSE);
3086 gtk_widget_set_visible(
g->illum_x,
FALSE);
3087 gtk_widget_set_visible(
g->illum_y,
FALSE);
3093 gtk_widget_set_visible(
g->adaptation,
TRUE);
3094 gtk_widget_set_visible(
g->temperature,
TRUE);
3095 gtk_widget_set_visible(
g->illum_fluo,
FALSE);
3096 gtk_widget_set_visible(
g->illum_led,
FALSE);
3097 gtk_widget_set_visible(
g->illum_x,
FALSE);
3098 gtk_widget_set_visible(
g->illum_y,
FALSE);
3103 gtk_widget_set_visible(
g->adaptation,
TRUE);
3104 gtk_widget_set_visible(
g->temperature,
FALSE);
3105 gtk_widget_set_visible(
g->illum_fluo,
TRUE);
3106 gtk_widget_set_visible(
g->illum_led,
FALSE);
3107 gtk_widget_set_visible(
g->illum_x,
FALSE);
3108 gtk_widget_set_visible(
g->illum_y,
FALSE);
3113 gtk_widget_set_visible(
g->adaptation,
TRUE);
3114 gtk_widget_set_visible(
g->temperature,
FALSE);
3115 gtk_widget_set_visible(
g->illum_fluo,
FALSE);
3116 gtk_widget_set_visible(
g->illum_led,
TRUE);
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,
FALSE);
3126 gtk_widget_set_visible(
g->illum_led,
FALSE);
3127 gtk_widget_set_visible(
g->illum_x,
TRUE);
3128 gtk_widget_set_visible(
g->illum_y,
TRUE);
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,
FALSE);
3137 gtk_widget_set_visible(
g->illum_x,
FALSE);
3138 gtk_widget_set_visible(
g->illum_y,
FALSE);
3144 gtk_widget_set_visible(
g->adaptation,
FALSE);
3145 gtk_widget_set_visible(
g->temperature,
FALSE);
3146 gtk_widget_set_visible(
g->illum_fluo,
FALSE);
3147 gtk_widget_set_visible(
g->illum_led,
FALSE);
3148 gtk_widget_set_visible(
g->illum_x,
FALSE);
3149 gtk_widget_set_visible(
g->illum_y,
FALSE);
3212 dt_xyY_to_Lch(xyY2,
Lch);
3224 gtk_widget_queue_draw(
g->illum_x);
3225 gtk_widget_queue_draw(
g->illum_y);
3242 dt_LCH_2_Lab(Lch_hue,
Lab);
3250 dt_LCH_2_Lab(Lch_lightness,
Lab);
3258 dt_LCH_2_Lab(Lch_chroma,
Lab);
3265 gtk_widget_queue_draw(
g->hue_spot);
3266 gtk_widget_queue_draw(
g->lightness_spot);
3267 gtk_widget_queue_draw(
g->chroma_spot);
3268 gtk_widget_queue_draw(
g->target_spot);
3284 = {
g->primaries_achromatic_hue,
g->primaries_achromatic_purity,
g->primaries_red_hue,
3285 g->primaries_red_purity,
g->primaries_green_hue,
g->primaries_green_purity,
3286 g->primaries_blue_hue,
g->primaries_blue_purity,
g->primaries_gain };
3290 const float rows[3][3] = { {
p->red[0],
p->red[1],
p->red[2] },
3291 {
p->green[0],
p->green[1],
p->green[2] },
3292 {
p->blue[0],
p->blue[1],
p->blue[2] } };
3293 const gboolean
normalize[3] = {
p->normalize_R,
p->normalize_G,
p->normalize_B };
3294 float M[3][3] = { { 0.f } };
3295 float roundtrip[3][3] = { { 0.f } };
3318 = {
g->primaries_achromatic_hue,
g->primaries_achromatic_purity,
g->primaries_red_hue,
3319 g->primaries_red_purity,
g->primaries_green_hue,
g->primaries_green_purity,
3320 g->primaries_blue_hue,
g->primaries_blue_purity,
g->primaries_gain };
3339 convert_any_LMS_to_RGB(
LMS, work_rgb,
p->adaptation);
3343 for(
size_t c = 0; c < 3; c++) work_rgb[c] =
LMS[c];
3352 GtkWidget *w,
float stop,
float c,
float r,
float g,
float b)
3355 0.5f * (c *
g + 1 -
g),
3356 0.5f * (c * b + 1 - b)};
3375 const float sum =
RGB[0] +
RGB[1] +
RGB[2];
3376 if(sum != 0.f)
for(
int c = 0; c < 3; c++)
RGB[c] /= sum;
3392 gtk_widget_queue_draw(w_r);
3393 gtk_widget_queue_draw(w_b);
3394 gtk_widget_queue_draw(w_g);
3409 = {
g->simple_theta,
g->simple_psi,
g->simple_stretch_1,
g->simple_stretch_2,
g->simple_coupling_1,
3410 g->simple_coupling_2 };
3411 const float rows[3][3] = { {
p->red[0],
p->red[1],
p->red[2] },
3412 {
p->green[0],
p->green[1],
p->green[2] },
3413 {
p->blue[0],
p->blue[1],
p->blue[2] } };
3414 const gboolean
normalize[3] = {
p->normalize_R,
p->normalize_G,
p->normalize_B };
3415 float M[3][3] = { { 0.f } };
3416 float roundtrip[3][3] = { { 0.f } };
3454 = {
g->simple_theta,
g->simple_psi,
g->simple_stretch_1,
g->simple_stretch_2,
g->simple_coupling_1,
3455 g->simple_coupling_2 };
3469 gtk_widget_queue_draw(
g->illum_color);
3480 GtkAllocation allocation;
3481 gtk_widget_get_allocation(widget, &allocation);
3482 int width = allocation.width,
height = allocation.height;
3484 cairo_t *cr = cairo_create(cst);
3498 &
x, &y,
p->temperature,
p->illum_fluo,
p->illum_led);
3500 cairo_set_source_rgb(cr,
RGB[0],
RGB[1],
RGB[2]);
3507 cairo_set_source_surface(crf, cst, 0, 0);
3509 cairo_surface_destroy(cst);
3519 GtkAllocation allocation;
3520 gtk_widget_get_allocation(widget, &allocation);
3521 int width = allocation.width,
height = allocation.height;
3523 cairo_t *cr = cairo_create(cst);
3542 cairo_set_source_rgb(cr,
RGB[0],
RGB[1],
RGB[2]);
3549 cairo_set_source_surface(crf, cst, 0, 0);
3551 cairo_surface_destroy(cst);
3561 GtkAllocation allocation;
3562 gtk_widget_get_allocation(widget, &allocation);
3563 int width = allocation.width,
height = allocation.height;
3565 cairo_t *cr = cairo_create(cst);
3572 cairo_set_source_rgb(cr,
g->spot_RGB[0],
g->spot_RGB[1],
g->spot_RGB[2]);
3579 cairo_set_source_surface(crf, cst, 0, 0);
3581 cairo_surface_destroy(cst);
3598 check_if_close_to_daylight(
x, y, &
t, &test_illuminant, NULL);
3601 if(
t > 1667.f &&
t < 25000.f)
3605 str = g_strdup_printf(_(
"CCT: %.0f K (daylight)"),
t);
3606 gtk_widget_set_tooltip_text(GTK_WIDGET(
g->approx_cct),
3607 _(
"approximated correlated color temperature.\n"
3608 "this illuminant can be accurately modeled by a daylight spectrum,\n"
3609 "so its temperature is relevant and meaningful with a D illuminant."));
3613 str = g_strdup_printf(_(
"CCT: %.0f K (black body)"),
t);
3614 gtk_widget_set_tooltip_text(GTK_WIDGET(
g->approx_cct),
3615 _(
"approximated correlated color temperature.\n"
3616 "this illuminant can be accurately modeled by a black body spectrum,\n"
3617 "so its temperature is relevant and meaningful with a Planckian illuminant."));
3621 str = g_strdup_printf(_(
"CCT: %.0f K (invalid)"),
t);
3622 gtk_widget_set_tooltip_text(GTK_WIDGET(
g->approx_cct),
3623 _(
"approximated correlated color temperature.\n"
3624 "this illuminant cannot be accurately modeled by a daylight or black body spectrum,\n"
3625 "so its temperature is not relevant and meaningful and you need to use a custom illuminant."));
3630 str = g_strdup_printf(_(
"CCT: undefined"));
3631 gtk_widget_set_tooltip_text(GTK_WIDGET(
g->approx_cct),
3632 _(
"the approximated correlated color temperature\n"
3633 "cannot be computed at all so you need to use a custom illuminant."));
3635 gtk_label_set_text(GTK_LABEL(
g->approx_cct), str);
3669 dt_print(
DT_DEBUG_DEV,
"[picker/channelmixerrgb] history commit source=illum_xy_callback slider=%p\n",
3690 g->is_profiling_started =
FALSE;
3700 float simple_error = INFINITY;
3701 float primaries_error = INFINITY;
3717 gboolean use_mixing =
TRUE;
3719 use_mixing =
dt_conf_get_bool(
"darkroom/modules/channelmixerrgb/use_mixing");
3720 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(
g->use_mixing), use_mixing);
3722 float lightness = 50.f;
3739 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(
g->clip),
p->clip);
3740 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(
g->normalize_R),
p->normalize_R);
3741 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(
g->normalize_G),
p->normalize_G);
3742 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(
g->normalize_B),
p->normalize_B);
3762 gtk_widget_hide(GTK_WIDGET(
g->saturation_version));
3764 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(
g->normalize_sat),
p->normalize_sat);
3765 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(
g->normalize_light),
p->normalize_light);
3766 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(
g->normalize_grey),
p->normalize_grey);
3773 const int selected_checker =
dt_conf_get_int(
"darkroom/modules/channelmixerrgb/colorchecker");
3776 const int selected_color =
dt_conf_get_int(
"darkroom/modules/channelmixerrgb/colorchecker_color");
3780 const char *color_path = label ? label->
path : NULL;
3787 const int j =
dt_conf_get_int(
"darkroom/modules/channelmixerrgb/optimization");
3789 g->optimization = j;
3797 g->is_profiling_started =
FALSE;
3802 g->spot_RGB[0] = 0.f;
3803 g->spot_RGB[1] = 0.f;
3804 g->spot_RGB[2] = 0.f;
3805 g->spot_RGB[3] = 0.f;
3817 d->red[0] =
d->green[1] =
d->blue[2] = 1.0;
3818 d->normalize_R =
TRUE;
3819 d->normalize_G =
TRUE;
3820 d->normalize_B =
TRUE;
3827 d->normalize_R =
TRUE;
3828 d->normalize_G =
TRUE;
3829 d->normalize_B =
TRUE;
3831 d->x =
module->get_f("x")->Float.Default;
3832 d->y =
module->get_f("y")->Float.Default;
3833 d->temperature =
module->get_f("temperature")->Float.Default;
3834 d->illuminant =
module->get_f("illuminant")->Enum.Default;
3835 d->adaptation =
module->get_f("adaptation")->Enum.Default;
3840 const gboolean is_modern =
3847 const dt_image_t *img = &
module->dev->image_storage;
3850 gboolean CAT_already_applied =
3855 module->default_enabled = FALSE;
3858 if(!CAT_already_applied
3867 check_if_close_to_daylight(
d->x,
d->y, &(
d->temperature), &(
d->illuminant), &(
d->adaptation));
3868 module->workflow_enabled = TRUE;
3889 if(
g->delta_E_label_text)
3920 const gboolean use_mixing = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(
g->use_mixing));
3926 dt_conf_set_float(
"darkroom/modules/channelmixerrgb/hue", Lch_target[2] * 360.f);
3927 dt_conf_set_bool(
"darkroom/modules/channelmixerrgb/use_mixing", use_mixing);
3944 gtk_stack_set_visible_child_name(GTK_STACK(
g->mixer_stack),
3969 float error = INFINITY;
3972 dt_control_log(_(
"simple mixer mode requires all three output rows to be normalized with non-zero sums."));
3988 const float rows[3][3] = { {
p->red[0],
p->red[1],
p->red[2] },
3989 {
p->green[0],
p->green[1],
p->green[2] },
3990 {
p->blue[0],
p->blue[1],
p->blue[2] } };
3991 const gboolean
normalize[3] = {
p->normalize_R,
p->normalize_G,
p->normalize_B };
3992 float M[3][3] = { { 0.f } };
3993 float error = INFINITY;
3997 dt_control_log(_(
"primaries mixer mode requires a non-singular 3x3 matrix with non-zero affine sums."));
4006 gboolean changed =
p->normalize_R ||
p->normalize_G ||
p->normalize_B;
4007 for(
int col = 0; col < 3; col++)
4009 changed = changed ||
p->red[col] !=
M[0][col] ||
p->green[col] !=
M[1][col] ||
p->blue[col] !=
M[2][col];
4010 p->red[col] =
M[0][col];
4011 p->green[col] =
M[1][col];
4012 p->blue[col] =
M[2][col];
4020 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(
g->normalize_R),
FALSE);
4021 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(
g->normalize_G),
FALSE);
4022 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(
g->normalize_B),
FALSE);
4053 = {
g->simple_theta,
g->simple_psi,
g->simple_stretch_1,
g->simple_stretch_2,
g->simple_coupling_1,
4054 g->simple_coupling_2 };
4056 float M[3][3] = { { 0.f } };
4061 for(
int col = 0; col < 3; col++)
4063 p->red[col] =
M[0][col];
4064 p->green[col] =
M[1][col];
4065 p->blue[col] =
M[2][col];
4069 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(
g->normalize_R),
p->normalize_R);
4070 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(
g->normalize_G),
p->normalize_G);
4071 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(
g->normalize_B),
p->normalize_B);
4086 dt_print(
DT_DEBUG_DEV,
"[channelmixerrgb] history commit source=simple_slider slider=%p\n", (
void *)slider);
4098 = {
g->primaries_achromatic_hue,
g->primaries_achromatic_purity,
g->primaries_red_hue,
4099 g->primaries_red_purity,
g->primaries_green_hue,
g->primaries_green_purity,
4100 g->primaries_blue_hue,
g->primaries_blue_purity,
g->primaries_gain };
4104 float M[3][3] = { { 0.f } };
4109 dt_control_log(_(
"primaries mixer mode requires a non-singular 3x3 matrix with non-zero affine sums."));
4113 for(
int col = 0; col < 3; col++)
4115 p->red[col] =
M[0][col];
4116 p->green[col] =
M[1][col];
4117 p->blue[col] =
M[2][col];
4124 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(
g->normalize_R),
FALSE);
4125 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(
g->normalize_G),
FALSE);
4126 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(
g->normalize_B),
FALSE);
4141 dt_print(
DT_DEBUG_DEV,
"[channelmixerrgb] history commit source=primaries_slider slider=%p\n", (
void *)slider);
4150 const gboolean simple_widget
4151 = w ==
g->simple_theta || w ==
g->simple_psi || w ==
g->simple_stretch_1 || w ==
g->simple_stretch_2
4152 || w ==
g->simple_coupling_1 || w ==
g->simple_coupling_2;
4153 const gboolean primaries_widget
4154 = w ==
g->primaries_achromatic_hue || w ==
g->primaries_achromatic_purity || w ==
g->primaries_red_hue
4155 || w ==
g->primaries_red_purity || w ==
g->primaries_green_hue || w ==
g->primaries_green_purity
4156 || w ==
g->primaries_blue_hue || w ==
g->primaries_blue_purity || w ==
g->primaries_gain;
4157 const gboolean
normalize[3] = {
p->normalize_R,
p->normalize_G,
p->normalize_B };
4159 const gboolean complete_widget
4160 = w ==
g->scale_red_R || w ==
g->scale_red_G || w ==
g->scale_red_B || w ==
g->scale_green_R
4161 || w ==
g->scale_green_G || w ==
g->scale_green_B || w ==
g->scale_blue_R || w ==
g->scale_blue_G
4162 || w ==
g->scale_blue_B || w ==
g->normalize_R || w ==
g->normalize_G || w ==
g->normalize_B;
4165 const gboolean illuminant_widgets_ready
4185 check_if_close_to_daylight(
p->x,
p->y, &(
p->temperature), NULL, &(
p->adaptation));
4194 check_if_close_to_daylight(
p->x,
p->y, &(
p->temperature), NULL, &(
p->adaptation));
4197 dt_control_log(_(
"white balance successfully extracted from raw image"));
4207 if(!
IS_NULL_PTR(w) && (w ==
g->illuminant || w ==
g->illum_fluo || w ==
g->illum_led || w ==
g->temperature))
4214 illuminant_to_xy(
p->illuminant, NULL, NULL, &(
p->x), &(
p->y),
p->temperature,
p->illum_fluo,
p->illum_led);
4220 check_if_close_to_daylight(
p->x,
p->y, &(
p->temperature), NULL, NULL);
4226 if(
IS_NULL_PTR(w) || w ==
g->hue_spot || w ==
g->chroma_spot || w ==
g->lightness_spot || w ==
g->spot_settings)
4231 if((
IS_NULL_PTR(w) || w ==
g->illuminant || w ==
g->illum_fluo || w ==
g->illum_led || w ==
g->temperature)
4232 && illuminant_widgets_ready)
4256 if(w ==
g->adaptation)
4259 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)
4261 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)
4262 _update_RGB_colors(self, 0, 1, 0,
p->normalize_G,
p->green,
g->scale_green_R,
g->scale_green_G,
g->scale_green_B);
4263 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)
4264 _update_RGB_colors(self, 0, 0, 1,
p->normalize_B,
p->blue,
g->scale_blue_R,
g->scale_blue_G,
g->scale_blue_B);
4266 if(rows_are_normalized && !simple_widget && (
IS_NULL_PTR(w) || complete_widget))
4268 float error = INFINITY;
4274 error,
p->normalize_R,
p->normalize_G,
p->normalize_B);
4275 dt_control_log(_(
"simple mixer mode requires all three output rows to be normalized with non-zero sums."));
4285 if(!primaries_widget && (
IS_NULL_PTR(w) || complete_widget || simple_widget || w ==
g->adaptation))
4287 float error = INFINITY;
4300 if(
IS_NULL_PTR(w) || w ==
g->adaptation || complete_widget || simple_widget || primaries_widget)
4303 if(
IS_NULL_PTR(w) || w ==
g->adaptation || complete_widget || simple_widget || primaries_widget)
4308 if((
p->grey[0] != 0.f) || (
p->grey[1] != 0.f) || (
p->grey[2] != 0.f))
4309 if((
p->grey[0] +
p->grey[1] +
p->grey[2] == 0.f) &&
p->normalize_grey)
4310 dt_control_log(_(
"color calibration: the sum of the gray channel parameters is zero, normalization will be disabled."));
4315 gtk_widget_queue_draw(
g->adaptation);
4336 if(!isfinite(
RGB[0]) || !isfinite(
RGB[1]) || !isfinite(
RGB[2]))
4358 if(!isfinite(
XYZ[0]) || !isfinite(
XYZ[1]) || !isfinite(
XYZ[2]))
return;
4369 gtk_label_set_text(GTK_LABEL(
g->Lch_origin),
4370 g_strdup_printf(_(
"L: \t%.1f %%\nh: \t%.1f \302\260\nc: \t%.1f"),
4372 gtk_widget_queue_draw(
g->origin_spot);
4376 const gboolean use_mixing = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(
g->use_mixing));
4381 float norm_R = 1.0f;
4382 if(
p->normalize_R) norm_R =
p->red[0] +
p->red[1] +
p->red[2];
4384 float norm_G = 1.0f;
4385 if(
p->normalize_G) norm_G =
p->green[0] +
p->green[1] +
p->green[2];
4387 float norm_B = 1.0f;
4388 if(
p->normalize_B) norm_B =
p->blue[0] +
p->blue[1] +
p->blue[2];
4390 for(
int i = 0;
i < 3;
i++)
4392 MIX[0][
i] =
p->red[
i] / norm_R;
4393 MIX[1][
i] =
p->green[
i] / norm_G;
4394 MIX[2][
i] =
p->blue[
i] / norm_B;
4421 const float pp = powf(0.818155f / LMS_illuminant[2], 0.0834f);
4431 dt_load_simd_aligned(LMS_illuminant),
4440 dot_product(LMS_output, MIX, temp);
4448 dt_Lab_2_LCH(Lab_output, Lch_output);
4460 dt_conf_set_float(
"darkroom/modules/channelmixerrgb/hue", Lch_output[2] * 360.f);
4461 dt_conf_set_bool(
"darkroom/modules/channelmixerrgb/use_mixing", use_mixing);
4477 dt_LCH_2_Lab(Lch_target, Lab_target);
4479 const float Y_target = XYZ_target[1];
4480 for(
int c = 0; c < 3; c++) XYZ_target[c] /= Y_target;
4481 dt_store_simd_aligned(LMS_target, convert_any_XYZ_to_LMS(dt_load_simd_aligned(XYZ_target),
p->adaptation));
4499 float MIX_INV_3x3[9];
4516 dot_product(LMS_target, MIX_INV, temp);
4524 const float Y_mix = XYZ_target[1];
4525 for(
int c = 0; c < 3; c++) XYZ_target[c] /= Y_mix;
4526 dt_store_simd_aligned(LMS_target, convert_any_XYZ_to_LMS(dt_load_simd_aligned(XYZ_target),
p->adaptation));
4536 const float Y =
XYZ[1];
4537 for(
int c = 0; c < 3; c++)
XYZ[c] /= Y;
4548 for(
int c = 0; c < 3; c++) illuminant_LMS[c] = D50[c] *
LMS[c] / LMS_target[c];
4549 dt_store_simd_aligned(illuminant_XYZ, convert_any_LMS_to_XYZ(dt_load_simd_aligned(illuminant_LMS),
p->adaptation));
4552 const float sum = fmaxf(illuminant_XYZ[0] + illuminant_XYZ[1] + illuminant_XYZ[2],
NORM_MIN);
4553 illuminant_XYZ[0] /= sum;
4554 illuminant_XYZ[2] = illuminant_XYZ[1];
4555 illuminant_XYZ[1] /= sum;
4557 p->x = illuminant_XYZ[0];
4558 p->y = illuminant_XYZ[1];
4566 check_if_close_to_daylight(
p->x,
p->y, &
p->temperature, NULL, NULL);
4574 dt_xyY_to_Lch(
xyY, Lch_illuminant);
4583 gtk_widget_queue_draw(
g->origin_spot);
4587 if(memcmp(&previous,
p,
sizeof(previous)) != 0)
4593 dt_print(
DT_DEBUG_DEV,
"[picker/channelmixerrgb] history commit source=auto_set_illuminant\n");
4603 dt_print(
DT_DEBUG_DEV,
"[picker/channelmixerrgb] apply picker=%p pipe=%p\n", (
void *)picker, (
void *)pipe);
4621 float norm_R = 1.0f;
4622 if(
p->normalize_R) norm_R =
p->red[0] +
p->red[1] +
p->red[2];
4624 float norm_G = 1.0f;
4625 if(
p->normalize_G) norm_G =
p->green[0] +
p->green[1] +
p->green[2];
4627 float norm_B = 1.0f;
4628 if(
p->normalize_B) norm_B =
p->blue[0] +
p->blue[1] +
p->blue[2];
4630 for(
int c = 0; c < 3; c++)
4632 MIX[0][c] =
p->red[c] / norm_R;
4633 MIX[1][c] =
p->green[c] / norm_G;
4634 MIX[2][c] =
p->blue[c] / norm_B;
4637 float lightness = 50.f;
4653 dt_LCH_2_Lab(Lch_target, Lab_target);
4657 const float Y_target = XYZ_target[1];
4658 for(
int c = 0; c < 3; c++) XYZ_target[c] /= Y_target;
4659 dt_store_simd_aligned(LMS_target, convert_any_XYZ_to_LMS(dt_load_simd_aligned(XYZ_target),
p->adaptation));
4664 float MIX_INV_3x3[9];
4671 dot_product(LMS_target, MIX_INV, temp);
4674 const float Y_mix = XYZ_target[1];
4676 for(
int c = 0; c < 3; c++) XYZ_target[c] /= Y_mix;
4677 dt_store_simd_aligned(LMS_target, convert_any_XYZ_to_LMS(dt_load_simd_aligned(XYZ_target),
p->adaptation));
4679 const float *
const restrict in = (
float *)
i;
4680 float average_L = 0.f;
4681 float average_M = 0.f;
4682 float average_S = 0.f;
4683 size_t valid_pixels = 0;
4687 for(
size_t k = 0;
k < roi_out->
width * roi_out->
height * 4;
k += 4)
4693 current_profile->unbounded_coeffs_in, current_profile->
lutsize,
4697 if(!isfinite(
LMS[0]) || !isfinite(
LMS[1]) || !isfinite(
LMS[2]))
continue;
4699 average_L +=
LMS[0];
4700 average_M +=
LMS[1];
4701 average_S +=
LMS[2];
4705 if(valid_pixels == 0)
return;
4707 const float norm = 1.f / (float)valid_pixels;
4708 dt_aligned_pixel_t average_LMS = { average_L * norm, average_M * norm, average_S * norm, 0.f };
4712 dt_store_simd_aligned(average_XYZ, convert_any_LMS_to_XYZ(dt_load_simd_aligned(average_LMS),
p->adaptation));
4713 const float Y_average_lms = average_XYZ[1];
4714 for(
int c = 0; c < 3; c++) average_XYZ[c] /= Y_average_lms;
4715 dt_store_simd_aligned(average_LMS, convert_any_XYZ_to_LMS(dt_load_simd_aligned(average_XYZ),
p->adaptation));
4722 for(
int c = 0; c < 3; c++)
4724 const float target = copysignf(fmaxf(fabsf(LMS_target[c]),
NORM_MIN), LMS_target[c]);
4725 illuminant_LMS[c] = D50[c] * average_LMS[c] / target;
4728 dt_store_simd_aligned(illuminant_XYZ, convert_any_LMS_to_XYZ(dt_load_simd_aligned(illuminant_LMS),
p->adaptation));
4730 const float sum = fmaxf(illuminant_XYZ[0] + illuminant_XYZ[1] + illuminant_XYZ[2],
NORM_MIN);
4731 p->x = illuminant_XYZ[0] / sum;
4732 p->y = illuminant_XYZ[1] / sum;
4734 check_if_close_to_daylight(
p->x,
p->y, &
p->temperature, NULL, NULL);
4743 for(
size_t k = 0;
k < 4;
k++)
4745 g->box[
k].x =
g->box[
k].y = -1.;
4748 g->is_cursor_close =
FALSE;
4750 g->is_profiling_started =
FALSE;
4752 g->run_validation =
FALSE;
4753 g->profile_ready =
FALSE;
4754 g->checker_ready =
FALSE;
4755 g->delta_E_in = NULL;
4756 g->delta_E_label_text = NULL;
4757 g->colorcheckers = NULL;
4773 gtk_widget_set_tooltip_text(GTK_WIDGET(
g->adaptation),
4774 _(
"choose the method to adapt the illuminant\n"
4775 "and the colorspace in which the module works: \n"
4776 "- Linear Bradford (1985) is consistent with ICC v4 toolchain.\n"
4777 "- CAT16 (2016) is more robust and accurate.\n"
4778 "- Non-linear Bradford (1985) is the original Bradford,\n"
4779 " it can produce better results than the linear version, but is unreliable.\n"
4780 "- XYZ is a simple scaling in XYZ space. It is not recommended in general.\n"
4781 "- none disables any adaptation and uses pipeline working RGB."));
4786 gtk_box_pack_start(GTK_BOX(hbox),
g->approx_cct,
FALSE,
FALSE, 0);
4788 g->illum_color = GTK_WIDGET(gtk_drawing_area_new());
4791 gtk_widget_set_tooltip_text(GTK_WIDGET(
g->illum_color),
4792 _(
"this is the color of the scene illuminant before chromatic adaptation\n"
4793 "this color will be turned into pure white by the adaptation."));
4796 gtk_box_pack_start(GTK_BOX(hbox),
g->illum_color,
TRUE,
TRUE, 0);
4799 gtk_widget_set_tooltip_text(
g->color_picker, _(
"set white balance to detected from area"));
4801 gtk_box_pack_start(GTK_BOX(self->
widget), GTK_WIDGET(hbox),
FALSE,
FALSE, 0);
4814 g_signal_connect(G_OBJECT(
g->illum_x),
"value-changed", G_CALLBACK(
illum_xy_callback), self);
4815 gtk_box_pack_start(GTK_BOX(self->
widget), GTK_WIDGET(
g->illum_x),
FALSE,
FALSE, 0);
4821 g_signal_connect(G_OBJECT(
g->illum_y),
"value-changed", G_CALLBACK(
illum_xy_callback), self);
4822 gtk_box_pack_start(GTK_BOX(self->
widget), GTK_WIDGET(
g->illum_y),
FALSE,
FALSE, 0);
4837 "plugins/darkroom/channelmixerrgb/expand_picker_mapping",
4838 _(
"spot color mapping"),
4839 GTK_BOX(self->
widget), GTK_PACK_END);
4841 gtk_widget_set_tooltip_text(
g->csspot.expander, _(
"use a color checker target to autoset CAT and channels"));
4844 _(
"\"correction\" automatically adjust the illuminant\n"
4845 "such that the input color is mapped to the target.\n"
4846 "\"measure\" simply shows how an input color is mapped by the CAT\n"
4847 "and can be used to sample a target."),
4851 gtk_box_pack_start(GTK_BOX(
g->csspot.container), GTK_WIDGET(
g->spot_mode),
TRUE,
TRUE, 0);
4854 gchar *label = N_(
"take channel mixing into account");
4855 g->use_mixing = gtk_check_button_new_with_label(_(label));
4856 gtk_label_set_ellipsize(GTK_LABEL(gtk_bin_get_child(GTK_BIN(
g->use_mixing))), PANGO_ELLIPSIZE_END);
4857 gtk_widget_set_tooltip_text(
g->use_mixing,
4858 _(
"compute the target by taking the channel mixing into account.\n"
4859 "if disabled, only the CAT is considered."));
4860 gtk_box_pack_start(GTK_BOX(
g->csspot.container), GTK_WIDGET(
g->use_mixing),
TRUE,
TRUE, 0);
4868 g->origin_spot = GTK_WIDGET(gtk_drawing_area_new());
4871 gtk_widget_set_tooltip_text(GTK_WIDGET(
g->origin_spot),
4872 _(
"the input color that should be mapped to the target"));
4874 g_signal_connect(G_OBJECT(
g->origin_spot),
"draw", G_CALLBACK(
origin_color_draw), self);
4875 gtk_box_pack_start(GTK_BOX(vvbox),
g->origin_spot,
TRUE,
TRUE, 0);
4877 g->Lch_origin = gtk_label_new(_(
"L: \tN/A\nh: \tN/A\nc: \tN/A"));
4878 gtk_widget_set_tooltip_text(GTK_WIDGET(
g->Lch_origin),
4879 _(
"these LCh coordinates are computed from CIE Lab 1976 coordinates"));
4880 gtk_box_pack_start(GTK_BOX(vvbox), GTK_WIDGET(
g->Lch_origin),
FALSE,
FALSE, 0);
4888 g->target_spot = GTK_WIDGET(gtk_drawing_area_new());
4891 gtk_widget_set_tooltip_text(GTK_WIDGET(
g->target_spot),
4892 _(
"the desired target color after mapping"));
4894 g_signal_connect(G_OBJECT(
g->target_spot),
"draw", G_CALLBACK(
target_color_draw), self);
4895 gtk_box_pack_start(GTK_BOX(vvbox),
g->target_spot,
TRUE,
TRUE, 0);
4901 gtk_box_pack_start(GTK_BOX(vvbox), GTK_WIDGET(
g->lightness_spot),
TRUE,
TRUE, 0);
4908 gtk_box_pack_start(GTK_BOX(vvbox), GTK_WIDGET(
g->hue_spot),
TRUE,
TRUE, 0);
4914 gtk_box_pack_start(GTK_BOX(vvbox), GTK_WIDGET(
g->chroma_spot),
TRUE,
TRUE, 0);
4919 gtk_box_pack_start(GTK_BOX(
g->csspot.container), GTK_WIDGET(hhbox),
FALSE,
FALSE, 0);
4927 gtk_widget_set_tooltip_text(
g->mixer_mode,
4928 _(
"complete exposes the original nine mixer coefficients.\n"
4929 "simple rebuilds the normalized mixer as an exact chroma-plane rotation,\n"
4930 "two signed stretches and two neutral couplings.\n"
4931 "primaries rebuilds the mixer as a generalized primaries, white tint and gain model."));
4932 gtk_box_pack_start(GTK_BOX(mixer_page), GTK_WIDGET(
g->mixer_mode),
FALSE,
FALSE, 0);
4935 g->mixer_stack = gtk_stack_new();
4936 gtk_stack_set_transition_type(GTK_STACK(
g->mixer_stack), GTK_STACK_TRANSITION_TYPE_NONE);
4937 gtk_box_pack_start(GTK_BOX(mixer_page), GTK_WIDGET(
g->mixer_stack),
FALSE,
FALSE, 0);
4940 gtk_stack_add_named(GTK_STACK(
g->mixer_stack), mixer_complete,
"complete");
4943#define MIXER_ROW(var, short, section, swap) \
4944 gtk_box_pack_start(GTK_BOX(mixer_complete), dt_ui_section_label_new(section), FALSE, FALSE, 0); \
4945 self->widget = mixer_complete; \
4946 first = dt_bauhaus_slider_from_params(self, swap ? #var "[2]" : #var "[0]");\
4947 dt_bauhaus_slider_set_digits(first, 3); \
4948 dt_bauhaus_widget_set_label(first, N_("input R")); \
4949 second = dt_bauhaus_slider_from_params(self, #var "[1]"); \
4950 dt_bauhaus_slider_set_digits(second, 3); \
4951 dt_bauhaus_widget_set_label(second, N_("input G")); \
4952 third = dt_bauhaus_slider_from_params(self, swap ? #var "[0]" : #var "[2]");\
4953 dt_bauhaus_slider_set_digits(third, 3); \
4954 dt_bauhaus_widget_set_label(third, N_("input B")); \
4955 g->scale_##var##_R = swap ? third : first; \
4956 g->scale_##var##_G = second; \
4957 g->scale_##var##_B = swap ? first : third; \
4958 g->normalize_##short = dt_bauhaus_toggle_from_params(self, "normalize_" #short);
4965 gtk_stack_add_named(GTK_STACK(
g->mixer_stack), mixer_simple,
"simple");
4971 gtk_widget_set_tooltip_text(
g->simple_theta, _(
"global rotation of the normalized chroma plane."));
4972 gtk_box_pack_start(GTK_BOX(mixer_simple), GTK_WIDGET(
g->simple_theta),
FALSE,
FALSE, 0);
4981 gtk_widget_set_tooltip_text(
g->simple_psi, _(
"orientation of the principal stretch axes in the chroma plane."));
4982 gtk_box_pack_start(GTK_BOX(mixer_simple), GTK_WIDGET(
g->simple_psi),
FALSE,
FALSE, 0);
4987 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."));
4988 gtk_box_pack_start(GTK_BOX(mixer_simple), GTK_WIDGET(
g->simple_stretch_1),
FALSE,
FALSE, 0);
4993 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."));
4994 gtk_box_pack_start(GTK_BOX(mixer_simple), GTK_WIDGET(
g->simple_stretch_2),
FALSE,
FALSE, 0);
5003 gtk_widget_set_tooltip_text(
g->simple_coupling_2, _(
"chroma direction, in the fixed chroma basis, that is coupled into the achromatic axis."));
5004 gtk_box_pack_start(GTK_BOX(mixer_simple), GTK_WIDGET(
g->simple_coupling_2),
FALSE,
FALSE, 0);
5009 gtk_widget_set_tooltip_text(
g->simple_coupling_1, _(
"strength of the chroma-to-achromatic coupling in the fixed chroma basis."));
5010 gtk_box_pack_start(GTK_BOX(mixer_simple), GTK_WIDGET(
g->simple_coupling_1),
FALSE,
FALSE, 0);
5014 gtk_stack_add_named(GTK_STACK(
g->mixer_stack), mixer_primaries,
"primaries");
5022 gtk_widget_set_tooltip_text(
g->primaries_achromatic_hue,
5023 _(
"rotate the custom white vector around the D50 white of the current mixer basis."));
5024 gtk_box_pack_start(GTK_BOX(mixer_primaries), GTK_WIDGET(
g->primaries_achromatic_hue),
FALSE,
FALSE, 0);
5025 g_signal_connect(G_OBJECT(
g->primaries_achromatic_hue),
"value-changed",
5030 gtk_widget_set_tooltip_text(
g->primaries_achromatic_purity,
5031 _(
"distance of the custom white vector from the D50 white within the current mixer basis."));
5032 gtk_box_pack_start(GTK_BOX(mixer_primaries), GTK_WIDGET(
g->primaries_achromatic_purity),
FALSE,
FALSE, 0);
5033 g_signal_connect(G_OBJECT(
g->primaries_achromatic_purity),
"value-changed",
5042 gtk_widget_set_tooltip_text(
g->primaries_red_hue,
5043 _(
"rotate the first basis vector around the D50 white of the current mixer basis."));
5044 gtk_box_pack_start(GTK_BOX(mixer_primaries), GTK_WIDGET(
g->primaries_red_hue),
FALSE,
FALSE, 0);
5045 g_signal_connect(G_OBJECT(
g->primaries_red_hue),
"value-changed",
5050 gtk_widget_set_tooltip_text(
g->primaries_red_purity,
5051 _(
"radial scaling of the first basis vector inside the affine primaries footprint."));
5052 gtk_box_pack_start(GTK_BOX(mixer_primaries), GTK_WIDGET(
g->primaries_red_purity),
FALSE,
FALSE, 0);
5053 g_signal_connect(G_OBJECT(
g->primaries_red_purity),
"value-changed",
5062 gtk_widget_set_tooltip_text(
g->primaries_green_hue,
5063 _(
"rotate the second basis vector around the D50 white of the current mixer basis."));
5064 gtk_box_pack_start(GTK_BOX(mixer_primaries), GTK_WIDGET(
g->primaries_green_hue),
FALSE,
FALSE, 0);
5065 g_signal_connect(G_OBJECT(
g->primaries_green_hue),
"value-changed",
5070 gtk_widget_set_tooltip_text(
g->primaries_green_purity,
5071 _(
"radial scaling of the second basis vector inside the affine primaries footprint."));
5072 gtk_box_pack_start(GTK_BOX(mixer_primaries), GTK_WIDGET(
g->primaries_green_purity),
FALSE,
FALSE, 0);
5073 g_signal_connect(G_OBJECT(
g->primaries_green_purity),
"value-changed",
5082 gtk_widget_set_tooltip_text(
g->primaries_blue_hue,
5083 _(
"rotate the third basis vector around the D50 white of the current mixer basis."));
5084 gtk_box_pack_start(GTK_BOX(mixer_primaries), GTK_WIDGET(
g->primaries_blue_hue),
FALSE,
FALSE, 0);
5085 g_signal_connect(G_OBJECT(
g->primaries_blue_hue),
"value-changed",
5090 gtk_widget_set_tooltip_text(
g->primaries_blue_purity,
5091 _(
"radial scaling of the third basis vector inside the affine primaries footprint."));
5092 gtk_box_pack_start(GTK_BOX(mixer_primaries), GTK_WIDGET(
g->primaries_blue_purity),
FALSE,
FALSE, 0);
5093 g_signal_connect(G_OBJECT(
g->primaries_blue_purity),
"value-changed",
5100 gtk_widget_set_tooltip_text(
g->primaries_gain,
5101 _(
"global gain multiplying the custom white vector after the affine primaries transform."));
5102 gtk_box_pack_start(GTK_BOX(mixer_primaries), GTK_WIDGET(
g->primaries_gain),
FALSE,
FALSE, 0);
5103 g_signal_connect(G_OBJECT(
g->primaries_gain),
"value-changed",
5107 _(
"output colorfulness, brightness and B&W mixing"));
5108 self->
widget = outputs_page;
5110#define OUTPUT_SECTION(var, short, section, swap) \
5111 gtk_box_pack_start(GTK_BOX(outputs_page), dt_ui_section_label_new(section), FALSE, FALSE, 0); \
5113 first = dt_bauhaus_slider_from_params(self, swap ? #var "[2]" : #var "[0]");\
5114 dt_bauhaus_slider_set_digits(first, 3); \
5115 dt_bauhaus_widget_set_label(first, N_("input R")); \
5117 second = dt_bauhaus_slider_from_params(self, #var "[1]"); \
5118 dt_bauhaus_slider_set_digits(second, 3); \
5119 dt_bauhaus_widget_set_label(second, N_("input G")); \
5121 third = dt_bauhaus_slider_from_params(self, swap ? #var "[0]" : #var "[2]");\
5122 dt_bauhaus_slider_set_digits(third, 3); \
5123 dt_bauhaus_widget_set_label(third, N_("input B")); \
5125 g->scale_##var##_R = swap ? third : first; \
5126 g->scale_##var##_G = second; \
5127 g->scale_##var##_B = swap ? first : third; \
5129 g->normalize_##short = dt_bauhaus_toggle_from_params(self, "normalize_" #short);
5139 gtk_box_pack_start(GTK_BOX(self->
widget), GTK_WIDGET(
g->notebook),
FALSE,
FALSE, 0);
5140 const int saved_page =
dt_conf_get_int(
"plugins/darkroom/channelmixerrgb/gui_page");
5141 const int active_page = saved_page > 2 ? 2 : CLAMP(saved_page, 0, 2);
5142 gtk_widget_show(gtk_notebook_get_nth_page(
g->notebook, active_page));
5143 gtk_notebook_set_current_page(
g->notebook, active_page);
5149 "plugins/darkroom/channelmixerrgb/expand_values",
5150 _(
"calibrate with a color checker"),
5151 GTK_BOX(self->
widget), GTK_PACK_END);
5153 gtk_widget_set_tooltip_text(
g->cs.toggle,
5154 _(
"use a color checker target to autoset CAT and channels"));
5157 GtkWidget *collapsible = GTK_WIDGET(
g->cs.container);
5159 gchar *tip_files_loc = NULL;
5164 gchar *user_CGATS_dir = g_build_filename(confdir,
"color",
"checker", NULL);
5165 tip_files_loc = g_strdup_printf(_(
"'%s'"), user_CGATS_dir);
5171 gtk_box_pack_start(GTK_BOX(collapsible), GTK_WIDGET(
g->checkers_list),
TRUE,
TRUE, 0);
5173 gchar *
tooltip = g_strdup_printf(_(
"Choose the vendor and the type of your chart.\n"
5174 ".cht files in %s."), tip_files_loc);
5175 gtk_widget_set_tooltip_text(
g->checkers_list,
tooltip);
5181 gtk_box_pack_start(GTK_BOX(collapsible), GTK_WIDGET(
g->checkers_color_list),
TRUE,
TRUE, 0);
5184 tooltip = g_strdup_printf(_(
"Choose the definition of your chart.\n"
5185 "CGATS.17 files in %s."), tip_files_loc);
5186 gtk_widget_set_tooltip_text(
g->checkers_color_list,
tooltip);
5190 tooltip = g_markup_printf_escaped(_(
"<i>No CGATS.17 color file matches the selected chart.\n"
5191 "Add a compatible CGATS.17 file to <b>%s</b>.</i>"), tip_files_loc);
5192 g->checker_msg = gtk_label_new(NULL);
5193 gtk_label_set_markup(GTK_LABEL(
g->checker_msg),
tooltip);
5195 gtk_label_set_line_wrap(GTK_LABEL(
g->checker_msg),
TRUE);
5196 gtk_label_set_line_wrap_mode(GTK_LABEL(
g->checker_msg), PANGO_WRAP_WORD);
5197 gtk_box_pack_start(GTK_BOX(collapsible), GTK_WIDGET(
g->checker_msg),
FALSE,
TRUE, 0);
5198 gtk_widget_set_visible(
g->checker_msg,
FALSE);
5204 _(
"choose the colors that will be optimized with higher priority.\n"
5205 "neutral colors gives the lowest average delta E but a high maximum delta E\n"
5206 "saturated colors gives the lowest maximum delta E but a high average delta E\n"
5207 "none is a trade-off between both\n"
5208 "the others are special behaviours to protect some hues"),
5211 N_(
"neutral colors"),
5212 N_(
"saturated colors"),
5213 N_(
"skin and soil colors"),
5214 N_(
"foliage colors"),
5215 N_(
"sky and water colors"),
5216 N_(
"average delta E"),
5217 N_(
"maximum delta E"));
5218 gtk_box_pack_start(GTK_BOX(collapsible), GTK_WIDGET(
g->optimize),
TRUE,
TRUE, 0);
5222 gtk_widget_set_tooltip_text(
g->safety, _(
"reduce the radius of the patches to select the more or less central part.\n"
5223 "useful when the perspective correction is sloppy or\n"
5224 "the patches frame cast a shadows on the edges of the patch." ));
5226 gtk_box_pack_start(GTK_BOX(collapsible), GTK_WIDGET(
g->safety),
TRUE,
TRUE, 0);
5229 gtk_box_pack_start(GTK_BOX(collapsible), GTK_WIDGET(
g->label_delta_E),
TRUE,
TRUE, 0);
5230 gtk_widget_set_tooltip_text(
g->label_delta_E, _(
"the delta E is using the CIE 2000 formula"));
5235 gtk_box_pack_end(GTK_BOX(toolbar), GTK_WIDGET(
g->button_commit),
FALSE,
FALSE, 0);
5236 gtk_widget_set_tooltip_text(
g->button_commit, _(
"accept the computed profile and set it in the module"));
5237 g_signal_connect(G_OBJECT(
g->button_commit),
"button-press-event", G_CALLBACK(
commit_profile_callback), (gpointer)self);
5240 g_signal_connect(G_OBJECT(
g->button_profile),
"button-press-event", G_CALLBACK(
run_profile_callback), (gpointer)self);
5241 gtk_widget_set_tooltip_text(
g->button_profile, _(
"recompute the profile"));
5242 gtk_box_pack_end(GTK_BOX(toolbar), GTK_WIDGET(
g->button_profile),
FALSE,
FALSE, 0);
5245 g_signal_connect(G_OBJECT(
g->button_validate),
"button-press-event", G_CALLBACK(
run_validation_callback), (gpointer)self);
5246 gtk_widget_set_tooltip_text(
g->button_validate, _(
"check the output delta E"));
5247 gtk_box_pack_end(GTK_BOX(toolbar), GTK_WIDGET(
g->button_validate),
FALSE,
FALSE, 0);
5249 gtk_box_pack_start(GTK_BOX(collapsible), GTK_WIDGET(toolbar),
FALSE,
FALSE, 0);
5260 dt_conf_set_int(
"plugins/darkroom/channelmixerrgb/gui_page", gtk_notebook_get_current_page (
g->notebook));
5265 g->delta_E_in = NULL;
5272 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 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 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...
#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)
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)
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_gui_gtk_t * gui
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]
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.