ezk_image/color/
transfer.rs1use crate::vector::Vector;
2
3#[derive(Debug, Clone, Copy, PartialEq, Eq)]
5pub enum ColorTransfer {
6 Linear,
8 Gamma22,
10 Gamma28,
12 SRGB,
14 SDR,
16 BT2100PQ,
18 BT2100HLG,
20}
21
22impl ColorTransfer {
23 pub fn linear_to_scaled(&self, mut i: f32) -> f32 {
24 unsafe { self.linear_to_scaled_v(&mut [&mut i]) }
26
27 i
28 }
29
30 pub fn scaled_to_linear(&self, mut i: f32) -> f32 {
31 unsafe { self.scaled_to_linear_v(&mut [&mut i]) }
33
34 i
35 }
36
37 #[inline(always)]
38 pub(crate) unsafe fn linear_to_scaled_v<const N: usize, V: Vector>(&self, i: &mut [&mut V; N]) {
39 match self {
40 ColorTransfer::Linear => {}
41 ColorTransfer::Gamma22 => {
42 for v in i {
43 **v = gamma::linear_to_scaled::<22, V>(**v)
44 }
45 }
46 ColorTransfer::Gamma28 => {
47 for v in i {
48 **v = gamma::linear_to_scaled::<28, V>(**v)
49 }
50 }
51 ColorTransfer::SRGB => {
52 for v in i {
53 **v = srgb::linear_to_scaled(**v)
54 }
55 }
56 ColorTransfer::SDR => {
57 for v in i {
58 **v = sdr::linear_to_scaled(**v)
59 }
60 }
61 ColorTransfer::BT2100PQ => {
62 for v in i {
63 **v = bt2100_pq::linear_to_scaled(**v)
64 }
65 }
66 ColorTransfer::BT2100HLG => {
67 for v in i {
68 **v = bt2100_hlg::linear_to_scaled(**v)
69 }
70 }
71 }
72 }
73
74 #[inline(always)]
75 pub(crate) unsafe fn scaled_to_linear_v<const N: usize, V: Vector>(&self, i: &mut [&mut V; N]) {
76 match self {
77 ColorTransfer::Linear => {}
78 ColorTransfer::Gamma22 => {
79 for v in i {
80 **v = gamma::scaled_to_linear::<22, V>(**v)
81 }
82 }
83 ColorTransfer::Gamma28 => {
84 for v in i {
85 **v = gamma::scaled_to_linear::<28, V>(**v)
86 }
87 }
88 ColorTransfer::SRGB => {
89 for v in i {
90 **v = srgb::scaled_to_linear(**v)
91 }
92 }
93 ColorTransfer::SDR => {
94 for v in i {
95 **v = sdr::scaled_to_linear(**v)
96 }
97 }
98 ColorTransfer::BT2100PQ => {
99 for v in i {
100 **v = bt2100_pq::scaled_to_linear(**v)
101 }
102 }
103 ColorTransfer::BT2100HLG => {
104 for v in i {
105 **v = bt2100_hlg::scaled_to_linear(**v)
106 }
107 }
108 }
109 }
110}
111
112mod gamma {
113 use crate::vector::Vector;
114
115 #[inline(always)]
116 pub(super) unsafe fn linear_to_scaled<const GAMMA: u32, V: Vector>(i: V) -> V {
117 i.vpowf(1.0 / (GAMMA as f32 / 10.0))
118 }
119
120 #[inline(always)]
121 pub(super) unsafe fn scaled_to_linear<const GAMMA: u32, V: Vector>(i: V) -> V {
122 i.vpowf(GAMMA as f32 / 10.0)
123 }
124}
125
126mod srgb {
127 use crate::vector::Vector;
128
129 #[inline(always)]
130 pub(super) unsafe fn linear_to_scaled<V: Vector>(i: V) -> V {
131 let mask = i.lef(0.0031308);
132
133 let a = i.vmulf(12.92);
135
136 let b = V::splat(1.055).vmul(i.vpowf(1.0 / 2.4)).vsubf(0.055);
138
139 V::select(a, b, mask)
140 }
141
142 #[inline(always)]
143 pub(super) unsafe fn scaled_to_linear<V: Vector>(i: V) -> V {
144 let mask = i.lef(0.04045);
145
146 let a = i.vdivf(12.92);
148
149 let b = i.vaddf(0.055).vdivf(1.055).vpowf(2.4);
151
152 V::select(a, b, mask)
153 }
154}
155
156mod sdr {
157 use crate::vector::Vector;
158
159 #[inline(always)]
160 pub(crate) unsafe fn linear_to_scaled<V: Vector>(i: V) -> V {
161 let mask = i.lt(V::splat(0.018_053_97));
162
163 let a = V::splat(4.5).vmul(i);
165
166 let b = V::splat(1.099).vmul(i.vpowf(0.45)).vsubf(0.099);
168
169 V::select(a, b, mask)
170 }
171
172 #[inline(always)]
173 pub(crate) unsafe fn scaled_to_linear<V: Vector>(i: V) -> V {
174 let mask = i.ltf(0.081490956);
175
176 let a = i.vdivf(4.5);
178
179 let b = i.vaddf(0.0993).vdivf(1.099).vpowf(1.0 / 0.45);
181
182 V::select(a, b, mask)
183 }
184}
185
186pub(crate) mod bt2100_pq {
187 use crate::vector::Vector;
188
189 const M1: f32 = 0.15930176;
190 const M2: f32 = 78.84375;
191
192 const C1: f32 = 0.8359375;
193 const C2: f32 = 18.851563;
194 const C3: f32 = 18.6875;
195
196 const L: f32 = 10000.0;
197
198 #[inline(always)]
200 pub(crate) unsafe fn linear_to_scaled<V: Vector>(i: V) -> V {
201 let i = i.vmaxf(0.0);
203
204 let i = i.vdivf(L);
205 let ym1 = i.vpowf(M1);
206
207 let a = ym1.vmulf(C2).vaddf(C1);
208 let b = ym1.vmulf(C3).vaddf(1.0);
209
210 a.vdiv(b).vpowf(M2)
211 }
212
213 #[inline(always)]
215 pub(crate) unsafe fn scaled_to_linear<V: Vector>(i: V) -> V {
216 let i = i.vmaxf(0.0);
218
219 let epow1dm2 = i.vpowf(1.0 / M2);
220
221 let a = epow1dm2.vsubf(C1).vmaxf(0.0);
222 let b = V::splat(C2).vsub(epow1dm2.vmulf(C3));
223
224 a.vdiv(b).vpowf(1.0 / M1).vmulf(L)
225 }
226}
227
228pub(crate) mod bt2100_hlg {
229 use crate::vector::Vector;
230 use std::f32::consts::E;
231
232 const A: f32 = 0.178_832_77;
233 const B: f32 = 0.284_668_92;
234 const C: f32 = 0.559_910_7;
235
236 #[inline(always)]
237 pub(crate) unsafe fn linear_to_scaled<V: Vector>(i: V) -> V {
238 let i = i.vmaxf(0.0);
240
241 let mask = i.lef(1.0 / 12.0);
242
243 let a = i.vmulf(3.0).vsqrt();
245
246 let b = V::splat(A)
248 .vmul(V::splat(12.0).vmul(i).vsubf(B).vln())
249 .vaddf(C);
250
251 V::select(a, b, mask)
252 }
253
254 #[inline(always)]
255 pub(crate) unsafe fn scaled_to_linear<V: Vector>(i: V) -> V {
256 let i = i.vmaxf(0.0);
258
259 let mask = i.lef(0.5);
260
261 let a = i.vpowf(2.0).vdivf(3.0);
263
264 let b = V::splat(E).vpow(i.vsubf(C).vdivf(A)).vaddf(B).vdivf(12.0);
266
267 V::select(a, b, mask)
268 }
269}