app_units/
app_unit.rs

1/* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4
5#[cfg(feature = "malloc_size_of")]
6use malloc_size_of::malloc_size_of_is_0;
7#[cfg(feature = "num_traits")]
8use num_traits::Zero;
9#[cfg(feature = "serde_serialization")]
10use serde::{Deserialize, Deserializer, Serialize};
11
12use std::iter::Sum;
13use std::{
14    default::Default,
15    fmt,
16    ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Rem, Sub, SubAssign},
17};
18
19/// The number of app units in a pixel.
20pub const AU_PER_PX: i32 = 60;
21/// The minimum number of app units, same as in Gecko.
22pub const MIN_AU: Au = Au(-((1 << 30) - 1));
23/// The maximum number of app units, same as in Gecko.
24///
25/// (1 << 30) - 1 lets us add/subtract two Au and check for overflow after the operation.
26pub const MAX_AU: Au = Au((1 << 30) - 1);
27
28#[repr(transparent)]
29#[derive(Clone, Copy, Hash, PartialEq, PartialOrd, Eq, Ord, Default)]
30#[cfg_attr(feature = "serde_serialization", derive(Serialize), serde(transparent))]
31/// An App Unit, the fundamental unit of length in Servo. Usually
32/// 1/60th of a pixel (see `AU_PER_PX`)
33///
34/// Please ensure that the values are between `MIN_AU` and `MAX_AU`.
35/// It is safe to construct invalid `Au` values, but it may lead to
36/// panics and overflows.
37pub struct Au(pub i32);
38
39impl fmt::Debug for Au {
40    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
41        write!(f, "{}px", self.to_f64_px())
42    }
43}
44
45#[cfg(feature = "malloc_size_of")]
46malloc_size_of_is_0!(Au);
47
48#[cfg(feature = "serde_serialization")]
49impl<'de> Deserialize<'de> for Au {
50    fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Au, D::Error> {
51        Ok(Au(i32::deserialize(deserializer)?).clamp())
52    }
53}
54
55#[cfg(feature = "num_traits")]
56impl Zero for Au {
57    #[inline]
58    fn zero() -> Au {
59        Au(0)
60    }
61
62    #[inline]
63    fn is_zero(&self) -> bool {
64        self.0 == 0
65    }
66}
67
68impl Add for Au {
69    type Output = Au;
70
71    #[inline]
72    fn add(self, other: Au) -> Au {
73        Au(self.0 + other.0).clamp()
74    }
75}
76
77impl Sub for Au {
78    type Output = Au;
79
80    #[inline]
81    fn sub(self, other: Au) -> Au {
82        Au(self.0 - other.0).clamp()
83    }
84}
85
86impl Mul<Au> for i32 {
87    type Output = Au;
88
89    #[inline]
90    fn mul(self, other: Au) -> Au {
91        if let Some(new) = other.0.checked_mul(self) {
92            Au(new).clamp()
93        } else if (self > 0) ^ (other.0 > 0) {
94            MIN_AU
95        } else {
96            MAX_AU
97        }
98    }
99}
100
101impl Mul<i32> for Au {
102    type Output = Au;
103
104    #[inline]
105    fn mul(self, other: i32) -> Au {
106        if let Some(new) = self.0.checked_mul(other) {
107            Au(new).clamp()
108        } else if (self.0 > 0) ^ (other > 0) {
109            MIN_AU
110        } else {
111            MAX_AU
112        }
113    }
114}
115
116impl Div for Au {
117    type Output = i32;
118
119    #[inline]
120    fn div(self, other: Au) -> i32 {
121        self.0 / other.0
122    }
123}
124
125impl Div<i32> for Au {
126    type Output = Au;
127
128    #[inline]
129    fn div(self, other: i32) -> Au {
130        Au(self.0 / other)
131    }
132}
133
134impl Rem for Au {
135    type Output = Au;
136
137    #[inline]
138    fn rem(self, other: Au) -> Au {
139        Au(self.0 % other.0)
140    }
141}
142
143impl Rem<i32> for Au {
144    type Output = Au;
145
146    #[inline]
147    fn rem(self, other: i32) -> Au {
148        Au(self.0 % other)
149    }
150}
151
152impl Neg for Au {
153    type Output = Au;
154
155    #[inline]
156    fn neg(self) -> Au {
157        Au(-self.0)
158    }
159}
160
161impl AddAssign for Au {
162    #[inline]
163    fn add_assign(&mut self, other: Au) {
164        *self = (*self + other).clamp();
165    }
166}
167
168impl SubAssign for Au {
169    #[inline]
170    fn sub_assign(&mut self, other: Au) {
171        *self = (*self - other).clamp();
172    }
173}
174
175impl MulAssign<i32> for Au {
176    #[inline]
177    fn mul_assign(&mut self, other: i32) {
178        *self = (*self * other).clamp();
179    }
180}
181
182impl DivAssign<i32> for Au {
183    #[inline]
184    fn div_assign(&mut self, other: i32) {
185        *self = (*self / other).clamp();
186    }
187}
188
189impl<'a> Sum<&'a Self> for Au {
190    fn sum<I: Iterator<Item = &'a Self>>(iter: I) -> Self {
191        iter.fold(Self::zero(), |a, b| a + *b)
192    }
193}
194
195impl Sum<Self> for Au {
196    fn sum<I: Iterator<Item = Self>>(iter: I) -> Self {
197        iter.fold(Self::zero(), |a, b| a + b)
198    }
199}
200
201impl Au {
202    /// FIXME(pcwalton): Workaround for lack of cross crate inlining of newtype structs!
203    #[inline]
204    pub fn new(value: i32) -> Au {
205        Au(value).clamp()
206    }
207
208    #[inline]
209    fn clamp(self) -> Self {
210        Ord::clamp(self, MIN_AU, MAX_AU)
211    }
212
213    #[inline]
214    pub fn scale_by(self, factor: f32) -> Au {
215        let new_float = ((self.0 as f64) * factor as f64).round();
216        Au::from_f64_au(new_float)
217    }
218
219    #[inline]
220    /// Scale, but truncate (useful for viewport-relative units)
221    pub fn scale_by_trunc(self, factor: f32) -> Au {
222        let new_float = ((self.0 as f64) * factor as f64).trunc();
223        Au::from_f64_au(new_float)
224    }
225
226    #[inline]
227    pub fn from_f64_au(float: f64) -> Self {
228        // We *must* operate in f64. f32 isn't precise enough to handle MAX_AU
229        Au(float.clamp(MIN_AU.0 as f64, MAX_AU.0 as f64) as i32)
230    }
231
232    #[inline]
233    pub fn from_px(px: i32) -> Au {
234        Au(px) * AU_PER_PX
235    }
236
237    /// Round this app unit down to the pixel towards zero and return it.
238    #[inline]
239    pub fn to_px(self) -> i32 {
240        self.0 / AU_PER_PX
241    }
242
243    /// Ceil this app unit to the appropriate pixel boundary and return it.
244    #[inline]
245    pub fn ceil_to_px(self) -> i32 {
246        ((self.0 as f64) / (AU_PER_PX as f64)).ceil() as i32
247    }
248
249    #[inline]
250    pub fn to_nearest_px(self) -> i32 {
251        ((self.0 as f64) / (AU_PER_PX as f64)).round() as i32
252    }
253
254    #[inline]
255    pub fn to_nearest_pixel(self, pixels_per_px: f32) -> f32 {
256        ((self.0 as f32) / (AU_PER_PX as f32) * pixels_per_px).round() / pixels_per_px
257    }
258
259    #[inline]
260    pub fn to_f32_px(self) -> f32 {
261        (self.0 as f32) / (AU_PER_PX as f32)
262    }
263
264    #[inline]
265    pub fn to_f64_px(self) -> f64 {
266        (self.0 as f64) / (AU_PER_PX as f64)
267    }
268
269    #[inline]
270    pub fn from_f32_px(px: f32) -> Au {
271        let float = (px * AU_PER_PX as f32).round();
272        Au::from_f64_au(float as f64)
273    }
274
275    #[inline]
276    pub fn from_f64_px(px: f64) -> Au {
277        let float = (px * AU_PER_PX as f64).round();
278        Au::from_f64_au(float)
279    }
280
281    #[inline]
282    pub fn from_f32_px_trunc(px: f32) -> Au {
283        let float = (px * AU_PER_PX as f32).trunc();
284        Au::from_f64_au(float as f64)
285    }
286
287    #[inline]
288    pub fn from_f64_px_trunc(px: f64) -> Au {
289        let float = (px * AU_PER_PX as f64).trunc();
290        Au::from_f64_au(float)
291    }
292
293    #[inline]
294    pub fn abs(self) -> Self {
295        Au(self.0.abs())
296    }
297
298    #[inline]
299    pub fn max_assign(&mut self, other: Self) {
300        *self = (*self).max(other);
301    }
302
303    #[inline]
304    pub fn min_assign(&mut self, other: Self) {
305        *self = (*self).min(other);
306    }
307}
308
309#[test]
310fn create() {
311    assert_eq!(Au::zero(), Au(0));
312    assert_eq!(Au::default(), Au(0));
313    assert_eq!(Au::new(7), Au(7));
314}
315
316#[test]
317fn operations() {
318    assert_eq!(Au(7) + Au(5), Au(12));
319    assert_eq!(MAX_AU + Au(1), MAX_AU);
320
321    assert_eq!(Au(7) - Au(5), Au(2));
322    assert_eq!(MIN_AU - Au(1), MIN_AU);
323
324    assert_eq!(Au(7) * 5, Au(35));
325    assert_eq!(5 * Au(7), Au(35));
326    assert_eq!(MAX_AU * -1, MIN_AU);
327    assert_eq!(MIN_AU * -1, MAX_AU);
328    assert_eq!(-1 * MAX_AU, MIN_AU);
329    assert_eq!(-1 * MIN_AU, MAX_AU);
330
331    assert_eq!((Au(14) / 5) * 5 + Au(14) % 5, Au(14));
332    assert_eq!((Au(14) / Au(5)) * Au(5) + Au(14) % Au(5), Au(14));
333
334    assert_eq!(Au(35) / 5, Au(7));
335    assert_eq!(Au(35) % 6, Au(5));
336
337    assert_eq!(Au(35) / Au(5), 7);
338    assert_eq!(Au(35) / Au(5), 7);
339
340    assert_eq!(-Au(7), Au(-7));
341}
342
343#[test]
344fn saturate() {
345    let half = MAX_AU / 2;
346    assert_eq!(half + half + half + half + half, MAX_AU);
347    assert_eq!(-half - half - half - half - half, MIN_AU);
348    assert_eq!(half * -10, MIN_AU);
349    assert_eq!(-half * 10, MIN_AU);
350    assert_eq!(half * 10, MAX_AU);
351    assert_eq!(-half * -10, MAX_AU);
352}
353
354#[test]
355fn scale() {
356    assert_eq!(Au(12).scale_by(1.5), Au(18));
357    assert_eq!(Au(12).scale_by(1.7), Au(20));
358    assert_eq!(Au(12).scale_by(1.8), Au(22));
359    assert_eq!(Au(12).scale_by_trunc(1.8), Au(21));
360}
361
362#[test]
363fn abs() {
364    assert_eq!(Au(-10).abs(), Au(10));
365}
366
367#[test]
368fn convert() {
369    assert_eq!(Au::from_px(5), Au(300));
370
371    assert_eq!(Au(300).to_px(), 5);
372    assert_eq!(Au(330).to_px(), 5);
373    assert_eq!(Au(350).to_px(), 5);
374    assert_eq!(Au(360).to_px(), 6);
375
376    assert_eq!(Au(300).ceil_to_px(), 5);
377    assert_eq!(Au(310).ceil_to_px(), 6);
378    assert_eq!(Au(330).ceil_to_px(), 6);
379    assert_eq!(Au(350).ceil_to_px(), 6);
380    assert_eq!(Au(360).ceil_to_px(), 6);
381
382    assert_eq!(Au(300).to_nearest_px(), 5);
383    assert_eq!(Au(310).to_nearest_px(), 5);
384    assert_eq!(Au(330).to_nearest_px(), 6);
385    assert_eq!(Au(350).to_nearest_px(), 6);
386    assert_eq!(Au(360).to_nearest_px(), 6);
387
388    assert_eq!(Au(60).to_nearest_pixel(2.), 1.);
389    assert_eq!(Au(70).to_nearest_pixel(2.), 1.);
390    assert_eq!(Au(80).to_nearest_pixel(2.), 1.5);
391    assert_eq!(Au(90).to_nearest_pixel(2.), 1.5);
392    assert_eq!(Au(100).to_nearest_pixel(2.), 1.5);
393    assert_eq!(Au(110).to_nearest_pixel(2.), 2.);
394    assert_eq!(Au(120).to_nearest_pixel(2.), 2.);
395
396    assert_eq!(Au(300).to_f32_px(), 5.);
397    assert_eq!(Au(312).to_f32_px(), 5.2);
398    assert_eq!(Au(330).to_f32_px(), 5.5);
399    assert_eq!(Au(348).to_f32_px(), 5.8);
400    assert_eq!(Au(360).to_f32_px(), 6.);
401    assert_eq!((Au(367).to_f32_px() * 1000.).round(), 6_117.);
402    assert_eq!((Au(368).to_f32_px() * 1000.).round(), 6_133.);
403
404    assert_eq!(Au(300).to_f64_px(), 5.);
405    assert_eq!(Au(312).to_f64_px(), 5.2);
406    assert_eq!(Au(330).to_f64_px(), 5.5);
407    assert_eq!(Au(348).to_f64_px(), 5.8);
408    assert_eq!(Au(360).to_f64_px(), 6.);
409    assert_eq!((Au(367).to_f64_px() * 1000.).round(), 6_117.);
410    assert_eq!((Au(368).to_f64_px() * 1000.).round(), 6_133.);
411
412    assert_eq!(Au::from_f32_px(5.), Au(300));
413    assert_eq!(Au::from_f32_px(5.2), Au(312));
414    assert_eq!(Au::from_f32_px(5.5), Au(330));
415    assert_eq!(Au::from_f32_px(5.8), Au(348));
416    assert_eq!(Au::from_f32_px(6.), Au(360));
417    assert_eq!(Au::from_f32_px(6.12), Au(367));
418    assert_eq!(Au::from_f32_px(6.13), Au(368));
419
420    assert_eq!(Au::from_f64_px(5.), Au(300));
421    assert_eq!(Au::from_f64_px(5.2), Au(312));
422    assert_eq!(Au::from_f64_px(5.5), Au(330));
423    assert_eq!(Au::from_f64_px(5.8), Au(348));
424    assert_eq!(Au::from_f64_px(6.), Au(360));
425    assert_eq!(Au::from_f64_px(6.12), Au(367));
426    assert_eq!(Au::from_f64_px(6.13), Au(368));
427
428    assert_eq!(Au::from_f32_px_trunc(5.0), Au(300));
429    assert_eq!(Au::from_f32_px_trunc(5.2), Au(312));
430    assert_eq!(Au::from_f32_px_trunc(5.5), Au(330));
431    assert_eq!(Au::from_f32_px_trunc(5.8), Au(348));
432    assert_eq!(Au::from_f32_px_trunc(6.), Au(360));
433    assert_eq!(Au::from_f32_px_trunc(6.12), Au(367));
434    assert_eq!(Au::from_f32_px_trunc(6.13), Au(367));
435
436    assert_eq!(Au::from_f64_px_trunc(5.), Au(300));
437    assert_eq!(Au::from_f64_px_trunc(5.2), Au(312));
438    assert_eq!(Au::from_f64_px_trunc(5.5), Au(330));
439    assert_eq!(Au::from_f64_px_trunc(5.8), Au(348));
440    assert_eq!(Au::from_f64_px_trunc(6.), Au(360));
441    assert_eq!(Au::from_f64_px_trunc(6.12), Au(367));
442    assert_eq!(Au::from_f64_px_trunc(6.13), Au(367));
443}
444
445#[test]
446fn max_assign() {
447    let mut au = Au(5);
448    au.max_assign(Au(10));
449    assert_eq!(au, Au(10));
450
451    let mut au = Au(5);
452    au.max_assign(Au(-10));
453    assert_eq!(au, Au(5));
454
455    let mut au = Au(100);
456    au.max_assign(MAX_AU);
457    assert_eq!(au, MAX_AU);
458
459    let mut au = Au(-100);
460    au.max_assign(MAX_AU);
461    assert_eq!(au, MAX_AU);
462}
463
464#[test]
465fn min_assign() {
466    let mut au = Au(5);
467    au.min_assign(Au(10));
468    assert_eq!(au, Au(5));
469
470    let mut au = Au(5);
471    au.min_assign(Au(-10));
472    assert_eq!(au, Au(-10));
473
474    let mut au = Au(100);
475    au.min_assign(MAX_AU);
476    assert_eq!(au, Au(100));
477
478    let mut au = Au(-100);
479    au.min_assign(MAX_AU);
480    assert_eq!(au, Au(-100));
481}
482
483#[cfg(feature = "serde_serialization")]
484#[test]
485fn serialize() {
486    let serialized = ron::to_string(&Au(42)).unwrap();
487    assert_eq!(ron::from_str(&serialized), Ok(Au(42)));
488}