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(); }
64 // Sets the X position while preserving the width.
set_x(int x)65 void set_x(int x) {
66 origin_.set_x(x);
67 size_.set_width(GetClampedValue(x, width()));
68 }
69
y()70 constexpr int y() const { return origin_.y(); }
71 // Sets the Y position while preserving the height.
set_y(int y)72 void set_y(int y) {
73 origin_.set_y(y);
74 size_.set_height(GetClampedValue(y, height()));
75 }
76
width()77 constexpr int width() const { return size_.width(); }
set_width(int width)78 void set_width(int width) { size_.set_width(GetClampedValue(x(), width)); }
79
height()80 constexpr int height() const { return size_.height(); }
set_height(int height)81 void set_height(int height) {
82 size_.set_height(GetClampedValue(y(), height));
83 }
84
origin()85 constexpr const Point& origin() const { return origin_; }
set_origin(const Point & origin)86 void set_origin(const Point& origin) {
87 origin_ = origin;
88 // Ensure that width and height remain valid.
89 set_width(width());
90 set_height(height());
91 }
92
size()93 constexpr const Size& size() const { return size_; }
set_size(const Size & size)94 void set_size(const Size& size) {
95 set_width(size.width());
96 set_height(size.height());
97 }
98
right()99 constexpr int right() const { return x() + width(); }
bottom()100 constexpr int bottom() const { return y() + height(); }
101
top_right()102 constexpr Point top_right() const { return Point(right(), y()); }
bottom_left()103 constexpr Point bottom_left() const { return Point(x(), bottom()); }
bottom_right()104 constexpr Point bottom_right() const { return Point(right(), bottom()); }
105
OffsetFromOrigin()106 Vector2d OffsetFromOrigin() const { return Vector2d(x(), y()); }
107
SetRect(int x,int y,int width,int height)108 void SetRect(int x, int y, int width, int height) {
109 origin_.SetPoint(x, y);
110 // Ensure that width and height remain valid.
111 set_width(width);
112 set_height(height);
113 }
114
115 // Use in place of SetRect() when you know the edges of the rectangle instead
116 // of the dimensions, rather than trying to determine the width/height
117 // yourself. This safely handles cases where the width/height would overflow.
118 void SetByBounds(int left, int top, int right, int bottom);
119
120 // Shrink the rectangle by a horizontal and vertical distance on all sides.
Inset(int horizontal,int vertical)121 void Inset(int horizontal, int vertical) {
122 Inset(horizontal, vertical, horizontal, vertical);
123 }
124
125 // Shrink the rectangle by the given insets.
126 void Inset(const Insets& insets);
127
128 // Shrink the rectangle by the specified amount on each side.
129 void Inset(int left, int top, int right, int bottom);
130
131 // Move the rectangle by a horizontal and vertical distance.
132 void Offset(int horizontal, int vertical);
Offset(const Vector2d & distance)133 void Offset(const Vector2d& distance) { Offset(distance.x(), distance.y()); }
134 void operator+=(const Vector2d& offset);
135 void operator-=(const Vector2d& offset);
136
137 Insets InsetsFrom(const Rect& inner) const;
138
139 // Returns true if the area of the rectangle is zero.
IsEmpty()140 bool IsEmpty() const { return size_.IsEmpty(); }
141
142 // A rect is less than another rect if its origin is less than
143 // the other rect's origin. If the origins are equal, then the
144 // shortest rect is less than the other. If the origin and the
145 // height are equal, then the narrowest rect is less than.
146 // This comparison is required to use Rects in sets, or sorted
147 // vectors.
148 bool operator<(const Rect& other) const;
149
150 // Returns true if the point identified by point_x and point_y falls inside
151 // this rectangle. The point (x, y) is inside the rectangle, but the
152 // point (x + width, y + height) is not.
153 bool Contains(int point_x, int point_y) const;
154
155 // Returns true if the specified point is contained by this rectangle.
Contains(const Point & point)156 bool Contains(const Point& point) const {
157 return Contains(point.x(), point.y());
158 }
159
160 // Returns true if this rectangle contains the specified rectangle.
161 bool Contains(const Rect& rect) const;
162
163 // Returns true if this rectangle intersects the specified rectangle.
164 // An empty rectangle doesn't intersect any rectangle.
165 bool Intersects(const Rect& rect) const;
166
167 // Computes the intersection of this rectangle with the given rectangle.
168 void Intersect(const Rect& rect);
169
170 // Computes the union of this rectangle with the given rectangle. The union
171 // is the smallest rectangle containing both rectangles.
172 void Union(const Rect& rect);
173
174 // Computes the rectangle resulting from subtracting |rect| from |*this|,
175 // i.e. the bounding rect of |Region(*this) - Region(rect)|.
176 void Subtract(const Rect& rect);
177
178 // Fits as much of the receiving rectangle into the supplied rectangle as
179 // possible, becoming the result. For example, if the receiver had
180 // a x-location of 2 and a width of 4, and the supplied rectangle had
181 // an x-location of 0 with a width of 5, the returned rectangle would have
182 // an x-location of 1 with a width of 4.
183 void AdjustToFit(const Rect& rect);
184
185 // Returns the center of this rectangle.
186 Point CenterPoint() const;
187
188 // Becomes a rectangle that has the same center point but with a size capped
189 // at given |size|.
190 void ClampToCenteredSize(const Size& size);
191
192 // Splits |this| in two halves, |left_half| and |right_half|.
193 void SplitVertically(Rect* left_half, Rect* right_half) const;
194
195 // Returns true if this rectangle shares an entire edge (i.e., same width or
196 // same height) with the given rectangle, and the rectangles do not overlap.
197 bool SharesEdgeWith(const Rect& rect) const;
198
199 // Returns the manhattan distance from the rect to the point. If the point is
200 // inside the rect, returns 0.
201 int ManhattanDistanceToPoint(const Point& point) const;
202
203 // Returns the manhattan distance between the contents of this rect and the
204 // contents of the given rect. That is, if the intersection of the two rects
205 // is non-empty then the function returns 0. If the rects share a side, it
206 // returns the smallest non-zero value appropriate for int.
207 int ManhattanInternalDistance(const Rect& rect) const;
208
209 std::string ToString() const;
210
211 bool ApproximatelyEqual(const Rect& rect, int tolerance) const;
212
213 private:
214 gfx::Point origin_;
215 gfx::Size size_;
216
217 // Returns true iff a+b would overflow max int.
AddWouldOverflow(int a,int b)218 static constexpr bool AddWouldOverflow(int a, int b) {
219 // In this function, GCC tries to make optimizations that would only work if
220 // max - a wouldn't overflow but it isn't smart enough to notice that a > 0.
221 // So cast everything to unsigned to avoid this. As it is guaranteed that
222 // max - a and b are both already positive, the cast is a noop.
223 //
224 // This is intended to be: a > 0 && max - a < b
225 return a > 0 && b > 0 &&
226 static_cast<unsigned>(std::numeric_limits<int>::max() - a) <
227 static_cast<unsigned>(b);
228 }
229
230 // Clamp the size to avoid integer overflow in bottom() and right().
231 // This returns the width given an origin and a width.
232 // TODO(enne): this should probably use base::ClampAdd, but that
233 // function is not a constexpr.
GetClampedValue(int origin,int size)234 static constexpr int GetClampedValue(int origin, int size) {
235 return AddWouldOverflow(origin, size)
236 ? std::numeric_limits<int>::max() - origin
237 : size;
238 }
239 };
240
241 inline bool operator==(const Rect& lhs, const Rect& rhs) {
242 return lhs.origin() == rhs.origin() && lhs.size() == rhs.size();
243 }
244
245 inline bool operator!=(const Rect& lhs, const Rect& rhs) {
246 return !(lhs == rhs);
247 }
248
249 GFX_EXPORT Rect operator+(const Rect& lhs, const Vector2d& rhs);
250 GFX_EXPORT Rect operator-(const Rect& lhs, const Vector2d& rhs);
251
252 inline Rect operator+(const Vector2d& lhs, const Rect& rhs) {
253 return rhs + lhs;
254 }
255
256 GFX_EXPORT Rect IntersectRects(const Rect& a, const Rect& b);
257 GFX_EXPORT Rect UnionRects(const Rect& a, const Rect& b);
258 GFX_EXPORT Rect SubtractRects(const Rect& a, const Rect& b);
259
260 // Constructs a rectangle with |p1| and |p2| as opposite corners.
261 //
262 // This could also be thought of as "the smallest rect that contains both
263 // points", except that we consider points on the right/bottom edges of the
264 // rect to be outside the rect. So technically one or both points will not be
265 // contained within the rect, because they will appear on one of these edges.
266 GFX_EXPORT Rect BoundingRect(const Point& p1, const Point& p2);
267
268 // Scales the rect and returns the enclosing rect. Use this only the inputs are
269 // known to not overflow. Use ScaleToEnclosingRectSafe if the inputs are
270 // unknown and need to use saturated math.
ScaleToEnclosingRect(const Rect & rect,float x_scale,float y_scale)271 inline Rect ScaleToEnclosingRect(const Rect& rect,
272 float x_scale,
273 float y_scale) {
274 if (x_scale == 1.f && y_scale == 1.f)
275 return rect;
276 // These next functions cast instead of using e.g. ToFlooredInt() because we
277 // haven't checked to ensure that the clamping behavior of the helper
278 // functions doesn't degrade performance, and callers shouldn't be passing
279 // values that cause overflow anyway.
280 DCHECK(base::IsValueInRangeForNumericType<int>(
281 std::floor(rect.x() * x_scale)));
282 DCHECK(base::IsValueInRangeForNumericType<int>(
283 std::floor(rect.y() * y_scale)));
284 DCHECK(base::IsValueInRangeForNumericType<int>(
285 std::ceil(rect.right() * x_scale)));
286 DCHECK(base::IsValueInRangeForNumericType<int>(
287 std::ceil(rect.bottom() * y_scale)));
288 int x = static_cast<int>(std::floor(rect.x() * x_scale));
289 int y = static_cast<int>(std::floor(rect.y() * y_scale));
290 int r = rect.width() == 0 ?
291 x : static_cast<int>(std::ceil(rect.right() * x_scale));
292 int b = rect.height() == 0 ?
293 y : static_cast<int>(std::ceil(rect.bottom() * y_scale));
294 return Rect(x, y, r - x, b - y);
295 }
296
ScaleToEnclosingRect(const Rect & rect,float scale)297 inline Rect ScaleToEnclosingRect(const Rect& rect, float scale) {
298 return ScaleToEnclosingRect(rect, scale, scale);
299 }
300
301 // ScaleToEnclosingRect but clamping instead of asserting if the resulting rect
302 // would overflow.
ScaleToEnclosingRectSafe(const Rect & rect,float x_scale,float y_scale)303 inline Rect ScaleToEnclosingRectSafe(const Rect& rect,
304 float x_scale,
305 float y_scale) {
306 if (x_scale == 1.f && y_scale == 1.f)
307 return rect;
308 int x = base::saturated_cast<int>(std::floor(rect.x() * x_scale));
309 int y = base::saturated_cast<int>(std::floor(rect.y() * y_scale));
310 int w = base::saturated_cast<int>(std::ceil(rect.width() * x_scale));
311 int h = base::saturated_cast<int>(std::ceil(rect.height() * y_scale));
312 return Rect(x, y, w, h);
313 }
314
ScaleToEnclosingRectSafe(const Rect & rect,float scale)315 inline Rect ScaleToEnclosingRectSafe(const Rect& rect, float scale) {
316 return ScaleToEnclosingRectSafe(rect, scale, scale);
317 }
318
ScaleToEnclosedRect(const Rect & rect,float x_scale,float y_scale)319 inline Rect ScaleToEnclosedRect(const Rect& rect,
320 float x_scale,
321 float y_scale) {
322 if (x_scale == 1.f && y_scale == 1.f)
323 return rect;
324 DCHECK(base::IsValueInRangeForNumericType<int>(
325 std::ceil(rect.x() * x_scale)));
326 DCHECK(base::IsValueInRangeForNumericType<int>(
327 std::ceil(rect.y() * y_scale)));
328 DCHECK(base::IsValueInRangeForNumericType<int>(
329 std::floor(rect.right() * x_scale)));
330 DCHECK(base::IsValueInRangeForNumericType<int>(
331 std::floor(rect.bottom() * y_scale)));
332 int x = static_cast<int>(std::ceil(rect.x() * x_scale));
333 int y = static_cast<int>(std::ceil(rect.y() * y_scale));
334 int r = rect.width() == 0 ?
335 x : static_cast<int>(std::floor(rect.right() * x_scale));
336 int b = rect.height() == 0 ?
337 y : static_cast<int>(std::floor(rect.bottom() * y_scale));
338 return Rect(x, y, r - x, b - y);
339 }
340
ScaleToEnclosedRect(const Rect & rect,float scale)341 inline Rect ScaleToEnclosedRect(const Rect& rect, float scale) {
342 return ScaleToEnclosedRect(rect, scale, scale);
343 }
344
345 // Scales |rect| by scaling its four corner points. If the corner points lie on
346 // non-integral coordinate after scaling, their values are rounded to the
347 // nearest integer.
348 // This is helpful during layout when relative positions of multiple gfx::Rect
349 // in a given coordinate space needs to be same after scaling as it was before
350 // scaling. ie. this gives a lossless relative positioning of rects.
ScaleToRoundedRect(const Rect & rect,float x_scale,float y_scale)351 inline Rect ScaleToRoundedRect(const Rect& rect, float x_scale, float y_scale) {
352 if (x_scale == 1.f && y_scale == 1.f)
353 return rect;
354
355 DCHECK(
356 base::IsValueInRangeForNumericType<int>(std::round(rect.x() * x_scale)));
357 DCHECK(
358 base::IsValueInRangeForNumericType<int>(std::round(rect.y() * y_scale)));
359 DCHECK(base::IsValueInRangeForNumericType<int>(
360 std::round(rect.right() * x_scale)));
361 DCHECK(base::IsValueInRangeForNumericType<int>(
362 std::round(rect.bottom() * y_scale)));
363
364 int x = static_cast<int>(std::round(rect.x() * x_scale));
365 int y = static_cast<int>(std::round(rect.y() * y_scale));
366 int r = rect.width() == 0
367 ? x
368 : static_cast<int>(std::round(rect.right() * x_scale));
369 int b = rect.height() == 0
370 ? y
371 : static_cast<int>(std::round(rect.bottom() * y_scale));
372
373 return Rect(x, y, r - x, b - y);
374 }
375
ScaleToRoundedRect(const Rect & rect,float scale)376 inline Rect ScaleToRoundedRect(const Rect& rect, float scale) {
377 return ScaleToRoundedRect(rect, scale, scale);
378 }
379
380 // This is declared here for use in gtest-based unit tests but is defined in
381 // the //ui/gfx:test_support target. Depend on that to use this in your unit
382 // test. This should not be used in production code - call ToString() instead.
383 void PrintTo(const Rect& rect, ::std::ostream* os);
384
385 } // namespace gfx
386
387 #endif // UI_GFX_GEOMETRY_RECT_H_
388