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;
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));
2978 GdkEventButton *event
__attribute__((unused)), gpointer user_data)
2985 g->run_profile =
TRUE;
2993 GdkEventButton *event
__attribute__((unused)), gpointer user_data)
3000 g->run_validation =
TRUE;
3008 GdkEventButton *event
__attribute__((unused)), gpointer user_data)
3015 if(!
g->profile_ready)
return FALSE;
3022 check_if_close_to_daylight(
p->x,
p->y, &
p->temperature, NULL, NULL);
3024 p->red[0] =
g->mix[0][0];
3025 p->red[1] =
g->mix[0][1];
3026 p->red[2] =
g->mix[0][2];
3028 p->green[0] =
g->mix[1][0];
3029 p->green[1] =
g->mix[1][1];
3030 p->green[2] =
g->mix[1][2];
3032 p->blue[0] =
g->mix[2][0];
3033 p->blue[1] =
g->mix[2][1];
3034 p->blue[2] =
g->mix[2][2];
3084 check_if_close_to_daylight(
p->x,
p->y, &
p->temperature, &
p->illuminant, &
p->adaptation);
3117 gtk_label_set_markup(GTK_LABEL(
g->label_delta_E),
g->delta_E_label_text);
3128 d->version =
p->version;
3130 float norm_R = 1.0f;
3131 if(
p->normalize_R) norm_R =
p->red[0] +
p->red[1] +
p->red[2];
3133 float norm_G = 1.0f;
3134 if(
p->normalize_G) norm_G =
p->green[0] +
p->green[1] +
p->green[2];
3136 float norm_B = 1.0f;
3137 if(
p->normalize_B) norm_B =
p->blue[0] +
p->blue[1] +
p->blue[2];
3139 float norm_sat = 0.0f;
3140 if(
p->normalize_sat) norm_sat = (
p->saturation[0] +
p->saturation[1] +
p->saturation[2]) / 3.f;
3142 float norm_light = 0.0f;
3143 if(
p->normalize_light) norm_light = (
p->lightness[0] +
p->lightness[1] +
p->lightness[2]) / 3.f;
3145 float norm_grey =
p->grey[0] +
p->grey[1] +
p->grey[2];
3146 d->apply_grey = (
p->grey[0] != 0.f) || (
p->grey[1] != 0.f) || (
p->grey[2] != 0.f);
3147 if(!
p->normalize_grey || norm_grey == 0.f) norm_grey = 1.f;
3149 for(
int i = 0;
i < 3;
i++)
3151 d->MIX[0][
i] =
p->red[
i] / norm_R;
3152 d->MIX[1][
i] =
p->green[
i] / norm_G;
3153 d->MIX[2][
i] =
p->blue[
i] / norm_B;
3154 d->saturation[
i] = -
p->saturation[
i] + norm_sat;
3155 d->lightness[
i] =
p->lightness[
i] - norm_light;
3156 d->grey[
i] =
p->grey[
i] / norm_grey;
3162 d->saturation[0] = -
p->saturation[2] + norm_sat;
3163 d->saturation[2] = -
p->saturation[0] + norm_sat;
3171 d->adaptation =
p->adaptation;
3173 d->gamut = (
p->gamut == 0.f) ?
p->gamut : 1.f /
p->gamut;
3186 check_if_close_to_daylight(
x, y, NULL, NULL, &(
d->adaptation));
3188 d->illuminant_type =
p->illuminant;
3196 d->illuminant[3] = 0.f;
3198 dt_iop_fmt_log(self,
"commit: class=%s matrix_supported=%d illuminant=%d adaptation=%d custom_wb=[%.4f %.4f %.4f %.4f] xy=[%.4f %.4f]",
3201 p->illuminant,
d->adaptation, custom_wb[0], custom_wb[1], custom_wb[2], custom_wb[3],
x, y);
3211 d->p = powf(0.818155f /
d->illuminant[2], 0.0834f);
3222 piece->process_cl_ready = 0;
3231 piece->process_tiling_ready = 0;
3245 gtk_widget_set_visible(
g->illuminant,
FALSE);
3246 gtk_widget_set_visible(
g->illum_color,
FALSE);
3247 gtk_widget_set_visible(
g->approx_cct,
FALSE);
3248 gtk_widget_set_visible(
g->color_picker,
FALSE);
3249 gtk_widget_set_visible(
g->temperature,
FALSE);
3250 gtk_widget_set_visible(
g->illum_fluo,
FALSE);
3251 gtk_widget_set_visible(
g->illum_led,
FALSE);
3252 gtk_widget_set_visible(
g->illum_x,
FALSE);
3253 gtk_widget_set_visible(
g->illum_y,
FALSE);
3259 gtk_widget_set_visible(
g->illuminant,
TRUE);
3260 gtk_widget_set_visible(
g->illum_color,
TRUE);
3261 gtk_widget_set_visible(
g->approx_cct,
TRUE);
3262 gtk_widget_set_visible(
g->color_picker,
TRUE);
3263 gtk_widget_set_visible(
g->temperature,
TRUE);
3264 gtk_widget_set_visible(
g->illum_fluo,
TRUE);
3265 gtk_widget_set_visible(
g->illum_led,
TRUE);
3266 gtk_widget_set_visible(
g->illum_x,
TRUE);
3270 switch(
p->illuminant)
3276 gtk_widget_set_visible(
g->adaptation,
TRUE);
3277 gtk_widget_set_visible(
g->temperature,
FALSE);
3278 gtk_widget_set_visible(
g->illum_fluo,
FALSE);
3279 gtk_widget_set_visible(
g->illum_led,
FALSE);
3280 gtk_widget_set_visible(
g->illum_x,
FALSE);
3281 gtk_widget_set_visible(
g->illum_y,
FALSE);
3287 gtk_widget_set_visible(
g->adaptation,
TRUE);
3288 gtk_widget_set_visible(
g->temperature,
TRUE);
3289 gtk_widget_set_visible(
g->illum_fluo,
FALSE);
3290 gtk_widget_set_visible(
g->illum_led,
FALSE);
3291 gtk_widget_set_visible(
g->illum_x,
FALSE);
3292 gtk_widget_set_visible(
g->illum_y,
FALSE);
3297 gtk_widget_set_visible(
g->adaptation,
TRUE);
3298 gtk_widget_set_visible(
g->temperature,
FALSE);
3299 gtk_widget_set_visible(
g->illum_fluo,
TRUE);
3300 gtk_widget_set_visible(
g->illum_led,
FALSE);
3301 gtk_widget_set_visible(
g->illum_x,
FALSE);
3302 gtk_widget_set_visible(
g->illum_y,
FALSE);
3307 gtk_widget_set_visible(
g->adaptation,
TRUE);
3308 gtk_widget_set_visible(
g->temperature,
FALSE);
3309 gtk_widget_set_visible(
g->illum_fluo,
FALSE);
3310 gtk_widget_set_visible(
g->illum_led,
TRUE);
3311 gtk_widget_set_visible(
g->illum_x,
FALSE);
3312 gtk_widget_set_visible(
g->illum_y,
FALSE);
3317 gtk_widget_set_visible(
g->adaptation,
TRUE);
3318 gtk_widget_set_visible(
g->temperature,
FALSE);
3319 gtk_widget_set_visible(
g->illum_fluo,
FALSE);
3320 gtk_widget_set_visible(
g->illum_led,
FALSE);
3321 gtk_widget_set_visible(
g->illum_x,
TRUE);
3322 gtk_widget_set_visible(
g->illum_y,
TRUE);
3327 gtk_widget_set_visible(
g->adaptation,
TRUE);
3328 gtk_widget_set_visible(
g->temperature,
FALSE);
3329 gtk_widget_set_visible(
g->illum_fluo,
FALSE);
3330 gtk_widget_set_visible(
g->illum_led,
FALSE);
3331 gtk_widget_set_visible(
g->illum_x,
FALSE);
3332 gtk_widget_set_visible(
g->illum_y,
FALSE);
3338 gtk_widget_set_visible(
g->adaptation,
FALSE);
3339 gtk_widget_set_visible(
g->temperature,
FALSE);
3340 gtk_widget_set_visible(
g->illum_fluo,
FALSE);
3341 gtk_widget_set_visible(
g->illum_led,
FALSE);
3342 gtk_widget_set_visible(
g->illum_x,
FALSE);
3343 gtk_widget_set_visible(
g->illum_y,
FALSE);
3406 dt_xyY_to_Lch(xyY2,
Lch);
3418 gtk_widget_queue_draw(
g->illum_x);
3419 gtk_widget_queue_draw(
g->illum_y);
3436 dt_LCH_2_Lab(Lch_hue,
Lab);
3444 dt_LCH_2_Lab(Lch_lightness,
Lab);
3452 dt_LCH_2_Lab(Lch_chroma,
Lab);
3459 gtk_widget_queue_draw(
g->hue_spot);
3460 gtk_widget_queue_draw(
g->lightness_spot);
3461 gtk_widget_queue_draw(
g->chroma_spot);
3462 gtk_widget_queue_draw(
g->target_spot);
3478 = {
g->primaries_achromatic_hue,
g->primaries_achromatic_purity,
g->primaries_red_hue,
3479 g->primaries_red_purity,
g->primaries_green_hue,
g->primaries_green_purity,
3480 g->primaries_blue_hue,
g->primaries_blue_purity,
g->primaries_gain };
3484 const float rows[3][3] = { {
p->red[0],
p->red[1],
p->red[2] },
3485 {
p->green[0],
p->green[1],
p->green[2] },
3486 {
p->blue[0],
p->blue[1],
p->blue[2] } };
3487 const gboolean
normalize[3] = {
p->normalize_R,
p->normalize_G,
p->normalize_B };
3488 float M[3][3] = { { 0.f } };
3489 float roundtrip[3][3] = { { 0.f } };
3512 = {
g->primaries_achromatic_hue,
g->primaries_achromatic_purity,
g->primaries_red_hue,
3513 g->primaries_red_purity,
g->primaries_green_hue,
g->primaries_green_purity,
3514 g->primaries_blue_hue,
g->primaries_blue_purity,
g->primaries_gain };
3534 widgets[0] =
g->white_preserving_red_rotation;
3535 widgets[1] =
g->white_preserving_red_saturation;
3536 widgets[2] =
g->white_preserving_green_rotation;
3537 widgets[3] =
g->white_preserving_green_saturation;
3538 widgets[4] =
g->white_preserving_blue_rotation;
3539 widgets[5] =
g->white_preserving_blue_saturation;
3541 for(
int widget = 0; widget < 6; widget++)
3563 const float rows[3][3] = { {
p->red[0],
p->red[1],
p->red[2] },
3564 {
p->green[0],
p->green[1],
p->green[2] },
3565 {
p->blue[0],
p->blue[1],
p->blue[2] } };
3566 const gboolean
normalize[3] = {
p->normalize_R,
p->normalize_G,
p->normalize_B };
3567 float M[3][3] = { { 0.f } };
3568 float roundtrip[3][3] = { { 0.f } };
3600 &white_preserving, widgets);
3612 convert_any_LMS_to_RGB(
LMS, work_rgb,
p->adaptation);
3616 for(
size_t c = 0; c < 3; c++) work_rgb[c] =
LMS[c];
3625 GtkWidget *w,
float stop,
float c,
float r,
float g,
float b)
3628 0.5f * (c *
g + 1 -
g),
3629 0.5f * (c * b + 1 - b)};
3648 const float sum =
RGB[0] +
RGB[1] +
RGB[2];
3649 if(sum != 0.f)
for(
int c = 0; c < 3; c++)
RGB[c] /= sum;
3665 gtk_widget_queue_draw(w_r);
3666 gtk_widget_queue_draw(w_b);
3667 gtk_widget_queue_draw(w_g);
3682 = {
g->simple_theta,
g->simple_psi,
g->simple_stretch_1,
g->simple_stretch_2,
g->simple_coupling_1,
3683 g->simple_coupling_2 };
3684 const float rows[3][3] = { {
p->red[0],
p->red[1],
p->red[2] },
3685 {
p->green[0],
p->green[1],
p->green[2] },
3686 {
p->blue[0],
p->blue[1],
p->blue[2] } };
3687 const gboolean
normalize[3] = {
p->normalize_R,
p->normalize_G,
p->normalize_B };
3688 float M[3][3] = { { 0.f } };
3689 float roundtrip[3][3] = { { 0.f } };
3727 = {
g->simple_theta,
g->simple_psi,
g->simple_stretch_1,
g->simple_stretch_2,
g->simple_coupling_1,
3728 g->simple_coupling_2 };
3742 gtk_widget_queue_draw(
g->illum_color);
3753 GtkAllocation allocation;
3754 gtk_widget_get_allocation(widget, &allocation);
3755 int width = allocation.width,
height = allocation.height;
3757 cairo_t *cr = cairo_create(cst);
3771 &
x, &y,
p->temperature,
p->illum_fluo,
p->illum_led);
3773 cairo_set_source_rgb(cr,
RGB[0],
RGB[1],
RGB[2]);
3780 cairo_set_source_surface(crf, cst, 0, 0);
3782 cairo_surface_destroy(cst);
3792 GtkAllocation allocation;
3793 gtk_widget_get_allocation(widget, &allocation);
3794 int width = allocation.width,
height = allocation.height;
3796 cairo_t *cr = cairo_create(cst);
3815 cairo_set_source_rgb(cr,
RGB[0],
RGB[1],
RGB[2]);
3822 cairo_set_source_surface(crf, cst, 0, 0);
3824 cairo_surface_destroy(cst);
3834 GtkAllocation allocation;
3835 gtk_widget_get_allocation(widget, &allocation);
3836 int width = allocation.width,
height = allocation.height;
3838 cairo_t *cr = cairo_create(cst);
3845 cairo_set_source_rgb(cr,
g->spot_RGB[0],
g->spot_RGB[1],
g->spot_RGB[2]);
3852 cairo_set_source_surface(crf, cst, 0, 0);
3854 cairo_surface_destroy(cst);
3871 check_if_close_to_daylight(
x, y, &
t, &test_illuminant, NULL);
3874 if(
t > 1667.f &&
t < 25000.f)
3878 str = g_strdup_printf(_(
"CCT: %.0f K (daylight)"),
t);
3879 gtk_widget_set_tooltip_text(GTK_WIDGET(
g->approx_cct),
3880 _(
"approximated correlated color temperature.\n"
3881 "this illuminant can be accurately modeled by a daylight spectrum,\n"
3882 "so its temperature is relevant and meaningful with a D illuminant."));
3886 str = g_strdup_printf(_(
"CCT: %.0f K (black body)"),
t);
3887 gtk_widget_set_tooltip_text(GTK_WIDGET(
g->approx_cct),
3888 _(
"approximated correlated color temperature.\n"
3889 "this illuminant can be accurately modeled by a black body spectrum,\n"
3890 "so its temperature is relevant and meaningful with a Planckian illuminant."));
3894 str = g_strdup_printf(_(
"CCT: %.0f K (invalid)"),
t);
3895 gtk_widget_set_tooltip_text(GTK_WIDGET(
g->approx_cct),
3896 _(
"approximated correlated color temperature.\n"
3897 "this illuminant cannot be accurately modeled by a daylight or black body spectrum,\n"
3898 "so its temperature is not relevant and meaningful and you need to use a custom illuminant."));
3903 str = g_strdup_printf(_(
"CCT: undefined"));
3904 gtk_widget_set_tooltip_text(GTK_WIDGET(
g->approx_cct),
3905 _(
"the approximated correlated color temperature\n"
3906 "cannot be computed at all so you need to use a custom illuminant."));
3908 gtk_label_set_text(GTK_LABEL(
g->approx_cct), str);
3942 dt_print(
DT_DEBUG_DEV,
"[picker/channelmixerrgb] history commit source=illum_xy_callback slider=%p\n",
3963 g->is_profiling_started =
FALSE;
3973 float simple_error = INFINITY;
3974 float primaries_error = INFINITY;
3975 float white_preserving_error = INFINITY;
3991 gboolean use_mixing =
TRUE;
3993 use_mixing =
dt_conf_get_bool(
"darkroom/modules/channelmixerrgb/use_mixing");
3994 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(
g->use_mixing), use_mixing);
3996 float lightness = 50.f;
4013 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(
g->clip),
p->clip);
4014 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(
g->normalize_R),
p->normalize_R);
4015 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(
g->normalize_G),
p->normalize_G);
4016 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(
g->normalize_B),
p->normalize_B);
4020 const gboolean white_preserving_ok
4040 gtk_widget_hide(GTK_WIDGET(
g->saturation_version));
4042 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(
g->normalize_sat),
p->normalize_sat);
4043 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(
g->normalize_light),
p->normalize_light);
4044 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(
g->normalize_grey),
p->normalize_grey);
4053 gchar *selected_checker =
dt_conf_get_string(
"darkroom/modules/channelmixerrgb/colorchecker");
4062 gchar *selected_color =
dt_conf_get_string(
"darkroom/modules/channelmixerrgb/colorchecker_color");
4072 const int j =
dt_conf_get_int(
"darkroom/modules/channelmixerrgb/optimization");
4074 g->optimization = j;
4082 g->is_profiling_started =
FALSE;
4087 g->spot_RGB[0] = 0.f;
4088 g->spot_RGB[1] = 0.f;
4089 g->spot_RGB[2] = 0.f;
4090 g->spot_RGB[3] = 0.f;
4108 if(
g->delta_E_label_text)
4111 g->delta_E_label_text = NULL;
4134 d->red[0] =
d->green[1] =
d->blue[2] = 1.0;
4135 d->normalize_R =
TRUE;
4136 d->normalize_G =
TRUE;
4137 d->normalize_B =
TRUE;
4155 fprintf(stderr,
"[channelmixerrgb] BUG: temperature (iop_order %d) must run before channelmixerrgb "
4156 "(iop_order %d); white-balance-derived defaults will be wrong on a fresh history\n",
4160 d->normalize_R =
TRUE;
4161 d->normalize_G =
TRUE;
4162 d->normalize_B =
TRUE;
4164 d->x =
module->get_f("x")->Float.Default;
4165 d->y =
module->get_f("y")->Float.Default;
4166 d->temperature =
module->get_f("temperature")->Float.Default;
4167 d->illuminant =
module->get_f("illuminant")->Enum.Default;
4168 d->adaptation =
module->get_f("adaptation")->Enum.Default;
4173 const gboolean is_modern =
4180 const dt_image_t *img = &
module->dev->image_storage;
4183 gboolean CAT_already_applied =
4188 module->default_enabled = FALSE;
4191 if(!CAT_already_applied
4200 check_if_close_to_daylight(
d->x,
d->y, &(
d->temperature), &(
d->illuminant), &(
d->adaptation));
4201 module->workflow_enabled = dt_image_needs_rawprepare(img);
4229 const gboolean use_mixing = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(
g->use_mixing));
4235 dt_conf_set_float(
"darkroom/modules/channelmixerrgb/hue", Lch_target[2] * 360.f);
4236 dt_conf_set_bool(
"darkroom/modules/channelmixerrgb/use_mixing", use_mixing);
4253 gtk_stack_set_visible_child_name(GTK_STACK(
g->mixer_stack),
4274 gboolean changed =
p->normalize_R ||
p->normalize_G ||
p->normalize_B;
4276 for(
int col = 0; col < 3; col++)
4278 changed = changed ||
p->red[col] !=
M[0][col] ||
p->green[col] !=
M[1][col] ||
p->blue[col] !=
M[2][col];
4279 p->red[col] =
M[0][col];
4280 p->green[col] =
M[1][col];
4281 p->blue[col] =
M[2][col];
4289 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(
g->normalize_R),
FALSE);
4290 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(
g->normalize_G),
FALSE);
4291 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(
g->normalize_B),
FALSE);
4343 float error = INFINITY;
4346 dt_control_log(_(
"simple mixer mode requires all three output rows to be normalized with non-zero sums."));
4362 const float rows[3][3] = { {
p->red[0],
p->red[1],
p->red[2] },
4363 {
p->green[0],
p->green[1],
p->green[2] },
4364 {
p->blue[0],
p->blue[1],
p->blue[2] } };
4365 const gboolean
normalize[3] = {
p->normalize_R,
p->normalize_G,
p->normalize_B };
4366 float M[3][3] = { { 0.f } };
4367 float error = INFINITY;
4371 dt_control_log(_(
"primaries mixer mode requires a non-singular 3x3 matrix with non-zero affine sums."));
4380 gboolean changed =
p->normalize_R ||
p->normalize_G ||
p->normalize_B;
4381 for(
int col = 0; col < 3; col++)
4383 changed = changed ||
p->red[col] !=
M[0][col] ||
p->green[col] !=
M[1][col] ||
p->blue[col] !=
M[2][col];
4384 p->red[col] =
M[0][col];
4385 p->green[col] =
M[1][col];
4386 p->blue[col] =
M[2][col];
4394 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(
g->normalize_R),
FALSE);
4395 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(
g->normalize_G),
FALSE);
4396 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(
g->normalize_B),
FALSE);
4420 const float rows[3][3] = { {
p->red[0],
p->red[1],
p->red[2] },
4421 {
p->green[0],
p->green[1],
p->green[2] },
4422 {
p->blue[0],
p->blue[1],
p->blue[2] } };
4423 const gboolean
normalize[3] = {
p->normalize_R,
p->normalize_G,
p->normalize_B };
4424 float M[3][3] = { { 0.f } };
4425 float error = INFINITY;
4430 self, _(
"white-preserving mixer mode requires a matrix that leaves the basis white unchanged."));
4441 dt_print(
DT_DEBUG_DEV,
"[channelmixerrgb] history commit source=mixer_mode_white_preserving\n");
4455 = {
g->simple_theta,
g->simple_psi,
g->simple_stretch_1,
g->simple_stretch_2,
g->simple_coupling_1,
4456 g->simple_coupling_2 };
4458 float M[3][3] = { { 0.f } };
4463 for(
int col = 0; col < 3; col++)
4465 p->red[col] =
M[0][col];
4466 p->green[col] =
M[1][col];
4467 p->blue[col] =
M[2][col];
4471 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(
g->normalize_R),
p->normalize_R);
4472 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(
g->normalize_G),
p->normalize_G);
4473 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(
g->normalize_B),
p->normalize_B);
4488 dt_print(
DT_DEBUG_DEV,
"[channelmixerrgb] history commit source=simple_slider slider=%p\n", (
void *)slider);
4500 = {
g->primaries_achromatic_hue,
g->primaries_achromatic_purity,
g->primaries_red_hue,
4501 g->primaries_red_purity,
g->primaries_green_hue,
g->primaries_green_purity,
4502 g->primaries_blue_hue,
g->primaries_blue_purity,
g->primaries_gain };
4506 float M[3][3] = { { 0.f } };
4511 dt_control_log(_(
"primaries mixer mode requires a non-singular 3x3 matrix with non-zero affine sums."));
4515 for(
int col = 0; col < 3; col++)
4517 p->red[col] =
M[0][col];
4518 p->green[col] =
M[1][col];
4519 p->blue[col] =
M[2][col];
4526 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(
g->normalize_R),
FALSE);
4527 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(
g->normalize_G),
FALSE);
4528 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(
g->normalize_B),
FALSE);
4543 dt_print(
DT_DEBUG_DEV,
"[channelmixerrgb] history commit source=primaries_slider slider=%p\n", (
void *)slider);
4559 float M[3][3] = { { 0.f } };
4569 dt_control_log(_(
"white-preserving mixer mode requires non-degenerate primaries."));
4578 dt_print(
DT_DEBUG_DEV,
"[channelmixerrgb] history commit source=white_preserving_slider slider=%p\n",
4588 const gboolean simple_widget
4589 = w ==
g->simple_theta || w ==
g->simple_psi || w ==
g->simple_stretch_1 || w ==
g->simple_stretch_2
4590 || w ==
g->simple_coupling_1 || w ==
g->simple_coupling_2;
4591 const gboolean primaries_widget
4592 = w ==
g->primaries_achromatic_hue || w ==
g->primaries_achromatic_purity || w ==
g->primaries_red_hue
4593 || w ==
g->primaries_red_purity || w ==
g->primaries_green_hue || w ==
g->primaries_green_purity
4594 || w ==
g->primaries_blue_hue || w ==
g->primaries_blue_purity || w ==
g->primaries_gain;
4595 const gboolean white_preserving_widget
4596 = w ==
g->white_preserving_red_rotation || w ==
g->white_preserving_red_saturation
4597 || w ==
g->white_preserving_green_rotation || w ==
g->white_preserving_green_saturation
4598 || w ==
g->white_preserving_blue_rotation || w ==
g->white_preserving_blue_saturation;
4599 GtkWidget *white_preserving_widgets[6] = { NULL };
4602 const gboolean
normalize[3] = {
p->normalize_R,
p->normalize_G,
p->normalize_B };
4604 const gboolean complete_widget
4605 = w ==
g->scale_red_R || w ==
g->scale_red_G || w ==
g->scale_red_B || w ==
g->scale_green_R
4606 || w ==
g->scale_green_G || w ==
g->scale_green_B || w ==
g->scale_blue_R || w ==
g->scale_blue_G
4607 || w ==
g->scale_blue_B || w ==
g->normalize_R || w ==
g->normalize_G || w ==
g->normalize_B;
4610 const gboolean illuminant_widgets_ready
4630 check_if_close_to_daylight(
p->x,
p->y, &(
p->temperature), NULL, &(
p->adaptation));
4639 check_if_close_to_daylight(
p->x,
p->y, &(
p->temperature), NULL, &(
p->adaptation));
4642 dt_control_log(_(
"white balance successfully extracted from raw image"));
4652 if(!
IS_NULL_PTR(w) && (w ==
g->illuminant || w ==
g->illum_fluo || w ==
g->illum_led || w ==
g->temperature))
4659 illuminant_to_xy(
p->illuminant, NULL, NULL, &(
p->x), &(
p->y),
p->temperature,
p->illum_fluo,
p->illum_led);
4665 check_if_close_to_daylight(
p->x,
p->y, &(
p->temperature), NULL, NULL);
4671 if(
IS_NULL_PTR(w) || w ==
g->hue_spot || w ==
g->chroma_spot || w ==
g->lightness_spot || w ==
g->spot_settings)
4676 if((
IS_NULL_PTR(w) || w ==
g->illuminant || w ==
g->illum_fluo || w ==
g->illum_led || w ==
g->temperature)
4677 && illuminant_widgets_ready)
4701 if(w ==
g->adaptation)
4704 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)
4706 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)
4707 _update_RGB_colors(self, 0, 1, 0,
p->normalize_G,
p->green,
g->scale_green_R,
g->scale_green_G,
g->scale_green_B);
4708 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)
4709 _update_RGB_colors(self, 0, 0, 1,
p->normalize_B,
p->blue,
g->scale_blue_R,
g->scale_blue_G,
g->scale_blue_B);
4711 if(rows_are_normalized && !simple_widget && (
IS_NULL_PTR(w) || complete_widget))
4713 float error = INFINITY;
4719 error,
p->normalize_R,
p->normalize_G,
p->normalize_B);
4720 dt_control_log(_(
"simple mixer mode requires all three output rows to be normalized with non-zero sums."));
4730 if(!primaries_widget && (
IS_NULL_PTR(w) || complete_widget || simple_widget || w ==
g->adaptation))
4732 float error = INFINITY;
4745 if(white_preserving_ready && !white_preserving_widget
4746 && (
IS_NULL_PTR(w) || complete_widget || simple_widget || primaries_widget || w ==
g->adaptation))
4748 float error = INFINITY;
4761 if(
IS_NULL_PTR(w) || w ==
g->adaptation || complete_widget || simple_widget || primaries_widget
4762 || white_preserving_widget)
4765 if(
IS_NULL_PTR(w) || w ==
g->adaptation || complete_widget || simple_widget || primaries_widget
4766 || white_preserving_widget)
4769 if(
IS_NULL_PTR(w) || w ==
g->adaptation || complete_widget || simple_widget || primaries_widget
4770 || white_preserving_widget)
4775 if((
p->grey[0] != 0.f) || (
p->grey[1] != 0.f) || (
p->grey[2] != 0.f))
4776 if((
p->grey[0] +
p->grey[1] +
p->grey[2] == 0.f) &&
p->normalize_grey)
4777 dt_control_log(_(
"color calibration: the sum of the gray channel parameters is zero, normalization will be disabled."));
4782 gtk_widget_queue_draw(
g->adaptation);
4803 if(!isfinite(
RGB[0]) || !isfinite(
RGB[1]) || !isfinite(
RGB[2]))
4825 if(!isfinite(
XYZ[0]) || !isfinite(
XYZ[1]) || !isfinite(
XYZ[2]))
return;
4836 gtk_label_set_text(GTK_LABEL(
g->Lch_origin),
4837 g_strdup_printf(_(
"L: \t%.1f %%\nh: \t%.1f \302\260\nc: \t%.1f"),
4839 gtk_widget_queue_draw(
g->origin_spot);
4843 const gboolean use_mixing = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(
g->use_mixing));
4848 float norm_R = 1.0f;
4849 if(
p->normalize_R) norm_R =
p->red[0] +
p->red[1] +
p->red[2];
4851 float norm_G = 1.0f;
4852 if(
p->normalize_G) norm_G =
p->green[0] +
p->green[1] +
p->green[2];
4854 float norm_B = 1.0f;
4855 if(
p->normalize_B) norm_B =
p->blue[0] +
p->blue[1] +
p->blue[2];
4857 for(
int i = 0;
i < 3;
i++)
4859 MIX[0][
i] =
p->red[
i] / norm_R;
4860 MIX[1][
i] =
p->green[
i] / norm_G;
4861 MIX[2][
i] =
p->blue[
i] / norm_B;
4888 const float pp = powf(0.818155f / LMS_illuminant[2], 0.0834f);
4898 dt_load_simd_aligned(LMS_illuminant),
4907 dot_product(LMS_output, MIX, temp);
4915 dt_Lab_2_LCH(Lab_output, Lch_output);
4927 dt_conf_set_float(
"darkroom/modules/channelmixerrgb/hue", Lch_output[2] * 360.f);
4928 dt_conf_set_bool(
"darkroom/modules/channelmixerrgb/use_mixing", use_mixing);
4944 dt_LCH_2_Lab(Lch_target, Lab_target);
4946 const float Y_target = XYZ_target[1];
4947 for(
int c = 0; c < 3; c++) XYZ_target[c] /= Y_target;
4948 dt_store_simd_aligned(LMS_target, convert_any_XYZ_to_LMS(dt_load_simd_aligned(XYZ_target),
p->adaptation));
4966 float MIX_INV_3x3[9];
4983 dot_product(LMS_target, MIX_INV, temp);
4991 const float Y_mix = XYZ_target[1];
4992 for(
int c = 0; c < 3; c++) XYZ_target[c] /= Y_mix;
4993 dt_store_simd_aligned(LMS_target, convert_any_XYZ_to_LMS(dt_load_simd_aligned(XYZ_target),
p->adaptation));
5003 const float Y =
XYZ[1];
5004 for(
int c = 0; c < 3; c++)
XYZ[c] /= Y;
5015 for(
int c = 0; c < 3; c++) illuminant_LMS[c] = D50[c] *
LMS[c] / LMS_target[c];
5016 dt_store_simd_aligned(illuminant_XYZ, convert_any_LMS_to_XYZ(dt_load_simd_aligned(illuminant_LMS),
p->adaptation));
5019 const float sum = fmaxf(illuminant_XYZ[0] + illuminant_XYZ[1] + illuminant_XYZ[2],
NORM_MIN);
5020 illuminant_XYZ[0] /= sum;
5021 illuminant_XYZ[2] = illuminant_XYZ[1];
5022 illuminant_XYZ[1] /= sum;
5024 p->x = illuminant_XYZ[0];
5025 p->y = illuminant_XYZ[1];
5033 check_if_close_to_daylight(
p->x,
p->y, &
p->temperature, NULL, NULL);
5041 dt_xyY_to_Lch(
xyY, Lch_illuminant);
5050 gtk_widget_queue_draw(
g->origin_spot);
5054 if(memcmp(&previous,
p,
sizeof(previous)) != 0)
5061 dt_print(
DT_DEBUG_DEV,
"[picker/channelmixerrgb] history commit source=auto_set_illuminant\n");
5081 if(
piece->dsc_in.channels != 4)
return;
5089 float norm_R = 1.0f;
5090 if(
p->normalize_R) norm_R =
p->red[0] +
p->red[1] +
p->red[2];
5092 float norm_G = 1.0f;
5093 if(
p->normalize_G) norm_G =
p->green[0] +
p->green[1] +
p->green[2];
5095 float norm_B = 1.0f;
5096 if(
p->normalize_B) norm_B =
p->blue[0] +
p->blue[1] +
p->blue[2];
5098 for(
int c = 0; c < 3; c++)
5100 MIX[0][c] =
p->red[c] / norm_R;
5101 MIX[1][c] =
p->green[c] / norm_G;
5102 MIX[2][c] =
p->blue[c] / norm_B;
5105 float lightness = 50.f;
5121 dt_LCH_2_Lab(Lch_target, Lab_target);
5125 const float Y_target = XYZ_target[1];
5126 for(
int c = 0; c < 3; c++) XYZ_target[c] /= Y_target;
5127 dt_store_simd_aligned(LMS_target, convert_any_XYZ_to_LMS(dt_load_simd_aligned(XYZ_target),
p->adaptation));
5132 float MIX_INV_3x3[9];
5139 dot_product(LMS_target, MIX_INV, temp);
5142 const float Y_mix = XYZ_target[1];
5144 for(
int c = 0; c < 3; c++) XYZ_target[c] /= Y_mix;
5145 dt_store_simd_aligned(LMS_target, convert_any_XYZ_to_LMS(dt_load_simd_aligned(XYZ_target),
p->adaptation));
5147 const float *
const restrict in = (
float *)
i;
5148 float average_L = 0.f;
5149 float average_M = 0.f;
5150 float average_S = 0.f;
5151 size_t valid_pixels = 0;
5155 for(
size_t k = 0;
k < roi_out->
width * roi_out->
height * 4;
k += 4)
5161 current_profile->unbounded_coeffs_in, current_profile->
lutsize,
5165 if(!isfinite(
LMS[0]) || !isfinite(
LMS[1]) || !isfinite(
LMS[2]))
continue;
5167 average_L +=
LMS[0];
5168 average_M +=
LMS[1];
5169 average_S +=
LMS[2];
5173 if(valid_pixels == 0)
return;
5175 const float norm = 1.f / (float)valid_pixels;
5176 dt_aligned_pixel_t average_LMS = { average_L * norm, average_M * norm, average_S * norm, 0.f };
5180 dt_store_simd_aligned(average_XYZ, convert_any_LMS_to_XYZ(dt_load_simd_aligned(average_LMS),
p->adaptation));
5181 const float Y_average_lms = average_XYZ[1];
5182 for(
int c = 0; c < 3; c++) average_XYZ[c] /= Y_average_lms;
5183 dt_store_simd_aligned(average_LMS, convert_any_XYZ_to_LMS(dt_load_simd_aligned(average_XYZ),
p->adaptation));
5190 for(
int c = 0; c < 3; c++)
5192 const float target = copysignf(fmaxf(fabsf(LMS_target[c]),
NORM_MIN), LMS_target[c]);
5193 illuminant_LMS[c] = D50[c] * average_LMS[c] / target;
5196 dt_store_simd_aligned(illuminant_XYZ, convert_any_LMS_to_XYZ(dt_load_simd_aligned(illuminant_LMS),
p->adaptation));
5198 const float sum = fmaxf(illuminant_XYZ[0] + illuminant_XYZ[1] + illuminant_XYZ[2],
NORM_MIN);
5199 p->x = illuminant_XYZ[0] / sum;
5200 p->y = illuminant_XYZ[1] / sum;
5202 check_if_close_to_daylight(
p->x,
p->y, &
p->temperature, NULL, NULL);
5211 for(
size_t k = 0;
k < 4;
k++)
5213 g->box[
k].x =
g->box[
k].y = -1.;
5216 g->is_cursor_close =
FALSE;
5218 g->is_profiling_started =
FALSE;
5220 g->run_validation =
FALSE;
5221 g->profile_ready =
FALSE;
5222 g->checker_ready =
FALSE;
5223 g->delta_E_in = NULL;
5224 g->delta_E_label_text = NULL;
5225 g->colorcheckers = NULL;
5244 gtk_widget_set_tooltip_text(GTK_WIDGET(
g->adaptation),
5245 _(
"choose the method to adapt the illuminant\n"
5246 "and the colorspace in which the module works: \n"
5247 "- Linear Bradford (1985) is consistent with ICC v4 toolchain.\n"
5248 "- CAT16 (2016) is more robust and accurate.\n"
5249 "- Non-linear Bradford (1985) is the original Bradford,\n"
5250 " it can produce better results than the linear version, but is unreliable.\n"
5251 "- XYZ is a simple scaling in XYZ space. It is not recommended in general.\n"
5252 "- none disables any adaptation and uses pipeline working RGB."));
5257 gtk_box_pack_start(GTK_BOX(hbox),
g->approx_cct,
FALSE,
FALSE, 0);
5259 g->illum_color = GTK_WIDGET(gtk_drawing_area_new());
5262 gtk_widget_set_tooltip_text(GTK_WIDGET(
g->illum_color),
5263 _(
"this is the color of the scene illuminant before chromatic adaptation\n"
5264 "this color will be turned into pure white by the adaptation."));
5267 gtk_box_pack_start(GTK_BOX(hbox),
g->illum_color,
TRUE,
TRUE, 0);
5270 gtk_widget_set_tooltip_text(
g->color_picker, _(
"set white balance to detected from area"));
5285 g_signal_connect(G_OBJECT(
g->illum_x),
"value-changed", G_CALLBACK(
illum_xy_callback), self);
5292 g_signal_connect(G_OBJECT(
g->illum_y),
"value-changed", G_CALLBACK(
illum_xy_callback), self);
5308 "plugins/darkroom/channelmixerrgb/expand_picker_mapping",
5309 _(
"spot color mapping"),
5310 GTK_BOX(self->
gui->
widget), GTK_PACK_END);
5312 gtk_widget_set_tooltip_text(
g->csspot.expander, _(
"use a color checker target to autoset CAT and channels"));
5315 _(
"\"correction\" automatically adjust the illuminant\n"
5316 "such that the input color is mapped to the target.\n"
5317 "\"measure\" simply shows how an input color is mapped by the CAT\n"
5318 "and can be used to sample a target."),
5322 gtk_box_pack_start(GTK_BOX(
g->csspot.container), GTK_WIDGET(
g->spot_mode),
TRUE,
TRUE, 0);
5325 gchar *label = N_(
"take channel mixing into account");
5326 g->use_mixing = gtk_check_button_new_with_label(_(label));
5327 gtk_label_set_ellipsize(GTK_LABEL(gtk_bin_get_child(GTK_BIN(
g->use_mixing))), PANGO_ELLIPSIZE_END);
5328 gtk_widget_set_tooltip_text(
g->use_mixing,
5329 _(
"compute the target by taking the channel mixing into account.\n"
5330 "if disabled, only the CAT is considered."));
5331 gtk_box_pack_start(GTK_BOX(
g->csspot.container), GTK_WIDGET(
g->use_mixing),
TRUE,
TRUE, 0);
5339 g->origin_spot = GTK_WIDGET(gtk_drawing_area_new());
5342 gtk_widget_set_tooltip_text(GTK_WIDGET(
g->origin_spot),
5343 _(
"the input color that should be mapped to the target"));
5345 g_signal_connect(G_OBJECT(
g->origin_spot),
"draw", G_CALLBACK(
origin_color_draw), self);
5346 gtk_box_pack_start(GTK_BOX(vvbox),
g->origin_spot,
TRUE,
TRUE, 0);
5348 g->Lch_origin = gtk_label_new(_(
"L: \tN/A\nh: \tN/A\nc: \tN/A"));
5349 gtk_widget_set_tooltip_text(GTK_WIDGET(
g->Lch_origin),
5350 _(
"these LCh coordinates are computed from CIE Lab 1976 coordinates"));
5351 gtk_box_pack_start(GTK_BOX(vvbox), GTK_WIDGET(
g->Lch_origin),
FALSE,
FALSE, 0);
5359 g->target_spot = GTK_WIDGET(gtk_drawing_area_new());
5362 gtk_widget_set_tooltip_text(GTK_WIDGET(
g->target_spot),
5363 _(
"the desired target color after mapping"));
5365 g_signal_connect(G_OBJECT(
g->target_spot),
"draw", G_CALLBACK(
target_color_draw), self);
5366 gtk_box_pack_start(GTK_BOX(vvbox),
g->target_spot,
TRUE,
TRUE, 0);
5372 gtk_box_pack_start(GTK_BOX(vvbox), GTK_WIDGET(
g->lightness_spot),
TRUE,
TRUE, 0);
5379 gtk_box_pack_start(GTK_BOX(vvbox), GTK_WIDGET(
g->hue_spot),
TRUE,
TRUE, 0);
5385 gtk_box_pack_start(GTK_BOX(vvbox), GTK_WIDGET(
g->chroma_spot),
TRUE,
TRUE, 0);
5390 gtk_box_pack_start(GTK_BOX(
g->csspot.container), GTK_WIDGET(hhbox),
FALSE,
FALSE, 0);
5399 gtk_widget_set_tooltip_text(
g->mixer_mode,
5400 _(
"complete exposes the original nine mixer coefficients.\n"
5401 "simple rebuilds the normalized mixer as an exact chroma-plane rotation,\n"
5402 "two signed stretches and two neutral couplings.\n"
5403 "primaries rebuilds the mixer as a generalized primaries, white tint and gain model.\n"
5404 "white-preserving rotates and saturates each primary around the white,\n"
5405 "which stays exactly where it is."));
5406 gtk_box_pack_start(GTK_BOX(mixer_page), GTK_WIDGET(
g->mixer_mode),
FALSE,
FALSE, 0);
5409 g->mixer_stack = gtk_stack_new();
5410 gtk_stack_set_transition_type(GTK_STACK(
g->mixer_stack), GTK_STACK_TRANSITION_TYPE_NONE);
5411 gtk_box_pack_start(GTK_BOX(mixer_page), GTK_WIDGET(
g->mixer_stack),
FALSE,
FALSE, 0);
5414 gtk_stack_add_named(GTK_STACK(
g->mixer_stack), mixer_complete,
"complete");
5417#define MIXER_ROW(var, short, section, swap) \
5418 gtk_box_pack_start(GTK_BOX(mixer_complete), dt_ui_section_label_new(section), FALSE, FALSE, 0); \
5419 self->gui->widget = mixer_complete; \
5420 first = dt_bauhaus_slider_from_params(self, swap ? #var "[2]" : #var "[0]");\
5421 dt_bauhaus_slider_set_digits(first, 3); \
5422 dt_bauhaus_widget_set_label(first, N_("input R")); \
5423 second = dt_bauhaus_slider_from_params(self, #var "[1]"); \
5424 dt_bauhaus_slider_set_digits(second, 3); \
5425 dt_bauhaus_widget_set_label(second, N_("input G")); \
5426 third = dt_bauhaus_slider_from_params(self, swap ? #var "[0]" : #var "[2]");\
5427 dt_bauhaus_slider_set_digits(third, 3); \
5428 dt_bauhaus_widget_set_label(third, N_("input B")); \
5429 g->scale_##var##_R = swap ? third : first; \
5430 g->scale_##var##_G = second; \
5431 g->scale_##var##_B = swap ? first : third; \
5432 g->normalize_##short = dt_bauhaus_toggle_from_params(self, "normalize_" #short);
5439 gtk_stack_add_named(GTK_STACK(
g->mixer_stack), mixer_simple,
"simple");
5445 gtk_widget_set_tooltip_text(
g->simple_theta, _(
"global rotation of the normalized chroma plane."));
5446 gtk_box_pack_start(GTK_BOX(mixer_simple), GTK_WIDGET(
g->simple_theta),
FALSE,
FALSE, 0);
5455 gtk_widget_set_tooltip_text(
g->simple_psi, _(
"orientation of the principal stretch axes in the chroma plane."));
5456 gtk_box_pack_start(GTK_BOX(mixer_simple), GTK_WIDGET(
g->simple_psi),
FALSE,
FALSE, 0);
5461 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."));
5462 gtk_box_pack_start(GTK_BOX(mixer_simple), GTK_WIDGET(
g->simple_stretch_1),
FALSE,
FALSE, 0);
5467 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."));
5468 gtk_box_pack_start(GTK_BOX(mixer_simple), GTK_WIDGET(
g->simple_stretch_2),
FALSE,
FALSE, 0);
5477 gtk_widget_set_tooltip_text(
g->simple_coupling_2, _(
"chroma direction, in the fixed chroma basis, that is coupled into the achromatic axis."));
5478 gtk_box_pack_start(GTK_BOX(mixer_simple), GTK_WIDGET(
g->simple_coupling_2),
FALSE,
FALSE, 0);
5483 gtk_widget_set_tooltip_text(
g->simple_coupling_1, _(
"strength of the chroma-to-achromatic coupling in the fixed chroma basis."));
5484 gtk_box_pack_start(GTK_BOX(mixer_simple), GTK_WIDGET(
g->simple_coupling_1),
FALSE,
FALSE, 0);
5488 gtk_stack_add_named(GTK_STACK(
g->mixer_stack), mixer_primaries,
"primaries");
5496 gtk_widget_set_tooltip_text(
g->primaries_achromatic_hue,
5497 _(
"rotate the custom white vector around the D50 white of the current mixer basis."));
5498 gtk_box_pack_start(GTK_BOX(mixer_primaries), GTK_WIDGET(
g->primaries_achromatic_hue),
FALSE,
FALSE, 0);
5499 g_signal_connect(G_OBJECT(
g->primaries_achromatic_hue),
"value-changed",
5504 gtk_widget_set_tooltip_text(
g->primaries_achromatic_purity,
5505 _(
"distance of the custom white vector from the D50 white within the current mixer basis."));
5506 gtk_box_pack_start(GTK_BOX(mixer_primaries), GTK_WIDGET(
g->primaries_achromatic_purity),
FALSE,
FALSE, 0);
5507 g_signal_connect(G_OBJECT(
g->primaries_achromatic_purity),
"value-changed",
5516 gtk_widget_set_tooltip_text(
g->primaries_red_hue,
5517 _(
"rotate the first basis vector around the D50 white of the current mixer basis."));
5518 gtk_box_pack_start(GTK_BOX(mixer_primaries), GTK_WIDGET(
g->primaries_red_hue),
FALSE,
FALSE, 0);
5519 g_signal_connect(G_OBJECT(
g->primaries_red_hue),
"value-changed",
5524 gtk_widget_set_tooltip_text(
g->primaries_red_purity,
5525 _(
"radial scaling of the first basis vector inside the affine primaries footprint."));
5526 gtk_box_pack_start(GTK_BOX(mixer_primaries), GTK_WIDGET(
g->primaries_red_purity),
FALSE,
FALSE, 0);
5527 g_signal_connect(G_OBJECT(
g->primaries_red_purity),
"value-changed",
5536 gtk_widget_set_tooltip_text(
g->primaries_green_hue,
5537 _(
"rotate the second basis vector around the D50 white of the current mixer basis."));
5538 gtk_box_pack_start(GTK_BOX(mixer_primaries), GTK_WIDGET(
g->primaries_green_hue),
FALSE,
FALSE, 0);
5539 g_signal_connect(G_OBJECT(
g->primaries_green_hue),
"value-changed",
5544 gtk_widget_set_tooltip_text(
g->primaries_green_purity,
5545 _(
"radial scaling of the second basis vector inside the affine primaries footprint."));
5546 gtk_box_pack_start(GTK_BOX(mixer_primaries), GTK_WIDGET(
g->primaries_green_purity),
FALSE,
FALSE, 0);
5547 g_signal_connect(G_OBJECT(
g->primaries_green_purity),
"value-changed",
5556 gtk_widget_set_tooltip_text(
g->primaries_blue_hue,
5557 _(
"rotate the third basis vector around the D50 white of the current mixer basis."));
5558 gtk_box_pack_start(GTK_BOX(mixer_primaries), GTK_WIDGET(
g->primaries_blue_hue),
FALSE,
FALSE, 0);
5559 g_signal_connect(G_OBJECT(
g->primaries_blue_hue),
"value-changed",
5564 gtk_widget_set_tooltip_text(
g->primaries_blue_purity,
5565 _(
"radial scaling of the third basis vector inside the affine primaries footprint."));
5566 gtk_box_pack_start(GTK_BOX(mixer_primaries), GTK_WIDGET(
g->primaries_blue_purity),
FALSE,
FALSE, 0);
5567 g_signal_connect(G_OBJECT(
g->primaries_blue_purity),
"value-changed",
5574 gtk_widget_set_tooltip_text(
g->primaries_gain,
5575 _(
"global gain multiplying the custom white vector after the affine primaries transform."));
5576 gtk_box_pack_start(GTK_BOX(mixer_primaries), GTK_WIDGET(
g->primaries_gain),
FALSE,
FALSE, 0);
5577 g_signal_connect(G_OBJECT(
g->primaries_gain),
"value-changed",
5581 gtk_stack_add_named(GTK_STACK(
g->mixer_stack), mixer_white_preserving,
"white-preserving");
5583#define WHITE_PRESERVING_PRIMARY(var, section, rotation_label, rotation_tooltip, saturation_label, \
5584 saturation_tooltip) \
5585 gtk_box_pack_start(GTK_BOX(mixer_white_preserving), dt_ui_section_label_new(section), FALSE, FALSE, 0); \
5586 g->white_preserving_##var##_rotation \
5587 = dt_bauhaus_slider_new_with_range(dt_bauhaus_get_global(), DT_GUI_MODULE(self), -1.f, 1.f, 0, 0, 3); \
5588 dt_bauhaus_widget_set_label(g->white_preserving_##var##_rotation, rotation_label); \
5589 dt_bauhaus_slider_set_factor(g->white_preserving_##var##_rotation, 90.f); \
5590 dt_bauhaus_slider_set_format(g->white_preserving_##var##_rotation, "\302\260"); \
5591 gtk_widget_set_tooltip_text(g->white_preserving_##var##_rotation, rotation_tooltip); \
5592 gtk_box_pack_start(GTK_BOX(mixer_white_preserving), GTK_WIDGET(g->white_preserving_##var##_rotation), FALSE,\
5594 g_signal_connect(G_OBJECT(g->white_preserving_##var##_rotation), "value-changed", \
5595 G_CALLBACK(_channelmixerrgb_white_preserving_slider_callback), self); \
5599 g->white_preserving_##var##_saturation \
5600 = dt_bauhaus_slider_new_with_range(dt_bauhaus_get_global(), DT_GUI_MODULE(self), -4.f, 4.f, 0, 0, 3); \
5601 dt_bauhaus_slider_set_soft_range(g->white_preserving_##var##_saturation, -1.f, 1.f); \
5602 dt_bauhaus_widget_set_label(g->white_preserving_##var##_saturation, saturation_label); \
5606 dt_bauhaus_slider_set_format(g->white_preserving_##var##_saturation, "%"); \
5607 gtk_widget_set_tooltip_text(g->white_preserving_##var##_saturation, saturation_tooltip); \
5608 gtk_box_pack_start(GTK_BOX(mixer_white_preserving), GTK_WIDGET(g->white_preserving_##var##_saturation), \
5610 g_signal_connect(G_OBJECT(g->white_preserving_##var##_saturation), "value-changed", \
5611 G_CALLBACK(_channelmixerrgb_white_preserving_slider_callback), self);
5614 _(
"rotate the red primary around the white of the current mixer basis.\n"
5615 "the white itself is left exactly where it is."),
5616 N_(
"red saturation"),
5617 _(
"scale the red 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 green primary around the white of the current mixer basis.\n"
5621 "the white itself is left exactly where it is."),
5622 N_(
"green saturation"),
5623 _(
"scale the green primary's distance to the white of the current mixer basis.\n"
5624 "-100% collapses it onto the white, +100% doubles it."))
5626 _(
"rotate the blue primary around the white of the current mixer basis.\n"
5627 "the white itself is left exactly where it is."),
5628 N_(
"blue saturation"),
5629 _(
"scale the blue primary's distance to the white of the current mixer basis.\n"
5630 "-100% collapses it onto the white, +100% doubles it."))
5632#undef WHITE_PRESERVING_PRIMARY
5635 _(
"output colorfulness, brightness and B&W mixing"));
5638#define OUTPUT_SECTION(var, short, section, swap) \
5639 gtk_box_pack_start(GTK_BOX(outputs_page), dt_ui_section_label_new(section), FALSE, FALSE, 0); \
5641 first = dt_bauhaus_slider_from_params(self, swap ? #var "[2]" : #var "[0]");\
5642 dt_bauhaus_slider_set_digits(first, 3); \
5643 dt_bauhaus_widget_set_label(first, N_("input R")); \
5645 second = dt_bauhaus_slider_from_params(self, #var "[1]"); \
5646 dt_bauhaus_slider_set_digits(second, 3); \
5647 dt_bauhaus_widget_set_label(second, N_("input G")); \
5649 third = dt_bauhaus_slider_from_params(self, swap ? #var "[0]" : #var "[2]");\
5650 dt_bauhaus_slider_set_digits(third, 3); \
5651 dt_bauhaus_widget_set_label(third, N_("input B")); \
5653 g->scale_##var##_R = swap ? third : first; \
5654 g->scale_##var##_G = second; \
5655 g->scale_##var##_B = swap ? first : third; \
5657 g->normalize_##short = dt_bauhaus_toggle_from_params(self, "normalize_" #short);
5668 const int saved_page =
dt_conf_get_int(
"plugins/darkroom/channelmixerrgb/gui_page");
5669 const int active_page = saved_page > 2 ? 2 : CLAMP(saved_page, 0, 2);
5670 gtk_widget_show(gtk_notebook_get_nth_page(
g->notebook, active_page));
5671 gtk_notebook_set_current_page(
g->notebook, active_page);
5677 "plugins/darkroom/channelmixerrgb/expand_values",
5678 _(
"calibrate with a color checker"),
5679 GTK_BOX(self->
gui->
widget), GTK_PACK_END);
5681 gtk_widget_set_tooltip_text(
g->cs.toggle,
5682 _(
"use a color checker target to autoset CAT and channels"));
5685 GtkWidget *collapsible = GTK_WIDGET(
g->cs.container);
5687 gchar *tip_files_loc = NULL;
5692 gchar *user_CGATS_dir = g_build_filename(confdir,
"color",
"checker", NULL);
5693 tip_files_loc = g_strdup_printf(_(
"'%s'"), user_CGATS_dir);
5699 gtk_box_pack_start(GTK_BOX(collapsible), GTK_WIDGET(
g->checkers_list),
TRUE,
TRUE, 0);
5701 gchar *
tooltip = g_strdup_printf(_(
"Choose the vendor and the type of your chart.\n"
5702 ".cht files in %s."), tip_files_loc);
5703 gtk_widget_set_tooltip_text(
g->checkers_list,
tooltip);
5709 gtk_box_pack_start(GTK_BOX(collapsible), GTK_WIDGET(
g->checkers_color_list),
TRUE,
TRUE, 0);
5712 tooltip = g_strdup_printf(_(
"Choose the definition of your chart.\n"
5713 "CGATS.17 files in %s."), tip_files_loc);
5714 gtk_widget_set_tooltip_text(
g->checkers_color_list,
tooltip);
5718 tooltip = g_markup_printf_escaped(_(
"<i>No CGATS.17 color file matches the selected chart.\n"
5719 "Add a compatible CGATS.17 file to <b>%s</b>.</i>"), tip_files_loc);
5720 g->checker_msg = gtk_label_new(NULL);
5721 gtk_label_set_markup(GTK_LABEL(
g->checker_msg),
tooltip);
5723 gtk_label_set_line_wrap(GTK_LABEL(
g->checker_msg),
TRUE);
5724 gtk_label_set_line_wrap_mode(GTK_LABEL(
g->checker_msg), PANGO_WRAP_WORD);
5725 gtk_box_pack_start(GTK_BOX(collapsible), GTK_WIDGET(
g->checker_msg),
FALSE,
TRUE, 0);
5726 gtk_widget_set_visible(
g->checker_msg,
FALSE);
5732 _(
"choose the colors that will be optimized with higher priority.\n"
5733 "neutral colors gives the lowest average delta E but a high maximum delta E\n"
5734 "saturated colors gives the lowest maximum delta E but a high average delta E\n"
5735 "none is a trade-off between both\n"
5736 "the others are special behaviours to protect some hues"),
5739 N_(
"neutral colors"),
5740 N_(
"saturated colors"),
5741 N_(
"skin and soil colors"),
5742 N_(
"foliage colors"),
5743 N_(
"sky and water colors"),
5744 N_(
"average delta E"),
5745 N_(
"maximum delta E"));
5746 gtk_box_pack_start(GTK_BOX(collapsible), GTK_WIDGET(
g->optimize),
TRUE,
TRUE, 0);
5750 gtk_widget_set_tooltip_text(
g->safety, _(
"reduce the radius of the patches to select the more or less central part.\n"
5751 "useful when the perspective correction is sloppy or\n"
5752 "the patches frame cast a shadows on the edges of the patch." ));
5754 gtk_box_pack_start(GTK_BOX(collapsible), GTK_WIDGET(
g->safety),
TRUE,
TRUE, 0);
5757 gtk_box_pack_start(GTK_BOX(collapsible), GTK_WIDGET(
g->label_delta_E),
TRUE,
TRUE, 0);
5758 gtk_widget_set_tooltip_text(
g->label_delta_E, _(
"the delta E is using the CIE 2000 formula"));
5763 gtk_box_pack_end(GTK_BOX(toolbar), GTK_WIDGET(
g->button_commit),
FALSE,
FALSE, 0);
5764 gtk_widget_set_tooltip_text(
g->button_commit, _(
"accept the computed profile and set it in the module"));
5765 g_signal_connect(G_OBJECT(
g->button_commit),
"button-press-event", G_CALLBACK(
commit_profile_callback), (gpointer)self);
5768 g_signal_connect(G_OBJECT(
g->button_profile),
"button-press-event", G_CALLBACK(
run_profile_callback), (gpointer)self);
5769 gtk_widget_set_tooltip_text(
g->button_profile, _(
"recompute the profile"));
5770 gtk_box_pack_end(GTK_BOX(toolbar), GTK_WIDGET(
g->button_profile),
FALSE,
FALSE, 0);
5773 g_signal_connect(G_OBJECT(
g->button_validate),
"button-press-event", G_CALLBACK(
run_validation_callback), (gpointer)self);
5774 gtk_widget_set_tooltip_text(
g->button_validate, _(
"check the output delta E"));
5775 gtk_box_pack_end(GTK_BOX(toolbar), GTK_WIDGET(
g->button_validate),
FALSE,
FALSE, 0);
5777 gtk_box_pack_start(GTK_BOX(collapsible), GTK_WIDGET(toolbar),
FALSE,
FALSE, 0);
5788 dt_conf_set_int(
"plugins/darkroom/channelmixerrgb/gui_page", gtk_notebook_get_current_page (
g->notebook));
5793 g->delta_E_in = NULL;
5800 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
static gboolean run_profile_callback(GtkWidget *widget __attribute__((unused)), GdkEventButton *event __attribute__((unused)), gpointer user_data)
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 gboolean commit_profile_callback(GtkWidget *widget __attribute__((unused)), GdkEventButton *event __attribute__((unused)), gpointer user_data)
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 gboolean illuminant_color_draw(GtkWidget *widget, cairo_t *crf, gpointer user_data)
static void update_illuminants(struct dt_iop_module_t *self)
static gboolean run_validation_callback(GtkWidget *widget __attribute__((unused)), GdkEventButton *event __attribute__((unused)), gpointer user_data)
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)
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)
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 const dt_dev_pixelpipe_t * pipe
static const dt_dev_pixelpipe_t const dt_dev_pixelpipe_iop_t * piece
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_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_iop_buffer_type_t datatype
dt_adaptation_t adaptation
dt_illuminant_t illuminant_type
dt_aligned_pixel_t illuminant
dt_iop_channelmixer_rgb_version_t version
float DT_ALIGNED_PIXEL lightness[4]
float DT_ALIGNED_PIXEL grey[4]
float DT_ALIGNED_PIXEL saturation[4]
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__