100#define CHANNEL_SIZE 4
101#define INVERSE_SQRT_3 0.5773502691896258f
102#define COLOR_MIN -2.0
104#define ILLUM_X_MAX 360.0
105#define ILLUM_Y_MAX 300.0
106#define LIGHTNESS_MAX 100.0
108#define CHROMA_MAX 128.0
109#define TEMP_MIN 1667.
110#define TEMP_MAX 25000.
111#define DT_CHANNELMIXERRGB_SIMPLE_MODE_CONF "plugins/darkroom/channelmixerrgb/mixer_mode"
112#define DT_CHANNELMIXERRGB_SIMPLE_TAN_SCALE DT_IOP_CHANNELMIXER_SHARED_SIMPLE_TAN_SCALE
113#define DT_CHANNELMIXERRGB_SIMPLE_EPS DT_IOP_CHANNELMIXER_SHARED_SIMPLE_EPS
114#define DT_CHANNELMIXERRGB_SIMPLE_CHROMA_PROBE DT_IOP_CHANNELMIXER_SHARED_SIMPLE_CHROMA_PROBE
184#define DT_CHANNELMIXERRGB_SIMPLE_PROBE_ROTATION DT_IOP_CHANNELMIXER_SHARED_SIMPLE_PROBE_ROTATION
185#define DT_CHANNELMIXERRGB_SIMPLE_PROBE_AXIS_1 DT_IOP_CHANNELMIXER_SHARED_SIMPLE_PROBE_AXIS_1
186#define DT_CHANNELMIXERRGB_SIMPLE_PROBE_AXIS_2 DT_IOP_CHANNELMIXER_SHARED_SIMPLE_PROBE_AXIS_2
187#define DT_CHANNELMIXERRGB_PRIMARIES_BASIS_RGB DT_IOP_CHANNELMIXER_SHARED_PRIMARIES_BASIS_RGB
188#define DT_CHANNELMIXERRGB_PRIMARIES_BASIS_XYZ DT_IOP_CHANNELMIXER_SHARED_PRIMARIES_BASIS_XYZ
189#define DT_CHANNELMIXERRGB_PRIMARIES_BASIS_BRADFORD DT_IOP_CHANNELMIXER_SHARED_PRIMARIES_BASIS_BRADFORD
190#define DT_CHANNELMIXERRGB_PRIMARIES_BASIS_CAT16 DT_IOP_CHANNELMIXER_SHARED_PRIMARIES_BASIS_CAT16
291 return _(
"color _calibration");
296 return _(
"channel mixer|white balance|monochrome");
302 "such as white balance, channels mixing\n"
303 "and conversions to monochrome emulating film"),
304 _(
"corrective or creative"),
305 _(
"linear, RGB, scene-referred"),
306 _(
"linear, RGB or XYZ"),
307 _(
"linear, RGB, scene-referred"));
334 const int new_version)
336 if(old_version == 1 && new_version == 3)
347 const float R =
n->saturation[0];
348 const float B =
n->saturation[2];
349 n->saturation[0] =
B;
350 n->saturation[2] =
R;
357 if(old_version == 2 && new_version == 3)
359 typedef struct dt_iop_channelmixer_rgb_params_v2_t
367 gboolean normalize_R, normalize_G, normalize_B, normalize_sat, normalize_light, normalize_grey;
376 } dt_iop_channelmixer_rgb_params_v2_t;
378 memcpy(new_params, old_params,
sizeof(dt_iop_channelmixer_rgb_params_v2_t));
383 const float B =
n->saturation[2];
384 n->saturation[0] =
B;
385 n->saturation[2] =
R;
398 memset(&
p, 0,
sizeof(
p));
411 p.temperature = 5003.f;
424 p.saturation[0] = 0.f;
425 p.saturation[1] = 0.f;
426 p.saturation[2] = 0.f;
427 p.lightness[0] = 0.f;
428 p.lightness[1] = 0.f;
429 p.lightness[2] = 0.f;
434 p.normalize_R =
TRUE;
435 p.normalize_G =
TRUE;
436 p.normalize_B =
TRUE;
438 p.normalize_light =
FALSE;
439 p.normalize_grey =
TRUE;
448 self->version(), &
p,
sizeof(
p), 1);
472 p.grey[0] = 0.25304098f;
473 p.grey[1] = 0.25958747f;
474 p.grey[2] = 0.48737156f;
477 self->version(), &
p,
sizeof(
p), 1);
481 p.grey[0] = 0.24552374f;
482 p.grey[1] = 0.25366007f;
483 p.grey[2] = 0.50081619f;
486 self->version(), &
p,
sizeof(
p), 1);
491 p.grey[0] = 0.24376712f;
492 p.grey[1] = 0.23613559f;
493 p.grey[2] = 0.52009729f;
496 self->version(), &
p,
sizeof(
p), 1);
500 p.grey[0] = 0.24149085f;
501 p.grey[1] = 0.22149272f;
502 p.grey[2] = 0.53701643f;
505 self->version(), &
p,
sizeof(
p), 1);
514 self->version(), &
p,
sizeof(
p), 1);
525 p.normalize_R =
TRUE;
526 p.normalize_G =
TRUE;
527 p.normalize_B =
TRUE;
531 self->version(), &
p,
sizeof(
p), 1);
544 self->version(), &
p,
sizeof(
p), 1);
557 self->version(), &
p,
sizeof(
p), 1);
570 self->version(), &
p,
sizeof(
p), 1);
584 for(
size_t k = 0;
k < 4;
k++) custom_wb[
k] = 1.f;
588 dt_iop_fmt_log(self,
"get_white_balance_coeff: class=%s matrix_supported=0 -> custom_wb=no-op (CAT uses identity)",
603 const double green = bwb[1];
619 if(wb_coeffs[0] != 0.f)
621 for(
size_t k = 0;
k < 4;
k++)
625 const float denom = wb_coeffs[
k];
626 custom_wb[
k] = (
k == 3 && !isnormal(denom)) ? custom_wb[1] : bwb[
k] / denom;
643gamut_mapping(
const dt_aligned_pixel_simd_t input,
const float compression,
const int clip)
646 const float sum = input[0] + input[1] + input[2];
647 const float Y = input[1];
649 if(sum > 0.f && Y > 0.f)
652 float x = input[0] / sum;
653 float y = input[1] / sum;
656 const float uv_denominator = -2.f *
x + 12.f * y + 3.f;
657 float u = 4.f *
x / uv_denominator;
658 float v = 9.f * y / uv_denominator;
661 const float D50[2]
DT_ALIGNED_PIXEL = { 0.20915914598542354f, 0.488075320769787f };
663 const float Delta = Y * (sqf(
delta[0]) + sqf(
delta[1]));
666 const float correction = (compression == 0.0f) ? 0.f : powf(Delta, compression);
669 const float tmp_u =
DT_FMA(correction,
delta[0], u);
671 u = (u > D50[0]) ? fmaxf(tmp_u, D50[0]) : fminf(tmp_u, D50[0]);
672 v = (
v > D50[1]) ? fmaxf(tmp_v, D50[1]) : fminf(tmp_v, D50[1]);
675 const float xy_denominator = 6.f * u - 16.f *
v + 12.f;
676 x = 9.f * u / xy_denominator;
677 y = 4.f *
v / xy_denominator;
692 const float scale =
x + y;
693 const int sanitize = (scale >= 1.f);
700 return (dt_aligned_pixel_simd_t){ Y *
x / y, Y, Y * (1.f -
x - y) / y, 0.f };
705 return (dt_aligned_pixel_simd_t){ 0.f };
715 float norm = euclidean_norm(input);
716 const float avg = fmaxf((input[0] + input[1] + input[2]) / 3.0f,
NORM_MIN);
718 if(norm > 0.f && avg > 0.f)
721 const float mix = scalar_product(input, lightness);
727 for(
size_t c = 0; c < 3; c++) output[c] = input[c] / norm;
730 float coeff_ratio = 0.f;
734 for(
size_t c = 0; c < 3; c++)
735 coeff_ratio += sqf(1.0f - output[c]) * saturation[c];
738 coeff_ratio = scalar_product(output, saturation) / 3.f;
741 for(
size_t c = 0; c < 3; c++)
745 const float min_ratio = (output[c] < 0.0f) ? output[c] : 0.0f;
746 const float output_inverse = 1.0f - output[c];
747 output[c] = fmaxf(
DT_FMA(output_inverse, coeff_ratio, output[c]),
756 norm *= fmaxf(1.f +
mix / avg, 0.f);
757 for(
size_t c = 0; c < 3; c++) output[c] *= norm;
762 for(
size_t c = 0; c < 3; c++) output[c] = input[c];
767static inline void loop_switch(
const float *
const restrict in,
float *
const restrict
out,
782 const dt_aligned_pixel_simd_t rgb_to_xyz0 = dt_colormatrix_row_to_simd(RGB_to_XYZ_t, 0);
783 const dt_aligned_pixel_simd_t rgb_to_xyz1 = dt_colormatrix_row_to_simd(RGB_to_XYZ_t, 1);
784 const dt_aligned_pixel_simd_t rgb_to_xyz2 = dt_colormatrix_row_to_simd(RGB_to_XYZ_t, 2);
785 const dt_aligned_pixel_simd_t xyz_to_rgb0 = dt_colormatrix_row_to_simd(XYZ_to_RGB_t, 0);
786 const dt_aligned_pixel_simd_t xyz_to_rgb1 = dt_colormatrix_row_to_simd(XYZ_to_RGB_t, 1);
787 const dt_aligned_pixel_simd_t xyz_to_rgb2 = dt_colormatrix_row_to_simd(XYZ_to_RGB_t, 2);
788 const dt_aligned_pixel_simd_t mix0 = dt_colormatrix_row_to_simd(MIX_t, 0);
789 const dt_aligned_pixel_simd_t mix1 = dt_colormatrix_row_to_simd(MIX_t, 1);
790 const dt_aligned_pixel_simd_t mix2 = dt_colormatrix_row_to_simd(MIX_t, 2);
791 const dt_aligned_pixel_simd_t illuminant_v = dt_load_simd_aligned(
illuminant);
795 const dt_aligned_pixel_simd_t in_v = dt_load_simd_aligned(in +
k);
796 dt_aligned_pixel_simd_t temp_one_v = { 0.f };
797 dt_aligned_pixel_simd_t temp_two_v = clip ? dt_simd_max_zero(in_v) : in_v;
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];
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];
830 temp_two_v = dt_mat3x4_mul_vec4(temp_one_v, mix0, mix1, mix2);
838 temp_one_v = dt_mat3x4_mul_vec4(temp_two_v, rgb_to_xyz0, rgb_to_xyz1, rgb_to_xyz2);
839 const float Y = temp_one_v[1];
844 temp_one_v = _upscale_vector_simd(
CAT16_adapt_D50(temp_two_v, illuminant_v, 1.0f,
TRUE), Y);
846 temp_two_v = dt_mat3x4_mul_vec4(temp_one_v, mix0, mix1, mix2);
854 temp_one_v = dt_mat3x4_mul_vec4(temp_two_v, rgb_to_xyz0, rgb_to_xyz1, rgb_to_xyz2);
855 const float Y = temp_one_v[1];
858 temp_two_v = _upscale_vector_simd(
XYZ_adapt_D50(_downscale_vector_simd(temp_one_v, Y), illuminant_v), Y);
859 temp_one_v = dt_mat3x4_mul_vec4(temp_two_v, mix0, mix1, mix2);
870 temp_one_v = dt_mat3x4_mul_vec4(temp_two_v, mix0, mix1, mix2);
871 temp_one_v = dt_mat3x4_mul_vec4(temp_one_v, rgb_to_xyz0, rgb_to_xyz1, rgb_to_xyz2);
879 temp_two_v = gamut_mapping(temp_one_v, gamut, clip);
889 temp_one_v = convert_any_XYZ_to_LMS(temp_two_v,
kind);
897 temp_one_v = dt_mat3x4_mul_vec4(temp_two_v, xyz_to_rgb0, xyz_to_rgb1, xyz_to_rgb2);
905 if(clip) temp_one_v = dt_simd_max_zero(temp_one_v);
911 luma_chroma(luma_input, saturation, lightness, luma_output, version);
912 temp_two_v = dt_load_simd_aligned(luma_output);
915 if(clip) temp_two_v = dt_simd_max_zero(temp_two_v);
921 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);
922 dt_store_simd_nontemporal(
out +
k, (dt_aligned_pixel_simd_t){ grey_mix, grey_mix, grey_mix, in_v[3] });
934 temp_one_v = convert_any_LMS_to_XYZ(temp_two_v,
kind);
942 temp_one_v = dt_mat3x4_mul_vec4(temp_two_v, rgb_to_xyz0, rgb_to_xyz1, rgb_to_xyz2);
950 if(clip) temp_one_v = dt_simd_max_zero(temp_one_v);
953 temp_two_v = dt_mat3x4_mul_vec4(temp_one_v, xyz_to_rgb0, xyz_to_rgb1, xyz_to_rgb2);
954 if(clip) temp_two_v = dt_simd_max_zero(temp_two_v);
956 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] });
963#define SHF(ii, jj, c) ((i + ii) * width + j + jj) * ch + c
990 for(
size_t j = 0; j <
width; j++)
992 const size_t index = (
i *
width + j) *
ch;
998 RGB[c] = fmaxf(in[index + c], 0.0f);
1001 dot_product(
RGB, RGB_to_XYZ,
XYZ);
1010 const float D50[2] = { 0.34567f, 0.35850f };
1011 const float norm = dt_fast_hypotf(D50[0], D50[1]);
1013 temp[index ] = (
XYZ[0] - D50[0]) / norm;
1014 temp[index + 1] = (
XYZ[1] - D50[1]) / norm;
1015 temp[index + 2] =
XYZ[2];
1018 float elements = 0.f;
1029 for(
size_t c = 0; c < 2; c++)
1033 2.f * temp[
SHF( 0, -
OFF, c)] + 4.f * temp[
SHF( 0, 0, c)] + 2.f * temp[
SHF( 0, +
OFF, c)] +
1035 central_average[c] = fmaxf(central_average[c], 0.0f);
1042 for(
size_t c = 0; c < 2; c++)
1045 sqf(temp[
SHF(-
OFF, -
OFF, c)] - central_average[c]) +
1046 sqf(temp[
SHF(-
OFF, 0, c)] - central_average[c]) +
1047 sqf(temp[
SHF(-
OFF, +
OFF, c)] - central_average[c]) +
1048 sqf(temp[
SHF(0, -
OFF, c)] - central_average[c]) +
1049 sqf(temp[
SHF(0, 0, c)] - central_average[c]) +
1050 sqf(temp[
SHF(0, +
OFF, c)] - central_average[c]) +
1051 sqf(temp[
SHF(+
OFF, -
OFF, c)] - central_average[c]) +
1052 sqf(temp[
SHF(+
OFF, 0, c)] - central_average[c]) +
1053 sqf(temp[
SHF(+
OFF, +
OFF, c)] - central_average[c])
1061 (temp[
SHF(-
OFF, -
OFF, 0)] - central_average[0]) * (temp[
SHF(-
OFF, -
OFF, 1)] - central_average[1]) +
1062 (temp[
SHF(-
OFF, 0, 0)] - central_average[0]) * (temp[
SHF(-
OFF, 0, 1)] - central_average[1]) +
1063 (temp[
SHF(-
OFF, +
OFF, 0)] - central_average[0]) * (temp[
SHF(-
OFF, +
OFF, 1)] - central_average[1]) +
1064 (temp[
SHF( 0, -
OFF, 0)] - central_average[0]) * (temp[
SHF( 0, -
OFF, 1)] - central_average[1]) +
1065 (temp[
SHF( 0, 0, 0)] - central_average[0]) * (temp[
SHF( 0, 0, 1)] - central_average[1]) +
1066 (temp[
SHF( 0, +
OFF, 0)] - central_average[0]) * (temp[
SHF( 0, +
OFF, 1)] - central_average[1]) +
1067 (temp[
SHF(+
OFF, -
OFF, 0)] - central_average[0]) * (temp[
SHF(+
OFF, -
OFF, 1)] - central_average[1]) +
1068 (temp[
SHF(+
OFF, 0, 0)] - central_average[0]) * (temp[
SHF(+
OFF, 0, 1)] - central_average[1]) +
1069 (temp[
SHF(+
OFF, +
OFF, 0)] - central_average[0]) * (temp[
SHF(+
OFF, +
OFF, 1)] - central_average[1])
1073 const float p = 8.f;
1075 = powf(powf(fabsf(central_average[0]),
p) + powf(fabsf(central_average[1]),
p), 1.f /
p) +
NORM_MIN;
1076 const float weight = var[0] * var[1] * var[2];
1078 for(
size_t c = 0; c < 2; c++)
xyY[c] += central_average[c] *
weight / p_norm;
1079 elements +=
weight / p_norm;
1091 for(
size_t c = 0; c < 2; c++)
1095 2.f * temp[
SHF( 0, -
OFF, c)] + 4.f * temp[
SHF( 0, 0, c)] + 2.f * temp[
SHF( 0, +
OFF, c)] +
1099 dd[c] = temp[
SHF(0, 0, c)] - central_average[c];
1103 const float p = 8.f;
1104 const float p_norm = powf(powf(fabsf(dd[0]),
p) + powf(fabsf(dd[1]),
p), 1.f /
p) +
NORM_MIN;
1106 for(
size_t c = 0; c < 2; c++)
xyY[c] -= dd[c] / p_norm;
1111 const float D50[2] = { 0.34567f, 0.35850 };
1112 const float norm_D50 = dt_fast_hypotf(D50[0], D50[1]);
1114 for(
size_t c = 0; c < 2; c++)
1115 xyz[c] = norm_D50 * (
xyY[c] / elements) + D50[c];
1172static inline __attribute__((always_inline))
void check_if_close_to_daylight(
const float x,
const float y,
float *temperature,
1187 if(t < 3000.f && t > 1667.f)
1194 float xy_ref[2] = {
x, y };
1198 float xy_test[2] = { 0.f };
1206 const float delta_daylight = dt_fast_hypotf((uv_test[0] - uv_ref[0]), (uv_test[1] - uv_ref[1]));
1213 const float delta_bb = dt_fast_hypotf((uv_test[0] - uv_ref[0]), (uv_test[1] - uv_ref[1]));
1216 if(delta_bb < 0.005f || delta_daylight < 0.005f)
1220 if(delta_bb < delta_daylight)
1236#define DEG_TO_RAD(x) (x * M_PI / 180.f)
1237#define RAD_TO_DEG(x) (x * 180.f / M_PI)
1241 float *
const restrict delta_E,
float *
const restrict avg_delta_E,
float *
const restrict max_delta_E)
1255 for(
size_t c = 0; c < 4; c++) XYZ_test[c] = patches[
k * 4 + c];
1258 const float *
const restrict Lab_ref = checker->
values[
k].
Lab;
1263 const float DL = Lab_ref[0] - Lab_test[0];
1264 const float L_avg = (Lab_ref[0] + Lab_test[0]) / 2.f;
1265 const float C_ref = dt_fast_hypotf(Lab_ref[1], Lab_ref[2]);
1266 const float C_test = dt_fast_hypotf(Lab_test[1], Lab_test[2]);
1267 const float C_avg = (C_ref + C_test) / 2.f;
1268 float C_avg_7 = C_avg * C_avg;
1272 const float C_avg_7_ratio_sqrt = sqrtf(C_avg_7 / (C_avg_7 + 6103515625.f));
1273 const float a_ref_prime = Lab_ref[1] * (1.f + 0.5f * (1.f - C_avg_7_ratio_sqrt));
1274 const float a_test_prime = Lab_test[1] * (1.f + 0.5f * (1.f - C_avg_7_ratio_sqrt));
1275 const float C_ref_prime = dt_fast_hypotf(a_ref_prime, Lab_ref[2]);
1276 const float C_test_prime = dt_fast_hypotf(a_test_prime, Lab_test[2]);
1277 const float DC_prime = C_ref_prime - C_test_prime;
1278 const float C_avg_prime = (C_ref_prime + C_test_prime) / 2.f;
1279 float h_ref_prime = atan2f(Lab_ref[2], a_ref_prime);
1280 float h_test_prime = atan2f(Lab_test[2], a_test_prime);
1283 if(C_ref_prime == 0.f) h_ref_prime = 0.f;
1284 if(C_test_prime == 0.f) h_test_prime = 0.f;
1288 if(h_ref_prime < 0.f) h_ref_prime = 2.f *
M_PI - h_ref_prime;
1289 if(h_test_prime < 0.f) h_test_prime = 2.f *
M_PI - h_test_prime;
1295 float Dh_prime = h_test_prime - h_ref_prime;
1296 float Dh_prime_abs = fabsf(Dh_prime);
1297 if(C_test_prime == 0.f || C_ref_prime == 0.f)
1299 else if(Dh_prime_abs <= 180.f)
1301 else if(Dh_prime_abs > 180.f && (h_test_prime <= h_ref_prime))
1303 else if(Dh_prime_abs > 180.f && (h_test_prime > h_ref_prime))
1307 Dh_prime_abs = fabsf(Dh_prime);
1309 const float DH_prime = 2.f * sqrtf(C_test_prime * C_ref_prime) * sinf(
DEG_TO_RAD(Dh_prime) / 2.f);
1310 float H_avg_prime = h_ref_prime + h_test_prime;
1311 if(C_test_prime == 0.f || C_ref_prime == 0.f)
1313 else if(Dh_prime_abs <= 180.f)
1315 else if(Dh_prime_abs > 180.f && (H_avg_prime < 360.f))
1316 H_avg_prime = (H_avg_prime + 360.f) / 2.f;
1317 else if(Dh_prime_abs > 180.f && (H_avg_prime >= 360.f))
1318 H_avg_prime = (H_avg_prime - 360.f) / 2.f;
1322 + 0.24f * cosf(2.f *
DEG_TO_RAD(H_avg_prime))
1326 const float S_L = 1.f + (0.015f * sqf(L_avg - 50.f)) / sqrtf(20.f + sqf(L_avg - 50.f));
1327 const float S_C = 1.f + 0.045f * C_avg_prime;
1328 const float S_H = 1.f + 0.015f * C_avg_prime * T;
1329 const float R_T = -2.f * C_avg_7_ratio_sqrt
1330 * sinf(
DEG_TO_RAD(60.f) * expf(-sqf((H_avg_prime - 275.f) / 25.f)));
1333 const float DE = sqrtf(sqf(DL / S_L) + sqf(DC_prime / S_C) + sqf(DH_prime / S_H)
1334 + R_T * (DC_prime / S_C) * (DH_prime / S_H));
1344 dE += DE / (float)checker->
patches;
1345 if(DE > max_dE) max_dE = DE;
1349 *max_delta_E = max_dE;
1353 float hue = atan2f(reference[2], reference[1]); \
1354 const float chroma = hypotf(reference[2], reference[1]); \
1355 float delta_hue = hue - ref_hue; \
1358 else if(fabsf(delta_hue) <= M_PI) \
1360 else if(fabsf(delta_hue) > M_PI && (hue <= ref_hue)) \
1361 delta_hue += 2.f * M_PI; \
1362 else if(fabsf(delta_hue) > M_PI && (hue > ref_hue)) \
1363 delta_hue -= 2.f * M_PI; \
1364 w = sqrtf(expf(-sqf(delta_hue) / 2.f));
1424 float forward[9],
float backward[9])
1428 for(
size_t c = 0; c < 3; c++)
1430 forward[0 * 3 + c] = map->
scale_x *
g->homography[0 * 3 + c] - map->
offset_x *
g->homography[2 * 3 + c];
1431 forward[1 * 3 + c] = map->
scale_y *
g->homography[1 * 3 + c] - map->
offset_y *
g->homography[2 * 3 + c];
1432 forward[2 * 3 + c] =
g->homography[2 * 3 + c];
1436 for(
size_t r = 0;
r < 3;
r++)
1438 const float column_x =
g->inverse_homography[
r * 3 + 0] / map->
scale_x;
1439 const float column_y =
g->inverse_homography[
r * 3 + 1] / map->
scale_y;
1440 backward[
r * 3 + 0] = column_x;
1441 backward[
r * 3 + 1] = column_y;
1442 backward[
r * 3 + 2] =
g->inverse_homography[
r * 3 + 2] + column_x * map->
offset_x
1453 float *
const restrict patches,
1454 const gboolean normalize_exposure,
1459 const float radius_x =
g->checker->radius * hypotf(1.f,
g->checker->ratio) *
g->safety_margin;
1460 const float radius_y = radius_x /
g->checker->ratio;
1465 float inverse_homography[9];
1469 "[channelmixerrgb] sampling %" G_GSIZE_FORMAT
" patches over a %" G_GSIZE_FORMAT
1470 "x%" G_GSIZE_FORMAT
" buffer, box scaled by (%.5f, %.5f) offset (%.1f, %.1f)\n",
1481 for(
size_t k = 0;
k <
g->checker->patches;
k++)
1484 const point_t center = {
g->checker->values[
k].
x,
g->checker->values[
k].y };
1487 const point_t corners[4] = { {center.
x - radius_x, center.
y - radius_y},
1488 {center.
x + radius_x, center.
y - radius_y},
1489 {center.
x + radius_x, center.
y + radius_y},
1490 {center.
x - radius_x, center.
y + radius_y} };
1495 size_t x_min =
width - 1;
1497 size_t y_min =
height - 1;
1499 for(
size_t c = 0; c < 4; c++) {
1501 x_min = fminf(new_corners[c].
x, x_min);
1502 x_max = fmaxf(new_corners[c].
x, x_max);
1503 y_min = fminf(new_corners[c].y, y_min);
1504 y_max = fmaxf(new_corners[c].y, y_max);
1507 x_min = CLAMP((
size_t)floorf(x_min), 0,
width - 1);
1508 x_max = CLAMP((
size_t)ceilf(x_max), 0,
width - 1);
1509 y_min = CLAMP((
size_t)floorf(y_min), 0,
height - 1);
1510 y_max = CLAMP((
size_t)ceilf(y_max), 0,
height - 1);
1513 patches[
k * 4] = patches[
k * 4 + 1] = patches[
k * 4 + 2] = patches[
k * 4 + 3] = 0.f;
1514 size_t num_elem = 0;
1517 for(
size_t j = y_min; j < y_max; j++)
1518 for(
size_t i = x_min;
i < x_max;
i++)
1521 point_t current_point = {
i + 0.5f, j + 0.5f };
1523 current_point.
x -= center.
x;
1524 current_point.
y -= center.
y;
1526 if(current_point.
x < radius_x && current_point.
x > -radius_x &&
1527 current_point.
y < radius_y && current_point.
y > -radius_y)
1529 for(
size_t c = 0; c < 3; c++)
1531 patches[
k * 4 + c] += in[(j *
width +
i) * 4 + c];
1546 "[channelmixerrgb] patch %" G_GSIZE_FORMAT
" (%s) sampled no pixel: the chart box"
1547 " does not lie on the %" G_GSIZE_FORMAT
"x%" G_GSIZE_FORMAT
" buffer."
1548 " Refusing to profile.\n",
1549 k,
g->checker->values[
k].name ?
g->checker->values[
k].name :
"?",
width,
height);
1550 dt_control_log(_(
"color calibration: the chart area does not lie on the image"));
1554 for(
size_t c = 0; c < 3; c++) patches[
k * 4 + c] /= (
float)num_elem;
1558 dot_product(patches +
k * 4, RGB_to_XYZ,
XYZ);
1559 for(
size_t c = 0; c < 3; c++) patches[
k * 4 + c] =
XYZ[c];
1564 dt_Lab_to_XYZ(
g->checker->values[
g->checker->white].Lab, XYZ_white_ref);
1565 const float white_ref_norm = euclidean_norm(XYZ_white_ref);
1569 for(
size_t c = 0; c < 3; c++) XYZ_white_test[c] = patches[
g->checker->white * 4 + c];
1570 const float white_test_norm = euclidean_norm(XYZ_white_test);
1575 float exposure = white_ref_norm / white_test_norm;
1581 if(normalize_exposure)
1583 for(
size_t k = 0;
k <
g->checker->patches;
k++)
1585 float *
const sample = patches +
k * 4;
1590 const float sample_norm = euclidean_norm(sample);
1591 const float ref_norm = euclidean_norm(XYZ_ref);
1593 const float relative_luminance_test = sample_norm / white_test_norm;
1594 const float relative_luminance_ref = ref_norm / white_ref_norm;
1596 const float luma_correction = relative_luminance_ref / relative_luminance_test;
1597 for(
size_t c = 0; c < 3; ++c) sample[c] *= luma_correction * exposure;
1609 float mean_ref = 0.f;
1610 float mean_test = 0.f;
1612 for(
size_t k = 0;
k <
g->checker->patches;
k++)
1617 for(
size_t c = 0; c < 3; c++) XYZ_test[c] = patches[
k * 4 + c];
1620 dot_product(XYZ_test, XYZ_to_CAM, RGB_test);
1621 dot_product(XYZ_ref, XYZ_to_CAM, RGB_ref);
1626 for(
int c = 0; c < 3; c++)
1628 mean_test += RGB_test[c];
1629 mean_ref += RGB_ref[c];
1632 mean_test /= 3.f *
g->checker->patches;
1633 mean_ref /= 3.f *
g->checker->patches;
1635 float variance = 0.f;
1636 float covariance = 0.f;
1638 for(
size_t k = 0;
k <
g->checker->patches;
k++)
1643 for(
size_t c = 0; c < 3; c++) XYZ_test[c] = patches[
k * 4 + c];
1646 dot_product(XYZ_test, XYZ_to_CAM, RGB_test);
1647 dot_product(XYZ_ref, XYZ_to_CAM, RGB_ref);
1649 for(
int c = 0; c < 3; c++)
1651 variance += sqf(RGB_test[c] - mean_test);
1657 covariance += (RGB_ref[c] - mean_ref) * (RGB_test[c] - mean_test);
1660 variance /= 3.f *
g->checker->patches;
1661 covariance /= 3.f *
g->checker->patches;
1667 exposure = covariance / variance;
1668 black = mean_ref - exposure * mean_test;
1676 result->
black = black;
1702 float pre_wb_delta_E = 0.f;
1703 float pre_wb_max_delta_E = 0.f;
1710 dt_Lab_to_XYZ(
g->checker->values[
g->checker->middle_grey].Lab, XYZ_grey_ref);
1714 for(
size_t c = 0; c < 3; c++) XYZ_grey_test[c] = patches[
g->checker->middle_grey * 4 + c];
1721 const float Y_test = XYZ_grey_test[1];
1722 const float Y_ref = XYZ_grey_ref[1];
1723 for(
size_t c = 0; c < 3; c++)
1725 XYZ_grey_ref[c] /= Y_ref;
1726 XYZ_grey_test[c] /= Y_test;
1737 for(
size_t c = 0; c < 3; c++)
illuminant[c] = D50_LMS[c] * LMS_grey_test[c] / LMS_grey_ref[c];
1742 const float Y_illu = illuminant_XYZ[1];
1743 for(
size_t c = 0; c < 3; c++) illuminant_XYZ[c] /= Y_illu;
1747 g->xy[0] = illuminant_xyY[0];
1748 g->xy[1] = illuminant_xyY[1];
1753 const float p = powf(0.818155f /
illuminant[2], 0.0834f);
1756 for(
size_t k = 0;
k <
g->checker->patches;
k++)
1759 float *
const sample = patches +
k * 4;
1760 const float Y = sample[1];
1761 downscale_vector(sample, Y);
1767 const dt_aligned_pixel_simd_t LMS_v = dt_load_simd_aligned(
LMS);
1768 const dt_aligned_pixel_simd_t illuminant_v = dt_load_simd_aligned(
illuminant);
1797 for(
size_t c = 0; c < 3; ++c) temp[c] =
LMS[c];
1803 upscale_vector(sample, Y);
1807 float post_wb_delta_E = 0.f;
1808 float post_wb_max_delta_E = 0.f;
1812 double *
const restrict Y =
dt_alloc_align(
g->checker->patches * 3 *
sizeof(
double));
1813 double *
const restrict
A =
dt_alloc_align(
g->checker->patches * 3 * 9 *
sizeof(
double));
1822 for(
size_t k = 0;
k <
g->checker->patches;
k++)
1824 float *
const sample = patches +
k * 4;
1828 const float *
const reference =
g->checker->values[
k].Lab;
1836 w = sqrtf(1.f / (
float)
g->checker->patches);
1844 const float ref_hue = 1.f;
1850 const float ref_hue = 2.23f;
1856 const float ref_hue = -1.93f;
1860 w = sqrtf(sqrtf(1.f /
g->delta_E_in[
k]));
1862 w = sqrtf(sqrtf(
g->delta_E_in[
k]));
1865 for(
size_t c = 0; c < 3; c++) Y[
k * 3 + c] = w * LMS_ref[c];
1868 A[
k * 3 * 9 + 0] = w * LMS_test[0];
1869 A[
k * 3 * 9 + 1] = w * LMS_test[1];
1870 A[
k * 3 * 9 + 2] = w * LMS_test[2];
1871 A[
k * 3 * 9 + 3] = 0.f;
1872 A[
k * 3 * 9 + 4] = 0.f;
1873 A[
k * 3 * 9 + 5] = 0.f;
1874 A[
k * 3 * 9 + 6] = 0.f;
1875 A[
k * 3 * 9 + 7] = 0.f;
1876 A[
k * 3 * 9 + 8] = 0.f;
1879 A[
k * 3 * 9 + 9 + 0] = 0.f;
1880 A[
k * 3 * 9 + 9 + 1] = 0.f;
1881 A[
k * 3 * 9 + 9 + 2] = 0.f;
1882 A[
k * 3 * 9 + 9 + 3] = w * LMS_test[0];
1883 A[
k * 3 * 9 + 9 + 4] = w * LMS_test[1];
1884 A[
k * 3 * 9 + 9 + 5] = w * LMS_test[2];
1885 A[
k * 3 * 9 + 9 + 6] = 0.f;
1886 A[
k * 3 * 9 + 9 + 7] = 0.f;
1887 A[
k * 3 * 9 + 9 + 8] = 0.f;
1890 A[
k * 3 * 9 + 18 + 0] = 0.f;
1891 A[
k * 3 * 9 + 18 + 1] = 0.f;
1892 A[
k * 3 * 9 + 18 + 2] = 0.f;
1893 A[
k * 3 * 9 + 18 + 3] = 0.f;
1894 A[
k * 3 * 9 + 18 + 4] = 0.f;
1895 A[
k * 3 * 9 + 18 + 5] = 0.f;
1896 A[
k * 3 * 9 + 18 + 6] = w * LMS_test[0];
1897 A[
k * 3 * 9 + 18 + 7] = w * LMS_test[1];
1898 A[
k * 3 * 9 + 18 + 8] = w * LMS_test[2];
1916 for(
size_t k = 0;
k <
g->checker->patches;
k++)
1918 float *
const sample = patches +
k * 4;
1923 for(
size_t c = 0; c < 3; c++) temp[c] = sample[c];
1926 dot_product(LMS_test,
g->mix, temp);
1931 float post_mix_delta_E = 0.f;
1932 float post_mix_max_delta_E = 0.f;
1938 float x = illuminant_xyY[0];
1939 float y = illuminant_xyY[1];
1940 check_if_close_to_daylight(
x, y, &temperature, &test_illuminant, NULL);
1943 string = _(
"(daylight)");
1945 string = _(
"(black body)");
1947 string = _(
"(invalid)");
1950 if(post_mix_delta_E <= 1.2f)
1951 diagnostic = _(
"very good");
1952 else if(post_mix_delta_E <= 2.3f)
1953 diagnostic = _(
"good");
1954 else if(post_mix_delta_E <= 3.4f)
1955 diagnostic = _(
"passable");
1957 diagnostic = _(
"bad");
1959 g->profile_ready =
TRUE;
1963 g->delta_E_label_text
1964 = g_strdup_printf(_(
"\n<b>Profile quality report: %s</b>\n"
1965 "input \316\224E: \tavg. %.2f ; \tmax. %.2f\n"
1966 "WB \316\224E: \tavg. %.2f; \tmax. %.2f\n"
1967 "output \316\224E: \tavg. %.2f; \tmax. %.2f\n\n"
1968 "<b>Profile data</b>\n"
1969 "illuminant: \t%.0f K \t%s\n"
1970 "matrix in adaptation space:\n"
1971 "<tt>%+.4f \t%+.4f \t%+.4f\n"
1972 "%+.4f \t%+.4f \t%+.4f\n"
1973 "%+.4f \t%+.4f \t%+.4f</tt>\n\n"
1974 "<b>Normalization values</b>\n"
1975 "exposure compensation: \t%+.2f EV\n"
1976 "black offset: \t%+.4f"
1978 diagnostic, pre_wb_delta_E, pre_wb_max_delta_E, post_wb_delta_E, post_wb_max_delta_E,
1979 post_mix_delta_E, post_mix_max_delta_E, temperature,
string,
g->mix[0][0],
g->mix[0][1],
1980 g->mix[0][2],
g->mix[1][0],
g->mix[1][1],
g->mix[1][2],
g->mix[2][0],
g->mix[2][1],
1981 g->mix[2][2], log2f(extraction_result.
exposure), extraction_result.
black);
2002 float pre_wb_delta_E = 0.f;
2003 float pre_wb_max_delta_E = 0.f;
2007 if(pre_wb_delta_E <= 1.2f)
2008 diagnostic = _(
"very good");
2009 else if(pre_wb_delta_E <= 2.3f)
2010 diagnostic = _(
"good");
2011 else if(pre_wb_delta_E <= 3.4f)
2012 diagnostic = _(
"passable");
2014 diagnostic = _(
"bad");
2018 g->delta_E_label_text = g_strdup_printf(_(
"\n<b>Profile quality report: %s</b>\n"
2019 "output \316\224E: \tavg. %.2f; \tmax. %.2f\n\n"
2020 "<b>Normalization values</b>\n"
2021 "exposure compensation: \t%+.2f EV\n"
2022 "black offset: \t%+.4f"),
2023 diagnostic, pre_wb_delta_E, pre_wb_max_delta_E, log2f(extraction_result.
exposure),
2024 extraction_result.
black);
2031 const void *
const restrict ivoid,
void *
const restrict
ovoid)
2050 memcpy(RGB_to_XYZ, work_profile->
matrix_in,
sizeof(RGB_to_XYZ));
2051 memcpy(XYZ_to_RGB, work_profile->
matrix_out,
sizeof(XYZ_to_RGB));
2052 memcpy(XYZ_to_CAM, input_profile->
matrix_out,
sizeof(XYZ_to_CAM));
2055 const size_t ch = 4;
2057 const float *
const restrict in = (
const float *
const restrict)ivoid;
2058 float *
const restrict
out = (
float *
const restrict)
ovoid;
2063 gboolean exit =
FALSE;
2070 RGB_to_XYZ, XYZ_to_RGB, XYZ_to_CAM, data->
adaptation);
2135 XYZ_to_RGB, RGB_to_XYZ, data->
MIX,
2143 XYZ_to_RGB, RGB_to_XYZ, data->
MIX,
2151 XYZ_to_RGB, RGB_to_XYZ, data->
MIX,
2159 XYZ_to_RGB, RGB_to_XYZ, data->
MIX,
2167 XYZ_to_RGB, RGB_to_XYZ, data->
MIX,
2185 g->run_validation =
FALSE;
2225 d->illuminant[3] = 0.f;
2231 const int devid = pipe->
devid;
2237 cl_mem input_matrix_cl = NULL;
2238 cl_mem output_matrix_cl = NULL;
2240 float input_matrix_3x4[12];
2241 float output_matrix_3x4[12];
2242 float mix_matrix_3x4[12];
2254 switch(
d->adaptation)
2302 if(err != CL_SUCCESS)
goto error;
2319 const int program = 32;
2346 const float x_increment,
const float y_increment)
2349 for(
size_t k = 0;
k < 4;
k++)
2351 if(
g->active_node[
k])
2353 g->box[
k].x += x_increment;
2354 g->box[
k].y += y_increment;
2365 if(
g->checker && !
g->checker_ready)
2368 g->box[0].x =
g->box[0].y = 10.;
2371 g->box[1].x = (
width - 10.);
2372 g->box[1].y =
g->box[0].y;
2375 g->box[2].x =
g->box[1].x;
2376 g->box[2].y = (
width - 10.) *
g->checker->ratio;
2379 g->box[3].x =
g->box[0].x;
2380 g->box[3].y =
g->box[2].y;
2382 g->checker_ready =
TRUE;
2385 g->center_box.x = 0.5f;
2386 g->center_box.y = 0.5f;
2388 g->ideal_box[0].x = 0.f;
2389 g->ideal_box[0].y = 0.f;
2390 g->ideal_box[1].x = 1.f;
2391 g->ideal_box[1].y = 0.f;
2392 g->ideal_box[2].x = 1.f;
2393 g->ideal_box[2].y = 1.f;
2394 g->ideal_box[3].x = 0.f;
2395 g->ideal_box[3].y = 1.f;
2408 if(
g->box[0].x == -1.0f ||
g->box[1].y == -1.0f)
return 0;
2413 if(wd == 0.f || ht == 0.f)
return 0;
2415 float pzxpy[2] = { (float)
x, (
float)y };
2418 const float pzx = pzxpy[0];
2419 const float pzy = pzxpy[1];
2427 g->click_end.x = pzx;
2428 g->click_end.y = pzy;
2432 g->click_start.x = pzx;
2433 g->click_start.y = pzy;
2442 g->is_cursor_close =
FALSE;
2444 for(
size_t k = 0;
k < 4;
k++)
2446 if(hypotf(pzx -
g->box[
k].x, pzy -
g->box[
k].y) < 15.f)
2448 g->active_node[
k] =
TRUE;
2449 g->is_cursor_close =
TRUE;
2457 if(
g->is_cursor_close)
2485 if(wd == 0.f || ht == 0.f)
return 0;
2488 if(
type == GDK_DOUBLE_BUTTON_PRESS)
2491 g->checker_ready =
FALSE;
2492 g->profile_ready =
FALSE;
2501 if(
g->box[0].x == -1.0f ||
g->box[1].y == -1.0f)
return 0;
2504 if(!
g->is_cursor_close)
return 0;
2506 float pzxpy[2] = { (float)
x, (
float)y };
2509 const float pzx = pzxpy[0];
2510 const float pzy = pzxpy[1];
2513 g->drag_drop =
TRUE;
2514 g->click_start.x = pzx;
2515 g->click_start.y = pzy;
2529 if(
g->box[0].x == -1.0f ||
g->box[1].y == -1.0f)
return 0;
2530 if(!
g->is_cursor_close || !
g->drag_drop)
return 0;
2535 if(wd == 0.f || ht == 0.f)
return 0;
2537 float pzxpy[2] = { (float)
x, (
float)y };
2540 const float pzx = pzxpy[0];
2541 const float pzy = pzxpy[1];
2545 g->click_end.x = pzx;
2546 g->click_end.y = pzy;
2556 int32_t pointerx, int32_t pointery)
2569 cairo_set_line_width(cr, 2.0 / zoom_scale);
2570 const double origin = 9. / zoom_scale;
2571 const double destination = 18. / zoom_scale;
2573 for(
size_t k = 0;
k < 4;
k++)
2575 if(
g->active_node[
k])
2578 cairo_set_source_rgba(cr, 1., 1., 1., 1.);
2580 cairo_move_to(cr,
g->box[
k].x - origin,
g->box[
k].y);
2581 cairo_line_to(cr,
g->box[
k].x - destination,
g->box[
k].y);
2583 cairo_move_to(cr,
g->box[
k].x + origin,
g->box[
k].y);
2584 cairo_line_to(cr,
g->box[
k].x + destination,
g->box[
k].y);
2586 cairo_move_to(cr,
g->box[
k].x,
g->box[
k].y - origin);
2587 cairo_line_to(cr,
g->box[
k].x,
g->box[
k].y - destination);
2589 cairo_move_to(cr,
g->box[
k].x,
g->box[
k].y + origin);
2590 cairo_line_to(cr,
g->box[
k].x,
g->box[
k].y + destination);
2596 cairo_set_source_rgba(cr, 1., 1., 1., 1.);
2597 cairo_arc(cr,
g->box[
k].x,
g->box[
k].y, 8. / zoom_scale, 0, 2. *
M_PI);
2601 cairo_set_source_rgba(cr, 0., 0., 0., 1.);
2602 cairo_arc(cr,
g->box[
k].x,
g->box[
k].y, 1.5 / zoom_scale, 0, 2. *
M_PI);
2607 cairo_set_line_width(cr, 1.5 / zoom_scale);
2608 cairo_set_source_rgba(cr, 1., 1., 1., 1.);
2609 const point_t top_ideal = { 0.5f, 1.f };
2611 const point_t bottom_ideal = { 0.5f, 0.f };
2613 cairo_move_to(cr,
top.x,
top.y);
2614 cairo_line_to(cr, bottom.
x, bottom.
y);
2617 const point_t left_ideal = { 0.f, 0.5f };
2619 const point_t right_ideal = { 1.f, 0.5f };
2621 cairo_move_to(cr, left.
x, left.
y);
2622 cairo_line_to(cr, right.
x, right.
y);
2635 if(!
g->checker || !
g->checker->finished || !
g->checker->values)
2639 const point_t target_center = { 0.5f, 0.5f };
2642 const char *msg = _(
"Error: No color data available for the selected chart.");
2643 cairo_set_font_size(cr, 20.0 / zoom_scale);
2644 cairo_text_extents_t extents;
2645 cairo_text_extents(cr, msg, &extents);
2648 cairo_set_source_rgba(cr, 1., 1., 1., 1.);
2650 new_target_center.
x - extents.width / 2.0 - 5.0 / zoom_scale,
2651 new_target_center.
y - extents.height / 2.0 - 5.0 / zoom_scale,
2652 extents.width + 10.0 / zoom_scale,
2653 extents.height + 10.0 / zoom_scale);
2657 cairo_set_source_rgba(cr, 1., 0., 0., 1.);
2658 cairo_select_font_face(cr,
"roboto", CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_NORMAL);
2659 cairo_move_to(cr, new_target_center.
x - extents.width / 2.0, new_target_center.
y + extents.height / 2.0);
2660 cairo_show_text(cr, msg);
2664 const float radius_x =
g->checker->radius * hypotf(1.f,
g->checker->ratio) *
g->safety_margin;
2665 const float radius_y = radius_x /
g->checker->ratio;
2667 for(
size_t k = 0;
k <
g->checker->patches;
k++)
2670 const point_t center = {
g->checker->values[
k].
x,
g->checker->values[
k].y };
2673 const point_t corners[4] = { {center.
x - radius_x, center.
y - radius_y},
2674 {center.
x + radius_x, center.
y - radius_y},
2675 {center.
x + radius_x, center.
y + radius_y},
2676 {center.
x - radius_x, center.
y + radius_y} };
2684 for(
size_t c = 0; c < 4; c++) new_corners[c] =
apply_homography(corners[c],
g->homography);
2686 cairo_set_line_cap(cr, CAIRO_LINE_CAP_SQUARE);
2687 cairo_set_source_rgba(cr, 0., 0., 0., 1.);
2688 cairo_move_to(cr, new_corners[0].
x, new_corners[0].y);
2689 cairo_line_to(cr, new_corners[1].
x, new_corners[1].y);
2690 cairo_line_to(cr, new_corners[2].
x, new_corners[2].y);
2691 cairo_line_to(cr, new_corners[3].
x, new_corners[3].y);
2692 cairo_line_to(cr, new_corners[0].
x, new_corners[0].y);
2697 if(
g->delta_E_in[
k] > 2.3f)
2700 cairo_move_to(cr, new_corners[0].
x, new_corners[0].y);
2701 cairo_line_to(cr, new_corners[2].
x, new_corners[2].y);
2703 if(
g->delta_E_in[
k] > 4.6f)
2706 cairo_move_to(cr, new_corners[1].
x, new_corners[1].y);
2707 cairo_line_to(cr, new_corners[3].
x, new_corners[3].y);
2711 cairo_set_line_width(cr, 5.0 / zoom_scale);
2712 cairo_stroke_preserve(cr);
2713 cairo_set_line_width(cr, 2.0 / zoom_scale);
2714 cairo_set_source_rgba(cr, 1., 1., 1., 1.);
2717 cairo_set_line_cap(cr, CAIRO_LINE_CAP_BUTT);
2721 work_profile->unbounded_coeffs_out, work_profile->
lutsize,
2724 cairo_set_source_rgba(cr,
RGB[0],
RGB[1],
RGB[2], 1.);
2725 cairo_arc(cr, new_center.
x, new_center.
y, 0.25 * (radius_x + radius_y) *
scaling, 0, 2. *
M_PI);
2747 for(GList *l = g_list_first(labels); l; l = g_list_next(l))
2750 if(!g_strcmp0(label->
name,
name))
return label;
2779 gtk_widget_show(GTK_WIDGET(
g->checkers_color_list));
2780 gtk_widget_hide(GTK_WIDGET(
g->checker_msg));
2784 gtk_widget_hide(GTK_WIDGET(
g->checkers_color_list));
2785 gtk_widget_show_now(GTK_WIDGET(
g->checker_msg));
2790 gtk_widget_hide(GTK_WIDGET(
g->checkers_color_list));
2791 gtk_widget_hide(GTK_WIDGET(
g->checker_msg));
2805 if(!
g->colorcheckers)
return;
2815 if(
g->n_color == 0)
return;
2819 g_list_free(
g->colorcheckers_color);
2820 g->colorcheckers_color = NULL;
2822 const int checker_patch_nb = checker_label->
patch_nb;
2824 if(checker_patch_nb > 0)
2826 for(GList *l = g_list_first(
g->colorcheckers_all_color); l; l = g_list_next(l))
2831 if(cht_label->
patch_nb == checker_patch_nb)
2834 g->colorcheckers_color = g_list_append(
g->colorcheckers_color, cht_label);
2852 gboolean has_builtin_checker =
FALSE;
2853 gboolean user_checker_separator_added =
FALSE;
2854 for(GList *l = g_list_first(
g->colorcheckers); l; l = g_list_next(l))
2860 has_builtin_checker =
TRUE;
2862 else if(has_builtin_checker && !user_checker_separator_added)
2865 user_checker_separator_added =
TRUE;
2882 g->optimization =
i;
2901 if(wd == 0.f || ht == 0.f)
return;
2904 g->profile_ready =
FALSE;
2930 if(wd == 0.f || ht == 0.f)
return;
2933 g->profile_ready =
FALSE;
2959 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(self->
gui->
off),
TRUE);
2964 if(wd == 0.f || ht == 0.f)
return;
2967 g->is_profiling_started = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(
g->cs.toggle));
2984 g->run_profile =
TRUE;
2997 g->run_validation =
TRUE;
3010 if(!
g->profile_ready)
return;
3017 check_if_close_to_daylight(
p->x,
p->y, &
p->temperature, NULL, NULL);
3019 p->red[0] =
g->mix[0][0];
3020 p->red[1] =
g->mix[0][1];
3021 p->red[2] =
g->mix[0][2];
3023 p->green[0] =
g->mix[1][0];
3024 p->green[1] =
g->mix[1][1];
3025 p->green[2] =
g->mix[1][2];
3027 p->blue[0] =
g->mix[2][0];
3028 p->blue[1] =
g->mix[2][1];
3029 p->blue[2] =
g->mix[2][2];
3078 check_if_close_to_daylight(
p->x,
p->y, &
p->temperature, &
p->illuminant, &
p->adaptation);
3111 gtk_label_set_markup(GTK_LABEL(
g->label_delta_E),
g->delta_E_label_text);
3122 d->version =
p->version;
3124 float norm_R = 1.0f;
3125 if(
p->normalize_R) norm_R =
p->red[0] +
p->red[1] +
p->red[2];
3127 float norm_G = 1.0f;
3128 if(
p->normalize_G) norm_G =
p->green[0] +
p->green[1] +
p->green[2];
3130 float norm_B = 1.0f;
3131 if(
p->normalize_B) norm_B =
p->blue[0] +
p->blue[1] +
p->blue[2];
3133 float norm_sat = 0.0f;
3134 if(
p->normalize_sat) norm_sat = (
p->saturation[0] +
p->saturation[1] +
p->saturation[2]) / 3.f;
3136 float norm_light = 0.0f;
3137 if(
p->normalize_light) norm_light = (
p->lightness[0] +
p->lightness[1] +
p->lightness[2]) / 3.f;
3139 float norm_grey =
p->grey[0] +
p->grey[1] +
p->grey[2];
3140 d->apply_grey = (
p->grey[0] != 0.f) || (
p->grey[1] != 0.f) || (
p->grey[2] != 0.f);
3141 if(!
p->normalize_grey || norm_grey == 0.f) norm_grey = 1.f;
3143 for(
int i = 0;
i < 3;
i++)
3145 d->MIX[0][
i] =
p->red[
i] / norm_R;
3146 d->MIX[1][
i] =
p->green[
i] / norm_G;
3147 d->MIX[2][
i] =
p->blue[
i] / norm_B;
3148 d->saturation[
i] = -
p->saturation[
i] + norm_sat;
3149 d->lightness[
i] =
p->lightness[
i] - norm_light;
3150 d->grey[
i] =
p->grey[
i] / norm_grey;
3156 d->saturation[0] = -
p->saturation[2] + norm_sat;
3157 d->saturation[2] = -
p->saturation[0] + norm_sat;
3165 d->adaptation =
p->adaptation;
3167 d->gamut = (
p->gamut == 0.f) ?
p->gamut : 1.f /
p->gamut;
3180 check_if_close_to_daylight(
x, y, NULL, NULL, &(
d->adaptation));
3182 d->illuminant_type =
p->illuminant;
3190 d->illuminant[3] = 0.f;
3192 dt_iop_fmt_log(self,
"commit: class=%s matrix_supported=%d illuminant=%d adaptation=%d custom_wb=[%.4f %.4f %.4f %.4f] xy=[%.4f %.4f]",
3195 p->illuminant,
d->adaptation, custom_wb[0], custom_wb[1], custom_wb[2], custom_wb[3],
x, y);
3205 d->p = powf(0.818155f /
d->illuminant[2], 0.0834f);
3239 gtk_widget_set_visible(
g->illuminant,
FALSE);
3240 gtk_widget_set_visible(
g->illum_color,
FALSE);
3241 gtk_widget_set_visible(
g->approx_cct,
FALSE);
3242 gtk_widget_set_visible(
g->color_picker,
FALSE);
3243 gtk_widget_set_visible(
g->temperature,
FALSE);
3244 gtk_widget_set_visible(
g->illum_fluo,
FALSE);
3245 gtk_widget_set_visible(
g->illum_led,
FALSE);
3246 gtk_widget_set_visible(
g->illum_x,
FALSE);
3247 gtk_widget_set_visible(
g->illum_y,
FALSE);
3253 gtk_widget_set_visible(
g->illuminant,
TRUE);
3254 gtk_widget_set_visible(
g->illum_color,
TRUE);
3255 gtk_widget_set_visible(
g->approx_cct,
TRUE);
3256 gtk_widget_set_visible(
g->color_picker,
TRUE);
3257 gtk_widget_set_visible(
g->temperature,
TRUE);
3258 gtk_widget_set_visible(
g->illum_fluo,
TRUE);
3259 gtk_widget_set_visible(
g->illum_led,
TRUE);
3260 gtk_widget_set_visible(
g->illum_x,
TRUE);
3264 switch(
p->illuminant)
3270 gtk_widget_set_visible(
g->adaptation,
TRUE);
3271 gtk_widget_set_visible(
g->temperature,
FALSE);
3272 gtk_widget_set_visible(
g->illum_fluo,
FALSE);
3273 gtk_widget_set_visible(
g->illum_led,
FALSE);
3274 gtk_widget_set_visible(
g->illum_x,
FALSE);
3275 gtk_widget_set_visible(
g->illum_y,
FALSE);
3281 gtk_widget_set_visible(
g->adaptation,
TRUE);
3282 gtk_widget_set_visible(
g->temperature,
TRUE);
3283 gtk_widget_set_visible(
g->illum_fluo,
FALSE);
3284 gtk_widget_set_visible(
g->illum_led,
FALSE);
3285 gtk_widget_set_visible(
g->illum_x,
FALSE);
3286 gtk_widget_set_visible(
g->illum_y,
FALSE);
3291 gtk_widget_set_visible(
g->adaptation,
TRUE);
3292 gtk_widget_set_visible(
g->temperature,
FALSE);
3293 gtk_widget_set_visible(
g->illum_fluo,
TRUE);
3294 gtk_widget_set_visible(
g->illum_led,
FALSE);
3295 gtk_widget_set_visible(
g->illum_x,
FALSE);
3296 gtk_widget_set_visible(
g->illum_y,
FALSE);
3301 gtk_widget_set_visible(
g->adaptation,
TRUE);
3302 gtk_widget_set_visible(
g->temperature,
FALSE);
3303 gtk_widget_set_visible(
g->illum_fluo,
FALSE);
3304 gtk_widget_set_visible(
g->illum_led,
TRUE);
3305 gtk_widget_set_visible(
g->illum_x,
FALSE);
3306 gtk_widget_set_visible(
g->illum_y,
FALSE);
3311 gtk_widget_set_visible(
g->adaptation,
TRUE);
3312 gtk_widget_set_visible(
g->temperature,
FALSE);
3313 gtk_widget_set_visible(
g->illum_fluo,
FALSE);
3314 gtk_widget_set_visible(
g->illum_led,
FALSE);
3315 gtk_widget_set_visible(
g->illum_x,
TRUE);
3316 gtk_widget_set_visible(
g->illum_y,
TRUE);
3321 gtk_widget_set_visible(
g->adaptation,
TRUE);
3322 gtk_widget_set_visible(
g->temperature,
FALSE);
3323 gtk_widget_set_visible(
g->illum_fluo,
FALSE);
3324 gtk_widget_set_visible(
g->illum_led,
FALSE);
3325 gtk_widget_set_visible(
g->illum_x,
FALSE);
3326 gtk_widget_set_visible(
g->illum_y,
FALSE);
3332 gtk_widget_set_visible(
g->adaptation,
FALSE);
3333 gtk_widget_set_visible(
g->temperature,
FALSE);
3334 gtk_widget_set_visible(
g->illum_fluo,
FALSE);
3335 gtk_widget_set_visible(
g->illum_led,
FALSE);
3336 gtk_widget_set_visible(
g->illum_x,
FALSE);
3337 gtk_widget_set_visible(
g->illum_y,
FALSE);
3400 dt_xyY_to_Lch(xyY2,
Lch);
3412 gtk_widget_queue_draw(
g->illum_x);
3413 gtk_widget_queue_draw(
g->illum_y);
3430 dt_LCH_2_Lab(Lch_hue,
Lab);
3438 dt_LCH_2_Lab(Lch_lightness,
Lab);
3446 dt_LCH_2_Lab(Lch_chroma,
Lab);
3453 gtk_widget_queue_draw(
g->hue_spot);
3454 gtk_widget_queue_draw(
g->lightness_spot);
3455 gtk_widget_queue_draw(
g->chroma_spot);
3456 gtk_widget_queue_draw(
g->target_spot);
3472 = {
g->primaries_achromatic_hue,
g->primaries_achromatic_purity,
g->primaries_red_hue,
3473 g->primaries_red_purity,
g->primaries_green_hue,
g->primaries_green_purity,
3474 g->primaries_blue_hue,
g->primaries_blue_purity,
g->primaries_gain };
3478 const float rows[3][3] = { {
p->red[0],
p->red[1],
p->red[2] },
3479 {
p->green[0],
p->green[1],
p->green[2] },
3480 {
p->blue[0],
p->blue[1],
p->blue[2] } };
3481 const gboolean
normalize[3] = {
p->normalize_R,
p->normalize_G,
p->normalize_B };
3482 float M[3][3] = { { 0.f } };
3483 float roundtrip[3][3] = { { 0.f } };
3506 = {
g->primaries_achromatic_hue,
g->primaries_achromatic_purity,
g->primaries_red_hue,
3507 g->primaries_red_purity,
g->primaries_green_hue,
g->primaries_green_purity,
3508 g->primaries_blue_hue,
g->primaries_blue_purity,
g->primaries_gain };
3528 widgets[0] =
g->white_preserving_red_rotation;
3529 widgets[1] =
g->white_preserving_red_saturation;
3530 widgets[2] =
g->white_preserving_green_rotation;
3531 widgets[3] =
g->white_preserving_green_saturation;
3532 widgets[4] =
g->white_preserving_blue_rotation;
3533 widgets[5] =
g->white_preserving_blue_saturation;
3535 for(
int widget = 0; widget < 6; widget++)
3557 const float rows[3][3] = { {
p->red[0],
p->red[1],
p->red[2] },
3558 {
p->green[0],
p->green[1],
p->green[2] },
3559 {
p->blue[0],
p->blue[1],
p->blue[2] } };
3560 const gboolean
normalize[3] = {
p->normalize_R,
p->normalize_G,
p->normalize_B };
3561 float M[3][3] = { { 0.f } };
3562 float roundtrip[3][3] = { { 0.f } };
3594 &white_preserving, widgets);
3606 convert_any_LMS_to_RGB(
LMS, work_rgb,
p->adaptation);
3610 for(
size_t c = 0; c < 3; c++) work_rgb[c] =
LMS[c];
3619 GtkWidget *w,
float stop,
float c,
float r,
float g,
float b)
3622 0.5f * (c *
g + 1 -
g),
3623 0.5f * (c * b + 1 - b)};
3642 const float sum =
RGB[0] +
RGB[1] +
RGB[2];
3643 if(sum != 0.f)
for(
int c = 0; c < 3; c++)
RGB[c] /= sum;
3659 gtk_widget_queue_draw(w_r);
3660 gtk_widget_queue_draw(w_b);
3661 gtk_widget_queue_draw(w_g);
3676 = {
g->simple_theta,
g->simple_psi,
g->simple_stretch_1,
g->simple_stretch_2,
g->simple_coupling_1,
3677 g->simple_coupling_2 };
3678 const float rows[3][3] = { {
p->red[0],
p->red[1],
p->red[2] },
3679 {
p->green[0],
p->green[1],
p->green[2] },
3680 {
p->blue[0],
p->blue[1],
p->blue[2] } };
3681 const gboolean
normalize[3] = {
p->normalize_R,
p->normalize_G,
p->normalize_B };
3682 float M[3][3] = { { 0.f } };
3683 float roundtrip[3][3] = { { 0.f } };
3721 = {
g->simple_theta,
g->simple_psi,
g->simple_stretch_1,
g->simple_stretch_2,
g->simple_coupling_1,
3722 g->simple_coupling_2 };
3736 gtk_widget_queue_draw(
g->illum_color);
3747 GtkAllocation allocation;
3748 gtk_widget_get_allocation(widget, &allocation);
3749 int width = allocation.width,
height = allocation.height;
3751 cairo_t *cr = cairo_create(cst);
3765 &
x, &y,
p->temperature,
p->illum_fluo,
p->illum_led);
3767 cairo_set_source_rgb(cr,
RGB[0],
RGB[1],
RGB[2]);
3774 cairo_set_source_surface(crf, cst, 0, 0);
3776 cairo_surface_destroy(cst);
3786 GtkAllocation allocation;
3787 gtk_widget_get_allocation(widget, &allocation);
3788 int width = allocation.width,
height = allocation.height;
3790 cairo_t *cr = cairo_create(cst);
3809 cairo_set_source_rgb(cr,
RGB[0],
RGB[1],
RGB[2]);
3816 cairo_set_source_surface(crf, cst, 0, 0);
3818 cairo_surface_destroy(cst);
3828 GtkAllocation allocation;
3829 gtk_widget_get_allocation(widget, &allocation);
3830 int width = allocation.width,
height = allocation.height;
3832 cairo_t *cr = cairo_create(cst);
3839 cairo_set_source_rgb(cr,
g->spot_RGB[0],
g->spot_RGB[1],
g->spot_RGB[2]);
3846 cairo_set_source_surface(crf, cst, 0, 0);
3848 cairo_surface_destroy(cst);
3865 check_if_close_to_daylight(
x, y, &
t, &test_illuminant, NULL);
3868 if(
t > 1667.f &&
t < 25000.f)
3872 str = g_strdup_printf(_(
"CCT: %.0f K (daylight)"),
t);
3873 gtk_widget_set_tooltip_text(GTK_WIDGET(
g->approx_cct),
3874 _(
"approximated correlated color temperature.\n"
3875 "this illuminant can be accurately modeled by a daylight spectrum,\n"
3876 "so its temperature is relevant and meaningful with a D illuminant."));
3880 str = g_strdup_printf(_(
"CCT: %.0f K (black body)"),
t);
3881 gtk_widget_set_tooltip_text(GTK_WIDGET(
g->approx_cct),
3882 _(
"approximated correlated color temperature.\n"
3883 "this illuminant can be accurately modeled by a black body spectrum,\n"
3884 "so its temperature is relevant and meaningful with a Planckian illuminant."));
3888 str = g_strdup_printf(_(
"CCT: %.0f K (invalid)"),
t);
3889 gtk_widget_set_tooltip_text(GTK_WIDGET(
g->approx_cct),
3890 _(
"approximated correlated color temperature.\n"
3891 "this illuminant cannot be accurately modeled by a daylight or black body spectrum,\n"
3892 "so its temperature is not relevant and meaningful and you need to use a custom illuminant."));
3897 str = g_strdup_printf(_(
"CCT: undefined"));
3898 gtk_widget_set_tooltip_text(GTK_WIDGET(
g->approx_cct),
3899 _(
"the approximated correlated color temperature\n"
3900 "cannot be computed at all so you need to use a custom illuminant."));
3902 gtk_label_set_text(GTK_LABEL(
g->approx_cct), str);
3936 dt_print(
DT_DEBUG_DEV,
"[picker/channelmixerrgb] history commit source=illum_xy_callback slider=%p\n",
3957 g->is_profiling_started =
FALSE;
3967 float simple_error = INFINITY;
3968 float primaries_error = INFINITY;
3969 float white_preserving_error = INFINITY;
3985 gboolean use_mixing =
TRUE;
3987 use_mixing =
dt_conf_get_bool(
"darkroom/modules/channelmixerrgb/use_mixing");
3988 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(
g->use_mixing), use_mixing);
3990 float lightness = 50.f;
4007 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(
g->clip),
p->clip);
4008 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(
g->normalize_R),
p->normalize_R);
4009 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(
g->normalize_G),
p->normalize_G);
4010 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(
g->normalize_B),
p->normalize_B);
4014 const gboolean white_preserving_ok
4034 gtk_widget_hide(GTK_WIDGET(
g->saturation_version));
4036 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(
g->normalize_sat),
p->normalize_sat);
4037 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(
g->normalize_light),
p->normalize_light);
4038 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(
g->normalize_grey),
p->normalize_grey);
4047 gchar *selected_checker =
dt_conf_get_string(
"darkroom/modules/channelmixerrgb/colorchecker");
4056 gchar *selected_color =
dt_conf_get_string(
"darkroom/modules/channelmixerrgb/colorchecker_color");
4066 const int j =
dt_conf_get_int(
"darkroom/modules/channelmixerrgb/optimization");
4068 g->optimization = j;
4076 g->is_profiling_started =
FALSE;
4081 g->spot_RGB[0] = 0.f;
4082 g->spot_RGB[1] = 0.f;
4083 g->spot_RGB[2] = 0.f;
4084 g->spot_RGB[3] = 0.f;
4102 if(
g->delta_E_label_text)
4105 g->delta_E_label_text = NULL;
4128 d->red[0] =
d->green[1] =
d->blue[2] = 1.0;
4129 d->normalize_R =
TRUE;
4130 d->normalize_G =
TRUE;
4131 d->normalize_B =
TRUE;
4149 fprintf(stderr,
"[channelmixerrgb] BUG: temperature (iop_order %d) must run before channelmixerrgb "
4150 "(iop_order %d); white-balance-derived defaults will be wrong on a fresh history\n",
4154 d->normalize_R =
TRUE;
4155 d->normalize_G =
TRUE;
4156 d->normalize_B =
TRUE;
4158 d->x =
module->get_f("x")->Float.Default;
4159 d->y =
module->get_f("y")->Float.Default;
4160 d->temperature =
module->get_f("temperature")->Float.Default;
4161 d->illuminant =
module->get_f("illuminant")->Enum.Default;
4162 d->adaptation =
module->get_f("adaptation")->Enum.Default;
4167 const gboolean is_modern =
4174 const dt_image_t *img = &
module->dev->image_storage;
4177 gboolean CAT_already_applied =
4182 module->default_enabled = FALSE;
4185 if(!CAT_already_applied
4194 check_if_close_to_daylight(
d->x,
d->y, &(
d->temperature), &(
d->illuminant), &(
d->adaptation));
4195 module->workflow_enabled = dt_image_needs_rawprepare(img);
4223 const gboolean use_mixing = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(
g->use_mixing));
4229 dt_conf_set_float(
"darkroom/modules/channelmixerrgb/hue", Lch_target[2] * 360.f);
4230 dt_conf_set_bool(
"darkroom/modules/channelmixerrgb/use_mixing", use_mixing);
4247 gtk_stack_set_visible_child_name(GTK_STACK(
g->mixer_stack),
4268 gboolean changed =
p->normalize_R ||
p->normalize_G ||
p->normalize_B;
4270 for(
int col = 0; col < 3; col++)
4272 changed = changed ||
p->red[col] !=
M[0][col] ||
p->green[col] !=
M[1][col] ||
p->blue[col] !=
M[2][col];
4273 p->red[col] =
M[0][col];
4274 p->green[col] =
M[1][col];
4275 p->blue[col] =
M[2][col];
4283 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(
g->normalize_R),
FALSE);
4284 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(
g->normalize_G),
FALSE);
4285 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(
g->normalize_B),
FALSE);
4337 float error = INFINITY;
4340 dt_control_log(_(
"simple mixer mode requires all three output rows to be normalized with non-zero sums."));
4356 const float rows[3][3] = { {
p->red[0],
p->red[1],
p->red[2] },
4357 {
p->green[0],
p->green[1],
p->green[2] },
4358 {
p->blue[0],
p->blue[1],
p->blue[2] } };
4359 const gboolean
normalize[3] = {
p->normalize_R,
p->normalize_G,
p->normalize_B };
4360 float M[3][3] = { { 0.f } };
4361 float error = INFINITY;
4365 dt_control_log(_(
"primaries mixer mode requires a non-singular 3x3 matrix with non-zero affine sums."));
4374 gboolean changed =
p->normalize_R ||
p->normalize_G ||
p->normalize_B;
4375 for(
int col = 0; col < 3; col++)
4377 changed = changed ||
p->red[col] !=
M[0][col] ||
p->green[col] !=
M[1][col] ||
p->blue[col] !=
M[2][col];
4378 p->red[col] =
M[0][col];
4379 p->green[col] =
M[1][col];
4380 p->blue[col] =
M[2][col];
4388 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(
g->normalize_R),
FALSE);
4389 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(
g->normalize_G),
FALSE);
4390 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(
g->normalize_B),
FALSE);
4414 const float rows[3][3] = { {
p->red[0],
p->red[1],
p->red[2] },
4415 {
p->green[0],
p->green[1],
p->green[2] },
4416 {
p->blue[0],
p->blue[1],
p->blue[2] } };
4417 const gboolean
normalize[3] = {
p->normalize_R,
p->normalize_G,
p->normalize_B };
4418 float M[3][3] = { { 0.f } };
4419 float error = INFINITY;
4424 self, _(
"white-preserving mixer mode requires a matrix that leaves the basis white unchanged."));
4435 dt_print(
DT_DEBUG_DEV,
"[channelmixerrgb] history commit source=mixer_mode_white_preserving\n");
4449 = {
g->simple_theta,
g->simple_psi,
g->simple_stretch_1,
g->simple_stretch_2,
g->simple_coupling_1,
4450 g->simple_coupling_2 };
4452 float M[3][3] = { { 0.f } };
4457 for(
int col = 0; col < 3; col++)
4459 p->red[col] =
M[0][col];
4460 p->green[col] =
M[1][col];
4461 p->blue[col] =
M[2][col];
4465 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(
g->normalize_R),
p->normalize_R);
4466 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(
g->normalize_G),
p->normalize_G);
4467 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(
g->normalize_B),
p->normalize_B);
4482 dt_print(
DT_DEBUG_DEV,
"[channelmixerrgb] history commit source=simple_slider slider=%p\n", (
void *)slider);
4494 = {
g->primaries_achromatic_hue,
g->primaries_achromatic_purity,
g->primaries_red_hue,
4495 g->primaries_red_purity,
g->primaries_green_hue,
g->primaries_green_purity,
4496 g->primaries_blue_hue,
g->primaries_blue_purity,
g->primaries_gain };
4500 float M[3][3] = { { 0.f } };
4505 dt_control_log(_(
"primaries mixer mode requires a non-singular 3x3 matrix with non-zero affine sums."));
4509 for(
int col = 0; col < 3; col++)
4511 p->red[col] =
M[0][col];
4512 p->green[col] =
M[1][col];
4513 p->blue[col] =
M[2][col];
4520 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(
g->normalize_R),
FALSE);
4521 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(
g->normalize_G),
FALSE);
4522 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(
g->normalize_B),
FALSE);
4537 dt_print(
DT_DEBUG_DEV,
"[channelmixerrgb] history commit source=primaries_slider slider=%p\n", (
void *)slider);
4553 float M[3][3] = { { 0.f } };
4563 dt_control_log(_(
"white-preserving mixer mode requires non-degenerate primaries."));
4572 dt_print(
DT_DEBUG_DEV,
"[channelmixerrgb] history commit source=white_preserving_slider slider=%p\n",
4582 const gboolean simple_widget
4583 = w ==
g->simple_theta || w ==
g->simple_psi || w ==
g->simple_stretch_1 || w ==
g->simple_stretch_2
4584 || w ==
g->simple_coupling_1 || w ==
g->simple_coupling_2;
4585 const gboolean primaries_widget
4586 = w ==
g->primaries_achromatic_hue || w ==
g->primaries_achromatic_purity || w ==
g->primaries_red_hue
4587 || w ==
g->primaries_red_purity || w ==
g->primaries_green_hue || w ==
g->primaries_green_purity
4588 || w ==
g->primaries_blue_hue || w ==
g->primaries_blue_purity || w ==
g->primaries_gain;
4589 const gboolean white_preserving_widget
4590 = w ==
g->white_preserving_red_rotation || w ==
g->white_preserving_red_saturation
4591 || w ==
g->white_preserving_green_rotation || w ==
g->white_preserving_green_saturation
4592 || w ==
g->white_preserving_blue_rotation || w ==
g->white_preserving_blue_saturation;
4593 GtkWidget *white_preserving_widgets[6] = { NULL };
4596 const gboolean
normalize[3] = {
p->normalize_R,
p->normalize_G,
p->normalize_B };
4598 const gboolean complete_widget
4599 = w ==
g->scale_red_R || w ==
g->scale_red_G || w ==
g->scale_red_B || w ==
g->scale_green_R
4600 || w ==
g->scale_green_G || w ==
g->scale_green_B || w ==
g->scale_blue_R || w ==
g->scale_blue_G
4601 || w ==
g->scale_blue_B || w ==
g->normalize_R || w ==
g->normalize_G || w ==
g->normalize_B;
4604 const gboolean illuminant_widgets_ready
4624 check_if_close_to_daylight(
p->x,
p->y, &(
p->temperature), NULL, &(
p->adaptation));
4633 check_if_close_to_daylight(
p->x,
p->y, &(
p->temperature), NULL, &(
p->adaptation));
4636 dt_control_log(_(
"white balance successfully extracted from raw image"));
4646 if(!
IS_NULL_PTR(w) && (w ==
g->illuminant || w ==
g->illum_fluo || w ==
g->illum_led || w ==
g->temperature))
4653 illuminant_to_xy(
p->illuminant, NULL, NULL, &(
p->x), &(
p->y),
p->temperature,
p->illum_fluo,
p->illum_led);
4659 check_if_close_to_daylight(
p->x,
p->y, &(
p->temperature), NULL, NULL);
4665 if(
IS_NULL_PTR(w) || w ==
g->hue_spot || w ==
g->chroma_spot || w ==
g->lightness_spot || w ==
g->spot_settings)
4670 if((
IS_NULL_PTR(w) || w ==
g->illuminant || w ==
g->illum_fluo || w ==
g->illum_led || w ==
g->temperature)
4671 && illuminant_widgets_ready)
4695 if(w ==
g->adaptation)
4698 if(
IS_NULL_PTR(w) || w ==
g->adaptation || primaries_widget || white_preserving_widget || w ==
g->scale_red_R || w ==
g->scale_red_G || w ==
g->scale_red_B || w ==
g->normalize_R)
4700 if(
IS_NULL_PTR(w) || w ==
g->adaptation || primaries_widget || white_preserving_widget || w ==
g->scale_green_R || w ==
g->scale_green_G || w ==
g->scale_green_B || w ==
g->normalize_G)
4701 _update_RGB_colors(self, 0, 1, 0,
p->normalize_G,
p->green,
g->scale_green_R,
g->scale_green_G,
g->scale_green_B);
4702 if(
IS_NULL_PTR(w) || w ==
g->adaptation || primaries_widget || white_preserving_widget || w ==
g->scale_blue_R || w ==
g->scale_blue_G || w ==
g->scale_blue_B || w ==
g->normalize_B)
4703 _update_RGB_colors(self, 0, 0, 1,
p->normalize_B,
p->blue,
g->scale_blue_R,
g->scale_blue_G,
g->scale_blue_B);
4705 if(rows_are_normalized && !simple_widget && (
IS_NULL_PTR(w) || complete_widget))
4707 float error = INFINITY;
4713 error,
p->normalize_R,
p->normalize_G,
p->normalize_B);
4714 dt_control_log(_(
"simple mixer mode requires all three output rows to be normalized with non-zero sums."));
4724 if(!primaries_widget && (
IS_NULL_PTR(w) || complete_widget || simple_widget || w ==
g->adaptation))
4726 float error = INFINITY;
4739 if(white_preserving_ready && !white_preserving_widget
4740 && (
IS_NULL_PTR(w) || complete_widget || simple_widget || primaries_widget || w ==
g->adaptation))
4742 float error = INFINITY;
4755 if(
IS_NULL_PTR(w) || w ==
g->adaptation || complete_widget || simple_widget || primaries_widget
4756 || white_preserving_widget)
4759 if(
IS_NULL_PTR(w) || w ==
g->adaptation || complete_widget || simple_widget || primaries_widget
4760 || white_preserving_widget)
4763 if(
IS_NULL_PTR(w) || w ==
g->adaptation || complete_widget || simple_widget || primaries_widget
4764 || white_preserving_widget)
4769 if((
p->grey[0] != 0.f) || (
p->grey[1] != 0.f) || (
p->grey[2] != 0.f))
4770 if((
p->grey[0] +
p->grey[1] +
p->grey[2] == 0.f) &&
p->normalize_grey)
4771 dt_control_log(_(
"color calibration: the sum of the gray channel parameters is zero, normalization will be disabled."));
4776 gtk_widget_queue_draw(
g->adaptation);
4797 if(!isfinite(
RGB[0]) || !isfinite(
RGB[1]) || !isfinite(
RGB[2]))
4819 if(!isfinite(
XYZ[0]) || !isfinite(
XYZ[1]) || !isfinite(
XYZ[2]))
return;
4830 gtk_label_set_text(GTK_LABEL(
g->Lch_origin),
4831 g_strdup_printf(_(
"L: \t%.1f %%\nh: \t%.1f \302\260\nc: \t%.1f"),
4833 gtk_widget_queue_draw(
g->origin_spot);
4837 const gboolean use_mixing = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(
g->use_mixing));
4842 float norm_R = 1.0f;
4843 if(
p->normalize_R) norm_R =
p->red[0] +
p->red[1] +
p->red[2];
4845 float norm_G = 1.0f;
4846 if(
p->normalize_G) norm_G =
p->green[0] +
p->green[1] +
p->green[2];
4848 float norm_B = 1.0f;
4849 if(
p->normalize_B) norm_B =
p->blue[0] +
p->blue[1] +
p->blue[2];
4851 for(
int i = 0;
i < 3;
i++)
4853 MIX[0][
i] =
p->red[
i] / norm_R;
4854 MIX[1][
i] =
p->green[
i] / norm_G;
4855 MIX[2][
i] =
p->blue[
i] / norm_B;
4882 const float pp = powf(0.818155f / LMS_illuminant[2], 0.0834f);
4892 dt_load_simd_aligned(LMS_illuminant),
4901 dot_product(LMS_output, MIX, temp);
4909 dt_Lab_2_LCH(Lab_output, Lch_output);
4921 dt_conf_set_float(
"darkroom/modules/channelmixerrgb/hue", Lch_output[2] * 360.f);
4922 dt_conf_set_bool(
"darkroom/modules/channelmixerrgb/use_mixing", use_mixing);
4938 dt_LCH_2_Lab(Lch_target, Lab_target);
4940 const float Y_target = XYZ_target[1];
4941 for(
int c = 0; c < 3; c++) XYZ_target[c] /= Y_target;
4942 dt_store_simd_aligned(LMS_target, convert_any_XYZ_to_LMS(dt_load_simd_aligned(XYZ_target),
p->adaptation));
4960 float MIX_INV_3x3[9];
4977 dot_product(LMS_target, MIX_INV, temp);
4985 const float Y_mix = XYZ_target[1];
4986 for(
int c = 0; c < 3; c++) XYZ_target[c] /= Y_mix;
4987 dt_store_simd_aligned(LMS_target, convert_any_XYZ_to_LMS(dt_load_simd_aligned(XYZ_target),
p->adaptation));
4997 const float Y =
XYZ[1];
4998 for(
int c = 0; c < 3; c++)
XYZ[c] /= Y;
5009 for(
int c = 0; c < 3; c++) illuminant_LMS[c] = D50[c] *
LMS[c] / LMS_target[c];
5010 dt_store_simd_aligned(illuminant_XYZ, convert_any_LMS_to_XYZ(dt_load_simd_aligned(illuminant_LMS),
p->adaptation));
5013 const float sum = fmaxf(illuminant_XYZ[0] + illuminant_XYZ[1] + illuminant_XYZ[2],
NORM_MIN);
5014 illuminant_XYZ[0] /= sum;
5015 illuminant_XYZ[2] = illuminant_XYZ[1];
5016 illuminant_XYZ[1] /= sum;
5018 p->x = illuminant_XYZ[0];
5019 p->y = illuminant_XYZ[1];
5027 check_if_close_to_daylight(
p->x,
p->y, &
p->temperature, NULL, NULL);
5035 dt_xyY_to_Lch(
xyY, Lch_illuminant);
5044 gtk_widget_queue_draw(
g->origin_spot);
5048 if(memcmp(&previous,
p,
sizeof(previous)) != 0)
5055 dt_print(
DT_DEBUG_DEV,
"[picker/channelmixerrgb] history commit source=auto_set_illuminant\n");
5065 dt_print(
DT_DEBUG_DEV,
"[picker/channelmixerrgb] apply picker=%p pipe=%p\n", (
void *)picker, (
void *)pipe);
5083 float norm_R = 1.0f;
5084 if(
p->normalize_R) norm_R =
p->red[0] +
p->red[1] +
p->red[2];
5086 float norm_G = 1.0f;
5087 if(
p->normalize_G) norm_G =
p->green[0] +
p->green[1] +
p->green[2];
5089 float norm_B = 1.0f;
5090 if(
p->normalize_B) norm_B =
p->blue[0] +
p->blue[1] +
p->blue[2];
5092 for(
int c = 0; c < 3; c++)
5094 MIX[0][c] =
p->red[c] / norm_R;
5095 MIX[1][c] =
p->green[c] / norm_G;
5096 MIX[2][c] =
p->blue[c] / norm_B;
5099 float lightness = 50.f;
5115 dt_LCH_2_Lab(Lch_target, Lab_target);
5119 const float Y_target = XYZ_target[1];
5120 for(
int c = 0; c < 3; c++) XYZ_target[c] /= Y_target;
5121 dt_store_simd_aligned(LMS_target, convert_any_XYZ_to_LMS(dt_load_simd_aligned(XYZ_target),
p->adaptation));
5126 float MIX_INV_3x3[9];
5133 dot_product(LMS_target, MIX_INV, temp);
5136 const float Y_mix = XYZ_target[1];
5138 for(
int c = 0; c < 3; c++) XYZ_target[c] /= Y_mix;
5139 dt_store_simd_aligned(LMS_target, convert_any_XYZ_to_LMS(dt_load_simd_aligned(XYZ_target),
p->adaptation));
5141 const float *
const restrict in = (
float *)
i;
5142 float average_L = 0.f;
5143 float average_M = 0.f;
5144 float average_S = 0.f;
5145 size_t valid_pixels = 0;
5149 for(
size_t k = 0;
k < roi_out->
width * roi_out->
height * 4;
k += 4)
5155 current_profile->unbounded_coeffs_in, current_profile->
lutsize,
5159 if(!isfinite(
LMS[0]) || !isfinite(
LMS[1]) || !isfinite(
LMS[2]))
continue;
5161 average_L +=
LMS[0];
5162 average_M +=
LMS[1];
5163 average_S +=
LMS[2];
5167 if(valid_pixels == 0)
return;
5169 const float norm = 1.f / (float)valid_pixels;
5170 dt_aligned_pixel_t average_LMS = { average_L * norm, average_M * norm, average_S * norm, 0.f };
5174 dt_store_simd_aligned(average_XYZ, convert_any_LMS_to_XYZ(dt_load_simd_aligned(average_LMS),
p->adaptation));
5175 const float Y_average_lms = average_XYZ[1];
5176 for(
int c = 0; c < 3; c++) average_XYZ[c] /= Y_average_lms;
5177 dt_store_simd_aligned(average_LMS, convert_any_XYZ_to_LMS(dt_load_simd_aligned(average_XYZ),
p->adaptation));
5184 for(
int c = 0; c < 3; c++)
5186 const float target = copysignf(fmaxf(fabsf(LMS_target[c]),
NORM_MIN), LMS_target[c]);
5187 illuminant_LMS[c] = D50[c] * average_LMS[c] / target;
5190 dt_store_simd_aligned(illuminant_XYZ, convert_any_LMS_to_XYZ(dt_load_simd_aligned(illuminant_LMS),
p->adaptation));
5192 const float sum = fmaxf(illuminant_XYZ[0] + illuminant_XYZ[1] + illuminant_XYZ[2],
NORM_MIN);
5193 p->x = illuminant_XYZ[0] / sum;
5194 p->y = illuminant_XYZ[1] / sum;
5196 check_if_close_to_daylight(
p->x,
p->y, &
p->temperature, NULL, NULL);
5205 for(
size_t k = 0;
k < 4;
k++)
5207 g->box[
k].x =
g->box[
k].y = -1.;
5210 g->is_cursor_close =
FALSE;
5212 g->is_profiling_started =
FALSE;
5214 g->run_validation =
FALSE;
5215 g->profile_ready =
FALSE;
5216 g->checker_ready =
FALSE;
5217 g->delta_E_in = NULL;
5218 g->delta_E_label_text = NULL;
5219 g->colorcheckers = NULL;
5238 gtk_widget_set_tooltip_text(GTK_WIDGET(
g->adaptation),
5239 _(
"choose the method to adapt the illuminant\n"
5240 "and the colorspace in which the module works: \n"
5241 "- Linear Bradford (1985) is consistent with ICC v4 toolchain.\n"
5242 "- CAT16 (2016) is more robust and accurate.\n"
5243 "- Non-linear Bradford (1985) is the original Bradford,\n"
5244 " it can produce better results than the linear version, but is unreliable.\n"
5245 "- XYZ is a simple scaling in XYZ space. It is not recommended in general.\n"
5246 "- none disables any adaptation and uses pipeline working RGB."));
5251 gtk_box_pack_start(GTK_BOX(hbox),
g->approx_cct,
FALSE,
FALSE, 0);
5253 g->illum_color = GTK_WIDGET(gtk_drawing_area_new());
5256 gtk_widget_set_tooltip_text(GTK_WIDGET(
g->illum_color),
5257 _(
"this is the color of the scene illuminant before chromatic adaptation\n"
5258 "this color will be turned into pure white by the adaptation."));
5261 gtk_box_pack_start(GTK_BOX(hbox),
g->illum_color,
TRUE,
TRUE, 0);
5264 gtk_widget_set_tooltip_text(
g->color_picker, _(
"set white balance to detected from area"));
5279 g_signal_connect(G_OBJECT(
g->illum_x),
"value-changed", G_CALLBACK(
illum_xy_callback), self);
5286 g_signal_connect(G_OBJECT(
g->illum_y),
"value-changed", G_CALLBACK(
illum_xy_callback), self);
5302 "plugins/darkroom/channelmixerrgb/expand_picker_mapping",
5303 _(
"spot color mapping"),
5304 GTK_BOX(self->
gui->
widget), GTK_PACK_END);
5306 gtk_widget_set_tooltip_text(
g->csspot.expander, _(
"use a color checker target to autoset CAT and channels"));
5309 _(
"\"correction\" automatically adjust the illuminant\n"
5310 "such that the input color is mapped to the target.\n"
5311 "\"measure\" simply shows how an input color is mapped by the CAT\n"
5312 "and can be used to sample a target."),
5316 gtk_box_pack_start(GTK_BOX(
g->csspot.container), GTK_WIDGET(
g->spot_mode),
TRUE,
TRUE, 0);
5319 gchar *label = N_(
"take channel mixing into account");
5320 g->use_mixing = gtk_check_button_new_with_label(_(label));
5321 gtk_label_set_ellipsize(GTK_LABEL(gtk_bin_get_child(GTK_BIN(
g->use_mixing))), PANGO_ELLIPSIZE_END);
5322 gtk_widget_set_tooltip_text(
g->use_mixing,
5323 _(
"compute the target by taking the channel mixing into account.\n"
5324 "if disabled, only the CAT is considered."));
5325 gtk_box_pack_start(GTK_BOX(
g->csspot.container), GTK_WIDGET(
g->use_mixing),
TRUE,
TRUE, 0);
5333 g->origin_spot = GTK_WIDGET(gtk_drawing_area_new());
5336 gtk_widget_set_tooltip_text(GTK_WIDGET(
g->origin_spot),
5337 _(
"the input color that should be mapped to the target"));
5339 g_signal_connect(G_OBJECT(
g->origin_spot),
"draw", G_CALLBACK(
origin_color_draw), self);
5340 gtk_box_pack_start(GTK_BOX(vvbox),
g->origin_spot,
TRUE,
TRUE, 0);
5342 g->Lch_origin = gtk_label_new(_(
"L: \tN/A\nh: \tN/A\nc: \tN/A"));
5343 gtk_widget_set_tooltip_text(GTK_WIDGET(
g->Lch_origin),
5344 _(
"these LCh coordinates are computed from CIE Lab 1976 coordinates"));
5345 gtk_box_pack_start(GTK_BOX(vvbox), GTK_WIDGET(
g->Lch_origin),
FALSE,
FALSE, 0);
5353 g->target_spot = GTK_WIDGET(gtk_drawing_area_new());
5356 gtk_widget_set_tooltip_text(GTK_WIDGET(
g->target_spot),
5357 _(
"the desired target color after mapping"));
5359 g_signal_connect(G_OBJECT(
g->target_spot),
"draw", G_CALLBACK(
target_color_draw), self);
5360 gtk_box_pack_start(GTK_BOX(vvbox),
g->target_spot,
TRUE,
TRUE, 0);
5366 gtk_box_pack_start(GTK_BOX(vvbox), GTK_WIDGET(
g->lightness_spot),
TRUE,
TRUE, 0);
5373 gtk_box_pack_start(GTK_BOX(vvbox), GTK_WIDGET(
g->hue_spot),
TRUE,
TRUE, 0);
5379 gtk_box_pack_start(GTK_BOX(vvbox), GTK_WIDGET(
g->chroma_spot),
TRUE,
TRUE, 0);
5384 gtk_box_pack_start(GTK_BOX(
g->csspot.container), GTK_WIDGET(hhbox),
FALSE,
FALSE, 0);
5393 gtk_widget_set_tooltip_text(
g->mixer_mode,
5394 _(
"complete exposes the original nine mixer coefficients.\n"
5395 "simple rebuilds the normalized mixer as an exact chroma-plane rotation,\n"
5396 "two signed stretches and two neutral couplings.\n"
5397 "primaries rebuilds the mixer as a generalized primaries, white tint and gain model.\n"
5398 "white-preserving rotates and saturates each primary around the white,\n"
5399 "which stays exactly where it is."));
5400 gtk_box_pack_start(GTK_BOX(mixer_page), GTK_WIDGET(
g->mixer_mode),
FALSE,
FALSE, 0);
5403 g->mixer_stack = gtk_stack_new();
5404 gtk_stack_set_transition_type(GTK_STACK(
g->mixer_stack), GTK_STACK_TRANSITION_TYPE_NONE);
5405 gtk_box_pack_start(GTK_BOX(mixer_page), GTK_WIDGET(
g->mixer_stack),
FALSE,
FALSE, 0);
5408 gtk_stack_add_named(GTK_STACK(
g->mixer_stack), mixer_complete,
"complete");
5411#define MIXER_ROW(var, short, section, swap) \
5412 gtk_box_pack_start(GTK_BOX(mixer_complete), dt_ui_section_label_new(section), FALSE, FALSE, 0); \
5413 self->gui->widget = mixer_complete; \
5414 first = dt_bauhaus_slider_from_params(self, swap ? #var "[2]" : #var "[0]");\
5415 dt_bauhaus_slider_set_digits(first, 3); \
5416 dt_bauhaus_widget_set_label(first, N_("input R")); \
5417 second = dt_bauhaus_slider_from_params(self, #var "[1]"); \
5418 dt_bauhaus_slider_set_digits(second, 3); \
5419 dt_bauhaus_widget_set_label(second, N_("input G")); \
5420 third = dt_bauhaus_slider_from_params(self, swap ? #var "[0]" : #var "[2]");\
5421 dt_bauhaus_slider_set_digits(third, 3); \
5422 dt_bauhaus_widget_set_label(third, N_("input B")); \
5423 g->scale_##var##_R = swap ? third : first; \
5424 g->scale_##var##_G = second; \
5425 g->scale_##var##_B = swap ? first : third; \
5426 g->normalize_##short = dt_bauhaus_toggle_from_params(self, "normalize_" #short);
5433 gtk_stack_add_named(GTK_STACK(
g->mixer_stack), mixer_simple,
"simple");
5439 gtk_widget_set_tooltip_text(
g->simple_theta, _(
"global rotation of the normalized chroma plane."));
5440 gtk_box_pack_start(GTK_BOX(mixer_simple), GTK_WIDGET(
g->simple_theta),
FALSE,
FALSE, 0);
5449 gtk_widget_set_tooltip_text(
g->simple_psi, _(
"orientation of the principal stretch axes in the chroma plane."));
5450 gtk_box_pack_start(GTK_BOX(mixer_simple), GTK_WIDGET(
g->simple_psi),
FALSE,
FALSE, 0);
5455 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."));
5456 gtk_box_pack_start(GTK_BOX(mixer_simple), GTK_WIDGET(
g->simple_stretch_1),
FALSE,
FALSE, 0);
5461 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."));
5462 gtk_box_pack_start(GTK_BOX(mixer_simple), GTK_WIDGET(
g->simple_stretch_2),
FALSE,
FALSE, 0);
5471 gtk_widget_set_tooltip_text(
g->simple_coupling_2, _(
"chroma direction, in the fixed chroma basis, that is coupled into the achromatic axis."));
5472 gtk_box_pack_start(GTK_BOX(mixer_simple), GTK_WIDGET(
g->simple_coupling_2),
FALSE,
FALSE, 0);
5477 gtk_widget_set_tooltip_text(
g->simple_coupling_1, _(
"strength of the chroma-to-achromatic coupling in the fixed chroma basis."));
5478 gtk_box_pack_start(GTK_BOX(mixer_simple), GTK_WIDGET(
g->simple_coupling_1),
FALSE,
FALSE, 0);
5482 gtk_stack_add_named(GTK_STACK(
g->mixer_stack), mixer_primaries,
"primaries");
5490 gtk_widget_set_tooltip_text(
g->primaries_achromatic_hue,
5491 _(
"rotate the custom white vector around the D50 white of the current mixer basis."));
5492 gtk_box_pack_start(GTK_BOX(mixer_primaries), GTK_WIDGET(
g->primaries_achromatic_hue),
FALSE,
FALSE, 0);
5493 g_signal_connect(G_OBJECT(
g->primaries_achromatic_hue),
"value-changed",
5498 gtk_widget_set_tooltip_text(
g->primaries_achromatic_purity,
5499 _(
"distance of the custom white vector from the D50 white within the current mixer basis."));
5500 gtk_box_pack_start(GTK_BOX(mixer_primaries), GTK_WIDGET(
g->primaries_achromatic_purity),
FALSE,
FALSE, 0);
5501 g_signal_connect(G_OBJECT(
g->primaries_achromatic_purity),
"value-changed",
5510 gtk_widget_set_tooltip_text(
g->primaries_red_hue,
5511 _(
"rotate the first basis vector around the D50 white of the current mixer basis."));
5512 gtk_box_pack_start(GTK_BOX(mixer_primaries), GTK_WIDGET(
g->primaries_red_hue),
FALSE,
FALSE, 0);
5513 g_signal_connect(G_OBJECT(
g->primaries_red_hue),
"value-changed",
5518 gtk_widget_set_tooltip_text(
g->primaries_red_purity,
5519 _(
"radial scaling of the first basis vector inside the affine primaries footprint."));
5520 gtk_box_pack_start(GTK_BOX(mixer_primaries), GTK_WIDGET(
g->primaries_red_purity),
FALSE,
FALSE, 0);
5521 g_signal_connect(G_OBJECT(
g->primaries_red_purity),
"value-changed",
5530 gtk_widget_set_tooltip_text(
g->primaries_green_hue,
5531 _(
"rotate the second basis vector around the D50 white of the current mixer basis."));
5532 gtk_box_pack_start(GTK_BOX(mixer_primaries), GTK_WIDGET(
g->primaries_green_hue),
FALSE,
FALSE, 0);
5533 g_signal_connect(G_OBJECT(
g->primaries_green_hue),
"value-changed",
5538 gtk_widget_set_tooltip_text(
g->primaries_green_purity,
5539 _(
"radial scaling of the second basis vector inside the affine primaries footprint."));
5540 gtk_box_pack_start(GTK_BOX(mixer_primaries), GTK_WIDGET(
g->primaries_green_purity),
FALSE,
FALSE, 0);
5541 g_signal_connect(G_OBJECT(
g->primaries_green_purity),
"value-changed",
5550 gtk_widget_set_tooltip_text(
g->primaries_blue_hue,
5551 _(
"rotate the third basis vector around the D50 white of the current mixer basis."));
5552 gtk_box_pack_start(GTK_BOX(mixer_primaries), GTK_WIDGET(
g->primaries_blue_hue),
FALSE,
FALSE, 0);
5553 g_signal_connect(G_OBJECT(
g->primaries_blue_hue),
"value-changed",
5558 gtk_widget_set_tooltip_text(
g->primaries_blue_purity,
5559 _(
"radial scaling of the third basis vector inside the affine primaries footprint."));
5560 gtk_box_pack_start(GTK_BOX(mixer_primaries), GTK_WIDGET(
g->primaries_blue_purity),
FALSE,
FALSE, 0);
5561 g_signal_connect(G_OBJECT(
g->primaries_blue_purity),
"value-changed",
5568 gtk_widget_set_tooltip_text(
g->primaries_gain,
5569 _(
"global gain multiplying the custom white vector after the affine primaries transform."));
5570 gtk_box_pack_start(GTK_BOX(mixer_primaries), GTK_WIDGET(
g->primaries_gain),
FALSE,
FALSE, 0);
5571 g_signal_connect(G_OBJECT(
g->primaries_gain),
"value-changed",
5575 gtk_stack_add_named(GTK_STACK(
g->mixer_stack), mixer_white_preserving,
"white-preserving");
5577#define WHITE_PRESERVING_PRIMARY(var, section, rotation_label, rotation_tooltip, saturation_label, \
5578 saturation_tooltip) \
5579 gtk_box_pack_start(GTK_BOX(mixer_white_preserving), dt_ui_section_label_new(section), FALSE, FALSE, 0); \
5580 g->white_preserving_##var##_rotation \
5581 = dt_bauhaus_slider_new_with_range(dt_bauhaus_get_global(), DT_GUI_MODULE(self), -1.f, 1.f, 0, 0, 3); \
5582 dt_bauhaus_widget_set_label(g->white_preserving_##var##_rotation, rotation_label); \
5583 dt_bauhaus_slider_set_factor(g->white_preserving_##var##_rotation, 90.f); \
5584 dt_bauhaus_slider_set_format(g->white_preserving_##var##_rotation, "\302\260"); \
5585 gtk_widget_set_tooltip_text(g->white_preserving_##var##_rotation, rotation_tooltip); \
5586 gtk_box_pack_start(GTK_BOX(mixer_white_preserving), GTK_WIDGET(g->white_preserving_##var##_rotation), FALSE,\
5588 g_signal_connect(G_OBJECT(g->white_preserving_##var##_rotation), "value-changed", \
5589 G_CALLBACK(_channelmixerrgb_white_preserving_slider_callback), self); \
5593 g->white_preserving_##var##_saturation \
5594 = dt_bauhaus_slider_new_with_range(dt_bauhaus_get_global(), DT_GUI_MODULE(self), -4.f, 4.f, 0, 0, 3); \
5595 dt_bauhaus_slider_set_soft_range(g->white_preserving_##var##_saturation, -1.f, 1.f); \
5596 dt_bauhaus_widget_set_label(g->white_preserving_##var##_saturation, saturation_label); \
5600 dt_bauhaus_slider_set_format(g->white_preserving_##var##_saturation, "%"); \
5601 gtk_widget_set_tooltip_text(g->white_preserving_##var##_saturation, saturation_tooltip); \
5602 gtk_box_pack_start(GTK_BOX(mixer_white_preserving), GTK_WIDGET(g->white_preserving_##var##_saturation), \
5604 g_signal_connect(G_OBJECT(g->white_preserving_##var##_saturation), "value-changed", \
5605 G_CALLBACK(_channelmixerrgb_white_preserving_slider_callback), self);
5608 _(
"rotate the red primary around the white of the current mixer basis.\n"
5609 "the white itself is left exactly where it is."),
5610 N_(
"red saturation"),
5611 _(
"scale the red primary's distance to the white of the current mixer basis.\n"
5612 "-100% collapses it onto the white, +100% doubles it."))
5614 _(
"rotate the green primary around the white of the current mixer basis.\n"
5615 "the white itself is left exactly where it is."),
5616 N_(
"green saturation"),
5617 _(
"scale the green primary's distance to the white of the current mixer basis.\n"
5618 "-100% collapses it onto the white, +100% doubles it."))
5620 _(
"rotate the blue primary around the white of the current mixer basis.\n"
5621 "the white itself is left exactly where it is."),
5622 N_(
"blue saturation"),
5623 _(
"scale the blue primary's distance to the white of the current mixer basis.\n"
5624 "-100% collapses it onto the white, +100% doubles it."))
5626#undef WHITE_PRESERVING_PRIMARY
5629 _(
"output colorfulness, brightness and B&W mixing"));
5632#define OUTPUT_SECTION(var, short, section, swap) \
5633 gtk_box_pack_start(GTK_BOX(outputs_page), dt_ui_section_label_new(section), FALSE, FALSE, 0); \
5635 first = dt_bauhaus_slider_from_params(self, swap ? #var "[2]" : #var "[0]");\
5636 dt_bauhaus_slider_set_digits(first, 3); \
5637 dt_bauhaus_widget_set_label(first, N_("input R")); \
5639 second = dt_bauhaus_slider_from_params(self, #var "[1]"); \
5640 dt_bauhaus_slider_set_digits(second, 3); \
5641 dt_bauhaus_widget_set_label(second, N_("input G")); \
5643 third = dt_bauhaus_slider_from_params(self, swap ? #var "[0]" : #var "[2]");\
5644 dt_bauhaus_slider_set_digits(third, 3); \
5645 dt_bauhaus_widget_set_label(third, N_("input B")); \
5647 g->scale_##var##_R = swap ? third : first; \
5648 g->scale_##var##_G = second; \
5649 g->scale_##var##_B = swap ? first : third; \
5651 g->normalize_##short = dt_bauhaus_toggle_from_params(self, "normalize_" #short);
5662 const int saved_page =
dt_conf_get_int(
"plugins/darkroom/channelmixerrgb/gui_page");
5663 const int active_page = saved_page > 2 ? 2 : CLAMP(saved_page, 0, 2);
5664 gtk_widget_show(gtk_notebook_get_nth_page(
g->notebook, active_page));
5665 gtk_notebook_set_current_page(
g->notebook, active_page);
5671 "plugins/darkroom/channelmixerrgb/expand_values",
5672 _(
"calibrate with a color checker"),
5673 GTK_BOX(self->
gui->
widget), GTK_PACK_END);
5675 gtk_widget_set_tooltip_text(
g->cs.toggle,
5676 _(
"use a color checker target to autoset CAT and channels"));
5679 GtkWidget *collapsible = GTK_WIDGET(
g->cs.container);
5681 gchar *tip_files_loc = NULL;
5686 gchar *user_CGATS_dir = g_build_filename(confdir,
"color",
"checker", NULL);
5687 tip_files_loc = g_strdup_printf(_(
"'%s'"), user_CGATS_dir);
5693 gtk_box_pack_start(GTK_BOX(collapsible), GTK_WIDGET(
g->checkers_list),
TRUE,
TRUE, 0);
5695 gchar *
tooltip = g_strdup_printf(_(
"Choose the vendor and the type of your chart.\n"
5696 ".cht files in %s."), tip_files_loc);
5697 gtk_widget_set_tooltip_text(
g->checkers_list,
tooltip);
5703 gtk_box_pack_start(GTK_BOX(collapsible), GTK_WIDGET(
g->checkers_color_list),
TRUE,
TRUE, 0);
5706 tooltip = g_strdup_printf(_(
"Choose the definition of your chart.\n"
5707 "CGATS.17 files in %s."), tip_files_loc);
5708 gtk_widget_set_tooltip_text(
g->checkers_color_list,
tooltip);
5712 tooltip = g_markup_printf_escaped(_(
"<i>No CGATS.17 color file matches the selected chart.\n"
5713 "Add a compatible CGATS.17 file to <b>%s</b>.</i>"), tip_files_loc);
5714 g->checker_msg = gtk_label_new(NULL);
5715 gtk_label_set_markup(GTK_LABEL(
g->checker_msg),
tooltip);
5717 gtk_label_set_line_wrap(GTK_LABEL(
g->checker_msg),
TRUE);
5718 gtk_label_set_line_wrap_mode(GTK_LABEL(
g->checker_msg), PANGO_WRAP_WORD);
5719 gtk_box_pack_start(GTK_BOX(collapsible), GTK_WIDGET(
g->checker_msg),
FALSE,
TRUE, 0);
5720 gtk_widget_set_visible(
g->checker_msg,
FALSE);
5726 _(
"choose the colors that will be optimized with higher priority.\n"
5727 "neutral colors gives the lowest average delta E but a high maximum delta E\n"
5728 "saturated colors gives the lowest maximum delta E but a high average delta E\n"
5729 "none is a trade-off between both\n"
5730 "the others are special behaviours to protect some hues"),
5733 N_(
"neutral colors"),
5734 N_(
"saturated colors"),
5735 N_(
"skin and soil colors"),
5736 N_(
"foliage colors"),
5737 N_(
"sky and water colors"),
5738 N_(
"average delta E"),
5739 N_(
"maximum delta E"));
5740 gtk_box_pack_start(GTK_BOX(collapsible), GTK_WIDGET(
g->optimize),
TRUE,
TRUE, 0);
5744 gtk_widget_set_tooltip_text(
g->safety, _(
"reduce the radius of the patches to select the more or less central part.\n"
5745 "useful when the perspective correction is sloppy or\n"
5746 "the patches frame cast a shadows on the edges of the patch." ));
5748 gtk_box_pack_start(GTK_BOX(collapsible), GTK_WIDGET(
g->safety),
TRUE,
TRUE, 0);
5751 gtk_box_pack_start(GTK_BOX(collapsible), GTK_WIDGET(
g->label_delta_E),
TRUE,
TRUE, 0);
5752 gtk_widget_set_tooltip_text(
g->label_delta_E, _(
"the delta E is using the CIE 2000 formula"));
5757 gtk_box_pack_end(GTK_BOX(toolbar), GTK_WIDGET(
g->button_commit),
FALSE,
FALSE, 0);
5758 gtk_widget_set_tooltip_text(
g->button_commit, _(
"accept the computed profile and set it in the module"));
5759 g_signal_connect(G_OBJECT(
g->button_commit),
"button-press-event", G_CALLBACK(
commit_profile_callback), (gpointer)self);
5762 g_signal_connect(G_OBJECT(
g->button_profile),
"button-press-event", G_CALLBACK(
run_profile_callback), (gpointer)self);
5763 gtk_widget_set_tooltip_text(
g->button_profile, _(
"recompute the profile"));
5764 gtk_box_pack_end(GTK_BOX(toolbar), GTK_WIDGET(
g->button_profile),
FALSE,
FALSE, 0);
5767 g_signal_connect(G_OBJECT(
g->button_validate),
"button-press-event", G_CALLBACK(
run_validation_callback), (gpointer)self);
5768 gtk_widget_set_tooltip_text(
g->button_validate, _(
"check the output delta E"));
5769 gtk_box_pack_end(GTK_BOX(toolbar), GTK_WIDGET(
g->button_validate),
FALSE,
FALSE, 0);
5771 gtk_box_pack_start(GTK_BOX(collapsible), GTK_WIDGET(toolbar),
FALSE,
FALSE, 0);
5782 dt_conf_set_int(
"plugins/darkroom/channelmixerrgb/gui_page", gtk_notebook_get_current_page (
g->notebook));
5787 g->delta_E_in = NULL;
5794 g_list_free(
g->colorcheckers_color);
static __DT_CLONE_TARGETS__ void homography(float *homograph, const float angle, const float shift_v, const float shift_h, const float shear, const float f_length_kb, const float orthocorr, const float aspect, const int width, const int height, dt_iop_ashift_homodir_t dir)
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)
gboolean dt_bauhaus_combobox_set_from_text(GtkWidget *widget, const char *text)
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
static __DT_CLONE_TARGETS__ void normalize(float *const buffer, const size_t width, const size_t height, const float norm)
typedef void((*dt_cache_allocate_t)(void *userdata, dt_cache_entry_t *entry))
static void checker_color_changed_callback(GtkWidget *widget, gpointer user_data)
#define DT_CHANNELMIXERRGB_SIMPLE_MODE_CONF
dt_iop_channelmixer_shared_white_preserving_params_t dt_iop_channelmixer_rgb_white_preserving_params_t
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)
__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_iop_channelmixer_rgb_box_map_t *const box_map, 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)
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)
static dt_iop_channelmixer_rgb_box_map_t _box_map(const dt_dev_pixelpipe_t *const pipe, const dt_iop_roi_t *const roi)
Derive that map from the request this pipe was planned from.
dt_iop_channelmixer_rgb_mixer_mode_t
@ DT_CHANNELMIXERRGB_MIXER_SIMPLE
@ DT_CHANNELMIXERRGB_MIXER_WHITE_PRESERVING
@ DT_CHANNELMIXERRGB_MIXER_PRIMARIES
@ DT_CHANNELMIXERRGB_MIXER_COMPLETE
void reload_defaults(dt_iop_module_t *module)
static const dt_colorchecker_label_t * _checker_label_from_combobox(GtkWidget *combobox, GList *labels)
Find the chart a combo box currently shows, in the label list backing that combo box.
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 void _channelmixerrgb_reject_mixer_mode(dt_iop_module_t *self, const char *message)
Leave a reduced mixer mode that cannot represent the current matrix.
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)
void init_pipe(struct dt_iop_module_t *self, dt_dev_pixelpipe_t *pipe, dt_dev_pixelpipe_iop_t *piece)
static void start_profiling_callback(GtkWidget *togglebutton, dt_iop_module_t *self)
static __DT_CLONE_TARGETS__ int _custom_wb_from_coeffs(struct dt_iop_module_t *self, const dt_aligned_pixel_t wb_coeffs, dt_aligned_pixel_t custom_wb)
static void _update_RGB_slider_stop(dt_iop_channelmixer_rgb_params_t *p, const struct dt_iop_order_iccprofile_info_t *const work_profile, const struct dt_iop_order_iccprofile_info_t *const display_profile, GtkWidget *w, float stop, float c, float r, float g, float b)
static void _update_RGB_colors(dt_iop_module_t *self, float r, float g, float b, gboolean normalize, float *a, GtkWidget *w_r, GtkWidget *w_g, GtkWidget *w_b)
static void _channelmixerrgb_update_simple_colors(dt_iop_module_t *self)
Paint the simple mixer sliders from the exact chroma-basis model.
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_iop_channelmixer_rgb_box_map_t *const box_map, 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)
dt_iop_channelmixer_shared_simple_params_t dt_iop_channelmixer_rgb_simple_params_t
void gui_update(struct dt_iop_module_t *self)
static void _set_checker_from_labels(dt_iop_channelmixer_rgb_gui_data_t *g, const dt_colorchecker_label_t *checker_label, const dt_colorchecker_label_t *color_label)
Rebuild g->checker from the charts currently selected in both combo boxes.
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 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)
static gboolean _channelmixerrgb_push_matrix_to_params(dt_iop_module_t *self, const float M[3][3])
Write a mixer matrix into the module params and mirror it on the complete-mode sliders.
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)
void color_list_visibility(dt_iop_module_t *self, const dt_colorchecker_label_t *checker_label)
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 _channelmixerrgb_update_white_preserving_colors(dt_iop_module_t *self)
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)
#define WHITE_PRESERVING_PRIMARY(var, section, rotation_label, rotation_tooltip, saturation_label, saturation_tooltip)
static void _channelmixerrgb_white_preserving_slider_callback(GtkWidget *slider, gpointer user_data)
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)
static void _map_homographies(const dt_iop_channelmixer_rgb_gui_data_t *const g, const dt_iop_channelmixer_rgb_box_map_t *const map, float forward[9], float backward[9])
Compose the box->buffer map onto the chart homographies.
static gboolean _channelmixerrgb_sync_white_preserving_from_params(dt_iop_module_t *self, float *error)
Synchronize the white-preserving mode GUI from the effective mixer matrix.
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)
static gboolean _channelmixerrgb_white_preserving_widgets(const dt_iop_channelmixer_rgb_gui_data_t *const g, GtkWidget *widgets[6])
Collect the six white-preserving mode widgets.
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)
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_iop_channelmixer_rgb_box_map_t *const box_map, const dt_colormatrix_t RGB_to_XYZ, const dt_colormatrix_t XYZ_to_RGB, const dt_colormatrix_t XYZ_to_CAM)
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_paint_white_preserving_sliders(const dt_adaptation_t adaptation, const dt_iop_order_iccprofile_info_t *const work_profile, const dt_iop_order_iccprofile_info_t *const display_profile, const dt_iop_channelmixer_shared_primaries_basis_t basis, const dt_iop_channelmixer_shared_white_preserving_params_t *const white_preserving, GtkWidget *const widgets[6])
void dt_iop_channelmixer_shared_white_preserving_from_sliders(GtkWidget *const widgets[6], dt_iop_channelmixer_shared_white_preserving_params_t *white_preserving)
void dt_iop_channelmixer_shared_simple_from_sliders(GtkWidget *const widgets[6], dt_iop_channelmixer_shared_simple_params_t *simple)
void dt_iop_channelmixer_shared_paint_temperature_slider(GtkWidget *const widget, const float temperature_min, const float temperature_max)
void dt_iop_channelmixer_shared_white_preserving_to_sliders(const dt_iop_channelmixer_shared_white_preserving_params_t *const white_preserving, GtkWidget *const widgets[6])
void dt_iop_channelmixer_shared_primaries_to_sliders(const dt_iop_channelmixer_shared_primaries_params_t *const primaries, GtkWidget *const widgets[9])
void dt_iop_channelmixer_shared_simple_to_matrix(const dt_iop_channelmixer_shared_simple_params_t *const simple, float M[3][3])
gboolean dt_iop_channelmixer_shared_white_preserving_from_matrix(const dt_iop_channelmixer_shared_primaries_basis_t basis, const float M[3][3], dt_iop_channelmixer_shared_white_preserving_params_t *white_preserving)
Read a white-preserving primaries state back from a mixer matrix.
gboolean dt_iop_channelmixer_shared_white_preserving_to_matrix(const dt_iop_channelmixer_shared_primaries_basis_t basis, const dt_iop_channelmixer_shared_white_preserving_params_t *const white_preserving, float M[3][3])
Build the mixer matrix of a white-preserving primaries state.
void dt_iop_channelmixer_shared_simple_to_sliders(const dt_iop_channelmixer_shared_simple_params_t *const simple, GtkWidget *const widgets[6])
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])
float dt_iop_channelmixer_shared_roundtrip_error_relative(const float M[3][3], const float roundtrip[3][3])
Roundtrip error, measured against the magnitude of the matrix itself.
void dt_iop_channelmixer_shared_primaries_from_sliders(GtkWidget *const widgets[9], dt_iop_channelmixer_shared_primaries_params_t *primaries)
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 float scaling
static const dt_aligned_pixel_simd_t illuminant
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.
void dt_gui_update_collapsible_section(dt_gui_collapsible_section_t *cs)
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)
static int dt_colorchecker_find_color(GList **color_label)
Find all builtin and CGATS colorcheckers.
@ COLOR_CHECKER_XRITE_24_2000
static void dt_colorchecker_cleanup(dt_color_checker_t *checker)
static dt_color_checker_t * dt_get_color_checker(const dt_color_checker_targets target_type, const char *cht_filename, const char *color_filename)
Build a color checker of the given type.
static void dt_colorchecker_label_list_cleanup(GList **colorcheckers)
static int dt_colorchecker_find(GList **colorcheckers_label)
Find all builtin and .cht 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, double(*out_RGB_to_CAM)[3], double(*out_CAM_to_RGB)[4], const float *embedded_matrix, double *mul)
Compute the sRGB->camera and camera->sRGB matrices, and the default white balance multipliers.
struct _GtkWidget GtkWidget
GtkWidget, opaque, spelled exactly as GTK spells it.
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
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)
Does key have a value?
void dt_conf_set_float(const char *name, float val)
float dt_conf_get_float(const char *name)
Float for name, clamped to its declared bounds.
gchar * dt_conf_get_string(const char *name)
Read the stored string for name as a private copy.
void dt_conf_set_int(const char *name, int val)
int dt_conf_get_int(const char *name)
Integer for name, clamped to the bounds declared in the XML.
void dt_conf_set_string(const char *name, const char *val)
gboolean dt_conf_is_equal(const char *name, const char *value)
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)
void dt_control_log(const char *msg,...)
void dt_control_queue_redraw_center()
Request a redraw of the centre view.
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...
#define dt_control_set_cursor_visible(visible)
void * dt_alloc_align(size_t size)
Allocate cacheline-aligned memory.
struct dt_bauhaus_t * dt_bauhaus_get_global(void)
#define dt_dev_add_history_item(dev, module, enable, redraw)
#define dt_dev_pixelpipe_resync_history_preview(dev)
int32_t dt_dev_roi_request_preview_height(const dt_develop_t *dev)
int32_t dt_dev_roi_request_preview_width(const dt_develop_t *dev)
dt_dev_roi_request_t dt_dev_roi_request_of_pipe(const dt_dev_pixelpipe_t *pipe)
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)
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.
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_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)
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)
static int illuminant_to_xy(const dt_illuminant_t illuminant, const dt_image_t *img, const dt_aligned_pixel_t custom_wb, float *x_out, float *y_out, const float t, const dt_illuminant_fluo_t fluo, const dt_illuminant_led_t iled)
@ DT_ILLUMINANT_DETECT_EDGES
@ DT_ILLUMINANT_DETECT_SURFACES
static void illuminant_xy_to_RGB(const float x, const float y, dt_aligned_pixel_t RGB)
static void illuminant_xy_to_XYZ(const float x, const float y, dt_aligned_pixel_t XYZ)
static float xy_to_CCT(const float x, const float y)
static float CCT_reverse_lookup(const float x, const float y)
static int find_temperature_from_raw_coeffs(const dt_image_t *img, const dt_aligned_pixel_t custom_wb, float *chroma_x, float *chroma_y)
static void matrice_pseudoinverse(float(*in)[3], float(*out)[3], int size)
@ DT_ILLUMINANT_FLUO_LAST
static void xy_to_uv(const float xy[2], float uv[2])
static __DT_CLONE_TARGETS__ void dt_simd_memcpy(const float *const __restrict__ in, float *const __restrict__ out, const size_t num_elem)
static void dt_iop_image_copy_by_size(float *const __restrict__ out, const float *const __restrict__ in, const size_t width, const size_t height, const size_t ch)
dt_iop_module_t * dt_iop_get_module_from_list(GList *iop_list, const char *op)
gboolean dt_iop_is_first_instance(GList *modules, dt_iop_module_t *module)
void dt_iop_default_init(dt_iop_module_t *module)
const char ** dt_iop_set_description(dt_iop_module_t *module, const char *main_text, const char *purpose, const char *input, const char *process, const char *output)
void dt_iop_set_cache_bypass(dt_iop_module_t *module, gboolean state)
@ DT_REQUEST_COLORPICK_OFF
void dt_iop_gui_leave_critical_section(dt_iop_module_t *const module)
Release what dt_iop_gui_enter_critical_section() took. Also a no-op headless.
void dt_iop_request_focus(dt_iop_module_t *module)
Move darkroom focus to module, or clear it with NULL.
#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
void dt_iop_gui_enter_critical_section(dt_iop_module_t *const module)
Take the module's GUI lock, serialising access to its dt_iop_gui_data_t.
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 dt_iop_gui_data_t * dt_iop_gui_data(const struct dt_iop_module_t *m)
The module's GUI data blob, NULL-safe for headless callers: IOP process() implementations read it for...
#define IOP_GUI_ALLOC(module)
static float kernel(const float *x, const float *y)
struct dt_iop_tonecurve_params_t preset
GtkWidget * dt_ui_section_label_new(const gchar *str)
GtkWidget * dt_ui_label_new(const gchar *str)
static float mix(const float a, const float b, const float t)
_lib_location_type_t type
void dt_print(dt_debug_thread_t thread, const char *msg,...) __attribute__((format(printf
Print to stdout when thread is enabled, prefixed with seconds since startup.
float *const restrict const size_t k
float *const restrict const size_t const size_t ch
#define IS_NULL_PTR(p)
C is way too permissive with !=, == and if(var) checks, which can mean too many things depending on w...
static void 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])
#define DT_ALIGNED_PIXEL
Align a 4-float pixel on 16 bytes, enough for SSE. Same struct-member caveat as DT_ALIGNED_ARRAY,...
#define dt_free_align(ptr)
Release memory from dt_alloc_align() and set ptr to NULL.
static void * dt_calloc_align(size_t size)
dt_alloc_align() followed by a zero fill.
static float * dt_alloc_align_float(size_t pixels)
Allocate pixels floats, cacheline-aligned and marked as such.
#define dt_free(ptr)
g_free() ptr and set it to NULL, skipping both if it is already NULL.
#define DT_MODULE_INTROSPECTION(MODVER, PARAMSTYPE)
DT_MODULE() for a module whose params struct is introspected.
GtkWidget * dt_ui_notebook_page(GtkNotebook *notebook, const char *text, const char *tooltip)
GtkNotebook * dt_ui_notebook_new()
void dt_ui_notebook_set_picker_owner(GtkNotebook *notebook, gpointer owner)
Register an opaque owner for a GtkNotebook's page switches, and report every "switch_page" to the hos...
static double reference(const op_t op, const double x)
int dt_opencl_enqueue_kernel_2d(const int dev, const int kernel, const size_t *sizes)
int dt_opencl_create_kernel(const int prog, const char *name)
void * dt_opencl_copy_host_to_device_constant(const int devid, const size_t size, void *host)
void dt_opencl_free_kernel(const int kernel)
int dt_opencl_set_kernel_arg(const int dev, const int kernel, const int num, const size_t size, const void *arg)
void dt_opencl_release_mem_object(cl_mem mem)
#define dt_omploop_sfence()
#define __OMP_DECLARE_SIMD__(...)
#define __OMP_PARALLEL_FOR__(...)
#define DT_PATH_MAX
Buffer size for a filesystem path anywhere in Ansel.
@ DT_DEV_PIXELPIPE_PREVIEW
#define dt_pixelpipe_cache_alloc_align_float_cache(pixels, id)
#define dt_pixelpipe_cache_free_align(mem)
static cairo_surface_t * dt_cairo_image_surface_create(cairo_format_t format, int width, int height)
#define DT_DEBUG_CONTROL_SIGNAL_DISCONNECT(ctlsig, cb, user_data)
struct dt_control_signal_t * dt_control_signal_get_global(void)
@ DT_SIGNAL_DEVELOP_PREVIEW_PIPE_FINISHED
This signal is raised when develop preview pipe process is finished no param, no returned value.
@ DT_SIGNAL_DEVELOP_UI_PIPE_FINISHED
This signal is raised when pipe is finished and the gui is attached no param, no returned value.
#define DT_DEBUG_CONTROL_SIGNAL_CONNECT(ctlsig, signal, cb, user_data)
DT_ALIGNED_PIXEL float dt_aligned_pixel_t[4]
#define for_each_channel(_var,...)
float dt_aligned_pixel_simd_t __attribute__((vector_size(16), aligned(16)))
Apply one channel's tone curve to each of the three colour channels, or pass the channel through unto...
const float uint32_t state[4]
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
The coherent set of numbers a darkroom pipe plans its ROI from.
dt_aligned_pixel_t wb_coeffs
struct dt_iop_module_t * chroma_adaptation
struct dt_develop_t::@14 proxy
struct dt_dev_pixelpipe_t * pipe
float d65_color_matrix[9]
float adobe_XYZ_to_CAM[4][3]
dt_aligned_pixel_t coeffs
dt_iop_buffer_type_t datatype
struct dt_iop_buffer_dsc_t::@56 temperature
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]
The affine map taking chart bounding-box coordinates to the buffer process() was handed.
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 * white_preserving_red_rotation
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 * white_preserving_red_saturation
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 * white_preserving_green_saturation
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 * white_preserving_blue_saturation
GtkWidget * normalize_light
float inverse_homography[9]
dt_gui_collapsible_section_t cs
GtkWidget * white_preserving_blue_rotation
GtkWidget * simple_coupling_2
GtkWidget * white_preserving_green_rotation
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
White-preserving primaries model : per-primary rotation and saturation, no white freedom.
dt_iop_global_data_t * data
dt_dev_request_colorpick_flags_t request_color_pick
dt_iop_params_t * default_params
struct dt_iop_module_gui_t * gui
struct dt_develop_t * dev
dt_iop_global_data_t * global_data
dt_aligned_pixel_t picked_color_min
dt_aligned_pixel_t picked_color_max
dt_aligned_pixel_t picked_color
A profile reduced to the arithmetic the pixel loop can run: two matrices and six tone-curve LUTs,...
int nonlinearlut
Non-zero when the profile has tone curves at all; tested as a boolean everywhere, but it is really th...
int lutsize
Entry count of each of the six LUTs. Always 65536 in practice: both callers of dt_ioppr_init_profile_...
dt_colormatrix_t matrix_out_transposed
float grey
Luminance of 18.42% grey through this profile, used by the tone-mapping modules to place middle grey....
float * lut_out[3]
Per-channel linear -> encoded tone curve, same convention as lut_in.
dt_colormatrix_t matrix_out
XYZ (D50) -> RGB, row-major; the inverse of matrix_in.
float * lut_in[3]
Per-channel encoded -> linear tone curve, lutsize entries each, sampled over [0,1]....
dt_colormatrix_t matrix_in_transposed
dt_colormatrix_t matrix_in
RGB -> XYZ (D50), row-major. matrix_in[1][*] is the luminance row. NaN in [0][0] marks the whole prof...
Region of interest passed through the pixelpipe.
#define __DT_CLONE_TARGETS__