Coverage Report

Created: 2025-07-18 06:49

/rust/registry/src/index.crates.io-6f17d22bba15001f/exr-1.73.0/src/block/samples.rs
Line
Count
Source (jump to first uncovered line)
1
//! Extract pixel samples from a block of pixel bytes.
2
3
use crate::prelude::*;
4
use half::prelude::HalfFloatSliceExt;
5
6
7
/// A single red, green, blue, or alpha value.
8
#[derive(Copy, Clone, Debug)]
9
pub enum Sample {
10
11
    /// A 16-bit float sample.
12
    F16(f16),
13
14
    /// A 32-bit float sample.
15
    F32(f32),
16
17
    /// An unsigned integer sample.
18
    U32(u32)
19
}
20
21
impl Sample {
22
23
    /// Create a sample containing a 32-bit float.
24
0
    pub fn f32(f32: f32) -> Self { Sample::F32(f32) }
25
26
    /// Create a sample containing a 16-bit float.
27
0
    pub fn f16(f16: f16) -> Self { Sample::F16(f16) }
28
29
    /// Create a sample containing a 32-bit integer.
30
0
    pub fn u32(u32: u32) -> Self { Sample::U32(u32) }
31
32
    /// Convert the sample to an f16 value. This has lower precision than f32.
33
    /// Note: An f32 can only represent integers up to `1024` as precise as a u32 could.
34
    #[inline]
35
0
    pub fn to_f16(self) -> f16 {
36
0
        match self {
37
0
            Sample::F16(sample) => sample,
38
0
            Sample::F32(sample) => f16::from_f32(sample),
39
0
            Sample::U32(sample) => f16::from_f32(sample as f32),
40
        }
41
0
    }
42
43
    /// Convert the sample to an f32 value.
44
    /// Note: An f32 can only represent integers up to `8388608` as precise as a u32 could.
45
    #[inline]
46
0
    pub fn to_f32(self) -> f32 {
47
0
        match self {
48
0
            Sample::F32(sample) => sample,
49
0
            Sample::F16(sample) => sample.to_f32(),
50
0
            Sample::U32(sample) => sample as f32,
51
        }
52
0
    }
53
54
    /// Convert the sample to a u32. Rounds floats to integers the same way that `3.1 as u32` does.
55
    #[inline]
56
0
    pub fn to_u32(self) -> u32 {
57
0
        match self {
58
0
            Sample::F16(sample) => sample.to_f32() as u32,
59
0
            Sample::F32(sample) => sample as u32,
60
0
            Sample::U32(sample) => sample,
61
        }
62
0
    }
63
64
    /// Is this value not a number?
65
    #[inline]
66
0
    pub fn is_nan(self) -> bool {
67
0
        match self {
68
0
            Sample::F16(value) => value.is_nan(),
69
0
            Sample::F32(value) => value.is_nan(),
70
0
            Sample::U32(_) => false,
71
        }
72
0
    }
73
74
    /// Is this value zero or negative zero?
75
    #[inline]
76
0
    pub fn is_zero(&self) -> bool {
77
0
        match *self {
78
0
            Sample::F16(value) => value == f16::ZERO || value == f16::NEG_ZERO,
79
0
            Sample::F32(value) => value == 0.0,
80
0
            Sample::U32(value) => value == 0,
81
        }
82
0
    }
83
}
84
85
impl PartialEq for Sample {
86
0
    fn eq(&self, other: &Self) -> bool {
87
0
        match *self {
88
0
            Sample::F16(num) => num == other.to_f16(),
89
0
            Sample::F32(num) => num == other.to_f32(),
90
0
            Sample::U32(num) => num == other.to_u32(),
91
        }
92
0
    }
93
}
94
95
// this is not recommended because it may hide whether a color is transparent or opaque and might be undesired for depth channels
96
impl Default for Sample {
97
0
    fn default() -> Self { Sample::F32(0.0) }
98
}
99
100
0
impl From<f16> for Sample { #[inline] fn from(f: f16) -> Self { Sample::F16(f) } }
101
0
impl From<f32> for Sample { #[inline] fn from(f: f32) -> Self { Sample::F32(f) } }
102
0
impl From<u32> for Sample { #[inline] fn from(f: u32) -> Self { Sample::U32(f) } }
103
104
impl<T> From<Option<T>> for Sample where T: Into<Sample> + Default {
105
0
    #[inline] fn from(num: Option<T>) -> Self { num.unwrap_or_default().into() }
106
}
107
108
109
0
impl From<Sample> for f16 { #[inline] fn from(s: Sample) -> Self { s.to_f16() } }
110
0
impl From<Sample> for f32 { #[inline] fn from(s: Sample) -> Self { s.to_f32() } }
111
0
impl From<Sample> for u32 { #[inline] fn from(s: Sample) -> Self { s.to_u32() } }
112
113
114
/// Create an arbitrary sample type from one of the defined sample types.
115
/// Should be compiled to a no-op where the file contains the predicted sample type.
116
/// The slice functions should be optimized into a `memcpy` where there is no conversion needed.
117
pub trait FromNativeSample: Sized + Copy + Default + 'static {
118
119
    /// Create this sample from a f16, trying to represent the same numerical value
120
    fn from_f16(value: f16) -> Self;
121
122
    /// Create this sample from a f32, trying to represent the same numerical value
123
    fn from_f32(value: f32) -> Self;
124
125
    /// Create this sample from a u32, trying to represent the same numerical value
126
    fn from_u32(value: u32) -> Self;
127
128
    /// Convert all values from the slice into this type.
129
    /// This function exists to allow the compiler to perform a vectorization optimization.
130
    /// Note that this default implementation will **not** be vectorized by the compiler automatically.
131
    /// For maximum performance you will need to override this function and implement it via
132
    /// 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)
133
    #[inline]
134
0
    fn from_f16s(from: &[f16], to: &mut [Self]) {
135
0
        assert_eq!(from.len(), to.len(), "slices must have the same length");
136
0
        for (from, to) in from.iter().zip(to.iter_mut()) {
137
0
            *to = Self::from_f16(*from);
138
0
        }
139
0
    }
140
141
    /// Convert all values from the slice into this type.
142
    /// This function exists to allow the compiler to perform a vectorization optimization.
143
    /// Note that this default implementation will be vectorized by the compiler automatically.
144
    #[inline]
145
1.06M
    fn from_f32s(from: &[f32], to: &mut [Self]) {
146
1.06M
        assert_eq!(from.len(), to.len(), "slices must have the same length");
147
16.9M
        for (from, to) in from.iter().zip(to.iter_mut()) {
148
16.9M
            *to = Self::from_f32(*from);
149
16.9M
        }
150
1.06M
    }
Unexecuted instantiation: <f32 as exr::block::samples::FromNativeSample>::from_f32s
Unexecuted instantiation: <_ as exr::block::samples::FromNativeSample>::from_f32s
Unexecuted instantiation: <f32 as exr::block::samples::FromNativeSample>::from_f32s
Unexecuted instantiation: <f32 as exr::block::samples::FromNativeSample>::from_f32s
Unexecuted instantiation: <f32 as exr::block::samples::FromNativeSample>::from_f32s
Unexecuted instantiation: <f32 as exr::block::samples::FromNativeSample>::from_f32s
Unexecuted instantiation: <f32 as exr::block::samples::FromNativeSample>::from_f32s
Unexecuted instantiation: <f32 as exr::block::samples::FromNativeSample>::from_f32s
Unexecuted instantiation: <f32 as exr::block::samples::FromNativeSample>::from_f32s
Unexecuted instantiation: <f32 as exr::block::samples::FromNativeSample>::from_f32s
Unexecuted instantiation: <f32 as exr::block::samples::FromNativeSample>::from_f32s
Unexecuted instantiation: <f32 as exr::block::samples::FromNativeSample>::from_f32s
<f32 as exr::block::samples::FromNativeSample>::from_f32s
Line
Count
Source
145
1.06M
    fn from_f32s(from: &[f32], to: &mut [Self]) {
146
1.06M
        assert_eq!(from.len(), to.len(), "slices must have the same length");
147
16.9M
        for (from, to) in from.iter().zip(to.iter_mut()) {
148
16.9M
            *to = Self::from_f32(*from);
149
16.9M
        }
150
1.06M
    }
151
152
    /// Convert all values from the slice into this type.
153
    /// This function exists to allow the compiler to perform a vectorization optimization.
154
    /// Note that this default implementation will be vectorized by the compiler automatically,
155
    /// provided that the CPU supports the necessary conversion instructions.
156
    /// For example, x86_64 lacks the instructions to convert `u32` to floats,
157
    /// so this will inevitably be slow on x86_64.
158
    #[inline]
159
42.6k
    fn from_u32s(from: &[u32], to: &mut [Self]) {
160
42.6k
        assert_eq!(from.len(), to.len(), "slices must have the same length");
161
681k
        for (from, to) in from.iter().zip(to.iter_mut()) {
162
681k
            *to = Self::from_u32(*from);
163
681k
        }
164
42.6k
    }
Unexecuted instantiation: <f32 as exr::block::samples::FromNativeSample>::from_u32s
Unexecuted instantiation: <_ as exr::block::samples::FromNativeSample>::from_u32s
Unexecuted instantiation: <f32 as exr::block::samples::FromNativeSample>::from_u32s
Unexecuted instantiation: <f32 as exr::block::samples::FromNativeSample>::from_u32s
Unexecuted instantiation: <f32 as exr::block::samples::FromNativeSample>::from_u32s
Unexecuted instantiation: <f32 as exr::block::samples::FromNativeSample>::from_u32s
Unexecuted instantiation: <f32 as exr::block::samples::FromNativeSample>::from_u32s
Unexecuted instantiation: <f32 as exr::block::samples::FromNativeSample>::from_u32s
Unexecuted instantiation: <f32 as exr::block::samples::FromNativeSample>::from_u32s
Unexecuted instantiation: <f32 as exr::block::samples::FromNativeSample>::from_u32s
Unexecuted instantiation: <f32 as exr::block::samples::FromNativeSample>::from_u32s
Unexecuted instantiation: <f32 as exr::block::samples::FromNativeSample>::from_u32s
<f32 as exr::block::samples::FromNativeSample>::from_u32s
Line
Count
Source
159
42.6k
    fn from_u32s(from: &[u32], to: &mut [Self]) {
160
42.6k
        assert_eq!(from.len(), to.len(), "slices must have the same length");
161
681k
        for (from, to) in from.iter().zip(to.iter_mut()) {
162
681k
            *to = Self::from_u32(*from);
163
681k
        }
164
42.6k
    }
165
}
166
167
// TODO haven't i implemented this exact behaviour already somewhere else in this library...??
168
impl FromNativeSample for f32 {
169
0
    #[inline] fn from_f16(value: f16) -> Self { value.to_f32() }
170
31.7M
    #[inline] fn from_f32(value: f32) -> Self { value }
Unexecuted instantiation: <f32 as exr::block::samples::FromNativeSample>::from_f32
<f32 as exr::block::samples::FromNativeSample>::from_f32
Line
Count
Source
170
14.7M
    #[inline] fn from_f32(value: f32) -> Self { value }
Unexecuted instantiation: <f32 as exr::block::samples::FromNativeSample>::from_f32
Unexecuted instantiation: <f32 as exr::block::samples::FromNativeSample>::from_f32
Unexecuted instantiation: <f32 as exr::block::samples::FromNativeSample>::from_f32
Unexecuted instantiation: <f32 as exr::block::samples::FromNativeSample>::from_f32
Unexecuted instantiation: <f32 as exr::block::samples::FromNativeSample>::from_f32
Unexecuted instantiation: <f32 as exr::block::samples::FromNativeSample>::from_f32
Unexecuted instantiation: <f32 as exr::block::samples::FromNativeSample>::from_f32
Unexecuted instantiation: <f32 as exr::block::samples::FromNativeSample>::from_f32
Unexecuted instantiation: <f32 as exr::block::samples::FromNativeSample>::from_f32
<f32 as exr::block::samples::FromNativeSample>::from_f32
Line
Count
Source
170
1.53k
    #[inline] fn from_f32(value: f32) -> Self { value }
<f32 as exr::block::samples::FromNativeSample>::from_f32
Line
Count
Source
170
16.9M
    #[inline] fn from_f32(value: f32) -> Self { value }
171
681k
    #[inline] fn from_u32(value: u32) -> Self { value as f32 }
Unexecuted instantiation: <f32 as exr::block::samples::FromNativeSample>::from_u32
Unexecuted instantiation: <f32 as exr::block::samples::FromNativeSample>::from_u32
Unexecuted instantiation: <f32 as exr::block::samples::FromNativeSample>::from_u32
Unexecuted instantiation: <f32 as exr::block::samples::FromNativeSample>::from_u32
Unexecuted instantiation: <f32 as exr::block::samples::FromNativeSample>::from_u32
Unexecuted instantiation: <f32 as exr::block::samples::FromNativeSample>::from_u32
Unexecuted instantiation: <f32 as exr::block::samples::FromNativeSample>::from_u32
Unexecuted instantiation: <f32 as exr::block::samples::FromNativeSample>::from_u32
Unexecuted instantiation: <f32 as exr::block::samples::FromNativeSample>::from_u32
Unexecuted instantiation: <f32 as exr::block::samples::FromNativeSample>::from_u32
Unexecuted instantiation: <f32 as exr::block::samples::FromNativeSample>::from_u32
Unexecuted instantiation: <f32 as exr::block::samples::FromNativeSample>::from_u32
<f32 as exr::block::samples::FromNativeSample>::from_u32
Line
Count
Source
171
681k
    #[inline] fn from_u32(value: u32) -> Self { value as f32 }
172
173
    // f16 is a custom type
174
    // so the compiler can not automatically vectorize the conversion
175
    // that's why we need to specialize this function
176
    #[inline]
177
0
    fn from_f16s(from: &[f16], to: &mut [Self]) {
178
0
        from.convert_to_f32_slice(to);
179
0
    }
Unexecuted instantiation: <f32 as exr::block::samples::FromNativeSample>::from_f16s
Unexecuted instantiation: <f32 as exr::block::samples::FromNativeSample>::from_f16s
Unexecuted instantiation: <f32 as exr::block::samples::FromNativeSample>::from_f16s
Unexecuted instantiation: <f32 as exr::block::samples::FromNativeSample>::from_f16s
Unexecuted instantiation: <f32 as exr::block::samples::FromNativeSample>::from_f16s
Unexecuted instantiation: <f32 as exr::block::samples::FromNativeSample>::from_f16s
Unexecuted instantiation: <f32 as exr::block::samples::FromNativeSample>::from_f16s
Unexecuted instantiation: <f32 as exr::block::samples::FromNativeSample>::from_f16s
Unexecuted instantiation: <f32 as exr::block::samples::FromNativeSample>::from_f16s
Unexecuted instantiation: <f32 as exr::block::samples::FromNativeSample>::from_f16s
Unexecuted instantiation: <f32 as exr::block::samples::FromNativeSample>::from_f16s
Unexecuted instantiation: <f32 as exr::block::samples::FromNativeSample>::from_f16s
Unexecuted instantiation: <f32 as exr::block::samples::FromNativeSample>::from_f16s
180
}
181
182
impl FromNativeSample for u32 {
183
0
    #[inline] fn from_f16(value: f16) -> Self { value.to_f32() as u32 }
184
0
    #[inline] fn from_f32(value: f32) -> Self { value as u32 }
185
0
    #[inline] fn from_u32(value: u32) -> Self { value }
186
}
187
188
impl FromNativeSample for f16 {
189
0
    #[inline] fn from_f16(value: f16) -> Self { value }
190
0
    #[inline] fn from_f32(value: f32) -> Self { f16::from_f32(value) }
191
0
    #[inline] fn from_u32(value: u32) -> Self { f16::from_f32(value as f32) }
192
193
    // f16 is a custom type
194
    // so the compiler can not automatically vectorize the conversion
195
    // that's why we need to specialize this function
196
    #[inline]
197
0
    fn from_f32s(from: &[f32], to: &mut [Self]) {
198
0
        to.convert_from_f32_slice(from)
199
0
    }
200
}
201
202
impl FromNativeSample for Sample {
203
0
    #[inline] fn from_f16(value: f16) -> Self { Self::from(value) }
204
0
    #[inline] fn from_f32(value: f32) -> Self { Self::from(value) }
205
0
    #[inline] fn from_u32(value: u32) -> Self { Self::from(value) }
206
}
207
208
209
/// Convert any type into one of the supported sample types.
210
/// Should be compiled to a no-op where the file contains the predicted sample type
211
pub trait IntoNativeSample: Copy + Default + Sync + 'static {
212
213
    /// Convert this sample to an f16, trying to represent the same numerical value.
214
    fn to_f16(&self) -> f16;
215
216
    /// Convert this sample to an f32, trying to represent the same numerical value.
217
    fn to_f32(&self) -> f32;
218
219
    /// Convert this sample to an u16, trying to represent the same numerical value.
220
    fn to_u32(&self) -> u32;
221
}
222
223
impl IntoNativeSample for f16 {
224
0
    fn to_f16(&self) -> f16 { f16::from_f16(*self) }
225
0
    fn to_f32(&self) -> f32 { f32::from_f16(*self) }
226
0
    fn to_u32(&self) -> u32 { u32::from_f16(*self) }
227
}
228
229
impl IntoNativeSample for f32 {
230
0
    fn to_f16(&self) -> f16 { f16::from_f32(*self) }
231
14.7M
    fn to_f32(&self) -> f32 { f32::from_f32(*self) }
232
0
    fn to_u32(&self) -> u32 { u32::from_f32(*self) }
233
}
234
235
impl IntoNativeSample for u32 {
236
0
    fn to_f16(&self) -> f16 { f16::from_u32(*self) }
237
0
    fn to_f32(&self) -> f32 { f32::from_u32(*self) }
238
0
    fn to_u32(&self) -> u32 { u32::from_u32(*self) }
239
}
240
241
impl IntoNativeSample for Sample {
242
0
    fn to_f16(&self) -> f16 { Sample::to_f16(*self) }
243
0
    fn to_f32(&self) -> f32 { Sample::to_f32(*self) }
244
0
    fn to_u32(&self) -> u32 { Sample::to_u32(*self) }
245
}
246
247
248