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