• 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(); }
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