macaw/
fixed.rs

1#![allow(clippy::manual_range_contains)]
2#![allow(clippy::cast_lossless)]
3
4use core::ops::*;
5
6const U16_MAX: f32 = u16::MAX as f32;
7const U8_MAX: f32 = u8::MAX as f32;
8
9#[derive(Debug, Clone)]
10pub enum UNormError {
11    UnnormalizedFloat,
12}
13
14impl core::fmt::Display for UNormError {
15    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
16        write!(f, "{:?}", self)
17    }
18}
19
20#[cfg(not(target_arch = "spirv"))]
21impl std::error::Error for UNormError {}
22
23/// A 16-bit fixed point value in the range `[0.0..=1.0]` (inclusive).
24///
25/// Basic arithmetic operations are implemented. Multiplication of two
26/// `UNorm16` is always defined. However, addition, subtraction, and
27/// division can very easily overflow or underflow, so be careful!
28#[derive(Default, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
29#[cfg_attr(feature = "with_serde", derive(serde::Serialize, serde::Deserialize))]
30#[cfg_attr(feature = "with_serde", serde(transparent))]
31#[cfg_attr(feature = "with_speedy", derive(speedy::Readable, speedy::Writable))]
32#[cfg_attr(feature = "bytemuck", derive(bytemuck::Pod, bytemuck::Zeroable))]
33#[repr(transparent)]
34pub struct UNorm16(pub u16);
35
36impl UNorm16 {
37    #[inline]
38    pub fn new(x: f32) -> Result<Self, UNormError> {
39        if !x.is_nan() && x >= 0.0 && x <= 1.0 {
40            Ok(Self::new_unchecked(x))
41        } else {
42            Err(UNormError::UnnormalizedFloat)
43        }
44    }
45
46    #[inline]
47    pub fn new_clamped(x: f32) -> Self {
48        Self::new_unchecked(x.clamp(0.0, 1.0))
49    }
50
51    #[inline(always)]
52    pub fn new_unchecked(x: f32) -> Self {
53        Self((x * U16_MAX) as u16)
54    }
55
56    #[inline(always)]
57    pub fn to_f32(self) -> f32 {
58        self.0 as f32 / U16_MAX
59    }
60}
61
62impl Add<Self> for UNorm16 {
63    type Output = Self;
64    #[inline]
65    fn add(self, rhs: Self) -> Self::Output {
66        Self(self.0 + rhs.0)
67    }
68}
69
70impl AddAssign for UNorm16 {
71    #[inline]
72    fn add_assign(&mut self, rhs: Self) {
73        self.0 += rhs.0;
74    }
75}
76
77impl Sub<Self> for UNorm16 {
78    type Output = Self;
79    #[inline]
80    fn sub(self, rhs: Self) -> Self::Output {
81        Self(self.0 - rhs.0)
82    }
83}
84
85impl SubAssign for UNorm16 {
86    #[inline]
87    fn sub_assign(&mut self, rhs: Self) {
88        self.0 -= rhs.0;
89    }
90}
91
92impl Mul<Self> for UNorm16 {
93    type Output = Self;
94    #[inline]
95    fn mul(self, rhs: Self) -> Self::Output {
96        // numerator = numerator * numerator = self * rhs
97        // denominator = denominator * denominator = 2^16 * 2^16
98        // we want numerator / 2^16, so need to divide numerator by 2^16 (aka rsh 16)
99        // this can't overflow :)
100        let numerator: u32 = self.0 as u32 * rhs.0 as u32;
101        let output: u32 = numerator >> 16;
102        Self(output as u16)
103    }
104}
105
106impl MulAssign for UNorm16 {
107    #[inline]
108    fn mul_assign(&mut self, rhs: Self) {
109        *self = *self * rhs;
110    }
111}
112
113impl Div<Self> for UNorm16 {
114    type Output = Self;
115    #[inline]
116    fn div(self, rhs: Self) -> Self::Output {
117        // numerator = numerator / numerator = self / rhs
118        // denominator = denominator / denominator = 2^16 / 2^16 = 1
119        // we want numerator / 2^16 so need to multiply numerator by 2^16 (aka lsh 16)
120        // we do this before the division to preserve as much precision as we can.
121        // the result can overflow u16 very easily...
122        let output: u32 = ((self.0 as u32) << 16) / (rhs.0 as u32);
123        Self(output as u16)
124    }
125}
126
127impl DivAssign for UNorm16 {
128    #[inline]
129    fn div_assign(&mut self, rhs: Self) {
130        *self = *self / rhs;
131    }
132}
133
134#[cfg(not(target_arch = "spirv"))]
135impl core::fmt::Debug for UNorm16 {
136    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
137        write!(f, "{}", self.to_f32())?;
138        if f.alternate() {
139            write!(f, " ({}/{})", self.0, u16::MAX)?;
140        }
141        Ok(())
142    }
143}
144
145/// An 8-bit fixed point value in the range `[0.0..=1.0]` (inclusive).
146///
147/// Basic arithmetic operations are implemented. Multiplication of two
148/// `UNorm8` is always defined. However, addition, subtraction, and
149/// division can very easily overflow or underflow, so be careful!
150#[derive(Default, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
151#[cfg_attr(feature = "with_serde", derive(serde::Serialize, serde::Deserialize))]
152#[cfg_attr(feature = "with_serde", serde(transparent))]
153#[cfg_attr(feature = "with_speedy", derive(speedy::Readable, speedy::Writable))]
154#[cfg_attr(feature = "bytemuck", derive(bytemuck::Pod, bytemuck::Zeroable))]
155#[repr(transparent)]
156pub struct UNorm8(pub u8);
157
158impl UNorm8 {
159    #[inline]
160    pub fn new(x: f32) -> Result<Self, UNormError> {
161        if !x.is_nan() && x >= 0.0 && x <= 1.0 {
162            Ok(Self::new_unchecked(x))
163        } else {
164            Err(UNormError::UnnormalizedFloat)
165        }
166    }
167
168    #[inline]
169    pub fn new_clamped(x: f32) -> Self {
170        Self::new_unchecked(x.clamp(0.0, 1.0))
171    }
172
173    #[inline(always)]
174    pub fn new_unchecked(x: f32) -> Self {
175        Self((x * U8_MAX) as u8)
176    }
177
178    #[inline(always)]
179    pub fn to_f32(self) -> f32 {
180        self.0 as f32 / U8_MAX
181    }
182}
183
184impl Add<Self> for UNorm8 {
185    type Output = Self;
186    #[inline]
187    fn add(self, rhs: Self) -> Self::Output {
188        Self(self.0 + rhs.0)
189    }
190}
191
192impl AddAssign for UNorm8 {
193    #[inline]
194    fn add_assign(&mut self, rhs: Self) {
195        self.0 += rhs.0;
196    }
197}
198
199impl Sub<Self> for UNorm8 {
200    type Output = Self;
201    #[inline]
202    fn sub(self, rhs: Self) -> Self::Output {
203        Self(self.0 - rhs.0)
204    }
205}
206
207impl SubAssign for UNorm8 {
208    #[inline]
209    fn sub_assign(&mut self, rhs: Self) {
210        self.0 -= rhs.0;
211    }
212}
213
214impl Mul<Self> for UNorm8 {
215    type Output = Self;
216    #[inline]
217    fn mul(self, rhs: Self) -> Self::Output {
218        // numerator = numerator * numerator = self * rhs
219        // denominator = denominator * denominator = 2^8 * 2^8
220        // we want denominator = 2^8, so need to divide both by 2^8 (aka rsh 8)
221        // this can't overflow :)
222        let numerator: u32 = self.0 as u32 * rhs.0 as u32;
223        let output: u32 = numerator >> 8;
224        Self(output as u8)
225    }
226}
227
228impl MulAssign for UNorm8 {
229    #[inline]
230    fn mul_assign(&mut self, rhs: Self) {
231        *self = *self * rhs;
232    }
233}
234
235impl Div<Self> for UNorm8 {
236    type Output = Self;
237    #[inline]
238    fn div(self, rhs: Self) -> Self::Output {
239        // numerator = numerator / numerator = self / rhs
240        // denominator = denominator / denominator = 2^8 / 2^8 = 1
241        // we want denominator = 2^8 so need to multiply both by 2^8 (aka lsh 8)
242        // we do this before the division to preserve as much precision as we can.
243        // the result can overflow u8 very easily...
244        let output: u32 = ((self.0 as u32) << 8) / (rhs.0 as u32);
245        Self(output as u8)
246    }
247}
248
249impl DivAssign for UNorm8 {
250    #[inline]
251    fn div_assign(&mut self, rhs: Self) {
252        *self = *self / rhs;
253    }
254}
255
256#[cfg(not(target_arch = "spirv"))]
257impl core::fmt::Debug for UNorm8 {
258    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
259        write!(f, "{}", self.to_f32())?;
260        if f.alternate() {
261            write!(f, " ({}/{})", self.0, u8::MAX)?;
262        }
263        Ok(())
264    }
265}