1use crate::illuminant::{AcesWhitePoint, D50, D65, DciWhite, Illuminant};
13use crate::math::Mat3;
14use crate::observer::Cie1931;
15
16pub const fn derive_rgb_to_xyz(r: [f32; 2], g: [f32; 2], b: [f32; 2], w_xyz: [f32; 3]) -> Mat3 {
23 let [xr, yr] = r;
24 let zr = 1.0 - xr - yr;
25 let [xg, yg] = g;
26 let zg = 1.0 - xg - yg;
27 let [xb, yb] = b;
28 let zb = 1.0 - xb - yb;
29
30 let det = xr * (yg * zb - yb * zg) - xg * (yr * zb - yb * zr) + xb * (yr * zg - yg * zr);
31
32 let inv00 = (yg * zb - yb * zg) / det;
33 let inv01 = (xb * zg - xg * zb) / det;
34 let inv02 = (xg * yb - xb * yg) / det;
35 let inv10 = (yb * zr - yr * zb) / det;
36 let inv11 = (xr * zb - xb * zr) / det;
37 let inv12 = (xb * yr - xr * yb) / det;
38 let inv20 = (yr * zg - yg * zr) / det;
39 let inv21 = (xg * zr - xr * zg) / det;
40 let inv22 = (xr * yg - xg * yr) / det;
41
42 let [xw, yw, zw] = w_xyz;
43
44 let sr = inv00 * xw + inv01 * yw + inv02 * zw;
45 let sg = inv10 * xw + inv11 * yw + inv12 * zw;
46 let sb = inv20 * xw + inv21 * yw + inv22 * zw;
47
48 Mat3 {
49 col0: [sr * xr, sr * yr, sr * zr, 0.0],
50 col1: [sg * xg, sg * yg, sg * zg, 0.0],
51 col2: [sb * xb, sb * yb, sb * zb, 0.0],
52 }
53}
54
55pub trait Primaries: 'static {
57 type Native: Illuminant<Observer = Cie1931>;
59
60 const R: [f32; 2];
62 const G: [f32; 2];
64 const B: [f32; 2];
66
67 const TO_XYZ_NATIVE: Mat3;
69 const FROM_XYZ_NATIVE: Mat3;
71
72 const LUMA_WEIGHTS: [f32; 3] = [
74 Self::TO_XYZ_NATIVE.col0[1],
75 Self::TO_XYZ_NATIVE.col1[1],
76 Self::TO_XYZ_NATIVE.col2[1],
77 ];
78}
79
80#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
87pub struct Srgb;
88
89const SRGB_TO_XYZ: Mat3 = derive_rgb_to_xyz(
90 [0.6400, 0.3300],
91 [0.3000, 0.6000],
92 [0.1500, 0.0600],
93 <D65<Cie1931> as Illuminant>::WHITE_POINT_XYZ,
94);
95const SRGB_FROM_XYZ: Mat3 = Mat3::invert(&SRGB_TO_XYZ);
96
97impl Primaries for Srgb {
98 type Native = D65;
99 const R: [f32; 2] = [0.6400, 0.3300];
100 const G: [f32; 2] = [0.3000, 0.6000];
101 const B: [f32; 2] = [0.1500, 0.0600];
102 const TO_XYZ_NATIVE: Mat3 = SRGB_TO_XYZ;
103 const FROM_XYZ_NATIVE: Mat3 = SRGB_FROM_XYZ;
104}
105
106#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
110pub struct P3;
111
112const P3_TO_XYZ: Mat3 = derive_rgb_to_xyz(
113 [0.6800, 0.3200],
114 [0.2650, 0.6900],
115 [0.1500, 0.0600],
116 <D65<Cie1931> as Illuminant>::WHITE_POINT_XYZ,
117);
118const P3_FROM_XYZ: Mat3 = Mat3::invert(&P3_TO_XYZ);
119
120impl Primaries for P3 {
121 type Native = D65;
122 const R: [f32; 2] = [0.6800, 0.3200];
123 const G: [f32; 2] = [0.2650, 0.6900];
124 const B: [f32; 2] = [0.1500, 0.0600];
125 const TO_XYZ_NATIVE: Mat3 = P3_TO_XYZ;
126 const FROM_XYZ_NATIVE: Mat3 = P3_FROM_XYZ;
127}
128
129#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
134pub struct Rec2020;
135
136const REC2020_TO_XYZ: Mat3 = derive_rgb_to_xyz(
137 [0.7080, 0.2920],
138 [0.1700, 0.7970],
139 [0.1310, 0.0460],
140 <D65<Cie1931> as Illuminant>::WHITE_POINT_XYZ,
141);
142const REC2020_FROM_XYZ: Mat3 = Mat3::invert(&REC2020_TO_XYZ);
143
144impl Primaries for Rec2020 {
145 type Native = D65;
146 const R: [f32; 2] = [0.7080, 0.2920];
147 const G: [f32; 2] = [0.1700, 0.7970];
148 const B: [f32; 2] = [0.1310, 0.0460];
149 const TO_XYZ_NATIVE: Mat3 = REC2020_TO_XYZ;
150 const FROM_XYZ_NATIVE: Mat3 = REC2020_FROM_XYZ;
151}
152
153#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
158pub struct AcesAp0;
159
160const AP0_TO_XYZ: Mat3 = derive_rgb_to_xyz(
161 [0.73470, 0.26530],
162 [0.00000, 1.00000],
163 [0.00010, -0.07700],
164 AcesWhitePoint::WHITE_POINT_XYZ,
165);
166const AP0_FROM_XYZ: Mat3 = Mat3::invert(&AP0_TO_XYZ);
167
168impl Primaries for AcesAp0 {
169 type Native = AcesWhitePoint;
170 const R: [f32; 2] = [0.73470, 0.26530];
171 const G: [f32; 2] = [0.00000, 1.00000];
172 const B: [f32; 2] = [0.00010, -0.07700];
173 const TO_XYZ_NATIVE: Mat3 = AP0_TO_XYZ;
174 const FROM_XYZ_NATIVE: Mat3 = AP0_FROM_XYZ;
175}
176
177#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
182pub struct AcesAp1;
183
184const AP1_TO_XYZ: Mat3 = derive_rgb_to_xyz(
185 [0.71300, 0.29300],
186 [0.16500, 0.83000],
187 [0.12800, 0.04400],
188 AcesWhitePoint::WHITE_POINT_XYZ,
189);
190const AP1_FROM_XYZ: Mat3 = Mat3::invert(&AP1_TO_XYZ);
191
192impl Primaries for AcesAp1 {
193 type Native = AcesWhitePoint;
194 const R: [f32; 2] = [0.71300, 0.29300];
195 const G: [f32; 2] = [0.16500, 0.83000];
196 const B: [f32; 2] = [0.12800, 0.04400];
197 const TO_XYZ_NATIVE: Mat3 = AP1_TO_XYZ;
198 const FROM_XYZ_NATIVE: Mat3 = AP1_FROM_XYZ;
199}
200
201#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
205pub struct ProPhoto;
206
207const PRO_PHOTO_TO_XYZ: Mat3 = derive_rgb_to_xyz(
208 [0.7347, 0.2653],
209 [0.1596, 0.8404],
210 [0.0366, 0.0001],
211 <D50<Cie1931> as Illuminant>::WHITE_POINT_XYZ,
212);
213const PRO_PHOTO_FROM_XYZ: Mat3 = Mat3::invert(&PRO_PHOTO_TO_XYZ);
214
215impl Primaries for ProPhoto {
216 type Native = D50;
217 const R: [f32; 2] = [0.7347, 0.2653];
218 const G: [f32; 2] = [0.1596, 0.8404];
219 const B: [f32; 2] = [0.0366, 0.0001];
220 const TO_XYZ_NATIVE: Mat3 = PRO_PHOTO_TO_XYZ;
221 const FROM_XYZ_NATIVE: Mat3 = PRO_PHOTO_FROM_XYZ;
222}
223
224#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
232pub struct DciP3;
233
234const DCI_P3_TO_XYZ: Mat3 = derive_rgb_to_xyz(
235 [0.6800, 0.3200],
236 [0.2650, 0.6900],
237 [0.1500, 0.0600],
238 DciWhite::WHITE_POINT_XYZ,
239);
240const DCI_P3_FROM_XYZ: Mat3 = Mat3::invert(&DCI_P3_TO_XYZ);
241
242impl Primaries for DciP3 {
243 type Native = DciWhite;
244 const R: [f32; 2] = [0.6800, 0.3200];
245 const G: [f32; 2] = [0.2650, 0.6900];
246 const B: [f32; 2] = [0.1500, 0.0600];
247 const TO_XYZ_NATIVE: Mat3 = DCI_P3_TO_XYZ;
248 const FROM_XYZ_NATIVE: Mat3 = DCI_P3_FROM_XYZ;
249}