// Copyright © SixtyFPS GmbH // SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-Royalty-free-2.0 OR LicenseRef-Slint-Software-3.0 /// A Fixed point, represented with the T underlying type, and shifted by so many bits #[derive(Default, Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)] pub struct Fixed(pub T); impl< T: Copy + core::ops::Shl + core::ops::Shr + core::ops::Div + core::ops::Add + core::ops::Rem, const SHIFT: usize, > Fixed { /// Create a fixed point from an integer value #[inline(always)] pub fn from_integer(value: T) -> Self { Self(value << SHIFT) } /// Get the integer part of the fixed point value #[inline(always)] pub fn truncate(self) -> T { self.0 >> SHIFT } /// Return the fractional part of the fixed point value #[inline(always)] pub fn fract(self) -> u8 where T: num_traits::AsPrimitive, { if SHIFT < 8 { (self.0 >> (SHIFT - 8)).as_() } else { (self.0 << (8 - SHIFT)).as_() } } #[inline(always)] pub fn from_fixed< T2: core::ops::Shl + core::ops::Shr + Into, const SHIFT2: usize, >( value: Fixed, ) -> Self { if SHIFT > SHIFT2 { let s: T = value.0.into(); Self(s << (SHIFT - SHIFT2)) } else { Self((value.0 >> (SHIFT2 - SHIFT)).into()) } } #[inline(always)] pub fn try_from_fixed< T2: core::ops::Shl + core::ops::Shr + TryInto, const SHIFT2: usize, >( value: Fixed, ) -> Result { Ok(if SHIFT > SHIFT2 { let s: T = value.0.try_into()?; Self(s << (SHIFT - SHIFT2)) } else { Self((value.0 >> (SHIFT2 - SHIFT)).try_into()?) }) } #[inline(always)] pub fn from_fraction(numerator: T, denominator: T) -> Self { Self((numerator << SHIFT) / denominator) } #[inline(always)] pub(crate) fn from_f32(value: f32) -> Option where T: num_traits::FromPrimitive, { Some(Self(T::from_f32(value * (1 << SHIFT) as f32)?)) } } impl, const SHIFT: usize> core::ops::Add for Fixed { type Output = Self; #[inline(always)] fn add(self, rhs: Self) -> Self::Output { Self(self.0.add(rhs.0)) } } impl, const SHIFT: usize> core::ops::Sub for Fixed { type Output = Self; #[inline(always)] fn sub(self, rhs: Self) -> Self::Output { Self(self.0.sub(rhs.0)) } } impl core::ops::AddAssign for Fixed { #[inline(always)] fn add_assign(&mut self, rhs: Self) { self.0.add_assign(rhs.0) } } impl core::ops::SubAssign for Fixed { #[inline(always)] fn sub_assign(&mut self, rhs: Self) { self.0.sub_assign(rhs.0) } } impl, const SHIFT: usize> core::ops::Mul for Fixed { type Output = Self; #[inline(always)] fn mul(self, rhs: T) -> Self::Output { Self(self.0.mul(rhs)) } } impl, const SHIFT: usize> core::ops::Mul> for Fixed where T: TryFrom + Into, >::Error: core::fmt::Debug, { type Output = Self; fn mul(self, rhs: Fixed) -> Self::Output { let lhs_i64: i64 = self.0.into(); let rhs_i64: i64 = rhs.0.into(); Self(T::try_from((lhs_i64 * rhs_i64) >> SHIFT).expect("attempt to multiply with overflow")) } } impl, const SHIFT: usize> core::ops::Neg for Fixed { type Output = Self; #[inline(always)] fn neg(self) -> Self::Output { Self(-self.0) } } impl, const SHIFT: usize> core::ops::Div for Fixed { type Output = T; #[inline(always)] fn div(self, rhs: Self) -> Self::Output { self.0 / rhs.0 } } impl, const SHIFT: usize> core::ops::Rem for Fixed { type Output = Self; #[inline(always)] fn rem(self, rhs: Self) -> Self::Output { Self(self.0 % rhs.0) } } impl, const SHIFT: usize> core::ops::Div for Fixed { type Output = Self; #[inline(always)] fn div(self, rhs: T) -> Self::Output { Self(self.0 / rhs) } }