tiny_skia/
geom.rs

1// Copyright 2006 The Android Open Source Project
2// Copyright 2020 Yevhenii Reizner
3//
4// Use of this source code is governed by a BSD-style license that can be
5// found in the LICENSE file.
6
7use core::convert::TryFrom;
8
9use tiny_skia_path::{IntRect, IntSize, Rect};
10
11use crate::LengthU32;
12
13/// A screen `IntRect`.
14///
15/// # Guarantees
16///
17/// - X and Y are in 0..=i32::MAX range.
18/// - Width and height are in 1..=i32::MAX range.
19/// - x+width and y+height does not overflow.
20#[allow(missing_docs)]
21#[derive(Copy, Clone, PartialEq, Debug)]
22pub struct ScreenIntRect {
23    x: u32,
24    y: u32,
25    width: LengthU32,
26    height: LengthU32,
27}
28
29impl ScreenIntRect {
30    /// Creates a new `ScreenIntRect`.
31    pub fn from_xywh(x: u32, y: u32, width: u32, height: u32) -> Option<Self> {
32        i32::try_from(x).ok()?;
33        i32::try_from(y).ok()?;
34        i32::try_from(width).ok()?;
35        i32::try_from(height).ok()?;
36
37        x.checked_add(width)?;
38        y.checked_add(height)?;
39
40        let width = LengthU32::new(width)?;
41        let height = LengthU32::new(height)?;
42
43        Some(ScreenIntRect {
44            x,
45            y,
46            width,
47            height,
48        })
49    }
50
51    /// Creates a new `ScreenIntRect`.
52    pub const fn from_xywh_safe(x: u32, y: u32, width: LengthU32, height: LengthU32) -> Self {
53        ScreenIntRect {
54            x,
55            y,
56            width,
57            height,
58        }
59    }
60
61    /// Returns rect's X position.
62    pub fn x(&self) -> u32 {
63        self.x
64    }
65
66    /// Returns rect's Y position.
67    pub fn y(&self) -> u32 {
68        self.y
69    }
70
71    /// Returns rect's width.
72    pub fn width(&self) -> u32 {
73        self.width.get()
74    }
75
76    /// Returns rect's height.
77    pub fn height(&self) -> u32 {
78        self.height.get()
79    }
80
81    /// Returns rect's width.
82    pub fn width_safe(&self) -> LengthU32 {
83        self.width
84    }
85
86    /// Returns rect's left edge.
87    pub fn left(&self) -> u32 {
88        self.x
89    }
90
91    /// Returns rect's top edge.
92    pub fn top(&self) -> u32 {
93        self.y
94    }
95
96    /// Returns rect's right edge.
97    ///
98    /// The right edge is at least 1.
99    pub fn right(&self) -> u32 {
100        // No overflow is guaranteed by constructors.
101        self.x + self.width.get()
102    }
103
104    /// Returns rect's bottom edge.
105    ///
106    /// The bottom edge is at least 1.
107    pub fn bottom(&self) -> u32 {
108        // No overflow is guaranteed by constructors.
109        self.y + self.height.get()
110    }
111
112    /// Returns rect's size.
113    pub fn size(&self) -> IntSize {
114        IntSize::from_wh(self.width(), self.height()).unwrap()
115    }
116
117    /// Checks that the rect is completely includes `other` Rect.
118    pub fn contains(&self, other: &Self) -> bool {
119        self.x <= other.x
120            && self.y <= other.y
121            && self.right() >= other.right()
122            && self.bottom() >= other.bottom()
123    }
124
125    /// Converts into a `IntRect`.
126    pub fn to_int_rect(&self) -> IntRect {
127        // Everything is already checked by constructors.
128        IntRect::from_xywh(
129            self.x as i32,
130            self.y as i32,
131            self.width.get(),
132            self.height.get(),
133        )
134        .unwrap()
135    }
136
137    /// Converts into a `Rect`.
138    pub fn to_rect(&self) -> Rect {
139        // Can't fail, because `ScreenIntRect` is always valid.
140        // And u32 always fits into f32.
141        Rect::from_ltrb(
142            self.x as f32,
143            self.y as f32,
144            self.x as f32 + self.width.get() as f32,
145            self.y as f32 + self.height.get() as f32,
146        )
147        .unwrap()
148    }
149}
150
151#[cfg(test)]
152mod screen_int_rect_tests {
153    use super::*;
154
155    #[test]
156    fn tests() {
157        assert_eq!(ScreenIntRect::from_xywh(0, 0, 0, 0), None);
158        assert_eq!(ScreenIntRect::from_xywh(0, 0, 1, 0), None);
159        assert_eq!(ScreenIntRect::from_xywh(0, 0, 0, 1), None);
160
161        assert_eq!(ScreenIntRect::from_xywh(0, 0, u32::MAX, u32::MAX), None);
162        assert_eq!(ScreenIntRect::from_xywh(0, 0, 1, u32::MAX), None);
163        assert_eq!(ScreenIntRect::from_xywh(0, 0, u32::MAX, 1), None);
164
165        assert_eq!(ScreenIntRect::from_xywh(u32::MAX, 0, 1, 1), None);
166        assert_eq!(ScreenIntRect::from_xywh(0, u32::MAX, 1, 1), None);
167
168        assert_eq!(
169            ScreenIntRect::from_xywh(u32::MAX, u32::MAX, u32::MAX, u32::MAX),
170            None
171        );
172
173        let r = ScreenIntRect::from_xywh(1, 2, 3, 4).unwrap();
174        assert_eq!(r.x(), 1);
175        assert_eq!(r.y(), 2);
176        assert_eq!(r.width(), 3);
177        assert_eq!(r.height(), 4);
178        assert_eq!(r.right(), 4);
179        assert_eq!(r.bottom(), 6);
180    }
181}
182
183pub trait IntSizeExt {
184    /// Converts the current size into a `IntRect` at a provided position.
185    fn to_screen_int_rect(&self, x: u32, y: u32) -> ScreenIntRect;
186}
187
188impl IntSizeExt for IntSize {
189    fn to_screen_int_rect(&self, x: u32, y: u32) -> ScreenIntRect {
190        ScreenIntRect::from_xywh(x, y, self.width(), self.height()).unwrap()
191    }
192}
193
194pub trait IntRectExt {
195    /// Converts into `ScreenIntRect`.
196    ///
197    /// # Checks
198    ///
199    /// - x >= 0
200    /// - y >= 0
201    fn to_screen_int_rect(&self) -> Option<ScreenIntRect>;
202}
203
204impl IntRectExt for IntRect {
205    fn to_screen_int_rect(&self) -> Option<ScreenIntRect> {
206        let x = u32::try_from(self.x()).ok()?;
207        let y = u32::try_from(self.y()).ok()?;
208        ScreenIntRect::from_xywh(x, y, self.width(), self.height())
209    }
210}