1use crate::matrix::{Matrix3f, Vector3f, Xyz};
30use crate::{Chromaticity, Matrix3d, Vector3d, XyY};
31
32pub(crate) const BRADFORD_D: Matrix3d = Matrix3d {
33 v: [
34 [0.8951, 0.2664, -0.1614],
35 [-0.7502, 1.7135, 0.0367],
36 [0.0389, -0.0685, 1.0296],
37 ],
38};
39
40pub(crate) const BRADFORD_F: Matrix3f = BRADFORD_D.to_f32();
41
42#[inline]
43pub(crate) const fn compute_chromatic_adaption(
44 source_white_point: Xyz,
45 dest_white_point: Xyz,
46 chad: Matrix3f,
47) -> Matrix3f {
48 let cone_source_xyz = Vector3f {
49 v: [
50 source_white_point.x,
51 source_white_point.y,
52 source_white_point.z,
53 ],
54 };
55 let cone_source_rgb = chad.mul_vector(cone_source_xyz);
56
57 let cone_dest_xyz = Vector3f {
58 v: [dest_white_point.x, dest_white_point.y, dest_white_point.z],
59 };
60 let cone_dest_rgb = chad.mul_vector(cone_dest_xyz);
61
62 let cone = Matrix3f {
63 v: [
64 [cone_dest_rgb.v[0] / cone_source_rgb.v[0], 0., 0.],
65 [0., cone_dest_rgb.v[1] / cone_source_rgb.v[1], 0.],
66 [0., 0., cone_dest_rgb.v[2] / cone_source_rgb.v[2]],
67 ],
68 };
69
70 let chad_inv = chad.inverse();
71
72 let p0 = cone.mat_mul_const(chad);
73 chad_inv.mat_mul_const(p0)
74}
75
76#[inline]
77pub(crate) const fn compute_chromatic_adaption_d(
78 source_white_point: Xyz,
79 dest_white_point: Xyz,
80 chad: Matrix3d,
81) -> Matrix3d {
82 let cone_source_xyz = Vector3d {
83 v: [
84 source_white_point.x as f64,
85 source_white_point.y as f64,
86 source_white_point.z as f64,
87 ],
88 };
89 let cone_source_rgb = chad.mul_vector(cone_source_xyz);
90
91 let cone_dest_xyz = Vector3d {
92 v: [
93 dest_white_point.x as f64,
94 dest_white_point.y as f64,
95 dest_white_point.z as f64,
96 ],
97 };
98 let cone_dest_rgb = chad.mul_vector(cone_dest_xyz);
99
100 let cone = Matrix3d {
101 v: [
102 [cone_dest_rgb.v[0] / cone_source_rgb.v[0], 0., 0.],
103 [0., cone_dest_rgb.v[1] / cone_source_rgb.v[1], 0.],
104 [0., 0., cone_dest_rgb.v[2] / cone_source_rgb.v[2]],
105 ],
106 };
107
108 let chad_inv = chad.inverse();
109
110 let p0 = cone.mat_mul_const(chad);
111 chad_inv.mat_mul_const(p0)
112}
113
114pub const fn adaption_matrix(source_illumination: Xyz, target_illumination: Xyz) -> Matrix3f {
115 compute_chromatic_adaption(source_illumination, target_illumination, BRADFORD_F)
116}
117
118pub const fn adaption_matrix_d(source_illumination: Xyz, target_illumination: Xyz) -> Matrix3d {
119 compute_chromatic_adaption_d(source_illumination, target_illumination, BRADFORD_D)
120}
121
122pub const fn adapt_to_d50(r: Matrix3f, source_white_pt: XyY) -> Matrix3f {
123 adapt_to_illuminant(r, source_white_pt, Chromaticity::D50.to_xyz())
124}
125
126pub const fn adapt_to_d50_d(r: Matrix3d, source_white_pt: XyY) -> Matrix3d {
127 adapt_to_illuminant_d(r, source_white_pt, Chromaticity::D50.to_xyz())
128}
129
130pub const fn adapt_to_illuminant(
131 r: Matrix3f,
132 source_white_pt: XyY,
133 illuminant_xyz: Xyz,
134) -> Matrix3f {
135 let bradford = adaption_matrix(source_white_pt.to_xyz(), illuminant_xyz);
136 bradford.mat_mul_const(r)
137}
138
139pub const fn adapt_to_illuminant_d(
140 r: Matrix3d,
141 source_white_pt: XyY,
142 illuminant_xyz: Xyz,
143) -> Matrix3d {
144 let bradford = adaption_matrix_d(source_white_pt.to_xyz(), illuminant_xyz);
145 bradford.mat_mul_const(r)
146}
147
148pub const fn adapt_to_illuminant_xyz(
149 r: Matrix3f,
150 source_white_pt: Xyz,
151 illuminant_xyz: Xyz,
152) -> Matrix3f {
153 if source_white_pt.y == 0.0 {
154 return r;
155 }
156
157 let bradford = adaption_matrix(source_white_pt, illuminant_xyz);
158 bradford.mat_mul_const(r)
159}
160
161pub const fn adapt_to_illuminant_xyz_d(
162 r: Matrix3d,
163 source_white_pt: Xyz,
164 illuminant_xyz: Xyz,
165) -> Matrix3d {
166 if source_white_pt.y == 0.0 {
167 return r;
168 }
169
170 let bradford = adaption_matrix_d(source_white_pt, illuminant_xyz);
171 bradford.mat_mul_const(r)
172}