/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 | | |