1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2 * This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 #ifndef MOZILLA_GFX_BASERECT_H_
7 #define MOZILLA_GFX_BASERECT_H_
13 #include "mozilla/Assertions.h"
14 #include "mozilla/FloatingPoint.h"
15 #include "mozilla/TypeTraits.h"
21 * Rectangles have two interpretations: a set of (zero-size) points,
22 * and a rectangular area of the plane. Most rectangle operations behave
23 * the same no matter what interpretation is being used, but some operations
25 * -- Equality tests behave differently. When a rectangle represents an area,
26 * all zero-width and zero-height rectangles are equal to each other since they
27 * represent the empty area. But when a rectangle represents a set of
28 * mathematical points, zero-width and zero-height rectangles can be unequal.
29 * -- The union operation can behave differently. When rectangles represent
30 * areas, taking the union of a zero-width or zero-height rectangle with
31 * another rectangle can just ignore the empty rectangle. But when rectangles
32 * represent sets of mathematical points, we may need to extend the latter
33 * rectangle to include the points of a zero-width or zero-height rectangle.
35 * To ensure that these interpretations are explicitly disambiguated, we
36 * deny access to the == and != operators and require use of IsEqualEdges and
37 * IsEqualInterior instead. Similarly we provide separate Union and UnionEdges
40 * Do not use this class directly. Subclass it, pass that subclass as the
41 * Sub parameter, and only use that subclass.
43 template <class T
, class Sub
, class Point
, class SizeT
, class MarginT
>
45 T x
, y
, width
, height
;
48 BaseRect() : x(0), y(0), width(0), height(0) {}
49 BaseRect(const Point
& aOrigin
, const SizeT
&aSize
) :
50 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
)
58 // Emptiness. An empty rect is one that has no area, i.e. its height or width
60 bool IsEmpty() const { return height
<= 0 || width
<= 0; }
61 void SetEmpty() { width
= height
= 0; }
63 // "Finite" means not inf and not NaN
66 typedef typename
mozilla::Conditional
<mozilla::IsSame
<T
, float>::value
, float, double>::Type FloatType
;
67 return (mozilla::IsFinite(FloatType(x
)) &&
68 mozilla::IsFinite(FloatType(y
)) &&
69 mozilla::IsFinite(FloatType(width
)) &&
70 mozilla::IsFinite(FloatType(height
)));
73 // Returns true if this rectangle contains the interior of aRect. Always
74 // returns true if aRect is empty, and always returns false is aRect is
75 // nonempty but this rect is empty.
76 bool Contains(const Sub
& aRect
) const
78 return aRect
.IsEmpty() ||
79 (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 bool Contains(T aX
, T aY
) const
87 return x
<= aX
&& aX
< XMost() &&
88 y
<= aY
&& aY
< YMost();
90 // Returns true if this rectangle contains the point. Points are considered
91 // in the rectangle if they are on the left or top edge, but outside if they
92 // are on the right or bottom edge.
93 bool Contains(const Point
& aPoint
) const { return Contains(aPoint
.x
, aPoint
.y
); }
95 // Intersection. Returns TRUE if the receiver's area has non-empty
96 // intersection with aRect's area, and FALSE otherwise.
97 // Always returns false if aRect is empty or 'this' is empty.
98 bool Intersects(const Sub
& aRect
) const
100 return !IsEmpty() && !aRect
.IsEmpty() &&
101 x
< aRect
.XMost() && aRect
.x
< XMost() &&
102 y
< aRect
.YMost() && aRect
.y
< YMost();
104 // Returns the rectangle containing the intersection of the points
105 // (including edges) of *this and aRect. If there are no points in that
106 // intersection, returns an empty rectangle with x/y set to the std::max of the x/y
107 // of *this and aRect.
108 Sub
Intersect(const Sub
& aRect
) const
111 result
.x
= std::max
<T
>(x
, aRect
.x
);
112 result
.y
= std::max
<T
>(y
, aRect
.y
);
113 result
.width
= std::min
<T
>(x
- result
.x
+ width
, aRect
.x
- result
.x
+ aRect
.width
);
114 result
.height
= std::min
<T
>(y
- result
.y
+ height
, aRect
.y
- result
.y
+ aRect
.height
);
115 if (result
.width
< 0 || result
.height
< 0) {
120 // Sets *this to be the rectangle containing the intersection of the points
121 // (including edges) of *this and aRect. If there are no points in that
122 // intersection, sets *this to be an empty rectangle with x/y set to the std::max
123 // of the x/y of *this and aRect.
125 // 'this' can be the same object as either aRect1 or aRect2
126 bool IntersectRect(const Sub
& aRect1
, const Sub
& aRect2
)
128 *static_cast<Sub
*>(this) = aRect1
.Intersect(aRect2
);
132 // Returns the smallest rectangle that contains both the area of both
134 // Thus, empty input rectangles are ignored.
135 // If both rectangles are empty, returns this.
136 Sub
Union(const Sub
& aRect
) const
140 } else if (aRect
.IsEmpty()) {
141 return *static_cast<const Sub
*>(this);
143 return UnionEdges(aRect
);
146 // Returns the smallest rectangle that contains both the points (including
147 // edges) of both aRect1 and aRect2.
148 // Thus, empty input rectangles are allowed to affect the result.
149 Sub
UnionEdges(const Sub
& aRect
) const
152 result
.x
= std::min(x
, aRect
.x
);
153 result
.y
= std::min(y
, aRect
.y
);
154 result
.width
= std::max(XMost(), aRect
.XMost()) - result
.x
;
155 result
.height
= std::max(YMost(), aRect
.YMost()) - result
.y
;
158 // Computes the smallest rectangle that contains both the area of both
159 // aRect1 and aRect2, and fills 'this' with the result.
160 // Thus, empty input rectangles are ignored.
161 // If both rectangles are empty, sets 'this' to aRect2.
163 // 'this' can be the same object as either aRect1 or aRect2
164 void UnionRect(const Sub
& aRect1
, const Sub
& aRect2
)
166 *static_cast<Sub
*>(this) = aRect1
.Union(aRect2
);
169 // Computes the smallest rectangle that contains both the points (including
170 // edges) of both aRect1 and aRect2.
171 // Thus, empty input rectangles are allowed to affect the result.
173 // 'this' can be the same object as either aRect1 or aRect2
174 void UnionRectEdges(const Sub
& aRect1
, const Sub
& aRect2
)
176 *static_cast<Sub
*>(this) = aRect1
.UnionEdges(aRect2
);
179 void SetRect(T aX
, T aY
, T aWidth
, T aHeight
)
181 x
= aX
; y
= aY
; width
= aWidth
; height
= aHeight
;
183 void SetRect(const Point
& aPt
, const SizeT
& aSize
)
185 SetRect(aPt
.x
, aPt
.y
, aSize
.width
, aSize
.height
);
187 void MoveTo(T aX
, T aY
) { x
= aX
; y
= aY
; }
188 void MoveTo(const Point
& aPoint
) { x
= aPoint
.x
; y
= aPoint
.y
; }
189 void MoveBy(T aDx
, T aDy
) { x
+= aDx
; y
+= aDy
; }
190 void MoveBy(const Point
& aPoint
) { x
+= aPoint
.x
; y
+= aPoint
.y
; }
191 void SizeTo(T aWidth
, T aHeight
) { width
= aWidth
; height
= aHeight
; }
192 void SizeTo(const SizeT
& aSize
) { width
= aSize
.width
; height
= aSize
.height
; }
194 void Inflate(T aD
) { Inflate(aD
, aD
); }
195 void Inflate(T aDx
, T aDy
)
202 void Inflate(const MarginT
& aMargin
)
206 width
+= aMargin
.LeftRight();
207 height
+= aMargin
.TopBottom();
209 void Inflate(const SizeT
& aSize
) { Inflate(aSize
.width
, aSize
.height
); }
211 void InflateToMultiple(const SizeT
& aMultiple
)
216 x
= static_cast<T
>(floor(x
/ aMultiple
.width
)) * aMultiple
.width
;
217 y
= static_cast<T
>(floor(y
/ aMultiple
.height
)) * aMultiple
.height
;
218 xMost
= static_cast<T
>(ceil(x
/ aMultiple
.width
)) * aMultiple
.width
;
219 yMost
= static_cast<T
>(ceil(y
/ aMultiple
.height
)) * aMultiple
.height
;
225 void Deflate(T aD
) { Deflate(aD
, aD
); }
226 void Deflate(T aDx
, T aDy
)
230 width
= std::max(T(0), width
- 2 * aDx
);
231 height
= std::max(T(0), height
- 2 * aDy
);
233 void Deflate(const MarginT
& aMargin
)
237 width
= std::max(T(0), width
- aMargin
.LeftRight());
238 height
= std::max(T(0), height
- aMargin
.TopBottom());
240 void Deflate(const SizeT
& aSize
) { Deflate(aSize
.width
, aSize
.height
); }
242 // Return true if the rectangles contain the same set of points, including
243 // points on the edges.
244 // Use when we care about the exact x/y/width/height values being
245 // equal (i.e. we care about differences in empty rectangles).
246 bool IsEqualEdges(const Sub
& aRect
) const
248 return x
== aRect
.x
&& y
== aRect
.y
&&
249 width
== aRect
.width
&& height
== aRect
.height
;
251 // Return true if the rectangles contain the same area of the plane.
252 // Use when we do not care about differences in empty rectangles.
253 bool IsEqualInterior(const Sub
& aRect
) const
255 return IsEqualEdges(aRect
) || (IsEmpty() && aRect
.IsEmpty());
258 friend Sub
operator+(Sub aSub
, const Point
& aPoint
)
263 friend Sub
operator-(Sub aSub
, const Point
& aPoint
)
268 friend Sub
operator+(Sub aSub
, const SizeT
& aSize
)
273 friend Sub
operator-(Sub aSub
, const SizeT
& aSize
)
278 Sub
& operator+=(const Point
& aPoint
)
281 return *static_cast<Sub
*>(this);
283 Sub
& operator-=(const Point
& aPoint
)
286 return *static_cast<Sub
*>(this);
288 Sub
& operator+=(const SizeT
& aSize
)
290 width
+= aSize
.width
;
291 height
+= aSize
.height
;
292 return *static_cast<Sub
*>(this);
294 Sub
& operator-=(const SizeT
& aSize
)
296 width
-= aSize
.width
;
297 height
-= aSize
.height
;
298 return *static_cast<Sub
*>(this);
300 // Find difference as a Margin
301 MarginT
operator-(const Sub
& aRect
) const
303 return MarginT(aRect
.y
- y
,
304 XMost() - aRect
.XMost(),
305 YMost() - aRect
.YMost(),
309 // Helpers for accessing the vertices
310 Point
TopLeft() const { return Point(x
, y
); }
311 Point
TopRight() const { return Point(XMost(), y
); }
312 Point
BottomLeft() const { return Point(x
, YMost()); }
313 Point
BottomRight() const { return Point(XMost(), YMost()); }
314 Point
Center() const { return Point(x
, y
) + Point(width
, height
)/2; }
315 SizeT
Size() const { return SizeT(width
, height
); }
317 // Helper methods for computing the extents
318 T
X() const { return x
; }
319 T
Y() const { return y
; }
320 T
Width() const { return width
; }
321 T
Height() const { return height
; }
322 T
XMost() const { return x
+ width
; }
323 T
YMost() const { return y
+ height
; }
325 // Moves one edge of the rect without moving the opposite edge.
326 void SetLeftEdge(T aX
) {
327 MOZ_ASSERT(aX
<= XMost());
328 width
= XMost() - aX
;
331 void SetRightEdge(T aXMost
) {
332 MOZ_ASSERT(aXMost
>= x
);
335 void SetTopEdge(T aY
) {
336 MOZ_ASSERT(aY
<= YMost());
337 height
= YMost() - aY
;
340 void SetBottomEdge(T aYMost
) {
341 MOZ_ASSERT(aYMost
>= y
);
345 // Round the rectangle edges to integer coordinates, such that the rounded
346 // rectangle has the same set of pixel centers as the original rectangle.
347 // Edges at offset 0.5 round up.
348 // Suitable for most places where integral device coordinates
349 // are needed, but note that any translation should be applied first to
350 // avoid pixel rounding errors.
351 // Note that this is *not* rounding to nearest integer if the values are negative.
352 // They are always rounding as floor(n + 0.5).
353 // See https://bugzilla.mozilla.org/show_bug.cgi?id=410748#c14
354 // If you need similar method which is using NS_round(), you should create
355 // new |RoundAwayFromZero()| method.
358 T x0
= static_cast<T
>(floor(T(X()) + 0.5));
359 T y0
= static_cast<T
>(floor(T(Y()) + 0.5));
360 T x1
= static_cast<T
>(floor(T(XMost()) + 0.5));
361 T y1
= static_cast<T
>(floor(T(YMost()) + 0.5));
370 // Snap the rectangle edges to integer coordinates, such that the
371 // original rectangle contains the resulting rectangle.
374 T x0
= static_cast<T
>(ceil(T(X())));
375 T y0
= static_cast<T
>(ceil(T(Y())));
376 T x1
= static_cast<T
>(floor(T(XMost())));
377 T y1
= static_cast<T
>(floor(T(YMost())));
386 // Snap the rectangle edges to integer coordinates, such that the
387 // resulting rectangle contains the original rectangle.
390 T x0
= static_cast<T
>(floor(T(X())));
391 T y0
= static_cast<T
>(floor(T(Y())));
392 T x1
= static_cast<T
>(ceil(T(XMost())));
393 T y1
= static_cast<T
>(ceil(T(YMost())));
402 // Scale 'this' by aScale without doing any rounding.
403 void Scale(T aScale
) { Scale(aScale
, aScale
); }
404 // Scale 'this' by aXScale and aYScale, without doing any rounding.
405 void Scale(T aXScale
, T aYScale
)
407 T right
= XMost() * aXScale
;
408 T bottom
= YMost() * aYScale
;
414 // Scale 'this' by aScale, converting coordinates to integers so that the result is
415 // the smallest integer-coordinate rectangle containing the unrounded result.
416 // Note: this can turn an empty rectangle into a non-empty rectangle
417 void ScaleRoundOut(double aScale
) { ScaleRoundOut(aScale
, aScale
); }
418 // Scale 'this' by aXScale and aYScale, converting coordinates to integers so
419 // that the result is the smallest integer-coordinate rectangle containing the
421 // Note: this can turn an empty rectangle into a non-empty rectangle
422 void ScaleRoundOut(double aXScale
, double aYScale
)
424 T right
= static_cast<T
>(ceil(double(XMost()) * aXScale
));
425 T bottom
= static_cast<T
>(ceil(double(YMost()) * aYScale
));
426 x
= static_cast<T
>(floor(double(x
) * aXScale
));
427 y
= static_cast<T
>(floor(double(y
) * aYScale
));
431 // Scale 'this' by aScale, converting coordinates to integers so that the result is
432 // the largest integer-coordinate rectangle contained by the unrounded result.
433 void ScaleRoundIn(double aScale
) { ScaleRoundIn(aScale
, aScale
); }
434 // Scale 'this' by aXScale and aYScale, converting coordinates to integers so
435 // that the result is the largest integer-coordinate rectangle contained by the
437 void ScaleRoundIn(double aXScale
, double aYScale
)
439 T right
= static_cast<T
>(floor(double(XMost()) * aXScale
));
440 T bottom
= static_cast<T
>(floor(double(YMost()) * aYScale
));
441 x
= static_cast<T
>(ceil(double(x
) * aXScale
));
442 y
= static_cast<T
>(ceil(double(y
) * aYScale
));
443 width
= std::max
<T
>(0, right
- x
);
444 height
= std::max
<T
>(0, bottom
- y
);
446 // Scale 'this' by 1/aScale, converting coordinates to integers so that the result is
447 // the smallest integer-coordinate rectangle containing the unrounded result.
448 // Note: this can turn an empty rectangle into a non-empty rectangle
449 void ScaleInverseRoundOut(double aScale
) { ScaleInverseRoundOut(aScale
, aScale
); }
450 // Scale 'this' by 1/aXScale and 1/aYScale, converting coordinates to integers so
451 // that the result is the smallest integer-coordinate rectangle containing the
453 // Note: this can turn an empty rectangle into a non-empty rectangle
454 void ScaleInverseRoundOut(double aXScale
, double aYScale
)
456 T right
= static_cast<T
>(ceil(double(XMost()) / aXScale
));
457 T bottom
= static_cast<T
>(ceil(double(YMost()) / aYScale
));
458 x
= static_cast<T
>(floor(double(x
) / aXScale
));
459 y
= static_cast<T
>(floor(double(y
) / aYScale
));
463 // Scale 'this' by 1/aScale, converting coordinates to integers so that the result is
464 // the largest integer-coordinate rectangle contained by the unrounded result.
465 void ScaleInverseRoundIn(double aScale
) { ScaleInverseRoundIn(aScale
, aScale
); }
466 // Scale 'this' by 1/aXScale and 1/aYScale, converting coordinates to integers so
467 // that the result is the largest integer-coordinate rectangle contained by the
469 void ScaleInverseRoundIn(double aXScale
, double aYScale
)
471 T right
= static_cast<T
>(floor(double(XMost()) / aXScale
));
472 T bottom
= static_cast<T
>(floor(double(YMost()) / aYScale
));
473 x
= static_cast<T
>(ceil(double(x
) / aXScale
));
474 y
= static_cast<T
>(ceil(double(y
) / aYScale
));
475 width
= std::max
<T
>(0, right
- x
);
476 height
= std::max
<T
>(0, bottom
- y
);
480 * Clamp aPoint to this rectangle. It is allowed to end up on any
481 * edge of the rectangle.
483 Point
ClampPoint(const Point
& aPoint
) const
485 return Point(std::max(x
, std::min(XMost(), aPoint
.x
)),
486 std::max(y
, std::min(YMost(), aPoint
.y
)));
490 * Clamp this rectangle to be inside aRect. The function returns a copy of
491 * this rect after it is forced inside the bounds of aRect. It will attempt to
492 * retain the size but will shrink the dimensions that don't fit.
494 Sub
ForceInside(const Sub
& aRect
) const
496 Sub
rect(std::max(aRect
.x
, x
),
497 std::max(aRect
.y
, y
),
498 std::min(aRect
.width
, width
),
499 std::min(aRect
.height
, height
));
500 rect
.x
= std::min(rect
.XMost(), aRect
.XMost()) - rect
.width
;
501 rect
.y
= std::min(rect
.YMost(), aRect
.YMost()) - rect
.height
;
505 friend std::ostream
& operator<<(std::ostream
& stream
,
506 const BaseRect
<T
, Sub
, Point
, SizeT
, MarginT
>& aRect
) {
507 return stream
<< '(' << aRect
.x
<< ',' << aRect
.y
<< ','
508 << aRect
.width
<< ',' << aRect
.height
<< ')';
512 // Do not use the default operator== or operator!= !
513 // Use IsEqualEdges or IsEqualInterior explicitly.
514 bool operator==(const Sub
& aRect
) const { return false; }
515 bool operator!=(const Sub
& aRect
) const { return false; }
521 #endif /* MOZILLA_GFX_BASERECT_H_ */