• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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