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 = u32::try_from(self.x()).ok()?;
207 let y: u32 = u32::try_from(self.y()).ok()?;
208 ScreenIntRect::from_xywh(x, y, self.width(), self.height())
209 }
210}
211