1 | // Copyright © SixtyFPS GmbH <info@slint.dev> |
2 | // SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-Royalty-free-2.0 OR LicenseRef-Slint-Software-3.0 |
3 | |
4 | /// A Fixed point, represented with the T underlying type, and shifted by so many bits |
5 | #[derive (Default, Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)] |
6 | pub struct Fixed<T, const SHIFT: usize>(pub T); |
7 | |
8 | impl< |
9 | T: Copy |
10 | + core::ops::Shl<usize, Output = T> |
11 | + core::ops::Shr<usize, Output = T> |
12 | + core::ops::Div<Output = T> |
13 | + core::ops::Add<Output = T> |
14 | + core::ops::Rem<Output = T>, |
15 | const SHIFT: usize, |
16 | > Fixed<T, SHIFT> |
17 | { |
18 | /// Create a fixed point from an integer value |
19 | #[inline (always)] |
20 | pub fn from_integer(value: T) -> Self { |
21 | Self(value << SHIFT) |
22 | } |
23 | |
24 | /// Get the integer part of the fixed point value |
25 | #[inline (always)] |
26 | pub fn truncate(self) -> T { |
27 | self.0 >> SHIFT |
28 | } |
29 | |
30 | /// Return the fractional part of the fixed point value |
31 | #[inline (always)] |
32 | pub fn fract(self) -> u8 |
33 | where |
34 | T: num_traits::AsPrimitive<u8>, |
35 | { |
36 | if SHIFT < 8 { |
37 | (self.0 >> (SHIFT - 8)).as_() |
38 | } else { |
39 | (self.0 << (8 - SHIFT)).as_() |
40 | } |
41 | } |
42 | |
43 | #[inline (always)] |
44 | pub fn from_fixed< |
45 | T2: core::ops::Shl<usize, Output = T2> + core::ops::Shr<usize, Output = T2> + Into<T>, |
46 | const SHIFT2: usize, |
47 | >( |
48 | value: Fixed<T2, SHIFT2>, |
49 | ) -> Self { |
50 | if SHIFT > SHIFT2 { |
51 | let s: T = value.0.into(); |
52 | Self(s << (SHIFT - SHIFT2)) |
53 | } else { |
54 | Self((value.0 >> (SHIFT2 - SHIFT)).into()) |
55 | } |
56 | } |
57 | #[inline (always)] |
58 | pub fn try_from_fixed< |
59 | T2: core::ops::Shl<usize, Output = T2> + core::ops::Shr<usize, Output = T2> + TryInto<T>, |
60 | const SHIFT2: usize, |
61 | >( |
62 | value: Fixed<T2, SHIFT2>, |
63 | ) -> Result<Self, T2::Error> { |
64 | Ok(if SHIFT > SHIFT2 { |
65 | let s: T = value.0.try_into()?; |
66 | Self(s << (SHIFT - SHIFT2)) |
67 | } else { |
68 | Self((value.0 >> (SHIFT2 - SHIFT)).try_into()?) |
69 | }) |
70 | } |
71 | |
72 | #[inline (always)] |
73 | pub fn from_fraction(numerator: T, denominator: T) -> Self { |
74 | Self((numerator << SHIFT) / denominator) |
75 | } |
76 | |
77 | #[inline (always)] |
78 | pub(crate) fn from_f32(value: f32) -> Option<Self> |
79 | where |
80 | T: num_traits::FromPrimitive, |
81 | { |
82 | Some(Self(T::from_f32(value * (1 << SHIFT) as f32)?)) |
83 | } |
84 | } |
85 | |
86 | impl<T: core::ops::Add<Output = T>, const SHIFT: usize> core::ops::Add for Fixed<T, SHIFT> { |
87 | type Output = Self; |
88 | #[inline (always)] |
89 | fn add(self, rhs: Self) -> Self::Output { |
90 | Self(self.0.add(rhs.0)) |
91 | } |
92 | } |
93 | |
94 | impl<T: core::ops::Sub<Output = T>, const SHIFT: usize> core::ops::Sub for Fixed<T, SHIFT> { |
95 | type Output = Self; |
96 | #[inline (always)] |
97 | fn sub(self, rhs: Self) -> Self::Output { |
98 | Self(self.0.sub(rhs.0)) |
99 | } |
100 | } |
101 | |
102 | impl<T: core::ops::AddAssign, const SHIFT: usize> core::ops::AddAssign for Fixed<T, SHIFT> { |
103 | #[inline (always)] |
104 | fn add_assign(&mut self, rhs: Self) { |
105 | self.0.add_assign(rhs.0) |
106 | } |
107 | } |
108 | |
109 | impl<T: core::ops::SubAssign, const SHIFT: usize> core::ops::SubAssign for Fixed<T, SHIFT> { |
110 | #[inline (always)] |
111 | fn sub_assign(&mut self, rhs: Self) { |
112 | self.0.sub_assign(rhs.0) |
113 | } |
114 | } |
115 | |
116 | impl<T: core::ops::Mul<Output = T>, const SHIFT: usize> core::ops::Mul<T> for Fixed<T, SHIFT> { |
117 | type Output = Self; |
118 | #[inline (always)] |
119 | fn mul(self, rhs: T) -> Self::Output { |
120 | Self(self.0.mul(rhs)) |
121 | } |
122 | } |
123 | |
124 | impl<T: core::ops::Mul<Output = T>, const SHIFT: usize> core::ops::Mul<Fixed<T, SHIFT>> |
125 | for Fixed<T, SHIFT> |
126 | where |
127 | T: TryFrom<i64> + Into<i64>, |
128 | <T as TryFrom<i64>>::Error: core::fmt::Debug, |
129 | { |
130 | type Output = Self; |
131 | fn mul(self, rhs: Fixed<T, SHIFT>) -> Self::Output { |
132 | let lhs_i64: i64 = self.0.into(); |
133 | let rhs_i64: i64 = rhs.0.into(); |
134 | Self(T::try_from((lhs_i64 * rhs_i64) >> SHIFT).expect(msg:"attempt to multiply with overflow" )) |
135 | } |
136 | } |
137 | |
138 | impl<T: core::ops::Neg<Output = T>, const SHIFT: usize> core::ops::Neg for Fixed<T, SHIFT> { |
139 | type Output = Self; |
140 | #[inline (always)] |
141 | fn neg(self) -> Self::Output { |
142 | Self(-self.0) |
143 | } |
144 | } |
145 | |
146 | impl<T: core::ops::Div<Output = T>, const SHIFT: usize> core::ops::Div for Fixed<T, SHIFT> { |
147 | type Output = T; |
148 | #[inline (always)] |
149 | fn div(self, rhs: Self) -> Self::Output { |
150 | self.0 / rhs.0 |
151 | } |
152 | } |
153 | |
154 | impl<T: core::ops::Rem<Output = T>, const SHIFT: usize> core::ops::Rem for Fixed<T, SHIFT> { |
155 | type Output = Self; |
156 | #[inline (always)] |
157 | fn rem(self, rhs: Self) -> Self::Output { |
158 | Self(self.0 % rhs.0) |
159 | } |
160 | } |
161 | |
162 | impl<T: core::ops::Div<Output = T>, const SHIFT: usize> core::ops::Div<T> for Fixed<T, SHIFT> { |
163 | type Output = Self; |
164 | #[inline (always)] |
165 | fn div(self, rhs: T) -> Self::Output { |
166 | Self(self.0 / rhs) |
167 | } |
168 | } |
169 | |