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 | |
7 | use core::convert::TryFrom; |
8 | |
9 | use tiny_skia_path::{IntRect, IntSize, Rect}; |
10 | |
11 | use 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)] |
22 | pub struct ScreenIntRect { |
23 | x: u32, |
24 | y: u32, |
25 | width: LengthU32, |
26 | height: LengthU32, |
27 | } |
28 | |
29 | impl 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)] |
152 | mod 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 | |
183 | pub 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 | |
188 | impl 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 | |
194 | pub 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 | |
204 | impl 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 | |