rect.cc 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394
  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. #include "ui/gfx/geometry/rect.h"
  5. #include <algorithm>
  6. #include "base/check.h"
  7. #include "base/numerics/clamped_math.h"
  8. #include "base/strings/stringprintf.h"
  9. #include "build/build_config.h"
  10. #include "ui/gfx/geometry/insets.h"
  11. #include "ui/gfx/geometry/outsets.h"
  12. #if BUILDFLAG(IS_WIN)
  13. #include <windows.h>
  14. #elif BUILDFLAG(IS_IOS)
  15. #include <CoreGraphics/CoreGraphics.h>
  16. #elif BUILDFLAG(IS_MAC)
  17. #include <ApplicationServices/ApplicationServices.h>
  18. #endif
  19. namespace {
  20. void AdjustAlongAxis(int dst_origin, int dst_size, int* origin, int* size) {
  21. *size = std::min(dst_size, *size);
  22. if (*origin < dst_origin)
  23. *origin = dst_origin;
  24. else
  25. *origin = std::min(dst_origin + dst_size, *origin + *size) - *size;
  26. }
  27. // This is the per-axis heuristic for picking the most useful origin and
  28. // width/height to represent the input range.
  29. void SaturatedClampRange(int min, int max, int* origin, int* span) {
  30. if (max < min) {
  31. *span = 0;
  32. *origin = min;
  33. return;
  34. }
  35. int effective_span = base::ClampSub(max, min);
  36. int span_loss = base::ClampSub(max, min + effective_span);
  37. // If the desired width is within the limits of ints, we can just
  38. // use the simple computations to represent the range precisely.
  39. if (span_loss == 0) {
  40. *span = effective_span;
  41. *origin = min;
  42. return;
  43. }
  44. // Now we have to approximate. If one of min or max is close enough
  45. // to zero we choose to represent that one precisely. The other side is
  46. // probably practically "infinite", so we move it.
  47. constexpr unsigned kMaxDimension = std::numeric_limits<int>::max() / 2;
  48. if (base::SafeUnsignedAbs(max) < kMaxDimension) {
  49. // Maintain origin + span == max.
  50. *span = effective_span;
  51. *origin = max - effective_span;
  52. } else if (base::SafeUnsignedAbs(min) < kMaxDimension) {
  53. // Maintain origin == min.
  54. *span = effective_span;
  55. *origin = min;
  56. } else {
  57. // Both are big, so keep the center.
  58. *span = effective_span;
  59. *origin = min + span_loss / 2;
  60. }
  61. }
  62. } // namespace
  63. namespace gfx {
  64. #if BUILDFLAG(IS_WIN)
  65. Rect::Rect(const RECT& r)
  66. : origin_(r.left, r.top),
  67. size_(std::abs(r.right - r.left), std::abs(r.bottom - r.top)) {}
  68. RECT Rect::ToRECT() const {
  69. RECT r;
  70. r.left = x();
  71. r.right = right();
  72. r.top = y();
  73. r.bottom = bottom();
  74. return r;
  75. }
  76. #elif BUILDFLAG(IS_APPLE)
  77. Rect::Rect(const CGRect& r)
  78. : origin_(r.origin.x, r.origin.y), size_(r.size.width, r.size.height) {}
  79. CGRect Rect::ToCGRect() const {
  80. return CGRectMake(x(), y(), width(), height());
  81. }
  82. #endif
  83. void Rect::AdjustForSaturatedRight(int right) {
  84. int new_x, width;
  85. SaturatedClampRange(x(), right, &new_x, &width);
  86. set_x(new_x);
  87. size_.set_width(width);
  88. }
  89. void Rect::AdjustForSaturatedBottom(int bottom) {
  90. int new_y, height;
  91. SaturatedClampRange(y(), bottom, &new_y, &height);
  92. set_y(new_y);
  93. size_.set_height(height);
  94. }
  95. void Rect::Inset(const Insets& insets) {
  96. origin_ += Vector2d(insets.left(), insets.top());
  97. set_width(base::ClampSub(width(), insets.width()));
  98. set_height(base::ClampSub(height(), insets.height()));
  99. }
  100. void Rect::Offset(const Vector2d& distance) {
  101. origin_ += distance;
  102. // Ensure that width and height remain valid.
  103. set_width(width());
  104. set_height(height());
  105. }
  106. Insets Rect::InsetsFrom(const Rect& inner) const {
  107. return Insets::TLBR(inner.y() - y(), inner.x() - x(),
  108. bottom() - inner.bottom(), right() - inner.right());
  109. }
  110. bool Rect::operator<(const Rect& other) const {
  111. if (origin_ == other.origin_) {
  112. if (width() == other.width()) {
  113. return height() < other.height();
  114. } else {
  115. return width() < other.width();
  116. }
  117. } else {
  118. return origin_ < other.origin_;
  119. }
  120. }
  121. bool Rect::Contains(int point_x, int point_y) const {
  122. return (point_x >= x()) && (point_x < right()) && (point_y >= y()) &&
  123. (point_y < bottom());
  124. }
  125. bool Rect::Contains(const Rect& rect) const {
  126. return (rect.x() >= x() && rect.right() <= right() && rect.y() >= y() &&
  127. rect.bottom() <= bottom());
  128. }
  129. bool Rect::Intersects(const Rect& rect) const {
  130. return !(IsEmpty() || rect.IsEmpty() || rect.x() >= right() ||
  131. rect.right() <= x() || rect.y() >= bottom() || rect.bottom() <= y());
  132. }
  133. void Rect::Intersect(const Rect& rect) {
  134. if (IsEmpty() || rect.IsEmpty()) {
  135. SetRect(0, 0, 0, 0); // Throws away empty position.
  136. return;
  137. }
  138. int left = std::max(x(), rect.x());
  139. int top = std::max(y(), rect.y());
  140. int new_right = std::min(right(), rect.right());
  141. int new_bottom = std::min(bottom(), rect.bottom());
  142. if (left >= new_right || top >= new_bottom) {
  143. SetRect(0, 0, 0, 0); // Throws away empty position.
  144. return;
  145. }
  146. SetByBounds(left, top, new_right, new_bottom);
  147. }
  148. bool Rect::InclusiveIntersect(const Rect& rect) {
  149. int left = std::max(x(), rect.x());
  150. int top = std::max(y(), rect.y());
  151. int new_right = std::min(right(), rect.right());
  152. int new_bottom = std::min(bottom(), rect.bottom());
  153. // Return a clean empty rectangle for non-intersecting cases.
  154. if (left > new_right || top > new_bottom) {
  155. SetRect(0, 0, 0, 0);
  156. return false;
  157. }
  158. SetByBounds(left, top, new_right, new_bottom);
  159. return true;
  160. }
  161. void Rect::Union(const Rect& rect) {
  162. if (IsEmpty()) {
  163. *this = rect;
  164. return;
  165. }
  166. if (rect.IsEmpty())
  167. return;
  168. UnionEvenIfEmpty(rect);
  169. }
  170. void Rect::UnionEvenIfEmpty(const Rect& rect) {
  171. SetByBounds(std::min(x(), rect.x()), std::min(y(), rect.y()),
  172. std::max(right(), rect.right()),
  173. std::max(bottom(), rect.bottom()));
  174. }
  175. void Rect::Subtract(const Rect& rect) {
  176. if (!Intersects(rect))
  177. return;
  178. if (rect.Contains(*this)) {
  179. SetRect(0, 0, 0, 0);
  180. return;
  181. }
  182. int rx = x();
  183. int ry = y();
  184. int rr = right();
  185. int rb = bottom();
  186. if (rect.y() <= y() && rect.bottom() >= bottom()) {
  187. // complete intersection in the y-direction
  188. if (rect.x() <= x()) {
  189. rx = rect.right();
  190. } else if (rect.right() >= right()) {
  191. rr = rect.x();
  192. }
  193. } else if (rect.x() <= x() && rect.right() >= right()) {
  194. // complete intersection in the x-direction
  195. if (rect.y() <= y()) {
  196. ry = rect.bottom();
  197. } else if (rect.bottom() >= bottom()) {
  198. rb = rect.y();
  199. }
  200. }
  201. SetByBounds(rx, ry, rr, rb);
  202. }
  203. void Rect::AdjustToFit(const Rect& rect) {
  204. int new_x = x();
  205. int new_y = y();
  206. int new_width = width();
  207. int new_height = height();
  208. AdjustAlongAxis(rect.x(), rect.width(), &new_x, &new_width);
  209. AdjustAlongAxis(rect.y(), rect.height(), &new_y, &new_height);
  210. SetRect(new_x, new_y, new_width, new_height);
  211. }
  212. Point Rect::CenterPoint() const {
  213. return Point(x() + width() / 2, y() + height() / 2);
  214. }
  215. void Rect::ClampToCenteredSize(const Size& size) {
  216. int new_width = std::min(width(), size.width());
  217. int new_height = std::min(height(), size.height());
  218. int new_x = x() + (width() - new_width) / 2;
  219. int new_y = y() + (height() - new_height) / 2;
  220. SetRect(new_x, new_y, new_width, new_height);
  221. }
  222. void Rect::Transpose() {
  223. SetRect(y(), x(), height(), width());
  224. }
  225. void Rect::SplitVertically(Rect* left_half, Rect* right_half) const {
  226. DCHECK(left_half);
  227. DCHECK(right_half);
  228. left_half->SetRect(x(), y(), width() / 2, height());
  229. right_half->SetRect(
  230. left_half->right(), y(), width() - left_half->width(), height());
  231. }
  232. bool Rect::SharesEdgeWith(const Rect& rect) const {
  233. return (y() == rect.y() && height() == rect.height() &&
  234. (x() == rect.right() || right() == rect.x())) ||
  235. (x() == rect.x() && width() == rect.width() &&
  236. (y() == rect.bottom() || bottom() == rect.y()));
  237. }
  238. int Rect::ManhattanDistanceToPoint(const Point& point) const {
  239. int x_distance =
  240. std::max<int>(0, std::max(x() - point.x(), point.x() - right()));
  241. int y_distance =
  242. std::max<int>(0, std::max(y() - point.y(), point.y() - bottom()));
  243. return x_distance + y_distance;
  244. }
  245. int Rect::ManhattanInternalDistance(const Rect& rect) const {
  246. Rect c(*this);
  247. c.Union(rect);
  248. int x = std::max(0, c.width() - width() - rect.width() + 1);
  249. int y = std::max(0, c.height() - height() - rect.height() + 1);
  250. return x + y;
  251. }
  252. std::string Rect::ToString() const {
  253. return base::StringPrintf("%s %s",
  254. origin().ToString().c_str(),
  255. size().ToString().c_str());
  256. }
  257. bool Rect::ApproximatelyEqual(const Rect& rect, int tolerance) const {
  258. return std::abs(x() - rect.x()) <= tolerance &&
  259. std::abs(y() - rect.y()) <= tolerance &&
  260. std::abs(right() - rect.right()) <= tolerance &&
  261. std::abs(bottom() - rect.bottom()) <= tolerance;
  262. }
  263. Rect operator+(const Rect& lhs, const Vector2d& rhs) {
  264. Rect result(lhs);
  265. result += rhs;
  266. return result;
  267. }
  268. Rect operator-(const Rect& lhs, const Vector2d& rhs) {
  269. Rect result(lhs);
  270. result -= rhs;
  271. return result;
  272. }
  273. Rect IntersectRects(const Rect& a, const Rect& b) {
  274. Rect result = a;
  275. result.Intersect(b);
  276. return result;
  277. }
  278. Rect UnionRects(const Rect& a, const Rect& b) {
  279. Rect result = a;
  280. result.Union(b);
  281. return result;
  282. }
  283. Rect UnionRectsEvenIfEmpty(const Rect& a, const Rect& b) {
  284. Rect result = a;
  285. result.UnionEvenIfEmpty(b);
  286. return result;
  287. }
  288. Rect SubtractRects(const Rect& a, const Rect& b) {
  289. Rect result = a;
  290. result.Subtract(b);
  291. return result;
  292. }
  293. Rect BoundingRect(const Point& p1, const Point& p2) {
  294. Rect result;
  295. result.SetByBounds(std::min(p1.x(), p2.x()), std::min(p1.y(), p2.y()),
  296. std::max(p1.x(), p2.x()), std::max(p1.y(), p2.y()));
  297. return result;
  298. }
  299. Rect MaximumCoveredRect(const Rect& a, const Rect& b) {
  300. // Check a or b by itself.
  301. Rect maximum = a;
  302. uint64_t maximum_area = a.size().Area64();
  303. if (b.size().Area64() > maximum_area) {
  304. maximum = b;
  305. maximum_area = b.size().Area64();
  306. }
  307. // Check the regions that include the intersection of a and b. This can be
  308. // done by taking the intersection and expanding it vertically and
  309. // horizontally. These expanded intersections will both still be covered by
  310. // a or b.
  311. Rect intersection = a;
  312. intersection.InclusiveIntersect(b);
  313. if (!intersection.size().IsZero()) {
  314. Rect vert_expanded_intersection = intersection;
  315. vert_expanded_intersection.SetVerticalBounds(
  316. std::min(a.y(), b.y()), std::max(a.bottom(), b.bottom()));
  317. if (vert_expanded_intersection.size().Area64() > maximum_area) {
  318. maximum = vert_expanded_intersection;
  319. maximum_area = vert_expanded_intersection.size().Area64();
  320. }
  321. Rect horiz_expanded_intersection = intersection;
  322. horiz_expanded_intersection.SetHorizontalBounds(
  323. std::min(a.x(), b.x()), std::max(a.right(), b.right()));
  324. if (horiz_expanded_intersection.size().Area64() > maximum_area) {
  325. maximum = horiz_expanded_intersection;
  326. maximum_area = horiz_expanded_intersection.size().Area64();
  327. }
  328. }
  329. return maximum;
  330. }
  331. } // namespace gfx