Coverage Report

Created: 2025-11-24 07:30

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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
}