1use crate::vector::Vector;
2
3#[derive(Debug, Clone, Copy, PartialEq, Eq)]
5pub enum ColorPrimaries {
6 ST240,
8 BT709,
10 BT2020,
12}
13
14impl ColorPrimaries {
15 pub fn rgb_to_xyz_mat(self) -> &'static [[f32; 3]; 3] {
17 use ColorPrimaries::*;
18
19 match self {
20 ST240 => &generated_consts::ST240_RGB_TO_XYZ,
21 BT709 => &generated_consts::BT709_RGB_TO_XYZ,
22 BT2020 => &generated_consts::BT2020_RGB_TO_XYZ,
23 }
24 }
25
26 pub fn xyz_to_rgb_mat(self) -> &'static [[f32; 3]; 3] {
28 use ColorPrimaries::*;
29
30 match self {
31 ST240 => &generated_consts::ST240_XYZ_TO_RGB,
32 BT709 => &generated_consts::BT709_XYZ_TO_RGB,
33 BT2020 => &generated_consts::BT2020_XYZ_TO_RGB,
34 }
35 }
36}
37
38#[inline(always)]
39pub(crate) unsafe fn rgb_to_xyz<V: Vector>(fw: &[[f32; 3]; 3], r: V, g: V, b: V) -> [V; 3] {
40 let x = r
41 .vmulf(fw[0][0])
42 .vadd(g.vmulf(fw[1][0]))
43 .vadd(b.vmulf(fw[2][0]));
44 let y = r
45 .vmulf(fw[0][1])
46 .vadd(g.vmulf(fw[1][1]))
47 .vadd(b.vmulf(fw[2][1]));
48 let z = r
49 .vmulf(fw[0][2])
50 .vadd(g.vmulf(fw[1][2]))
51 .vadd(b.vmulf(fw[2][2]));
52
53 [x, y, z]
54}
55
56#[inline(always)]
57pub(crate) unsafe fn xyz_to_rgb<V: Vector>(bw: &[[f32; 3]; 3], x: V, y: V, z: V) -> [V; 3] {
58 let r = x
59 .vmulf(bw[0][0])
60 .vadd(y.vmulf(bw[1][0]))
61 .vadd(z.vmulf(bw[2][0]));
62 let g = x
63 .vmulf(bw[0][1])
64 .vadd(y.vmulf(bw[1][1]))
65 .vadd(z.vmulf(bw[2][1]));
66 let b = x
67 .vmulf(bw[0][2])
68 .vadd(y.vmulf(bw[1][2]))
69 .vadd(z.vmulf(bw[2][2]));
70
71 [r, g, b]
72}
73
74mod generated_consts {
75 pub(super) const ST240_RGB_TO_XYZ: [[f32; 3]; 3] = [
76 [0.39031416, 0.20383073, 0.025401404],
77 [0.3700937, 0.71034116, 0.11341577],
78 [0.19004808, 0.08582816, 0.95024043],
79 ];
80 pub(super) const ST240_XYZ_TO_RGB: [[f32; 3]; 3] = [
81 [3.506003, -1.0092703, 0.026740361],
82 [-1.7397907, 1.9292052, -0.18375263],
83 [-0.5440582, 0.027603257, 1.0636142],
84 ];
85
86 pub(super) const BT709_RGB_TO_XYZ: [[f32; 3]; 3] = [
87 [0.41239083, 0.21263903, 0.01933082],
88 [0.35758436, 0.7151687, 0.11919474],
89 [0.1804808, 0.07219231, 0.95053214],
90 ];
91 pub(super) const BT709_XYZ_TO_RGB: [[f32; 3]; 3] = [
92 [3.2409694, -0.9692435, 0.055630032],
93 [-1.537383, 1.8759671, -0.20397685],
94 [-0.49861073, 0.04155508, 1.0569714],
95 ];
96
97 pub(super) const BT2020_RGB_TO_XYZ: [[f32; 3]; 3] = [
98 [0.63695806, 0.2627002, 0.0],
99 [0.14461692, 0.6779981, 0.028072689],
100 [0.16888095, 0.05930171, 1.060985],
101 ];
102 pub(super) const BT2020_XYZ_TO_RGB: [[f32; 3]; 3] = [
103 [1.7166512, -0.66668427, 0.017639855],
104 [-0.3556708, 1.6164812, -0.04277061],
105 [-0.25336626, 0.01576853, 0.9421032],
106 ];
107}
108
109#[cfg(test)]
110mod generate_matrices {
111 use super::ColorPrimaries::{self, *};
112 use nalgebra::{Matrix3, Vector3};
113
114 fn xy(x: f32, y: f32) -> Vector3<f32> {
115 Vector3::new(x, y, 1.0 - x - y)
116 }
117
118 fn xyz_rgbw(primaries: ColorPrimaries) -> [Vector3<f32>; 4] {
119 match primaries {
120 ST240 => [
121 xy(0.63, 0.3290),
122 xy(0.31, 0.595),
123 xy(0.155, 0.07),
124 xy(0.3127, 0.3290),
125 ],
126 BT709 => [
127 xy(0.64, 0.33),
128 xy(0.3, 0.6),
129 xy(0.15, 0.06),
130 xy(0.3127, 0.3290),
131 ],
132 BT2020 => [
133 xy(0.708, 0.292),
134 xy(0.170, 0.797),
135 xy(0.131, 0.046),
136 xy(0.3127, 0.3290),
137 ],
138 }
139 }
140
141 fn rgb_to_xyz_mat(primaries: ColorPrimaries) -> Matrix3<f32> {
142 let [r, g, b, mut w] = xyz_rgbw(primaries);
143
144 let y = w.y;
145 w.x *= 1.0 / y;
146 w.y *= 1.0 / y;
147 w.z *= 1.0 / y;
148
149 #[rustfmt::skip]
150 let m = Matrix3::new(
151 r.x, g.x, b.x,
152 r.y, g.y, b.y,
153 r.z, g.z, b.z
154 );
155
156 let s = m.try_inverse().unwrap() * (Vector3::new(w.x, w.y, w.z));
157
158 #[rustfmt::skip]
159 let s = Matrix3::new(
160 s.x, 0.0, 0.0,
161 0.0, s.y, 0.0,
162 0.0, 0.0, s.z
163 );
164
165 m * s
166 }
167
168 #[test]
169 #[ignore]
170 fn run() {
171 let primaries = [ST240, BT709, BT2020];
172
173 for primaries in primaries {
174 let rgb_to_xyz = rgb_to_xyz_mat(primaries);
175 let xyz_to_rgb = rgb_to_xyz.try_inverse().unwrap();
176
177 println!("pub(super) const {primaries:?}_RGB_TO_XYZ: [[f32; 3]; 3] = {rgb_to_xyz:?};");
178 println!("pub(super) const {primaries:?}_XYZ_TO_RGB: [[f32; 3]; 3] = {xyz_to_rgb:?};");
179 println!()
180 }
181 }
182}