Coverage Report

Created: 2026-09-01 07:45

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/rust/registry/src/index.crates.io-1949cf8c6b5b557f/exr-1.74.2/src/block/samples.rs
Line
Count
Source
1
//! Extract pixel samples from a block of pixel bytes.
2
3
use half::prelude::HalfFloatSliceExt;
4
5
use crate::prelude::*;
6
7
/// A single red, green, blue, or alpha value.
8
#[derive(Copy, Clone, Debug)]
9
pub enum Sample {
10
    /// A 16-bit float sample.
11
    F16(f16),
12
13
    /// A 32-bit float sample.
14
    F32(f32),
15
16
    /// An unsigned integer sample.
17
    U32(u32),
18
}
19
20
impl Sample {
21
    /// Create a sample containing a 32-bit float.
22
0
    pub fn f32(f32: f32) -> Self {
23
0
        Sample::F32(f32)
24
0
    }
25
26
    /// Create a sample containing a 16-bit float.
27
0
    pub fn f16(f16: f16) -> Self {
28
0
        Sample::F16(f16)
29
0
    }
30
31
    /// Create a sample containing a 32-bit integer.
32
0
    pub fn u32(u32: u32) -> Self {
33
0
        Sample::U32(u32)
34
0
    }
35
36
    /// Convert the sample to an f16 value. This has lower precision than f32.
37
    /// Note: An f32 can only represent integers up to `1024` as precise as a
38
    /// u32 could.
39
    #[inline]
40
0
    pub fn to_f16(self) -> f16 {
41
0
        match self {
42
0
            Sample::F16(sample) => sample,
43
0
            Sample::F32(sample) => f16::from_f32(sample),
44
0
            Sample::U32(sample) => f16::from_f32(sample as f32),
45
        }
46
0
    }
47
48
    /// Convert the sample to an f32 value.
49
    /// Note: An f32 can only represent integers up to `8388608` as precise as a
50
    /// u32 could.
51
    #[inline]
52
0
    pub fn to_f32(self) -> f32 {
53
0
        match self {
54
0
            Sample::F32(sample) => sample,
55
0
            Sample::F16(sample) => sample.to_f32(),
56
0
            Sample::U32(sample) => sample as f32,
57
        }
58
0
    }
59
60
    /// Convert the sample to a u32. Rounds floats to integers the same way that
61
    /// `3.1 as u32` does.
62
    #[inline]
63
0
    pub fn to_u32(self) -> u32 {
64
0
        match self {
65
0
            Sample::F16(sample) => sample.to_f32() as u32,
66
0
            Sample::F32(sample) => sample as u32,
67
0
            Sample::U32(sample) => sample,
68
        }
69
0
    }
70
71
    /// Is this value not a number?
72
    #[inline]
73
0
    pub fn is_nan(self) -> bool {
74
0
        match self {
75
0
            Sample::F16(value) => value.is_nan(),
76
0
            Sample::F32(value) => value.is_nan(),
77
0
            Sample::U32(_) => false,
78
        }
79
0
    }
80
81
    /// Is this value zero or negative zero?
82
    #[inline]
83
0
    pub fn is_zero(&self) -> bool {
84
0
        match *self {
85
0
            Sample::F16(value) => value == f16::ZERO || value == f16::NEG_ZERO,
86
0
            Sample::F32(value) => value == 0.0,
87
0
            Sample::U32(value) => value == 0,
88
        }
89
0
    }
90
}
91
92
impl PartialEq for Sample {
93
0
    fn eq(&self, other: &Self) -> bool {
94
0
        match *self {
95
0
            Self::F16(num) => num == other.to_f16(),
96
0
            Self::F32(num) => num == other.to_f32(),
97
0
            Self::U32(num) => num == other.to_u32(),
98
        }
99
0
    }
100
}
101
102
// this is not recommended because it may hide whether a color is transparent or
103
// opaque and might be undesired for depth channels
104
impl Default for Sample {
105
0
    fn default() -> Self {
106
0
        Self::F32(0.0)
107
0
    }
108
}
109
110
impl From<f16> for Sample {
111
    #[inline]
112
0
    fn from(f: f16) -> Self {
113
0
        Self::F16(f)
114
0
    }
115
}
116
impl From<f32> for Sample {
117
    #[inline]
118
0
    fn from(f: f32) -> Self {
119
0
        Self::F32(f)
120
0
    }
121
}
122
impl From<u32> for Sample {
123
    #[inline]
124
0
    fn from(f: u32) -> Self {
125
0
        Self::U32(f)
126
0
    }
127
}
128
129
impl<T> From<Option<T>> for Sample
130
where
131
    T: Into<Self> + Default,
132
{
133
    #[inline]
134
0
    fn from(num: Option<T>) -> Self {
135
0
        num.unwrap_or_default().into()
136
0
    }
137
}
138
139
impl From<Sample> for f16 {
140
    #[inline]
141
0
    fn from(s: Sample) -> Self {
142
0
        s.to_f16()
143
0
    }
144
}
145
impl From<Sample> for f32 {
146
    #[inline]
147
0
    fn from(s: Sample) -> Self {
148
0
        s.to_f32()
149
0
    }
150
}
151
impl From<Sample> for u32 {
152
    #[inline]
153
0
    fn from(s: Sample) -> Self {
154
0
        s.to_u32()
155
0
    }
156
}
157
158
/// Create an arbitrary sample type from one of the defined sample types.
159
///
160
/// Should be compiled to a no-op where the file contains the predicted sample
161
/// type. The slice functions should be optimized into a `memcpy` where there is
162
/// no conversion needed.
163
pub trait FromNativeSample: Sized + Copy + Default + 'static {
164
    /// Create this sample from a f16, trying to represent the same numerical
165
    /// value
166
    fn from_f16(value: f16) -> Self;
167
168
    /// Create this sample from a f32, trying to represent the same numerical
169
    /// value
170
    fn from_f32(value: f32) -> Self;
171
172
    /// Create this sample from a u32, trying to represent the same numerical
173
    /// value
174
    fn from_u32(value: u32) -> Self;
175
176
    /// Convert all values from the slice into this type.
177
    /// This function exists to allow the compiler to perform a vectorization
178
    /// optimization. Note that this default implementation will **not** be
179
    /// vectorized by the compiler automatically. For maximum performance
180
    /// you will need to override this function and implement it via an explicit batched conversion such as [`convert_to_f32_slice`](https://docs.rs/half/2.3.1/half/slice/trait.HalfFloatSliceExt.html#tymethod.convert_to_f32_slice)
181
    #[inline]
182
0
    fn from_f16s(from: &[f16], to: &mut [Self]) {
183
0
        assert_eq!(from.len(), to.len(), "slices must have the same length");
184
0
        for (from, to) in from.iter().zip(to.iter_mut()) {
185
0
            *to = Self::from_f16(*from);
186
0
        }
187
0
    }
188
189
    /// Convert all values from the slice into this type.
190
    /// This function exists to allow the compiler to perform a vectorization
191
    /// optimization. Note that this default implementation will be
192
    /// vectorized by the compiler automatically.
193
    #[inline]
194
77.6M
    fn from_f32s(from: &[f32], to: &mut [Self]) {
195
77.6M
        assert_eq!(from.len(), to.len(), "slices must have the same length");
196
290M
        for (from, to) in from.iter().zip(to.iter_mut()) {
197
290M
            *to = Self::from_f32(*from);
198
290M
        }
199
77.6M
    }
<f32 as exr::block::samples::FromNativeSample>::from_f32s
Line
Count
Source
194
77.6M
    fn from_f32s(from: &[f32], to: &mut [Self]) {
195
77.6M
        assert_eq!(from.len(), to.len(), "slices must have the same length");
196
290M
        for (from, to) in from.iter().zip(to.iter_mut()) {
197
290M
            *to = Self::from_f32(*from);
198
290M
        }
199
77.6M
    }
Unexecuted instantiation: <_ as exr::block::samples::FromNativeSample>::from_f32s
200
201
    /// Convert all values from the slice into this type.
202
    /// This function exists to allow the compiler to perform a vectorization
203
    /// optimization. Note that this default implementation will be
204
    /// vectorized by the compiler automatically, provided that the CPU
205
    /// supports the necessary conversion instructions. For example,
206
    /// `x86_64` lacks the instructions to convert `u32` to floats,
207
    /// so this will inevitably be slow on `x86_64`.
208
    #[inline]
209
72.2k
    fn from_u32s(from: &[u32], to: &mut [Self]) {
210
72.2k
        assert_eq!(from.len(), to.len(), "slices must have the same length");
211
1.14M
        for (from, to) in from.iter().zip(to.iter_mut()) {
212
1.14M
            *to = Self::from_u32(*from);
213
1.14M
        }
214
72.2k
    }
<f32 as exr::block::samples::FromNativeSample>::from_u32s
Line
Count
Source
209
72.2k
    fn from_u32s(from: &[u32], to: &mut [Self]) {
210
72.2k
        assert_eq!(from.len(), to.len(), "slices must have the same length");
211
1.14M
        for (from, to) in from.iter().zip(to.iter_mut()) {
212
1.14M
            *to = Self::from_u32(*from);
213
1.14M
        }
214
72.2k
    }
Unexecuted instantiation: <_ as exr::block::samples::FromNativeSample>::from_u32s
215
}
216
217
// TODO haven't i implemented this exact behaviour already somewhere else in
218
// this library...??
219
impl FromNativeSample for f32 {
220
    #[inline]
221
0
    fn from_f16(value: f16) -> Self {
222
0
        value.to_f32()
223
0
    }
224
225
    #[inline]
226
563M
    fn from_f32(value: f32) -> Self {
227
563M
        value
228
563M
    }
<f32 as exr::block::samples::FromNativeSample>::from_f32
Line
Count
Source
226
290M
    fn from_f32(value: f32) -> Self {
227
290M
        value
228
290M
    }
<f32 as exr::block::samples::FromNativeSample>::from_f32
Line
Count
Source
226
272M
    fn from_f32(value: f32) -> Self {
227
272M
        value
228
272M
    }
<f32 as exr::block::samples::FromNativeSample>::from_f32
Line
Count
Source
226
98
    fn from_f32(value: f32) -> Self {
227
98
        value
228
98
    }
229
230
    #[inline]
231
1.14M
    fn from_u32(value: u32) -> Self {
232
1.14M
        value as Self
233
1.14M
    }
<f32 as exr::block::samples::FromNativeSample>::from_u32
Line
Count
Source
231
1.14M
    fn from_u32(value: u32) -> Self {
232
1.14M
        value as Self
233
1.14M
    }
Unexecuted instantiation: <f32 as exr::block::samples::FromNativeSample>::from_u32
234
235
    // f16 is a custom type
236
    // so the compiler can not automatically vectorize the conversion
237
    // that's why we need to specialize this function
238
    #[inline]
239
128k
    fn from_f16s(from: &[f16], to: &mut [Self]) {
240
128k
        from.convert_to_f32_slice(to);
241
128k
    }
<f32 as exr::block::samples::FromNativeSample>::from_f16s
Line
Count
Source
239
128k
    fn from_f16s(from: &[f16], to: &mut [Self]) {
240
128k
        from.convert_to_f32_slice(to);
241
128k
    }
Unexecuted instantiation: <f32 as exr::block::samples::FromNativeSample>::from_f16s
Unexecuted instantiation: <f32 as exr::block::samples::FromNativeSample>::from_f16s
242
}
243
244
impl FromNativeSample for u32 {
245
    #[inline]
246
0
    fn from_f16(value: f16) -> Self {
247
0
        value.to_f32() as Self
248
0
    }
249
250
    #[inline]
251
0
    fn from_f32(value: f32) -> Self {
252
0
        value as Self
253
0
    }
254
255
    #[inline]
256
0
    fn from_u32(value: u32) -> Self {
257
0
        value
258
0
    }
259
}
260
261
impl FromNativeSample for f16 {
262
    #[inline]
263
0
    fn from_f16(value: f16) -> Self {
264
0
        value
265
0
    }
266
267
    #[inline]
268
0
    fn from_f32(value: f32) -> Self {
269
0
        Self::from_f32(value)
270
0
    }
271
272
    #[inline]
273
0
    fn from_u32(value: u32) -> Self {
274
0
        Self::from_f32(value as f32)
275
0
    }
276
277
    // f16 is a custom type
278
    // so the compiler can not automatically vectorize the conversion
279
    // that's why we need to specialize this function
280
    #[inline]
281
0
    fn from_f32s(from: &[f32], to: &mut [Self]) {
282
0
        to.convert_from_f32_slice(from);
283
0
    }
284
}
285
286
impl FromNativeSample for Sample {
287
    #[inline]
288
0
    fn from_f16(value: f16) -> Self {
289
0
        Self::from(value)
290
0
    }
291
292
    #[inline]
293
0
    fn from_f32(value: f32) -> Self {
294
0
        Self::from(value)
295
0
    }
296
297
    #[inline]
298
0
    fn from_u32(value: u32) -> Self {
299
0
        Self::from(value)
300
0
    }
301
}
302
303
/// Convert any type into one of the supported sample types.
304
/// Should be compiled to a no-op where the file contains the predicted sample
305
/// type
306
pub trait IntoNativeSample: Copy + Default + Sync + 'static {
307
    /// Convert this sample to an f16, trying to represent the same numerical
308
    /// value.
309
    fn to_f16(&self) -> f16;
310
311
    /// Convert this sample to an f32, trying to represent the same numerical
312
    /// value.
313
    fn to_f32(&self) -> f32;
314
315
    /// Convert this sample to an u16, trying to represent the same numerical
316
    /// value.
317
    fn to_u32(&self) -> u32;
318
}
319
320
impl IntoNativeSample for f16 {
321
0
    fn to_f16(&self) -> f16 {
322
0
        Self::from_f16(*self)
323
0
    }
324
325
0
    fn to_f32(&self) -> f32 {
326
0
        f32::from_f16(*self)
327
0
    }
328
329
0
    fn to_u32(&self) -> u32 {
330
0
        u32::from_f16(*self)
331
0
    }
332
}
333
334
impl IntoNativeSample for f32 {
335
0
    fn to_f16(&self) -> f16 {
336
0
        f16::from_f32(*self)
337
0
    }
338
339
272M
    fn to_f32(&self) -> f32 {
340
272M
        Self::from_f32(*self)
341
272M
    }
342
343
0
    fn to_u32(&self) -> u32 {
344
0
        u32::from_f32(*self)
345
0
    }
346
}
347
348
impl IntoNativeSample for u32 {
349
0
    fn to_f16(&self) -> f16 {
350
0
        f16::from_u32(*self)
351
0
    }
352
353
0
    fn to_f32(&self) -> f32 {
354
0
        f32::from_u32(*self)
355
0
    }
356
357
0
    fn to_u32(&self) -> u32 {
358
0
        Self::from_u32(*self)
359
0
    }
360
}
361
362
impl IntoNativeSample for Sample {
363
0
    fn to_f16(&self) -> f16 {
364
0
        Self::to_f16(*self)
365
0
    }
366
367
0
    fn to_f32(&self) -> f32 {
368
0
        Self::to_f32(*self)
369
0
    }
370
371
0
    fn to_u32(&self) -> u32 {
372
0
        Self::to_u32(*self)
373
0
    }
374
}