1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 // Defines a simple integer rectangle class. The containment semantics
6 // are array-like; that is, the coordinate (x, y) is considered to be
7 // contained by the rectangle, but the coordinate (x + width, y) is not.
8 // The class will happily let you create malformed rectangles (that is,
9 // rectangles with negative width and/or height), but there will be assertions
10 // in the operations (such as Contains()) to complain in this case.
11
12 #ifndef UI_GFX_GEOMETRY_RECT_H_
13 #define UI_GFX_GEOMETRY_RECT_H_
14
15 #include <cmath>
16 #include <iosfwd>
17 #include <string>
18
19 #include "base/logging.h"
20 #include "build/build_config.h"
21 #include "ui/gfx/geometry/point.h"
22 #include "ui/gfx/geometry/safe_integer_conversions.h"
23 #include "ui/gfx/geometry/size.h"
24 #include "ui/gfx/geometry/vector2d.h"
25
26 #if defined(OS_WIN)
27 typedef struct tagRECT RECT;
28 #elif defined(OS_MACOSX)
29 typedef struct CGRect CGRect;
30 #endif
31
32 namespace gfx {
33
34 class Insets;
35
36 class GFX_EXPORT Rect {
37 public:
38 constexpr Rect() = default;
Rect(int width,int height)39 constexpr Rect(int width, int height) : size_(width, height) {}
Rect(int x,int y,int width,int height)40 constexpr Rect(int x, int y, int width, int height)
41 : origin_(x, y),
42 size_(GetClampedValue(x, width), GetClampedValue(y, height)) {}
Rect(const Size & size)43 constexpr explicit Rect(const Size& size) : size_(size) {}
Rect(const Point & origin,const Size & size)44 constexpr Rect(const Point& origin, const Size& size)
45 : origin_(origin),
46 size_(GetClampedValue(origin.x(), size.width()),
47 GetClampedValue(origin.y(), size.height())) {}
48
49 #if defined(OS_WIN)
50 explicit Rect(const RECT& r);
51 #elif defined(OS_MACOSX)
52 explicit Rect(const CGRect& r);
53 #endif
54
55 #if defined(OS_WIN)
56 // Construct an equivalent Win32 RECT object.
57 RECT ToRECT() const;
58 #elif defined(OS_MACOSX)
59 // Construct an equivalent CoreGraphics object.
60 CGRect ToCGRect() const;
61 #endif
62
x()63 constexpr int x() const { return origin_.x(); }
set_x(int x)64 void set_x(int x) {
65 origin_.set_x(x);
66 size_.set_width(GetClampedValue(x, width()));
67 }
68
y()69 constexpr int y() const { return origin_.y(); }
set_y(int y)70 void set_y(int y) {
71 origin_.set_y(y);
72 size_.set_height(GetClampedValue(y, height()));
73 }
74
width()75 constexpr int width() const { return size_.width(); }
set_width(int width)76 void set_width(int width) { size_.set_width(GetClampedValue(x(), width)); }
77
height()78 constexpr int height() const { return size_.height(); }
set_height(int height)79 void set_height(int height) {
80 size_.set_height(GetClampedValue(y(), height));
81 }
82
origin()83 constexpr const Point& origin() const { return origin_; }
set_origin(const Point & origin)84 void set_origin(const Point& origin) {
85 origin_ = origin;
86 // Ensure that width and height remain valid.
87 set_width(width());
88 set_height(height());
89 }
90
size()91 constexpr const Size& size() const { return size_; }
set_size(const Size & size)92 void set_size(const Size& size) {
93 set_width(size.width());
94 set_height(size.height());
95 }
96
right()97 constexpr int right() const { return x() + width(); }
bottom()98 constexpr int bottom() const { return y() + height(); }
99
top_right()100 constexpr Point top_right() const { return Point(right(), y()); }
bottom_left()101 constexpr Point bottom_left() const { return Point(x(), bottom()); }
bottom_right()102 constexpr Point bottom_right() const { return Point(right(), bottom()); }
103
OffsetFromOrigin()104 Vector2d OffsetFromOrigin() const { return Vector2d(x(), y()); }
105
SetRect(int x,int y,int width,int height)106 void SetRect(int x, int y, int width, int height) {
107 origin_.SetPoint(x, y);
108 // Ensure that width and height remain valid.
109 set_width(width);
110 set_height(height);
111 }
112
113 // Use in place of SetRect() when you know the edges of the rectangle instead
114 // of the dimensions, rather than trying to determine the width/height
115 // yourself. This safely handles cases where the width/height would overflow.
116 void SetByBounds(int left, int top, int right, int bottom);
117
118 // Shrink the rectangle by a horizontal and vertical distance on all sides.
Inset(int horizontal,int vertical)119 void Inset(int horizontal, int vertical) {
120 Inset(horizontal, vertical, horizontal, vertical);
121 }
122
123 // Shrink the rectangle by the given insets.
124 void Inset(const Insets& insets);
125
126 // Shrink the rectangle by the specified amount on each side.
127 void Inset(int left, int top, int right, int bottom);
128
129 // Move the rectangle by a horizontal and vertical distance.
130 void Offset(int horizontal, int vertical);
Offset(const Vector2d & distance)131 void Offset(const Vector2d& distance) { Offset(distance.x(), distance.y()); }
132 void operator+=(const Vector2d& offset);
133 void operator-=(const Vector2d& offset);
134
135 Insets InsetsFrom(const Rect& inner) const;
136
137 // Returns true if the area of the rectangle is zero.
IsEmpty()138 bool IsEmpty() const { return size_.IsEmpty(); }
139
140 // A rect is less than another rect if its origin is less than
141 // the other rect's origin. If the origins are equal, then the
142 // shortest rect is less than the other. If the origin and the
143 // height are equal, then the narrowest rect is less than.
144 // This comparison is required to use Rects in sets, or sorted
145 // vectors.
146 bool operator<(const Rect& other) const;
147
148 // Returns true if the point identified by point_x and point_y falls inside
149 // this rectangle. The point (x, y) is inside the rectangle, but the
150 // point (x + width, y + height) is not.
151 bool Contains(int point_x, int point_y) const;
152
153 // Returns true if the specified point is contained by this rectangle.
Contains(const Point & point)154 bool Contains(const Point& point) const {
155 return Contains(point.x(), point.y());
156 }
157
158 // Returns true if this rectangle contains the specified rectangle.
159 bool Contains(const Rect& rect) const;
160
161 // Returns true if this rectangle intersects the specified rectangle.
162 // An empty rectangle doesn't intersect any rectangle.
163 bool Intersects(const Rect& rect) const;
164
165 // Computes the intersection of this rectangle with the given rectangle.
166 void Intersect(const Rect& rect);
167
168 // Computes the union of this rectangle with the given rectangle. The union
169 // is the smallest rectangle containing both rectangles.
170 void Union(const Rect& rect);
171
172 // Computes the rectangle resulting from subtracting |rect| from |*this|,
173 // i.e. the bounding rect of |Region(*this) - Region(rect)|.
174 void Subtract(const Rect& rect);
175
176 // Fits as much of the receiving rectangle into the supplied rectangle as
177 // possible, becoming the result. For example, if the receiver had
178 // a x-location of 2 and a width of 4, and the supplied rectangle had
179 // an x-location of 0 with a width of 5, the returned rectangle would have
180 // an x-location of 1 with a width of 4.
181 void AdjustToFit(const Rect& rect);
182
183 // Returns the center of this rectangle.
184 Point CenterPoint() const;
185
186 // Becomes a rectangle that has the same center point but with a size capped
187 // at given |size|.
188 void ClampToCenteredSize(const Size& size);
189
190 // Splits |this| in two halves, |left_half| and |right_half|.
191 void SplitVertically(Rect* left_half, Rect* right_half) const;
192
193 // Returns true if this rectangle shares an entire edge (i.e., same width or
194 // same height) with the given rectangle, and the rectangles do not overlap.
195 bool SharesEdgeWith(const Rect& rect) const;
196
197 // Returns the manhattan distance from the rect to the point. If the point is
198 // inside the rect, returns 0.
199 int ManhattanDistanceToPoint(const Point& point) const;
200
201 // Returns the manhattan distance between the contents of this rect and the
202 // contents of the given rect. That is, if the intersection of the two rects
203 // is non-empty then the function returns 0. If the rects share a side, it
204 // returns the smallest non-zero value appropriate for int.
205 int ManhattanInternalDistance(const Rect& rect) const;
206
207 std::string ToString() const;
208
209 bool ApproximatelyEqual(const Rect& rect, int tolerance) const;
210
211 private:
212 gfx::Point origin_;
213 gfx::Size size_;
214
215 // Returns true iff a+b would overflow max int.
AddWouldOverflow(int a,int b)216 static constexpr bool AddWouldOverflow(int a, int b) {
217 // In this function, GCC tries to make optimizations that would only work if
218 // max - a wouldn't overflow but it isn't smart enough to notice that a > 0.
219 // So cast everything to unsigned to avoid this. As it is guaranteed that
220 // max - a and b are both already positive, the cast is a noop.
221 //
222 // This is intended to be: a > 0 && max - a < b
223 return a > 0 && b > 0 &&
224 static_cast<unsigned>(std::numeric_limits<int>::max() - a) <
225 static_cast<unsigned>(b);
226 }
227
228 // Clamp the size to avoid integer overflow in bottom() and right().
229 // This returns the width given an origin and a width.
230 // TODO(enne): this should probably use base::SaturatedAddition, but that
231 // function is not a constexpr.
GetClampedValue(int origin,int size)232 static constexpr int GetClampedValue(int origin, int size) {
233 return AddWouldOverflow(origin, size)
234 ? std::numeric_limits<int>::max() - origin
235 : size;
236 }
237 };
238
239 inline bool operator==(const Rect& lhs, const Rect& rhs) {
240 return lhs.origin() == rhs.origin() && lhs.size() == rhs.size();
241 }
242
243 inline bool operator!=(const Rect& lhs, const Rect& rhs) {
244 return !(lhs == rhs);
245 }
246
247 GFX_EXPORT Rect operator+(const Rect& lhs, const Vector2d& rhs);
248 GFX_EXPORT Rect operator-(const Rect& lhs, const Vector2d& rhs);
249
250 inline Rect operator+(const Vector2d& lhs, const Rect& rhs) {
251 return rhs + lhs;
252 }
253
254 GFX_EXPORT Rect IntersectRects(const Rect& a, const Rect& b);
255 GFX_EXPORT Rect UnionRects(const Rect& a, const Rect& b);
256 GFX_EXPORT Rect SubtractRects(const Rect& a, const Rect& b);
257
258 // Constructs a rectangle with |p1| and |p2| as opposite corners.
259 //
260 // This could also be thought of as "the smallest rect that contains both
261 // points", except that we consider points on the right/bottom edges of the
262 // rect to be outside the rect. So technically one or both points will not be
263 // contained within the rect, because they will appear on one of these edges.
264 GFX_EXPORT Rect BoundingRect(const Point& p1, const Point& p2);
265
266 // Scales the rect and returns the enclosing rect. Use this only the inputs are
267 // known to not overflow. Use ScaleToEnclosingRectSafe if the inputs are
268 // unknown and need to use saturated math.
ScaleToEnclosingRect(const Rect & rect,float x_scale,float y_scale)269 inline Rect ScaleToEnclosingRect(const Rect& rect,
270 float x_scale,
271 float y_scale) {
272 if (x_scale == 1.f && y_scale == 1.f)
273 return rect;
274 // These next functions cast instead of using e.g. ToFlooredInt() because we
275 // haven't checked to ensure that the clamping behavior of the helper
276 // functions doesn't degrade performance, and callers shouldn't be passing
277 // values that cause overflow anyway.
278 DCHECK(base::IsValueInRangeForNumericType<int>(
279 std::floor(rect.x() * x_scale)));
280 DCHECK(base::IsValueInRangeForNumericType<int>(
281 std::floor(rect.y() * y_scale)));
282 DCHECK(base::IsValueInRangeForNumericType<int>(
283 std::ceil(rect.right() * x_scale)));
284 DCHECK(base::IsValueInRangeForNumericType<int>(
285 std::ceil(rect.bottom() * y_scale)));
286 int x = static_cast<int>(std::floor(rect.x() * x_scale));
287 int y = static_cast<int>(std::floor(rect.y() * y_scale));
288 int r = rect.width() == 0 ?
289 x : static_cast<int>(std::ceil(rect.right() * x_scale));
290 int b = rect.height() == 0 ?
291 y : static_cast<int>(std::ceil(rect.bottom() * y_scale));
292 return Rect(x, y, r - x, b - y);
293 }
294
ScaleToEnclosingRect(const Rect & rect,float scale)295 inline Rect ScaleToEnclosingRect(const Rect& rect, float scale) {
296 return ScaleToEnclosingRect(rect, scale, scale);
297 }
298
299 // ScaleToEnclosingRect but clamping instead of asserting if the resulting rect
300 // would overflow.
ScaleToEnclosingRectSafe(const Rect & rect,float x_scale,float y_scale)301 inline Rect ScaleToEnclosingRectSafe(const Rect& rect,
302 float x_scale,
303 float y_scale) {
304 if (x_scale == 1.f && y_scale == 1.f)
305 return rect;
306 int x = base::saturated_cast<int>(std::floor(rect.x() * x_scale));
307 int y = base::saturated_cast<int>(std::floor(rect.y() * y_scale));
308 int w = base::saturated_cast<int>(std::ceil(rect.width() * x_scale));
309 int h = base::saturated_cast<int>(std::ceil(rect.height() * y_scale));
310 return Rect(x, y, w, h);
311 }
312
ScaleToEnclosingRectSafe(const Rect & rect,float scale)313 inline Rect ScaleToEnclosingRectSafe(const Rect& rect, float scale) {
314 return ScaleToEnclosingRectSafe(rect, scale, scale);
315 }
316
ScaleToEnclosedRect(const Rect & rect,float x_scale,float y_scale)317 inline Rect ScaleToEnclosedRect(const Rect& rect,
318 float x_scale,
319 float y_scale) {
320 if (x_scale == 1.f && y_scale == 1.f)
321 return rect;
322 DCHECK(base::IsValueInRangeForNumericType<int>(
323 std::ceil(rect.x() * x_scale)));
324 DCHECK(base::IsValueInRangeForNumericType<int>(
325 std::ceil(rect.y() * y_scale)));
326 DCHECK(base::IsValueInRangeForNumericType<int>(
327 std::floor(rect.right() * x_scale)));
328 DCHECK(base::IsValueInRangeForNumericType<int>(
329 std::floor(rect.bottom() * y_scale)));
330 int x = static_cast<int>(std::ceil(rect.x() * x_scale));
331 int y = static_cast<int>(std::ceil(rect.y() * y_scale));
332 int r = rect.width() == 0 ?
333 x : static_cast<int>(std::floor(rect.right() * x_scale));
334 int b = rect.height() == 0 ?
335 y : static_cast<int>(std::floor(rect.bottom() * y_scale));
336 return Rect(x, y, r - x, b - y);
337 }
338
ScaleToEnclosedRect(const Rect & rect,float scale)339 inline Rect ScaleToEnclosedRect(const Rect& rect, float scale) {
340 return ScaleToEnclosedRect(rect, scale, scale);
341 }
342
343 // This is declared here for use in gtest-based unit tests but is defined in
344 // the //ui/gfx:test_support target. Depend on that to use this in your unit
345 // test. This should not be used in production code - call ToString() instead.
346 void PrintTo(const Rect& rect, ::std::ostream* os);
347
348 } // namespace gfx
349
350 #endif // UI_GFX_GEOMETRY_RECT_H_
351