summaryrefslogtreecommitdiffstats
path: root/third_party/rust/app_units/src/app_unit.rs
diff options
context:
space:
mode:
Diffstat (limited to 'third_party/rust/app_units/src/app_unit.rs')
-rw-r--r--third_party/rust/app_units/src/app_unit.rs417
1 files changed, 417 insertions, 0 deletions
diff --git a/third_party/rust/app_units/src/app_unit.rs b/third_party/rust/app_units/src/app_unit.rs
new file mode 100644
index 0000000000..e7ef4667d8
--- /dev/null
+++ b/third_party/rust/app_units/src/app_unit.rs
@@ -0,0 +1,417 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+ #[cfg(feature = "num_traits")]
+use num_traits::Zero;
+#[cfg(feature = "serde_serialization")]
+use serde::de::{Deserialize, Deserializer};
+#[cfg(feature = "serde_serialization")]
+use serde::ser::{Serialize, Serializer};
+use std::default::Default;
+use std::fmt;
+use std::i32;
+use std::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Rem, Sub, SubAssign};
+
+/// The number of app units in a pixel.
+pub const AU_PER_PX: i32 = 60;
+
+#[derive(Clone, Copy, Hash, PartialEq, PartialOrd, Eq, Ord)]
+/// An App Unit, the fundamental unit of length in Servo. Usually
+/// 1/60th of a pixel (see `AU_PER_PX`)
+///
+/// Please ensure that the values are between `MIN_AU` and `MAX_AU`.
+/// It is safe to construct invalid `Au` values, but it may lead to
+/// panics and overflows.
+pub struct Au(pub i32);
+
+ #[cfg(feature = "serde_serialization")]
+impl<'de> Deserialize<'de> for Au {
+ fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Au, D::Error> {
+ Ok(Au(try!(i32::deserialize(deserializer))).clamp())
+ }
+}
+
+#[cfg(feature = "serde_serialization")]
+impl Serialize for Au {
+ fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
+ self.0.serialize(serializer)
+ }
+}
+
+impl Default for Au {
+ #[inline]
+ fn default() -> Au {
+ Au(0)
+ }
+}
+
+#[cfg(feature = "num_traits")]
+impl Zero for Au {
+ #[inline]
+ fn zero() -> Au {
+ Au(0)
+ }
+
+ #[inline]
+ fn is_zero(&self) -> bool {
+ self.0 == 0
+ }
+}
+
+// (1 << 30) - 1 lets us add/subtract two Au and check for overflow
+// after the operation. Gecko uses the same min/max values
+pub const MAX_AU: Au = Au((1 << 30) - 1);
+pub const MIN_AU: Au = Au(- ((1 << 30) - 1));
+
+impl fmt::Debug for Au {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ write!(f, "{}px", self.to_f64_px())
+ }
+}
+
+impl Add for Au {
+ type Output = Au;
+
+ #[inline]
+ fn add(self, other: Au) -> Au {
+ Au(self.0 + other.0).clamp()
+ }
+}
+
+impl Sub for Au {
+ type Output = Au;
+
+ #[inline]
+ fn sub(self, other: Au) -> Au {
+ Au(self.0 - other.0).clamp()
+ }
+
+}
+
+impl Mul<Au> for i32 {
+ type Output = Au;
+
+ #[inline]
+ fn mul(self, other: Au) -> Au {
+ if let Some(new) = other.0.checked_mul(self) {
+ Au(new).clamp()
+ } else if (self > 0) ^ (other.0 > 0) {
+ MIN_AU
+ } else {
+ MAX_AU
+ }
+ }
+}
+
+impl Mul<i32> for Au {
+ type Output = Au;
+
+ #[inline]
+ fn mul(self, other: i32) -> Au {
+ if let Some(new) = self.0.checked_mul(other) {
+ Au(new).clamp()
+ } else if (self.0 > 0) ^ (other > 0) {
+ MIN_AU
+ } else {
+ MAX_AU
+ }
+ }
+}
+
+impl Div for Au {
+ type Output = i32;
+
+ #[inline]
+ fn div(self, other: Au) -> i32 {
+ self.0 / other.0
+ }
+}
+
+impl Div<i32> for Au {
+ type Output = Au;
+
+ #[inline]
+ fn div(self, other: i32) -> Au {
+ Au(self.0 / other)
+ }
+}
+
+impl Rem for Au {
+ type Output = Au;
+
+ #[inline]
+ fn rem(self, other: Au) -> Au {
+ Au(self.0 % other.0)
+ }
+}
+
+impl Rem<i32> for Au {
+ type Output = Au;
+
+ #[inline]
+ fn rem(self, other: i32) -> Au {
+ Au(self.0 % other)
+ }
+}
+
+impl Neg for Au {
+ type Output = Au;
+
+ #[inline]
+ fn neg(self) -> Au {
+ Au(-self.0)
+ }
+}
+
+impl AddAssign for Au {
+ #[inline]
+ fn add_assign(&mut self, other: Au) {
+ *self = *self + other;
+ self.clamp_self();
+ }
+}
+
+impl SubAssign for Au {
+ #[inline]
+ fn sub_assign(&mut self, other: Au) {
+ *self = *self - other;
+ self.clamp_self();
+ }
+}
+
+impl MulAssign<i32> for Au {
+ #[inline]
+ fn mul_assign(&mut self, other: i32) {
+ *self = *self * other;
+ self.clamp_self();
+ }
+}
+
+impl DivAssign<i32> for Au {
+ #[inline]
+ fn div_assign(&mut self, other: i32) {
+ *self = *self / other;
+ self.clamp_self();
+ }
+}
+
+impl Au {
+ /// FIXME(pcwalton): Workaround for lack of cross crate inlining of newtype structs!
+ #[inline]
+ pub fn new(value: i32) -> Au {
+ Au(value).clamp()
+ }
+
+ #[inline]
+ fn clamp(self) -> Self {
+ if self.0 > MAX_AU.0 {
+ MAX_AU
+ } else if self.0 < MIN_AU.0 {
+ MIN_AU
+ } else {
+ self
+ }
+ }
+
+ #[inline]
+ fn clamp_self(&mut self) {
+ *self = Au::clamp(*self)
+ }
+
+ #[inline]
+ pub fn scale_by(self, factor: f32) -> Au {
+ let new_float = ((self.0 as f64) * factor as f64).round();
+ Au::from_f64_au(new_float)
+ }
+
+ #[inline]
+ /// Scale, but truncate (useful for viewport-relative units)
+ pub fn scale_by_trunc(self, factor: f32) -> Au {
+ let new_float = ((self.0 as f64) * factor as f64).trunc();
+ Au::from_f64_au(new_float)
+ }
+
+ #[inline]
+ pub fn from_f64_au(float: f64) -> Self {
+ // We *must* operate in f64. f32 isn't precise enough
+ // to handle MAX_AU
+ Au(float.min(MAX_AU.0 as f64)
+ .max(MIN_AU.0 as f64)
+ as i32)
+ }
+
+ #[inline]
+ pub fn from_px(px: i32) -> Au {
+ Au(px) * AU_PER_PX
+ }
+
+ /// Rounds this app unit down to the pixel towards zero and returns it.
+ #[inline]
+ pub fn to_px(self) -> i32 {
+ self.0 / AU_PER_PX
+ }
+
+ /// Ceil this app unit to the appropriate pixel boundary and return it.
+ #[inline]
+ pub fn ceil_to_px(self) -> i32 {
+ ((self.0 as f64) / (AU_PER_PX as f64)).ceil() as i32
+ }
+
+ #[inline]
+ pub fn to_nearest_px(self) -> i32 {
+ ((self.0 as f64) / (AU_PER_PX as f64)).round() as i32
+ }
+
+ #[inline]
+ pub fn to_nearest_pixel(self, pixels_per_px: f32) -> f32 {
+ ((self.0 as f32) / (AU_PER_PX as f32) * pixels_per_px).round() / pixels_per_px
+ }
+
+ #[inline]
+ pub fn to_f32_px(self) -> f32 {
+ (self.0 as f32) / (AU_PER_PX as f32)
+ }
+
+ #[inline]
+ pub fn to_f64_px(self) -> f64 {
+ (self.0 as f64) / (AU_PER_PX as f64)
+ }
+
+ #[inline]
+ pub fn from_f32_px(px: f32) -> Au {
+ let float = (px * AU_PER_PX as f32).round();
+ Au::from_f64_au(float as f64)
+ }
+
+ #[inline]
+ pub fn from_f64_px(px: f64) -> Au {
+ let float = (px * AU_PER_PX as f64).round();
+ Au::from_f64_au(float)
+ }
+
+ #[inline]
+ pub fn abs(self) -> Self {
+ Au(self.0.abs())
+ }
+}
+
+#[test]
+fn create() {
+ assert_eq!(Au::zero(), Au(0));
+ assert_eq!(Au::default(), Au(0));
+ assert_eq!(Au::new(7), Au(7));
+}
+
+#[test]
+fn operations() {
+ assert_eq!(Au(7) + Au(5), Au(12));
+ assert_eq!(MAX_AU + Au(1), MAX_AU);
+
+ assert_eq!(Au(7) - Au(5), Au(2));
+ assert_eq!(MIN_AU - Au(1), MIN_AU);
+
+ assert_eq!(Au(7) * 5, Au(35));
+ assert_eq!(5 * Au(7), Au(35));
+ assert_eq!(MAX_AU * -1, MIN_AU);
+ assert_eq!(MIN_AU * -1, MAX_AU);
+ assert_eq!(-1 * MAX_AU, MIN_AU);
+ assert_eq!(-1 * MIN_AU, MAX_AU);
+
+ assert_eq!((Au(14) / 5) * 5 + Au(14) % 5, Au(14));
+ assert_eq!((Au(14) / Au(5)) * Au(5) + Au(14) % Au(5), Au(14));
+
+ assert_eq!(Au(35) / 5, Au(7));
+ assert_eq!(Au(35) % 6, Au(5));
+
+ assert_eq!(Au(35) / Au(5), 7);
+ assert_eq!(Au(35) / Au(5), 7);
+
+ assert_eq!(-Au(7), Au(-7));
+}
+
+#[test]
+fn saturate() {
+ let half = MAX_AU / 2;
+ assert_eq!(half + half + half + half + half, MAX_AU);
+ assert_eq!(-half - half - half - half - half, MIN_AU);
+ assert_eq!(half * -10, MIN_AU);
+ assert_eq!(-half * 10, MIN_AU);
+ assert_eq!(half * 10, MAX_AU);
+ assert_eq!(-half * -10, MAX_AU);
+}
+
+#[test]
+fn scale() {
+ assert_eq!(Au(12).scale_by(1.5), Au(18));
+ assert_eq!(Au(12).scale_by(1.7), Au(20));
+ assert_eq!(Au(12).scale_by(1.8), Au(22));
+ assert_eq!(Au(12).scale_by_trunc(1.8), Au(21));
+}
+
+#[test]
+fn abs() {
+ assert_eq!(Au(-10).abs(), Au(10));
+}
+
+#[test]
+fn convert() {
+ assert_eq!(Au::from_px(5), Au(300));
+
+ assert_eq!(Au(300).to_px(), 5);
+ assert_eq!(Au(330).to_px(), 5);
+ assert_eq!(Au(350).to_px(), 5);
+ assert_eq!(Au(360).to_px(), 6);
+
+ assert_eq!(Au(300).ceil_to_px(), 5);
+ assert_eq!(Au(310).ceil_to_px(), 6);
+ assert_eq!(Au(330).ceil_to_px(), 6);
+ assert_eq!(Au(350).ceil_to_px(), 6);
+ assert_eq!(Au(360).ceil_to_px(), 6);
+
+ assert_eq!(Au(300).to_nearest_px(), 5);
+ assert_eq!(Au(310).to_nearest_px(), 5);
+ assert_eq!(Au(330).to_nearest_px(), 6);
+ assert_eq!(Au(350).to_nearest_px(), 6);
+ assert_eq!(Au(360).to_nearest_px(), 6);
+
+ assert_eq!(Au(60).to_nearest_pixel(2.), 1.);
+ assert_eq!(Au(70).to_nearest_pixel(2.), 1.);
+ assert_eq!(Au(80).to_nearest_pixel(2.), 1.5);
+ assert_eq!(Au(90).to_nearest_pixel(2.), 1.5);
+ assert_eq!(Au(100).to_nearest_pixel(2.), 1.5);
+ assert_eq!(Au(110).to_nearest_pixel(2.), 2.);
+ assert_eq!(Au(120).to_nearest_pixel(2.), 2.);
+
+ assert_eq!(Au(300).to_f32_px(), 5.);
+ assert_eq!(Au(312).to_f32_px(), 5.2);
+ assert_eq!(Au(330).to_f32_px(), 5.5);
+ assert_eq!(Au(348).to_f32_px(), 5.8);
+ assert_eq!(Au(360).to_f32_px(), 6.);
+ assert_eq!((Au(367).to_f32_px() * 1000.).round(), 6_117.);
+ assert_eq!((Au(368).to_f32_px() * 1000.).round(), 6_133.);
+
+ assert_eq!(Au(300).to_f64_px(), 5.);
+ assert_eq!(Au(312).to_f64_px(), 5.2);
+ assert_eq!(Au(330).to_f64_px(), 5.5);
+ assert_eq!(Au(348).to_f64_px(), 5.8);
+ assert_eq!(Au(360).to_f64_px(), 6.);
+ assert_eq!((Au(367).to_f64_px() * 1000.).round(), 6_117.);
+ assert_eq!((Au(368).to_f64_px() * 1000.).round(), 6_133.);
+
+ assert_eq!(Au::from_f32_px(5.), Au(300));
+ assert_eq!(Au::from_f32_px(5.2), Au(312));
+ assert_eq!(Au::from_f32_px(5.5), Au(330));
+ assert_eq!(Au::from_f32_px(5.8), Au(348));
+ assert_eq!(Au::from_f32_px(6.), Au(360));
+ assert_eq!(Au::from_f32_px(6.12), Au(367));
+ assert_eq!(Au::from_f32_px(6.13), Au(368));
+
+ assert_eq!(Au::from_f64_px(5.), Au(300));
+ assert_eq!(Au::from_f64_px(5.2), Au(312));
+ assert_eq!(Au::from_f64_px(5.5), Au(330));
+ assert_eq!(Au::from_f64_px(5.8), Au(348));
+ assert_eq!(Au::from_f64_px(6.), Au(360));
+ assert_eq!(Au::from_f64_px(6.12), Au(367));
+ assert_eq!(Au::from_f64_px(6.13), Au(368));
+}