swash/internal/
fixed.rs

1//! Minimal fixed point math types and functions used internally.
2
3use super::parse::FromBeData;
4use core::ops::{Add, AddAssign, Div, Mul, Neg, Sub};
5
6/// Fixed point value in 16.16 format.
7#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Default, Debug)]
8pub struct Fixed(pub i32);
9
10impl Fixed {
11    pub const MIN: Self = Self(0x80000000u32 as i32);
12    pub const MAX: Self = Self(0x7FFFFFFF);
13    pub const EPSILON: Self = Self(1);
14    pub const ZERO: Self = Self(0);
15    pub const ONE: Self = Self(0x10000);
16
17    pub const fn from_i32(x: i32) -> Self {
18        Self(x << 16)
19    }
20
21    pub fn from_f32(x: f32) -> Self {
22        Self((x * 65536. + 0.5) as i32)
23    }
24
25    pub fn from_f2dot14(x: i16) -> Self {
26        Self(x as i32 * 4)
27    }
28
29    pub fn round(self) -> Self {
30        Self(((self.0 + 0x8000) as u32 & 0xFFFF0000) as i32)
31    }
32
33    pub fn floor(self) -> Self {
34        Self((self.0 as u32 & 0xFFFF0000) as i32)
35    }
36
37    pub fn abs(self) -> Self {
38        Self(self.0.abs())
39    }
40
41    pub fn min(self, rhs: Self) -> Self {
42        Self(self.0.min(rhs.0))
43    }
44
45    pub fn max(self, rhs: Self) -> Self {
46        Self(self.0.max(rhs.0))
47    }
48
49    pub fn fract(self) -> Self {
50        Self(self.0 - self.floor().0)
51    }
52
53    pub fn to_i32(self) -> i32 {
54        (self.0 + 0x8000) >> 16
55    }
56
57    pub fn to_f32(self) -> f32 {
58        self.0 as f32 / 65536.
59    }
60
61    pub fn to_f2dot14(self) -> i16 {
62        ((self.0 + 2) >> 2) as i16
63    }
64}
65
66impl Add for Fixed {
67    type Output = Self;
68    #[inline(always)]
69    fn add(self, rhs: Self) -> Self {
70        Self((self.0 as u32).wrapping_add(rhs.0 as u32) as i32)
71    }
72}
73
74impl AddAssign for Fixed {
75    fn add_assign(&mut self, rhs: Self) {
76        self.0 = self.0.wrapping_add(rhs.0);
77    }
78}
79
80impl Sub for Fixed {
81    type Output = Self;
82    #[inline(always)]
83    fn sub(self, rhs: Self) -> Self {
84        Self((self.0 as u32).wrapping_sub(rhs.0 as u32) as i32)
85    }
86}
87
88impl Mul for Fixed {
89    type Output = Self;
90    #[inline(always)]
91    fn mul(self, rhs: Self) -> Self {
92        Self(mul(self.0, rhs.0))
93    }
94}
95
96impl Div for Fixed {
97    type Output = Self;
98    #[inline(always)]
99    fn div(self, rhs: Self) -> Self {
100        Self(div(self.0, rhs.0))
101    }
102}
103
104impl Div<i32> for Fixed {
105    type Output = Self;
106    #[inline(always)]
107    fn div(self, rhs: i32) -> Self {
108        Self(self.0 / rhs)
109    }
110}
111
112impl Neg for Fixed {
113    type Output = Self;
114    fn neg(self) -> Self {
115        Self(-self.0)
116    }
117}
118
119impl From<f32> for Fixed {
120    fn from(f: f32) -> Self {
121        Self::from_f32(f)
122    }
123}
124
125impl From<Fixed> for f32 {
126    fn from(f: Fixed) -> Self {
127        f.to_f32()
128    }
129}
130
131impl FromBeData for Fixed {
132    unsafe fn from_be_data_unchecked(data: &[u8], offset: usize) -> Self {
133        Self(i32::from_be_data_unchecked(data, offset))
134    }
135}
136
137/// Fixed point floor.
138pub fn floor(x: i32) -> i32 {
139    x & !63
140}
141
142/// Fixed point ceil.
143pub fn ceil(x: i32) -> i32 {
144    floor(x + 63)
145}
146
147/// Fixed point round.
148pub fn round(x: i32) -> i32 {
149    floor(x + 32)
150}
151
152/// Fixed point multiply.
153#[inline(always)]
154pub fn mul(a: i32, b: i32) -> i32 {
155    let ab = a as i64 * b as i64;
156    ((ab + 0x8000 - if ab < 0 { 1 } else { 0 }) >> 16) as i32
157}
158
159/// Fixed point divide.
160pub fn div(mut a: i32, mut b: i32) -> i32 {
161    let mut s = 1;
162    if a < 0 {
163        a = -a;
164        s = -1;
165    }
166    if b < 0 {
167        b = -b;
168        s = -s;
169    }
170    let q = if b == 0 {
171        0x7FFFFFFF
172    } else {
173        ((((a as u64) << 16) + ((b as u64) >> 1)) / (b as u64)) as u32
174    };
175    if s < 0 {
176        -(q as i32)
177    } else {
178        q as i32
179    }
180}
181
182/// Fixed point multiply/divide.
183pub fn muldiv(mut a: i32, mut b: i32, mut c: i32) -> i32 {
184    let mut s = 1;
185    if a < 0 {
186        a = -a;
187        s = -1;
188    }
189    if b < 0 {
190        b = -b;
191        s = -s;
192    }
193    if c < 0 {
194        c = -c;
195        s = -s;
196    }
197    let d = if c > 0 {
198        ((a as i64) * (b as i64) + ((c as i64) >> 1)) / c as i64
199    } else {
200        0x7FFFFFFF
201    };
202    if s < 0 {
203        -(d as i32)
204    } else {
205        d as i32
206    }
207}