rect.h 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371
  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. // Defines a simple integer rectangle class. The containment semantics
  5. // are array-like; that is, the coordinate (x, y) is considered to be
  6. // contained by the rectangle, but the coordinate (x + width, y) is not.
  7. // The class will happily let you create malformed rectangles (that is,
  8. // rectangles with negative width and/or height), but there will be assertions
  9. // in the operations (such as Contains()) to complain in this case.
  10. #ifndef UI_GFX_GEOMETRY_RECT_H_
  11. #define UI_GFX_GEOMETRY_RECT_H_
  12. #include <cmath>
  13. #include <iosfwd>
  14. #include <string>
  15. #include "base/check.h"
  16. #include "base/numerics/clamped_math.h"
  17. #include "base/numerics/safe_conversions.h"
  18. #include "build/build_config.h"
  19. #include "ui/gfx/geometry/insets.h"
  20. #include "ui/gfx/geometry/outsets.h"
  21. #include "ui/gfx/geometry/point.h"
  22. #include "ui/gfx/geometry/size.h"
  23. #include "ui/gfx/geometry/vector2d.h"
  24. #if BUILDFLAG(IS_WIN)
  25. typedef struct tagRECT RECT;
  26. #elif BUILDFLAG(IS_APPLE)
  27. typedef struct CGRect CGRect;
  28. #endif
  29. namespace gfx {
  30. class GEOMETRY_EXPORT Rect {
  31. public:
  32. constexpr Rect() = default;
  33. constexpr Rect(int width, int height) : size_(width, height) {}
  34. constexpr Rect(int x, int y, int width, int height)
  35. : origin_(x, y),
  36. size_(ClampWidthOrHeight(x, width), ClampWidthOrHeight(y, height)) {}
  37. constexpr explicit Rect(const Size& size) : size_(size) {}
  38. constexpr Rect(const Point& origin, const Size& size)
  39. : origin_(origin),
  40. size_(ClampWidthOrHeight(origin.x(), size.width()),
  41. ClampWidthOrHeight(origin.y(), size.height())) {}
  42. #if BUILDFLAG(IS_WIN)
  43. explicit Rect(const RECT& r);
  44. #elif BUILDFLAG(IS_APPLE)
  45. explicit Rect(const CGRect& r);
  46. #endif
  47. #if BUILDFLAG(IS_WIN)
  48. // Construct an equivalent Win32 RECT object.
  49. RECT ToRECT() const;
  50. #elif BUILDFLAG(IS_APPLE)
  51. // Construct an equivalent CoreGraphics object.
  52. CGRect ToCGRect() const;
  53. #endif
  54. constexpr int x() const { return origin_.x(); }
  55. // Sets the X position while preserving the width.
  56. void set_x(int x) {
  57. origin_.set_x(x);
  58. size_.set_width(ClampWidthOrHeight(x, width()));
  59. }
  60. constexpr int y() const { return origin_.y(); }
  61. // Sets the Y position while preserving the height.
  62. void set_y(int y) {
  63. origin_.set_y(y);
  64. size_.set_height(ClampWidthOrHeight(y, height()));
  65. }
  66. constexpr int width() const { return size_.width(); }
  67. void set_width(int width) { size_.set_width(ClampWidthOrHeight(x(), width)); }
  68. constexpr int height() const { return size_.height(); }
  69. void set_height(int height) {
  70. size_.set_height(ClampWidthOrHeight(y(), height));
  71. }
  72. constexpr const Point& origin() const { return origin_; }
  73. void set_origin(const Point& origin) {
  74. origin_ = origin;
  75. // Ensure that width and height remain valid.
  76. set_width(width());
  77. set_height(height());
  78. }
  79. constexpr const Size& size() const { return size_; }
  80. void set_size(const Size& size) {
  81. set_width(size.width());
  82. set_height(size.height());
  83. }
  84. constexpr int right() const { return x() + width(); }
  85. constexpr int bottom() const { return y() + height(); }
  86. constexpr Point top_right() const { return Point(right(), y()); }
  87. constexpr Point bottom_left() const { return Point(x(), bottom()); }
  88. constexpr Point bottom_right() const { return Point(right(), bottom()); }
  89. constexpr Point left_center() const { return Point(x(), y() + height() / 2); }
  90. constexpr Point top_center() const { return Point(x() + width() / 2, y()); }
  91. constexpr Point right_center() const {
  92. return Point(right(), y() + height() / 2);
  93. }
  94. constexpr Point bottom_center() const {
  95. return Point(x() + width() / 2, bottom());
  96. }
  97. Vector2d OffsetFromOrigin() const { return Vector2d(x(), y()); }
  98. void SetRect(int x, int y, int width, int height) {
  99. origin_.SetPoint(x, y);
  100. // Ensure that width and height remain valid.
  101. set_width(width);
  102. set_height(height);
  103. }
  104. // Use in place of SetRect() when you know the edges of the rectangle instead
  105. // of the dimensions, rather than trying to determine the width/height
  106. // yourself. This safely handles cases where the width/height would overflow.
  107. void SetByBounds(int left, int top, int right, int bottom) {
  108. SetHorizontalBounds(left, right);
  109. SetVerticalBounds(top, bottom);
  110. }
  111. void SetHorizontalBounds(int left, int right) {
  112. set_x(left);
  113. set_width(base::ClampSub(right, left));
  114. if (UNLIKELY(this->right() != right))
  115. AdjustForSaturatedRight(right);
  116. }
  117. void SetVerticalBounds(int top, int bottom) {
  118. set_y(top);
  119. set_height(base::ClampSub(bottom, top));
  120. if (UNLIKELY(this->bottom() != bottom))
  121. AdjustForSaturatedBottom(bottom);
  122. }
  123. // Shrink the rectangle by |inset| on all sides.
  124. void Inset(int inset) { Inset(Insets(inset)); }
  125. // Shrink the rectangle by the given |insets|.
  126. void Inset(const Insets& insets);
  127. // Expand the rectangle by |outset| on all sides.
  128. void Outset(int outset) { Inset(-outset); }
  129. // Expand the rectangle by the given |outsets|.
  130. void Outset(const Outsets& outsets) { Inset(outsets.ToInsets()); }
  131. // Move the rectangle by a horizontal and vertical distance.
  132. void Offset(int horizontal, int vertical) {
  133. Offset(Vector2d(horizontal, vertical));
  134. }
  135. void Offset(const Vector2d& distance);
  136. void operator+=(const Vector2d& offset) { Offset(offset); }
  137. void operator-=(const Vector2d& offset) { Offset(-offset); }
  138. Insets InsetsFrom(const Rect& inner) const;
  139. // Returns true if the area of the rectangle is zero.
  140. bool IsEmpty() const { return size_.IsEmpty(); }
  141. // A rect is less than another rect if its origin is less than
  142. // the other rect's origin. If the origins are equal, then the
  143. // shortest rect is less than the other. If the origin and the
  144. // height are equal, then the narrowest rect is less than.
  145. // This comparison is required to use Rects in sets, or sorted
  146. // vectors.
  147. bool operator<(const Rect& other) const;
  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. // Returns true if the specified point is contained by this rectangle.
  153. bool Contains(const Point& point) const {
  154. return Contains(point.x(), point.y());
  155. }
  156. // Returns true if this rectangle contains the specified rectangle.
  157. bool Contains(const Rect& rect) const;
  158. // Returns true if this rectangle intersects the specified rectangle.
  159. // An empty rectangle doesn't intersect any rectangle.
  160. bool Intersects(const Rect& rect) const;
  161. // Sets this rect to be the intersection of this rectangle with the given
  162. // rectangle.
  163. void Intersect(const Rect& rect);
  164. // Sets this rect to be the intersection of itself and |rect| using
  165. // edge-inclusive geometry. If the two rectangles overlap but the overlap
  166. // region is zero-area (either because one of the two rectangles is zero-area,
  167. // or because the rectangles overlap at an edge or a corner), the result is
  168. // the zero-area intersection. The return value indicates whether the two
  169. // rectangle actually have an intersection, since checking the result for
  170. // isEmpty() is not conclusive.
  171. bool InclusiveIntersect(const Rect& rect);
  172. // Sets this rect to be the union of this rectangle with the given rectangle.
  173. // The union is the smallest rectangle containing both rectangles if not
  174. // empty. If both rects are empty, this rect will become |rect|.
  175. void Union(const Rect& rect);
  176. // Similar to Union(), but the result will contain both rectangles even if
  177. // either of them is empty. For example, union of (100, 100, 0x0) and
  178. // (200, 200, 50x0) is (100, 100, 150x100).
  179. void UnionEvenIfEmpty(const Rect& rect);
  180. // Sets this rect to be the rectangle resulting from subtracting |rect| from
  181. // |*this|, i.e. the bounding rect of |Region(*this) - Region(rect)|.
  182. void Subtract(const Rect& rect);
  183. // Fits as much of the receiving rectangle into the supplied rectangle as
  184. // possible, becoming the result. For example, if the receiver had
  185. // a x-location of 2 and a width of 4, and the supplied rectangle had
  186. // an x-location of 0 with a width of 5, the returned rectangle would have
  187. // an x-location of 1 with a width of 4.
  188. void AdjustToFit(const Rect& rect);
  189. // Returns the center of this rectangle.
  190. Point CenterPoint() const;
  191. // Becomes a rectangle that has the same center point but with a size capped
  192. // at given |size|.
  193. void ClampToCenteredSize(const Size& size);
  194. // Transpose x and y axis.
  195. void Transpose();
  196. // Splits |this| in two halves, |left_half| and |right_half|.
  197. void SplitVertically(Rect* left_half, Rect* right_half) const;
  198. // Returns true if this rectangle shares an entire edge (i.e., same width or
  199. // same height) with the given rectangle, and the rectangles do not overlap.
  200. bool SharesEdgeWith(const Rect& rect) const;
  201. // Returns the manhattan distance from the rect to the point. If the point is
  202. // inside the rect, returns 0.
  203. int ManhattanDistanceToPoint(const Point& point) const;
  204. // Returns the manhattan distance between the contents of this rect and the
  205. // contents of the given rect. That is, if the intersection of the two rects
  206. // is non-empty then the function returns 0. If the rects share a side, it
  207. // returns the smallest non-zero value appropriate for int.
  208. int ManhattanInternalDistance(const Rect& rect) const;
  209. std::string ToString() const;
  210. bool ApproximatelyEqual(const Rect& rect, int tolerance) const;
  211. private:
  212. // Clamp the width/height to avoid integer overflow in bottom() and right().
  213. // This returns the clamped width/height given an |x_or_y| and a
  214. // |width_or_height|.
  215. static constexpr int ClampWidthOrHeight(int x_or_y, int width_or_height) {
  216. return base::ClampAdd(x_or_y, width_or_height) - x_or_y;
  217. }
  218. void AdjustForSaturatedRight(int right);
  219. void AdjustForSaturatedBottom(int bottom);
  220. gfx::Point origin_;
  221. gfx::Size size_;
  222. };
  223. inline bool operator==(const Rect& lhs, const Rect& rhs) {
  224. return lhs.origin() == rhs.origin() && lhs.size() == rhs.size();
  225. }
  226. inline bool operator!=(const Rect& lhs, const Rect& rhs) {
  227. return !(lhs == rhs);
  228. }
  229. GEOMETRY_EXPORT Rect operator+(const Rect& lhs, const Vector2d& rhs);
  230. GEOMETRY_EXPORT Rect operator-(const Rect& lhs, const Vector2d& rhs);
  231. inline Rect operator+(const Vector2d& lhs, const Rect& rhs) {
  232. return rhs + lhs;
  233. }
  234. GEOMETRY_EXPORT Rect IntersectRects(const Rect& a, const Rect& b);
  235. GEOMETRY_EXPORT Rect UnionRects(const Rect& a, const Rect& b);
  236. GEOMETRY_EXPORT Rect UnionRectsEvenIfEmpty(const Rect& a, const Rect& b);
  237. GEOMETRY_EXPORT Rect SubtractRects(const Rect& a, const Rect& b);
  238. // Constructs a rectangle with |p1| and |p2| as opposite corners.
  239. //
  240. // This could also be thought of as "the smallest rect that contains both
  241. // points", except that we consider points on the right/bottom edges of the
  242. // rect to be outside the rect. So technically one or both points will not be
  243. // contained within the rect, because they will appear on one of these edges.
  244. GEOMETRY_EXPORT Rect BoundingRect(const Point& p1, const Point& p2);
  245. // Scales the rect and returns the enclosing rect. The components are clamped
  246. // if they would overflow.
  247. inline Rect ScaleToEnclosingRect(const Rect& rect,
  248. float x_scale,
  249. float y_scale) {
  250. if (x_scale == 1.f && y_scale == 1.f)
  251. return rect;
  252. int x = base::ClampFloor(rect.x() * x_scale);
  253. int y = base::ClampFloor(rect.y() * y_scale);
  254. int r = rect.width() == 0 ? x : base::ClampCeil(rect.right() * x_scale);
  255. int b = rect.height() == 0 ? y : base::ClampCeil(rect.bottom() * y_scale);
  256. Rect result;
  257. result.SetByBounds(x, y, r, b);
  258. return result;
  259. }
  260. inline Rect ScaleToEnclosingRect(const Rect& rect, float scale) {
  261. return ScaleToEnclosingRect(rect, scale, scale);
  262. }
  263. inline Rect ScaleToEnclosedRect(const Rect& rect,
  264. float x_scale,
  265. float y_scale) {
  266. if (x_scale == 1.f && y_scale == 1.f)
  267. return rect;
  268. int x = base::ClampCeil(rect.x() * x_scale);
  269. int y = base::ClampCeil(rect.y() * y_scale);
  270. int r = rect.width() == 0 ? x : base::ClampFloor(rect.right() * x_scale);
  271. int b = rect.height() == 0 ? y : base::ClampFloor(rect.bottom() * y_scale);
  272. Rect result;
  273. result.SetByBounds(x, y, r, b);
  274. return result;
  275. }
  276. inline Rect ScaleToEnclosedRect(const Rect& rect, float scale) {
  277. return ScaleToEnclosedRect(rect, scale, scale);
  278. }
  279. // Scales |rect| by scaling its four corner points. If the corner points lie on
  280. // non-integral coordinate after scaling, their values are rounded to the
  281. // nearest integer. The components are clamped if they would overflow.
  282. // This is helpful during layout when relative positions of multiple gfx::Rect
  283. // in a given coordinate space needs to be same after scaling as it was before
  284. // scaling. ie. this gives a lossless relative positioning of rects.
  285. inline Rect ScaleToRoundedRect(const Rect& rect, float x_scale, float y_scale) {
  286. if (x_scale == 1.f && y_scale == 1.f)
  287. return rect;
  288. int x = base::ClampRound(rect.x() * x_scale);
  289. int y = base::ClampRound(rect.y() * y_scale);
  290. int r = rect.width() == 0 ? x : base::ClampRound(rect.right() * x_scale);
  291. int b = rect.height() == 0 ? y : base::ClampRound(rect.bottom() * y_scale);
  292. Rect result;
  293. result.SetByBounds(x, y, r, b);
  294. return result;
  295. }
  296. inline Rect ScaleToRoundedRect(const Rect& rect, float scale) {
  297. return ScaleToRoundedRect(rect, scale, scale);
  298. }
  299. // Return a maximum rectangle that is covered by the a or b.
  300. GEOMETRY_EXPORT Rect MaximumCoveredRect(const Rect& a, const Rect& b);
  301. // This is declared here for use in gtest-based unit tests but is defined in
  302. // the //ui/gfx:test_support target. Depend on that to use this in your unit
  303. // test. This should not be used in production code - call ToString() instead.
  304. void PrintTo(const Rect& rect, ::std::ostream* os);
  305. } // namespace gfx
  306. #endif // UI_GFX_GEOMETRY_RECT_H_