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