Bug 1686668 [wpt PR 27185] - Update wpt metadata, a=testonly
[gecko.git] / gfx / 2d / BaseRect.h
blob58aaa7d5b34bf6b4873d25b339e834da18b50c55
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #ifndef MOZILLA_GFX_BASERECT_H_
8 #define MOZILLA_GFX_BASERECT_H_
10 #include <algorithm>
11 #include <cmath>
12 #include <ostream>
13 #include <type_traits>
15 #include "mozilla/Assertions.h"
16 #include "mozilla/FloatingPoint.h"
17 #include "Types.h"
19 namespace mozilla {
20 namespace gfx {
22 /**
23 * Rectangles have two interpretations: a set of (zero-size) points,
24 * and a rectangular area of the plane. Most rectangle operations behave
25 * the same no matter what interpretation is being used, but some operations
26 * differ:
27 * -- Equality tests behave differently. When a rectangle represents an area,
28 * all zero-width and zero-height rectangles are equal to each other since they
29 * represent the empty area. But when a rectangle represents a set of
30 * mathematical points, zero-width and zero-height rectangles can be unequal.
31 * -- The union operation can behave differently. When rectangles represent
32 * areas, taking the union of a zero-width or zero-height rectangle with
33 * another rectangle can just ignore the empty rectangle. But when rectangles
34 * represent sets of mathematical points, we may need to extend the latter
35 * rectangle to include the points of a zero-width or zero-height rectangle.
37 * To ensure that these interpretations are explicitly disambiguated, we
38 * deny access to the == and != operators and require use of IsEqualEdges and
39 * IsEqualInterior instead. Similarly we provide separate Union and UnionEdges
40 * methods.
42 * Do not use this class directly. Subclass it, pass that subclass as the
43 * Sub parameter, and only use that subclass.
45 template <class T, class Sub, class Point, class SizeT, class MarginT>
46 struct BaseRect {
47 T x, y, width, height;
49 // Constructors
50 BaseRect() : x(0), y(0), width(0), height(0) {}
51 BaseRect(const Point& aOrigin, const SizeT& aSize)
52 : x(aOrigin.x), y(aOrigin.y), width(aSize.width), height(aSize.height) {}
53 BaseRect(T aX, T aY, T aWidth, T aHeight)
54 : x(aX), y(aY), width(aWidth), height(aHeight) {}
56 // Emptiness. An empty rect is one that has no area, i.e. its height or width
57 // is <= 0. Zero rect is the one with height and width set to zero. Note
58 // that SetEmpty() may change a rectangle that identified as IsEmpty().
59 MOZ_ALWAYS_INLINE bool IsZeroArea() const {
60 return height == 0 || width == 0;
62 MOZ_ALWAYS_INLINE bool IsEmpty() const { return height <= 0 || width <= 0; }
63 void SetEmpty() { width = height = 0; }
65 // "Finite" means not inf and not NaN
66 bool IsFinite() const {
67 using FloatType =
68 std::conditional_t<std::is_same_v<T, float>, float, double>;
69 return (mozilla::IsFinite(FloatType(x)) &&
70 mozilla::IsFinite(FloatType(y)) &&
71 mozilla::IsFinite(FloatType(width)) &&
72 mozilla::IsFinite(FloatType(height)));
75 // Returns true if this rectangle contains the interior of aRect. Always
76 // returns true if aRect is empty, and always returns false is aRect is
77 // nonempty but this rect is empty.
78 bool Contains(const Sub& aRect) const {
79 return aRect.IsEmpty() || (x <= aRect.x && aRect.XMost() <= XMost() &&
80 y <= aRect.y && aRect.YMost() <= YMost());
82 // Returns true if this rectangle contains the point. Points are considered
83 // in the rectangle if they are on the left or top edge, but outside if they
84 // are on the right or bottom edge.
85 MOZ_ALWAYS_INLINE bool Contains(T aX, T aY) const {
86 return x <= aX && aX < XMost() && y <= aY && aY < YMost();
88 MOZ_ALWAYS_INLINE bool ContainsX(T aX) const {
89 return x <= aX && aX < XMost();
91 MOZ_ALWAYS_INLINE bool ContainsY(T aY) const {
92 return y <= aY && aY < YMost();
94 // Returns true if this rectangle contains the point. Points are considered
95 // in the rectangle if they are on the left or top edge, but outside if they
96 // are on the right or bottom edge.
97 bool Contains(const Point& aPoint) const {
98 return Contains(aPoint.x, aPoint.y);
101 // Intersection. Returns TRUE if the receiver's area has non-empty
102 // intersection with aRect's area, and FALSE otherwise.
103 // Always returns false if aRect is empty or 'this' is empty.
104 bool Intersects(const Sub& aRect) const {
105 return !IsEmpty() && !aRect.IsEmpty() && x < aRect.XMost() &&
106 aRect.x < XMost() && y < aRect.YMost() && aRect.y < YMost();
108 // Returns the rectangle containing the intersection of the points
109 // (including edges) of *this and aRect. If there are no points in that
110 // intersection, returns an empty rectangle with x/y set to the std::max of
111 // the x/y of *this and aRect.
112 [[nodiscard]] Sub Intersect(const Sub& aRect) const {
113 Sub result;
114 result.x = std::max<T>(x, aRect.x);
115 result.y = std::max<T>(y, aRect.y);
116 result.width =
117 std::min<T>(x - result.x + width, aRect.x - result.x + aRect.width);
118 result.height =
119 std::min<T>(y - result.y + height, aRect.y - result.y + aRect.height);
120 // See bug 1457110, this function expects to -only- size to 0,0 if the
121 // width/height is explicitly negative.
122 if (result.width < 0 || result.height < 0) {
123 result.SizeTo(0, 0);
125 return result;
127 // Sets *this to be the rectangle containing the intersection of the points
128 // (including edges) of *this and aRect. If there are no points in that
129 // intersection, sets *this to be an empty rectangle with x/y set to the
130 // std::max of the x/y of *this and aRect.
132 // 'this' can be the same object as either aRect1 or aRect2
133 bool IntersectRect(const Sub& aRect1, const Sub& aRect2) {
134 T newX = std::max<T>(aRect1.x, aRect2.x);
135 T newY = std::max<T>(aRect1.y, aRect2.y);
136 width = std::min<T>(aRect1.x - newX + aRect1.width,
137 aRect2.x - newX + aRect2.width);
138 height = std::min<T>(aRect1.y - newY + aRect1.height,
139 aRect2.y - newY + aRect2.height);
140 x = newX;
141 y = newY;
142 if (width <= 0 || height <= 0) {
143 SizeTo(0, 0);
144 return false;
146 return true;
149 // Returns the smallest rectangle that contains both the area of both
150 // this and aRect2.
151 // Thus, empty input rectangles are ignored.
152 // If both rectangles are empty, returns this.
153 // WARNING! This is not safe against overflow, prefer using SafeUnion instead
154 // when dealing with int-based rects.
155 [[nodiscard]] Sub Union(const Sub& aRect) const {
156 if (IsEmpty()) {
157 return aRect;
158 } else if (aRect.IsEmpty()) {
159 return *static_cast<const Sub*>(this);
160 } else {
161 return UnionEdges(aRect);
164 // Returns the smallest rectangle that contains both the points (including
165 // edges) of both aRect1 and aRect2.
166 // Thus, empty input rectangles are allowed to affect the result.
167 // WARNING! This is not safe against overflow, prefer using SafeUnionEdges
168 // instead when dealing with int-based rects.
169 [[nodiscard]] Sub UnionEdges(const Sub& aRect) const {
170 Sub result;
171 result.x = std::min(x, aRect.x);
172 result.y = std::min(y, aRect.y);
173 result.width = std::max(XMost(), aRect.XMost()) - result.x;
174 result.height = std::max(YMost(), aRect.YMost()) - result.y;
175 return result;
177 // Computes the smallest rectangle that contains both the area of both
178 // aRect1 and aRect2, and fills 'this' with the result.
179 // Thus, empty input rectangles are ignored.
180 // If both rectangles are empty, sets 'this' to aRect2.
182 // 'this' can be the same object as either aRect1 or aRect2
183 void UnionRect(const Sub& aRect1, const Sub& aRect2) {
184 *static_cast<Sub*>(this) = aRect1.Union(aRect2);
187 void OrWith(const Sub& aRect1) {
188 UnionRect(*static_cast<Sub*>(this), aRect1);
191 // Computes the smallest rectangle that contains both the points (including
192 // edges) of both aRect1 and aRect2.
193 // Thus, empty input rectangles are allowed to affect the result.
195 // 'this' can be the same object as either aRect1 or aRect2
196 void UnionRectEdges(const Sub& aRect1, const Sub& aRect2) {
197 *static_cast<Sub*>(this) = aRect1.UnionEdges(aRect2);
200 // Expands the rect to include the point
201 void ExpandToEnclose(const Point& aPoint) {
202 if (aPoint.x < x) {
203 width = XMost() - aPoint.x;
204 x = aPoint.x;
205 } else if (aPoint.x > XMost()) {
206 width = aPoint.x - x;
208 if (aPoint.y < y) {
209 height = YMost() - aPoint.y;
210 y = aPoint.y;
211 } else if (aPoint.y > YMost()) {
212 height = aPoint.y - y;
216 MOZ_ALWAYS_INLINE void SetRect(T aX, T aY, T aWidth, T aHeight) {
217 x = aX;
218 y = aY;
219 width = aWidth;
220 height = aHeight;
222 MOZ_ALWAYS_INLINE void SetRectX(T aX, T aWidth) {
223 x = aX;
224 width = aWidth;
226 MOZ_ALWAYS_INLINE void SetRectY(T aY, T aHeight) {
227 y = aY;
228 height = aHeight;
230 MOZ_ALWAYS_INLINE void SetBox(T aX, T aY, T aXMost, T aYMost) {
231 x = aX;
232 y = aY;
233 width = aXMost - aX;
234 height = aYMost - aY;
236 MOZ_ALWAYS_INLINE void SetNonEmptyBox(T aX, T aY, T aXMost, T aYMost) {
237 x = aX;
238 y = aY;
239 width = std::max(0, aXMost - aX);
240 height = std::max(0, aYMost - aY);
242 MOZ_ALWAYS_INLINE void SetBoxX(T aX, T aXMost) {
243 x = aX;
244 width = aXMost - aX;
246 MOZ_ALWAYS_INLINE void SetBoxY(T aY, T aYMost) {
247 y = aY;
248 height = aYMost - aY;
250 void SetRect(const Point& aPt, const SizeT& aSize) {
251 SetRect(aPt.x, aPt.y, aSize.width, aSize.height);
253 MOZ_ALWAYS_INLINE void GetRect(T* aX, T* aY, T* aWidth, T* aHeight) const {
254 *aX = x;
255 *aY = y;
256 *aWidth = width;
257 *aHeight = height;
260 MOZ_ALWAYS_INLINE void MoveTo(T aX, T aY) {
261 x = aX;
262 y = aY;
264 MOZ_ALWAYS_INLINE void MoveToX(T aX) { x = aX; }
265 MOZ_ALWAYS_INLINE void MoveToY(T aY) { y = aY; }
266 MOZ_ALWAYS_INLINE void MoveTo(const Point& aPoint) {
267 x = aPoint.x;
268 y = aPoint.y;
270 MOZ_ALWAYS_INLINE void MoveBy(T aDx, T aDy) {
271 x += aDx;
272 y += aDy;
274 MOZ_ALWAYS_INLINE void MoveByX(T aDx) { x += aDx; }
275 MOZ_ALWAYS_INLINE void MoveByY(T aDy) { y += aDy; }
276 MOZ_ALWAYS_INLINE void MoveBy(const Point& aPoint) {
277 x += aPoint.x;
278 y += aPoint.y;
280 MOZ_ALWAYS_INLINE void SizeTo(T aWidth, T aHeight) {
281 width = aWidth;
282 height = aHeight;
284 MOZ_ALWAYS_INLINE void SizeTo(const SizeT& aSize) {
285 width = aSize.width;
286 height = aSize.height;
289 void Inflate(T aD) { Inflate(aD, aD); }
290 void Inflate(T aDx, T aDy) {
291 x -= aDx;
292 y -= aDy;
293 width += 2 * aDx;
294 height += 2 * aDy;
296 void Inflate(const MarginT& aMargin) {
297 x -= aMargin.left;
298 y -= aMargin.top;
299 width += aMargin.LeftRight();
300 height += aMargin.TopBottom();
302 void Inflate(const SizeT& aSize) { Inflate(aSize.width, aSize.height); }
304 void Deflate(T aD) { Deflate(aD, aD); }
305 void Deflate(T aDx, T aDy) {
306 x += aDx;
307 y += aDy;
308 width = std::max(T(0), width - 2 * aDx);
309 height = std::max(T(0), height - 2 * aDy);
311 void Deflate(const MarginT& aMargin) {
312 x += aMargin.left;
313 y += aMargin.top;
314 width = std::max(T(0), width - aMargin.LeftRight());
315 height = std::max(T(0), height - aMargin.TopBottom());
317 void Deflate(const SizeT& aSize) { Deflate(aSize.width, aSize.height); }
319 // Return true if the rectangles contain the same set of points, including
320 // points on the edges.
321 // Use when we care about the exact x/y/width/height values being
322 // equal (i.e. we care about differences in empty rectangles).
323 bool IsEqualEdges(const Sub& aRect) const {
324 return x == aRect.x && y == aRect.y && width == aRect.width &&
325 height == aRect.height;
327 MOZ_ALWAYS_INLINE bool IsEqualRect(T aX, T aY, T aW, T aH) {
328 return x == aX && y == aY && width == aW && height == aH;
330 MOZ_ALWAYS_INLINE bool IsEqualXY(T aX, T aY) { return x == aX && y == aY; }
332 MOZ_ALWAYS_INLINE bool IsEqualSize(T aW, T aH) {
333 return width == aW && height == aH;
336 // Return true if the rectangles contain the same area of the plane.
337 // Use when we do not care about differences in empty rectangles.
338 bool IsEqualInterior(const Sub& aRect) const {
339 return IsEqualEdges(aRect) || (IsEmpty() && aRect.IsEmpty());
342 friend Sub operator+(Sub aSub, const Point& aPoint) {
343 aSub += aPoint;
344 return aSub;
346 friend Sub operator-(Sub aSub, const Point& aPoint) {
347 aSub -= aPoint;
348 return aSub;
350 friend Sub operator+(Sub aSub, const SizeT& aSize) {
351 aSub += aSize;
352 return aSub;
354 friend Sub operator-(Sub aSub, const SizeT& aSize) {
355 aSub -= aSize;
356 return aSub;
358 Sub& operator+=(const Point& aPoint) {
359 MoveBy(aPoint);
360 return *static_cast<Sub*>(this);
362 Sub& operator-=(const Point& aPoint) {
363 MoveBy(-aPoint);
364 return *static_cast<Sub*>(this);
366 Sub& operator+=(const SizeT& aSize) {
367 width += aSize.width;
368 height += aSize.height;
369 return *static_cast<Sub*>(this);
371 Sub& operator-=(const SizeT& aSize) {
372 width -= aSize.width;
373 height -= aSize.height;
374 return *static_cast<Sub*>(this);
376 // Find difference as a Margin
377 MarginT operator-(const Sub& aRect) const {
378 return MarginT(aRect.y - y, XMost() - aRect.XMost(),
379 YMost() - aRect.YMost(), aRect.x - x);
382 // Helpers for accessing the vertices
383 Point TopLeft() const { return Point(x, y); }
384 Point TopRight() const { return Point(XMost(), y); }
385 Point BottomLeft() const { return Point(x, YMost()); }
386 Point BottomRight() const { return Point(XMost(), YMost()); }
387 Point AtCorner(Corner aCorner) const {
388 switch (aCorner) {
389 case eCornerTopLeft:
390 return TopLeft();
391 case eCornerTopRight:
392 return TopRight();
393 case eCornerBottomRight:
394 return BottomRight();
395 case eCornerBottomLeft:
396 return BottomLeft();
398 MOZ_CRASH("GFX: Incomplete switch");
400 Point CCWCorner(mozilla::Side side) const {
401 switch (side) {
402 case eSideTop:
403 return TopLeft();
404 case eSideRight:
405 return TopRight();
406 case eSideBottom:
407 return BottomRight();
408 case eSideLeft:
409 return BottomLeft();
411 MOZ_CRASH("GFX: Incomplete switch");
413 Point CWCorner(mozilla::Side side) const {
414 switch (side) {
415 case eSideTop:
416 return TopRight();
417 case eSideRight:
418 return BottomRight();
419 case eSideBottom:
420 return BottomLeft();
421 case eSideLeft:
422 return TopLeft();
424 MOZ_CRASH("GFX: Incomplete switch");
426 Point Center() const { return Point(x, y) + Point(width, height) / 2; }
427 SizeT Size() const { return SizeT(width, height); }
429 T Area() const { return width * height; }
431 // Helper methods for computing the extents
432 MOZ_ALWAYS_INLINE T X() const { return x; }
433 MOZ_ALWAYS_INLINE T Y() const { return y; }
434 MOZ_ALWAYS_INLINE T Width() const { return width; }
435 MOZ_ALWAYS_INLINE T Height() const { return height; }
436 MOZ_ALWAYS_INLINE T XMost() const { return x + width; }
437 MOZ_ALWAYS_INLINE T YMost() const { return y + height; }
439 // Set width and height. SizeTo() sets them together.
440 MOZ_ALWAYS_INLINE void SetWidth(T aWidth) { width = aWidth; }
441 MOZ_ALWAYS_INLINE void SetHeight(T aHeight) { height = aHeight; }
443 // Get the coordinate of the edge on the given side.
444 T Edge(mozilla::Side aSide) const {
445 switch (aSide) {
446 case eSideTop:
447 return Y();
448 case eSideRight:
449 return XMost();
450 case eSideBottom:
451 return YMost();
452 case eSideLeft:
453 return X();
455 MOZ_CRASH("GFX: Incomplete switch");
458 // Moves one edge of the rect without moving the opposite edge.
459 void SetLeftEdge(T aX) {
460 width = XMost() - aX;
461 x = aX;
463 void SetRightEdge(T aXMost) { width = aXMost - x; }
464 void SetTopEdge(T aY) {
465 height = YMost() - aY;
466 y = aY;
468 void SetBottomEdge(T aYMost) { height = aYMost - y; }
469 void Swap() {
470 std::swap(x, y);
471 std::swap(width, height);
474 // Round the rectangle edges to integer coordinates, such that the rounded
475 // rectangle has the same set of pixel centers as the original rectangle.
476 // Edges at offset 0.5 round up.
477 // Suitable for most places where integral device coordinates
478 // are needed, but note that any translation should be applied first to
479 // avoid pixel rounding errors.
480 // Note that this is *not* rounding to nearest integer if the values are
481 // negative. They are always rounding as floor(n + 0.5). See
482 // https://bugzilla.mozilla.org/show_bug.cgi?id=410748#c14 If you need similar
483 // method which is using NS_round(), you should create new
484 // |RoundAwayFromZero()| method.
485 void Round() {
486 T x0 = static_cast<T>(std::floor(T(X()) + 0.5f));
487 T y0 = static_cast<T>(std::floor(T(Y()) + 0.5f));
488 T x1 = static_cast<T>(std::floor(T(XMost()) + 0.5f));
489 T y1 = static_cast<T>(std::floor(T(YMost()) + 0.5f));
491 x = x0;
492 y = y0;
494 width = x1 - x0;
495 height = y1 - y0;
498 // Snap the rectangle edges to integer coordinates, such that the
499 // original rectangle contains the resulting rectangle.
500 void RoundIn() {
501 T x0 = static_cast<T>(std::ceil(T(X())));
502 T y0 = static_cast<T>(std::ceil(T(Y())));
503 T x1 = static_cast<T>(std::floor(T(XMost())));
504 T y1 = static_cast<T>(std::floor(T(YMost())));
506 x = x0;
507 y = y0;
509 width = x1 - x0;
510 height = y1 - y0;
513 // Snap the rectangle edges to integer coordinates, such that the
514 // resulting rectangle contains the original rectangle.
515 void RoundOut() {
516 T x0 = static_cast<T>(std::floor(T(X())));
517 T y0 = static_cast<T>(std::floor(T(Y())));
518 T x1 = static_cast<T>(std::ceil(T(XMost())));
519 T y1 = static_cast<T>(std::ceil(T(YMost())));
521 x = x0;
522 y = y0;
524 width = x1 - x0;
525 height = y1 - y0;
528 // Scale 'this' by aScale without doing any rounding.
529 void Scale(T aScale) { Scale(aScale, aScale); }
530 // Scale 'this' by aXScale and aYScale, without doing any rounding.
531 void Scale(T aXScale, T aYScale) {
532 T right = XMost() * aXScale;
533 T bottom = YMost() * aYScale;
534 x = x * aXScale;
535 y = y * aYScale;
536 width = right - x;
537 height = bottom - y;
539 // Scale 'this' by aScale, converting coordinates to integers so that the
540 // result is the smallest integer-coordinate rectangle containing the
541 // unrounded result. Note: this can turn an empty rectangle into a non-empty
542 // rectangle
543 void ScaleRoundOut(double aScale) { ScaleRoundOut(aScale, aScale); }
544 // Scale 'this' by aXScale and aYScale, converting coordinates to integers so
545 // that the result is the smallest integer-coordinate rectangle containing the
546 // unrounded result.
547 // Note: this can turn an empty rectangle into a non-empty rectangle
548 void ScaleRoundOut(double aXScale, double aYScale) {
549 T right = static_cast<T>(ceil(double(XMost()) * aXScale));
550 T bottom = static_cast<T>(ceil(double(YMost()) * aYScale));
551 x = static_cast<T>(floor(double(x) * aXScale));
552 y = static_cast<T>(floor(double(y) * aYScale));
553 width = right - x;
554 height = bottom - y;
556 // Scale 'this' by aScale, converting coordinates to integers so that the
557 // result is the largest integer-coordinate rectangle contained by the
558 // unrounded result.
559 void ScaleRoundIn(double aScale) { ScaleRoundIn(aScale, aScale); }
560 // Scale 'this' by aXScale and aYScale, converting coordinates to integers so
561 // that the result is the largest integer-coordinate rectangle contained by
562 // the unrounded result.
563 void ScaleRoundIn(double aXScale, double aYScale) {
564 T right = static_cast<T>(floor(double(XMost()) * aXScale));
565 T bottom = static_cast<T>(floor(double(YMost()) * aYScale));
566 x = static_cast<T>(ceil(double(x) * aXScale));
567 y = static_cast<T>(ceil(double(y) * aYScale));
568 width = std::max<T>(0, right - x);
569 height = std::max<T>(0, bottom - y);
571 // Scale 'this' by 1/aScale, converting coordinates to integers so that the
572 // result is the smallest integer-coordinate rectangle containing the
573 // unrounded result. Note: this can turn an empty rectangle into a non-empty
574 // rectangle
575 void ScaleInverseRoundOut(double aScale) {
576 ScaleInverseRoundOut(aScale, aScale);
578 // Scale 'this' by 1/aXScale and 1/aYScale, converting coordinates to integers
579 // so that the result is the smallest integer-coordinate rectangle containing
580 // the unrounded result. Note: this can turn an empty rectangle into a
581 // non-empty rectangle
582 void ScaleInverseRoundOut(double aXScale, double aYScale) {
583 T right = static_cast<T>(ceil(double(XMost()) / aXScale));
584 T bottom = static_cast<T>(ceil(double(YMost()) / aYScale));
585 x = static_cast<T>(floor(double(x) / aXScale));
586 y = static_cast<T>(floor(double(y) / aYScale));
587 width = right - x;
588 height = bottom - y;
590 // Scale 'this' by 1/aScale, converting coordinates to integers so that the
591 // result is the largest integer-coordinate rectangle contained by the
592 // unrounded result.
593 void ScaleInverseRoundIn(double aScale) {
594 ScaleInverseRoundIn(aScale, aScale);
596 // Scale 'this' by 1/aXScale and 1/aYScale, converting coordinates to integers
597 // so that the result is the largest integer-coordinate rectangle contained by
598 // the unrounded result.
599 void ScaleInverseRoundIn(double aXScale, double aYScale) {
600 T right = static_cast<T>(floor(double(XMost()) / aXScale));
601 T bottom = static_cast<T>(floor(double(YMost()) / aYScale));
602 x = static_cast<T>(ceil(double(x) / aXScale));
603 y = static_cast<T>(ceil(double(y) / aYScale));
604 width = std::max<T>(0, right - x);
605 height = std::max<T>(0, bottom - y);
609 * Clamp aPoint to this rectangle. It is allowed to end up on any
610 * edge of the rectangle.
612 [[nodiscard]] Point ClampPoint(const Point& aPoint) const {
613 return Point(std::max(x, std::min(XMost(), aPoint.x)),
614 std::max(y, std::min(YMost(), aPoint.y)));
618 * Translate this rectangle to be inside aRect. If it doesn't fit inside
619 * aRect then the dimensions that don't fit will be shrunk so that they
620 * do fit. The resulting rect is returned.
622 [[nodiscard]] Sub MoveInsideAndClamp(const Sub& aRect) const {
623 Sub rect(std::max(aRect.x, x), std::max(aRect.y, y),
624 std::min(aRect.width, width), std::min(aRect.height, height));
625 rect.x = std::min(rect.XMost(), aRect.XMost()) - rect.width;
626 rect.y = std::min(rect.YMost(), aRect.YMost()) - rect.height;
627 return rect;
630 // Returns the largest rectangle that can be represented with 32-bit
631 // signed integers, centered around a point at 0,0. As BaseRect's represent
632 // the dimensions as a top-left point with a width and height, the width
633 // and height will be the largest positive 32-bit value. The top-left
634 // position coordinate is divided by two to center the rectangle around a
635 // point at 0,0.
636 static Sub MaxIntRect() {
637 return Sub(static_cast<T>(-std::numeric_limits<int32_t>::max() * 0.5),
638 static_cast<T>(-std::numeric_limits<int32_t>::max() * 0.5),
639 static_cast<T>(std::numeric_limits<int32_t>::max()),
640 static_cast<T>(std::numeric_limits<int32_t>::max()));
643 // Returns a point representing the distance, along each dimension, of the
644 // given point from this rectangle. The distance along a dimension is defined
645 // as zero if the point is within the bounds of the rectangle in that
646 // dimension; otherwise, it's the distance to the closer endpoint of the
647 // rectangle in that dimension.
648 Point DistanceTo(const Point& aPoint) const {
649 return {DistanceFromInterval(aPoint.x, x, XMost()),
650 DistanceFromInterval(aPoint.y, y, YMost())};
653 friend std::ostream& operator<<(
654 std::ostream& stream,
655 const BaseRect<T, Sub, Point, SizeT, MarginT>& aRect) {
656 return stream << "(x=" << aRect.x << ", y=" << aRect.y
657 << ", w=" << aRect.width << ", h=" << aRect.height << ')';
660 private:
661 // Do not use the default operator== or operator!= !
662 // Use IsEqualEdges or IsEqualInterior explicitly.
663 bool operator==(const Sub& aRect) const { return false; }
664 bool operator!=(const Sub& aRect) const { return false; }
666 // Helper function for DistanceTo() that computes the distance of a
667 // coordinate along one dimension from an interval in that dimension.
668 static T DistanceFromInterval(T aCoord, T aIntervalStart, T aIntervalEnd) {
669 if (aCoord < aIntervalStart) {
670 return aIntervalStart - aCoord;
672 if (aCoord > aIntervalEnd) {
673 return aCoord - aIntervalEnd;
675 return 0;
679 } // namespace gfx
680 } // namespace mozilla
682 #endif /* MOZILLA_GFX_BASERECT_H_ */