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 | pub fn f32(f32: f32) -> Self { Sample::F32(f32) }
|
25 |
|
26 | /// Create a sample containing a 16-bit float.
|
27 | pub fn f16(f16: f16) -> Self { Sample::F16(f16) }
|
28 |
|
29 | /// Create a sample containing a 32-bit integer.
|
30 | 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 | pub fn to_f16(self) -> f16 {
|
36 | match self {
|
37 | Sample::F16(sample) => sample,
|
38 | Sample::F32(sample) => f16::from_f32(sample),
|
39 | Sample::U32(sample) => f16::from_f32(sample as f32),
|
40 | }
|
41 | }
|
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 | pub fn to_f32(self) -> f32 {
|
47 | match self {
|
48 | Sample::F32(sample) => sample,
|
49 | Sample::F16(sample) => sample.to_f32(),
|
50 | Sample::U32(sample) => sample as f32,
|
51 | }
|
52 | }
|
53 |
|
54 | /// Convert the sample to a u32. Rounds floats to integers the same way that `3.1 as u32` does.
|
55 | #[inline ]
|
56 | pub fn to_u32(self) -> u32 {
|
57 | match self {
|
58 | Sample::F16(sample) => sample.to_f32() as u32,
|
59 | Sample::F32(sample) => sample as u32,
|
60 | Sample::U32(sample) => sample,
|
61 | }
|
62 | }
|
63 |
|
64 | /// Is this value not a number?
|
65 | #[inline ]
|
66 | pub fn is_nan(self) -> bool {
|
67 | match self {
|
68 | Sample::F16(value) => value.is_nan(),
|
69 | Sample::F32(value) => value.is_nan(),
|
70 | Sample::U32(_) => false,
|
71 | }
|
72 | }
|
73 |
|
74 | /// Is this value zero or negative zero?
|
75 | #[inline ]
|
76 | pub fn is_zero(&self) -> bool {
|
77 | match *self {
|
78 | Sample::F16(value) => value == f16::ZERO || value == f16::NEG_ZERO,
|
79 | Sample::F32(value) => value == 0.0,
|
80 | Sample::U32(value) => value == 0,
|
81 | }
|
82 | }
|
83 | }
|
84 |
|
85 | impl PartialEq for Sample {
|
86 | fn eq(&self, other: &Self) -> bool {
|
87 | match *self {
|
88 | Sample::F16(num: f16) => num == other.to_f16(),
|
89 | Sample::F32(num: f32) => num == other.to_f32(),
|
90 | Sample::U32(num: u32) => num == other.to_u32(),
|
91 | }
|
92 | }
|
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 | fn default() -> Self { Sample::F32(0.0) }
|
98 | }
|
99 |
|
100 | impl From<f16> for Sample { #[inline ] fn from(f: f16) -> Self { Sample::F16(f) } }
|
101 | impl From<f32> for Sample { #[inline ] fn from(f: f32) -> Self { Sample::F32(f) } }
|
102 | 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 | #[inline ] fn from(num: Option<T>) -> Self { num.unwrap_or_default().into() }
|
106 | }
|
107 |
|
108 |
|
109 | impl From<Sample> for f16 { #[inline ] fn from(s: Sample) -> Self { s.to_f16() } }
|
110 | impl From<Sample> for f32 { #[inline ] fn from(s: Sample) -> Self { s.to_f32() } }
|
111 | 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 | fn from_f16s(from: &[f16], to: &mut [Self]) {
|
135 | assert_eq!(from.len(), to.len(), "slices must have the same length" );
|
136 | for (from, to) in from.iter().zip(to.iter_mut()) {
|
137 | *to = Self::from_f16(*from);
|
138 | }
|
139 | }
|
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 | fn from_f32s(from: &[f32], to: &mut [Self]) {
|
146 | assert_eq!(from.len(), to.len(), "slices must have the same length" );
|
147 | for (from, to) in from.iter().zip(to.iter_mut()) {
|
148 | *to = Self::from_f32(*from);
|
149 | }
|
150 | }
|
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 | fn from_u32s(from: &[u32], to: &mut [Self]) {
|
160 | assert_eq!(from.len(), to.len(), "slices must have the same length" );
|
161 | for (from, to) in from.iter().zip(to.iter_mut()) {
|
162 | *to = Self::from_u32(*from);
|
163 | }
|
164 | }
|
165 | }
|
166 |
|
167 | // TODO haven't i implemented this exact behaviour already somewhere else in this library...??
|
168 | impl FromNativeSample for f32 {
|
169 | #[inline ] fn from_f16(value: f16) -> Self { value.to_f32() }
|
170 | #[inline ] fn from_f32(value: f32) -> Self { value }
|
171 | #[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 | fn from_f16s(from: &[f16], to: &mut [Self]) {
|
178 | from.convert_to_f32_slice(dst:to);
|
179 | }
|
180 | }
|
181 |
|
182 | impl FromNativeSample for u32 {
|
183 | #[inline ] fn from_f16(value: f16) -> Self { value.to_f32() as u32 }
|
184 | #[inline ] fn from_f32(value: f32) -> Self { value as u32 }
|
185 | #[inline ] fn from_u32(value: u32) -> Self { value }
|
186 | }
|
187 |
|
188 | impl FromNativeSample for f16 {
|
189 | #[inline ] fn from_f16(value: f16) -> Self { value }
|
190 | #[inline ] fn from_f32(value: f32) -> Self { f16::from_f32(value) }
|
191 | #[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 | fn from_f32s(from: &[f32], to: &mut [Self]) {
|
198 | to.convert_from_f32_slice(src:from)
|
199 | }
|
200 | }
|
201 |
|
202 | impl FromNativeSample for Sample {
|
203 | #[inline ] fn from_f16(value: f16) -> Self { Self::from(value) }
|
204 | #[inline ] fn from_f32(value: f32) -> Self { Self::from(value) }
|
205 | #[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 | fn to_f16(&self) -> f16 { f16::from_f16(*self) }
|
225 | fn to_f32(&self) -> f32 { f32::from_f16(*self) }
|
226 | fn to_u32(&self) -> u32 { u32::from_f16(*self) }
|
227 | }
|
228 |
|
229 | impl IntoNativeSample for f32 {
|
230 | fn to_f16(&self) -> f16 { f16::from_f32(*self) }
|
231 | fn to_f32(&self) -> f32 { f32::from_f32(*self) }
|
232 | fn to_u32(&self) -> u32 { u32::from_f32(*self) }
|
233 | }
|
234 |
|
235 | impl IntoNativeSample for u32 {
|
236 | fn to_f16(&self) -> f16 { f16::from_u32(*self) }
|
237 | fn to_f32(&self) -> f32 { f32::from_u32(*self) }
|
238 | fn to_u32(&self) -> u32 { u32::from_u32(*self) }
|
239 | }
|
240 |
|
241 | impl IntoNativeSample for Sample {
|
242 | fn to_f16(&self) -> f16 { Sample::to_f16(*self) }
|
243 | fn to_f32(&self) -> f32 { Sample::to_f32(*self) }
|
244 | fn to_u32(&self) -> u32 { Sample::to_u32(*self) }
|
245 | }
|
246 |
|
247 |
|
248 |
|
249 | |