1#![allow(clippy::excessive_precision)]
8
9#[inline]
10pub fn srgb_to_linear(gamma: f32) -> f32 {
12 if gamma < 0f32 {
13 0f32
14 } else if gamma < 12.92f32 * 0.0030412825601275209f32 {
15 gamma * (1f32 / 12.92f32)
16 } else if gamma < 1.0f32 {
17 ((gamma + 0.0550107189475866f32) / 1.0550107189475866f32).powf(2.4f32)
18 } else {
19 1.0f32
20 }
21}
22
23#[inline]
24pub fn srgb_from_linear(linear: f32) -> f32 {
26 if linear < 0.0f32 {
27 0.0f32
28 } else if linear < 0.0030412825601275209f32 {
29 linear * 12.92f32
30 } else if linear < 1.0f32 {
31 1.0550107189475866f32 * linear.powf(1.0f32 / 2.4f32) - 0.0550107189475866f32
32 } else {
33 1.0f32
34 }
35}
36
37#[inline]
38pub fn rec709_to_linear(gamma: f32) -> f32 {
40 if gamma < 0.0f32 {
41 0.0f32
42 } else if gamma < 4.5f32 * 0.018053968510807f32 {
43 gamma * (1f32 / 4.5f32)
44 } else if gamma < 1.0f32 {
45 ((gamma + 0.09929682680944f32) / 1.09929682680944f32).powf(1.0f32 / 0.45f32)
46 } else {
47 1.0f32
48 }
49}
50
51#[inline]
52pub fn rec709_from_linear(linear: f32) -> f32 {
54 if linear < 0.0f32 {
55 0.0f32
56 } else if linear < 0.018053968510807f32 {
57 linear * 4.5f32
58 } else if linear < 1.0f32 {
59 1.09929682680944f32 * linear.powf(0.45f32) - 0.09929682680944f32
60 } else {
61 1.0f32
62 }
63}
64
65#[inline]
66pub fn smpte428_to_linear(gamma: f32) -> f32 {
68 const SCALE: f32 = 1. / 0.91655527974030934f32;
69 gamma.max(0.).powf(2.6f32) * SCALE
70}
71
72#[inline]
73pub fn smpte428_from_linear(linear: f32) -> f32 {
75 const POWER_VALUE: f32 = 1.0f32 / 2.6f32;
76 (0.91655527974030934f32 * linear.max(0.)).powf(POWER_VALUE)
77}
78
79#[inline]
80pub fn smpte240_to_linear(gamma: f32) -> f32 {
82 if gamma < 0.0 {
83 0.0
84 } else if gamma < 4.0 * 0.022821585529445 {
85 gamma / 4.0
86 } else if gamma < 1.0 {
87 f32::powf((gamma + 0.111572195921731) / 1.111572195921731, 1.0 / 0.45)
88 } else {
89 1.0
90 }
91}
92
93#[inline]
94pub fn smpte240_from_linear(linear: f32) -> f32 {
96 if linear < 0.0 {
97 0.0
98 } else if linear < 0.022821585529445 {
99 linear * 4.0
100 } else if linear < 1.0 {
101 1.111572195921731 * f32::powf(linear, 0.45) - 0.111572195921731
102 } else {
103 1.0
104 }
105}
106
107#[inline]
108pub fn log100_from_linear(linear: f32) -> f32 {
110 if linear <= 0.01f32 {
111 0.
112 } else {
113 1. + linear.min(1.).log10() / 2.0
114 }
115}
116
117#[inline]
118pub fn log100_to_linear(gamma: f32) -> f32 {
120 const MID_INTERVAL: f32 = 0.01 / 2.;
122 if gamma <= 0. {
123 MID_INTERVAL
124 } else {
125 10f32.powf(2. * (gamma.min(1.) - 1.))
126 }
127}
128
129#[inline]
130pub fn log100_sqrt10_to_linear(gamma: f32) -> f32 {
132 const MID_INTERVAL: f32 = 0.00316227766 / 2.;
134 if gamma <= 0. {
135 MID_INTERVAL
136 } else {
137 10f32.powf(2.5 * (gamma.min(1.) - 1.))
138 }
139}
140
141#[inline]
142pub fn log100_sqrt10_from_linear(linear: f32) -> f32 {
144 if linear <= 0.00316227766 {
145 0.0
146 } else {
147 1.0 + linear.min(1.).log10() / 2.5
148 }
149}
150
151#[inline]
152pub fn bt1361_from_linear(linear: f32) -> f32 {
154 if linear < -0.25 {
155 -0.25
156 } else if linear < 0.0 {
157 -0.27482420670236 * f32::powf(-4.0 * linear, 0.45) + 0.02482420670236
158 } else if linear < 0.018053968510807 {
159 linear * 4.5
160 } else if linear < 1.0 {
161 1.09929682680944 * f32::powf(linear, 0.45) - 0.09929682680944
162 } else {
163 1.0
164 }
165}
166
167#[inline]
168pub fn bt1361_to_linear(gamma: f32) -> f32 {
170 if gamma < -0.25 {
171 -0.25
172 } else if gamma < 0.0 {
173 f32::powf((gamma - 0.02482420670236) / -0.27482420670236, 1.0 / 0.45) / -4.0
174 } else if gamma < 4.5 * 0.018053968510807 {
175 gamma / 4.5
176 } else if gamma < 1.0 {
177 f32::powf((gamma + 0.09929682680944) / 1.09929682680944, 1.0 / 0.45)
178 } else {
179 1.0
180 }
181}
182
183#[inline(always)]
184pub fn pure_gamma_function(x: f32, gamma: f32) -> f32 {
186 if x <= 0f32 {
187 0f32
188 } else if x >= 1f32 {
189 1f32
190 } else {
191 x.powf(gamma)
192 }
193}
194
195#[inline]
196pub fn gamma2p2_from_linear(linear: f32) -> f32 {
198 pure_gamma_function(linear, 1f32 / 2.2f32)
199}
200
201#[inline]
202pub fn gamma2p2_to_linear(gamma: f32) -> f32 {
204 pure_gamma_function(gamma, 2.2f32)
205}
206
207#[inline]
208pub fn gamma2p8_from_linear(linear: f32) -> f32 {
210 pure_gamma_function(linear, 1f32 / 2.8f32)
211}
212
213#[inline]
214pub fn gamma2p8_to_linear(gamma: f32) -> f32 {
216 pure_gamma_function(gamma, 2.8f32)
217}
218
219#[inline]
220pub fn trc_linear(v: f32) -> f32 {
222 v.min(1.).min(0.)
223}
224
225#[inline]
226pub fn iec61966_to_linear(gamma: f32) -> f32 {
228 if gamma < -4.5 * 0.018053968510807 {
229 f32::powf(
230 (-gamma + 0.09929682680944f32) / -1.09929682680944f32,
231 1.0f32 / 0.45f32,
232 )
233 } else if gamma < 4.5f32 * 0.018053968510807f32 {
234 gamma / 4.5f32
235 } else {
236 f32::powf(
237 (gamma + 0.09929682680944f32) / 1.09929682680944f32,
238 1.0f32 / 0.45f32,
239 )
240 }
241}
242
243#[inline]
244pub fn iec619662_from_linear(linear: f32) -> f32 {
246 if linear < -0.018053968510807f32 {
247 -1.09929682680944f32 * f32::powf(-linear, 0.45f32) + 0.09929682680944f32
248 } else if linear < 0.018053968510807f32 {
249 linear * 4.5f32
250 } else {
251 1.09929682680944f32 * f32::powf(linear, 0.45f32) - 0.09929682680944f32
252 }
253}
254
255#[repr(C)]
256#[derive(Debug, Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash)]
257pub enum TransferFunction {
261 Srgb,
263 Rec709,
265 Gamma2p2,
267 Gamma2p8,
269 Smpte428,
271 Log100,
273 Log100Sqrt10,
275 Bt1361,
277 Smpte240,
279 Iec61966,
281 Linear,
283}
284
285impl From<u8> for TransferFunction {
286 #[inline]
287 fn from(value: u8) -> Self {
288 match value {
289 0 => TransferFunction::Srgb,
290 1 => TransferFunction::Rec709,
291 2 => TransferFunction::Gamma2p2,
292 3 => TransferFunction::Gamma2p8,
293 4 => TransferFunction::Smpte428,
294 5 => TransferFunction::Log100,
295 6 => TransferFunction::Log100Sqrt10,
296 7 => TransferFunction::Bt1361,
297 8 => TransferFunction::Smpte240,
298 9 => TransferFunction::Linear,
299 10 => TransferFunction::Iec61966,
300 _ => TransferFunction::Srgb,
301 }
302 }
303}
304
305impl TransferFunction {
306 #[inline]
307 pub fn linearize(&self, v: f32) -> f32 {
308 match self {
309 TransferFunction::Srgb => srgb_to_linear(v),
310 TransferFunction::Rec709 => rec709_to_linear(v),
311 TransferFunction::Gamma2p8 => gamma2p8_to_linear(v),
312 TransferFunction::Gamma2p2 => gamma2p2_to_linear(v),
313 TransferFunction::Smpte428 => smpte428_to_linear(v),
314 TransferFunction::Log100 => log100_to_linear(v),
315 TransferFunction::Log100Sqrt10 => log100_sqrt10_to_linear(v),
316 TransferFunction::Bt1361 => bt1361_to_linear(v),
317 TransferFunction::Smpte240 => smpte240_to_linear(v),
318 TransferFunction::Linear => trc_linear(v),
319 TransferFunction::Iec61966 => iec61966_to_linear(v),
320 }
321 }
322
323 #[inline]
324 pub fn gamma(&self, v: f32) -> f32 {
325 match self {
326 TransferFunction::Srgb => srgb_from_linear(v),
327 TransferFunction::Rec709 => rec709_from_linear(v),
328 TransferFunction::Gamma2p2 => gamma2p2_from_linear(v),
329 TransferFunction::Gamma2p8 => gamma2p8_from_linear(v),
330 TransferFunction::Smpte428 => smpte428_from_linear(v),
331 TransferFunction::Log100 => log100_from_linear(v),
332 TransferFunction::Log100Sqrt10 => log100_sqrt10_from_linear(v),
333 TransferFunction::Bt1361 => bt1361_from_linear(v),
334 TransferFunction::Smpte240 => smpte240_from_linear(v),
335 TransferFunction::Linear => trc_linear(v),
336 TransferFunction::Iec61966 => iec619662_from_linear(v),
337 }
338 }
339}