Skip to main content

i_slint_renderer_software/
fixed.rs

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)]
6pub struct Fixed<T, const SHIFT: usize>(pub T);
7
8impl<
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 { (self.0 >> (SHIFT - 8)).as_() } else { (self.0 << (8 - SHIFT)).as_() }
37    }
38
39    #[inline(always)]
40    pub fn from_fixed<
41        T2: core::ops::Shl<usize, Output = T2> + core::ops::Shr<usize, Output = T2> + Into<T>,
42        const SHIFT2: usize,
43    >(
44        value: Fixed<T2, SHIFT2>,
45    ) -> Self {
46        if SHIFT > SHIFT2 {
47            let s: T = value.0.into();
48            Self(s << (SHIFT - SHIFT2))
49        } else {
50            Self((value.0 >> (SHIFT2 - SHIFT)).into())
51        }
52    }
53    #[inline(always)]
54    pub fn try_from_fixed<
55        T2: core::ops::Shl<usize, Output = T2> + core::ops::Shr<usize, Output = T2> + TryInto<T>,
56        const SHIFT2: usize,
57    >(
58        value: Fixed<T2, SHIFT2>,
59    ) -> Result<Self, T2::Error> {
60        Ok(if SHIFT > SHIFT2 {
61            let s: T = value.0.try_into()?;
62            Self(s << (SHIFT - SHIFT2))
63        } else {
64            Self((value.0 >> (SHIFT2 - SHIFT)).try_into()?)
65        })
66    }
67
68    #[inline(always)]
69    pub fn from_fraction(numerator: T, denominator: T) -> Self {
70        Self((numerator << SHIFT) / denominator)
71    }
72
73    #[inline(always)]
74    pub(crate) fn from_f32(value: f32) -> Option<Self>
75    where
76        T: num_traits::FromPrimitive,
77    {
78        Some(Self(T::from_f32(value * (1 << SHIFT) as f32)?))
79    }
80}
81
82impl<T: core::ops::Add<Output = T>, const SHIFT: usize> core::ops::Add for Fixed<T, SHIFT> {
83    type Output = Self;
84    #[inline(always)]
85    fn add(self, rhs: Self) -> Self::Output {
86        Self(self.0.add(rhs.0))
87    }
88}
89
90impl<T: core::ops::Sub<Output = T>, const SHIFT: usize> core::ops::Sub for Fixed<T, SHIFT> {
91    type Output = Self;
92    #[inline(always)]
93    fn sub(self, rhs: Self) -> Self::Output {
94        Self(self.0.sub(rhs.0))
95    }
96}
97
98impl<T: core::ops::AddAssign, const SHIFT: usize> core::ops::AddAssign for Fixed<T, SHIFT> {
99    #[inline(always)]
100    fn add_assign(&mut self, rhs: Self) {
101        self.0.add_assign(rhs.0)
102    }
103}
104
105impl<T: core::ops::SubAssign, const SHIFT: usize> core::ops::SubAssign for Fixed<T, SHIFT> {
106    #[inline(always)]
107    fn sub_assign(&mut self, rhs: Self) {
108        self.0.sub_assign(rhs.0)
109    }
110}
111
112impl<T: core::ops::Mul<Output = T>, const SHIFT: usize> core::ops::Mul<T> for Fixed<T, SHIFT> {
113    type Output = Self;
114    #[inline(always)]
115    fn mul(self, rhs: T) -> Self::Output {
116        Self(self.0.mul(rhs))
117    }
118}
119
120impl<T: core::ops::Mul<Output = T>, const SHIFT: usize> core::ops::Mul<Fixed<T, SHIFT>>
121    for Fixed<T, SHIFT>
122where
123    T: TryFrom<i64> + Into<i64>,
124    <T as TryFrom<i64>>::Error: core::fmt::Debug,
125{
126    type Output = Self;
127    fn mul(self, rhs: Fixed<T, SHIFT>) -> Self::Output {
128        let lhs_i64: i64 = self.0.into();
129        let rhs_i64: i64 = rhs.0.into();
130        Self(T::try_from((lhs_i64 * rhs_i64) >> SHIFT).expect("attempt to multiply with overflow"))
131    }
132}
133
134impl<T: core::ops::Neg<Output = T>, const SHIFT: usize> core::ops::Neg for Fixed<T, SHIFT> {
135    type Output = Self;
136    #[inline(always)]
137    fn neg(self) -> Self::Output {
138        Self(-self.0)
139    }
140}
141
142impl<T: core::ops::Div<Output = T>, const SHIFT: usize> core::ops::Div for Fixed<T, SHIFT> {
143    type Output = T;
144    #[inline(always)]
145    fn div(self, rhs: Self) -> Self::Output {
146        self.0 / rhs.0
147    }
148}
149
150impl<T: core::ops::Rem<Output = T>, const SHIFT: usize> core::ops::Rem for Fixed<T, SHIFT> {
151    type Output = Self;
152    #[inline(always)]
153    fn rem(self, rhs: Self) -> Self::Output {
154        Self(self.0 % rhs.0)
155    }
156}
157
158impl<T: core::ops::Div<Output = T>, const SHIFT: usize> core::ops::Div<T> for Fixed<T, SHIFT> {
159    type Output = Self;
160    #[inline(always)]
161    fn div(self, rhs: T) -> Self::Output {
162        Self(self.0 / rhs)
163    }
164}