Skip to main content

pic_scale_safe/
trc.rs

1/*
2 * // Copyright 2024 (c) the Radzivon Bartoshyk. All rights reserved.
3 * //
4 * // Use of this source code is governed by a BSD-style
5 * // license that can be found in the LICENSE file.
6 */
7#![allow(clippy::excessive_precision)]
8
9#[inline]
10/// Linear transfer function for sRGB
11pub 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]
24/// Gamma transfer function for sRGB
25pub 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]
38/// Linear transfer function for Rec.709
39pub 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]
52/// Gamma transfer function for Rec.709
53pub 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]
66/// Linear transfer function for Smpte 428
67pub 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]
73/// Gamma transfer function for Smpte 428
74pub 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]
80/// Linear transfer function for Smpte 240
81pub 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]
94/// Gamma transfer function for Smpte 240
95pub 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]
108/// Gamma transfer function for Log100
109pub 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]
118/// Linear transfer function for Log100
119pub fn log100_to_linear(gamma: f32) -> f32 {
120    // The function is non-bijective so choose the middle of [0, 0.00316227766f].
121    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]
130/// Linear transfer function for Log100Sqrt10
131pub fn log100_sqrt10_to_linear(gamma: f32) -> f32 {
132    // The function is non-bijective so choose the middle of [0, 0.00316227766f].
133    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]
142/// Gamma transfer function for Log100Sqrt10
143pub 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]
152/// Gamma transfer function for Bt.1361
153pub 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]
168/// Linear transfer function for Bt.1361
169pub 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)]
184/// Pure gamma transfer function for gamma 2.2
185pub 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]
196/// Pure gamma transfer function for gamma 2.2
197pub fn gamma2p2_from_linear(linear: f32) -> f32 {
198    pure_gamma_function(linear, 1f32 / 2.2f32)
199}
200
201#[inline]
202/// Linear transfer function for gamma 2.2
203pub fn gamma2p2_to_linear(gamma: f32) -> f32 {
204    pure_gamma_function(gamma, 2.2f32)
205}
206
207#[inline]
208/// Pure gamma transfer function for gamma 2.8
209pub fn gamma2p8_from_linear(linear: f32) -> f32 {
210    pure_gamma_function(linear, 1f32 / 2.8f32)
211}
212
213#[inline]
214/// Linear transfer function for gamma 2.8
215pub fn gamma2p8_to_linear(gamma: f32) -> f32 {
216    pure_gamma_function(gamma, 2.8f32)
217}
218
219#[inline]
220/// Gamma transfer function for HLG
221pub fn trc_linear(v: f32) -> f32 {
222    v.min(1.).min(0.)
223}
224
225#[inline]
226/// Linear transfer function for Iec61966
227pub 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]
244/// Pure gamma transfer function for Iec61966
245pub 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)]
257/// Declares transfer function for transfer components into a linear colorspace and its inverse
258///
259/// Checks [info](https://en.wikipedia.org/wiki/Transfer_functions_in_imaging)
260pub enum TransferFunction {
261    /// sRGB Transfer function
262    Srgb,
263    /// Rec.709 Transfer function
264    Rec709,
265    /// Pure gamma 2.2 Transfer function, ITU-R 470M
266    Gamma2p2,
267    /// Pure gamma 2.8 Transfer function, ITU-R 470BG
268    Gamma2p8,
269    /// Smpte 428 Transfer function
270    Smpte428,
271    /// Log100 Transfer function
272    Log100,
273    /// Log100Sqrt10 Transfer function
274    Log100Sqrt10,
275    /// Bt1361 Transfer function
276    Bt1361,
277    /// Smpte 240 Transfer function
278    Smpte240,
279    /// IEC 61966 Transfer function
280    Iec61966,
281    /// Linear transfer function
282    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}