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_
10 #include <mozilla/Assertions.h>
17 * Rectangles have two interpretations: a set of (zero-size) points,
18 * and a rectangular area of the plane. Most rectangle operations behave
19 * the same no matter what interpretation is being used, but some operations
21 * -- Equality tests behave differently. When a rectangle represents an area,
22 * all zero-width and zero-height rectangles are equal to each other since they
23 * represent the empty area. But when a rectangle represents a set of
24 * mathematical points, zero-width and zero-height rectangles can be unequal.
25 * -- The union operation can behave differently. When rectangles represent
26 * areas, taking the union of a zero-width or zero-height rectangle with
27 * another rectangle can just ignore the empty rectangle. But when rectangles
28 * represent sets of mathematical points, we may need to extend the latter
29 * rectangle to include the points of a zero-width or zero-height rectangle.
31 * To ensure that these interpretations are explicitly disambiguated, we
32 * deny access to the == and != operators and require use of IsEqualEdges and
33 * IsEqualInterior instead. Similarly we provide separate Union and UnionEdges
36 * Do not use this class directly. Subclass it, pass that subclass as the
37 * Sub parameter, and only use that subclass.
39 template <class T
, class Sub
, class Point
, class SizeT
, class Margin
>
41 T x
, y
, width
, height
;
44 BaseRect() : x(0), y(0), width(0), height(0) {}
45 BaseRect(const Point
& aOrigin
, const SizeT
&aSize
) :
46 x(aOrigin
.x
), y(aOrigin
.y
), width(aSize
.width
), height(aSize
.height
)
49 BaseRect(T aX
, T aY
, T aWidth
, T aHeight
) :
50 x(aX
), y(aY
), width(aWidth
), height(aHeight
)
54 // Emptiness. An empty rect is one that has no area, i.e. its height or width
56 bool IsEmpty() const { return height
<= 0 || width
<= 0; }
57 void SetEmpty() { width
= height
= 0; }
59 // Returns true if this rectangle contains the interior of aRect. Always
60 // returns true if aRect is empty, and always returns false is aRect is
61 // nonempty but this rect is empty.
62 bool Contains(const Sub
& aRect
) const
64 return aRect
.IsEmpty() ||
65 (x
<= aRect
.x
&& aRect
.XMost() <= XMost() &&
66 y
<= aRect
.y
&& aRect
.YMost() <= YMost());
68 // Returns true if this rectangle contains the rectangle (aX,aY,1,1).
69 bool Contains(T aX
, T aY
) const
71 return x
<= aX
&& aX
+ 1 <= XMost() &&
72 y
<= aY
&& aY
+ 1 <= YMost();
74 // Returns true if this rectangle contains the rectangle (aPoint.x,aPoint.y,1,1).
75 bool Contains(const Point
& aPoint
) const { return Contains(aPoint
.x
, aPoint
.y
); }
77 // Intersection. Returns TRUE if the receiver's area has non-empty
78 // intersection with aRect's area, and FALSE otherwise.
79 // Always returns false if aRect is empty or 'this' is empty.
80 bool Intersects(const Sub
& aRect
) const
82 return x
< aRect
.XMost() && aRect
.x
< XMost() &&
83 y
< aRect
.YMost() && aRect
.y
< YMost();
85 // Returns the rectangle containing the intersection of the points
86 // (including edges) of *this and aRect. If there are no points in that
87 // intersection, returns an empty rectangle with x/y set to the std::max of the x/y
88 // of *this and aRect.
89 Sub
Intersect(const Sub
& aRect
) const
92 result
.x
= std::max(x
, aRect
.x
);
93 result
.y
= std::max(y
, aRect
.y
);
94 result
.width
= std::min(XMost(), aRect
.XMost()) - result
.x
;
95 result
.height
= std::min(YMost(), aRect
.YMost()) - result
.y
;
96 if (result
.width
< 0 || result
.height
< 0) {
101 // Sets *this to be the rectangle containing the intersection of the points
102 // (including edges) of *this and aRect. If there are no points in that
103 // intersection, sets *this to be an empty rectangle with x/y set to the std::max
104 // of the x/y of *this and aRect.
106 // 'this' can be the same object as either aRect1 or aRect2
107 bool IntersectRect(const Sub
& aRect1
, const Sub
& aRect2
)
109 *static_cast<Sub
*>(this) = aRect1
.Intersect(aRect2
);
113 // Returns the smallest rectangle that contains both the area of both
115 // Thus, empty input rectangles are ignored.
116 // If both rectangles are empty, returns this.
117 Sub
Union(const Sub
& aRect
) const
121 } else if (aRect
.IsEmpty()) {
122 return *static_cast<const Sub
*>(this);
124 return UnionEdges(aRect
);
127 // Returns the smallest rectangle that contains both the points (including
128 // edges) of both aRect1 and aRect2.
129 // Thus, empty input rectangles are allowed to affect the result.
130 Sub
UnionEdges(const Sub
& aRect
) const
133 result
.x
= std::min(x
, aRect
.x
);
134 result
.y
= std::min(y
, aRect
.y
);
135 result
.width
= std::max(XMost(), aRect
.XMost()) - result
.x
;
136 result
.height
= std::max(YMost(), aRect
.YMost()) - result
.y
;
139 // Computes the smallest rectangle that contains both the area of both
140 // aRect1 and aRect2, and fills 'this' with the result.
141 // Thus, empty input rectangles are ignored.
142 // If both rectangles are empty, sets 'this' to aRect2.
144 // 'this' can be the same object as either aRect1 or aRect2
145 void UnionRect(const Sub
& aRect1
, const Sub
& aRect2
)
147 *static_cast<Sub
*>(this) = aRect1
.Union(aRect2
);
150 // Computes the smallest rectangle that contains both the points (including
151 // edges) of both aRect1 and aRect2.
152 // Thus, empty input rectangles are allowed to affect the result.
154 // 'this' can be the same object as either aRect1 or aRect2
155 void UnionRectEdges(const Sub
& aRect1
, const Sub
& aRect2
)
157 *static_cast<Sub
*>(this) = aRect1
.UnionEdges(aRect2
);
160 void SetRect(T aX
, T aY
, T aWidth
, T aHeight
)
162 x
= aX
; y
= aY
; width
= aWidth
; height
= aHeight
;
164 void SetRect(const Point
& aPt
, const SizeT
& aSize
)
166 SetRect(aPt
.x
, aPt
.y
, aSize
.width
, aSize
.height
);
168 void MoveTo(T aX
, T aY
) { x
= aX
; y
= aY
; }
169 void MoveTo(const Point
& aPoint
) { x
= aPoint
.x
; y
= aPoint
.y
; }
170 void MoveBy(T aDx
, T aDy
) { x
+= aDx
; y
+= aDy
; }
171 void MoveBy(const Point
& aPoint
) { x
+= aPoint
.x
; y
+= aPoint
.y
; }
172 void SizeTo(T aWidth
, T aHeight
) { width
= aWidth
; height
= aHeight
; }
173 void SizeTo(const SizeT
& aSize
) { width
= aSize
.width
; height
= aSize
.height
; }
175 void Inflate(T aD
) { Inflate(aD
, aD
); }
176 void Inflate(T aDx
, T aDy
)
183 void Inflate(const Margin
& aMargin
)
187 width
+= aMargin
.LeftRight();
188 height
+= aMargin
.TopBottom();
190 void Inflate(const SizeT
& aSize
) { Inflate(aSize
.width
, aSize
.height
); }
192 void Deflate(T aD
) { Deflate(aD
, aD
); }
193 void Deflate(T aDx
, T aDy
)
197 width
= std::max(T(0), width
- 2 * aDx
);
198 height
= std::max(T(0), height
- 2 * aDy
);
200 void Deflate(const Margin
& aMargin
)
204 width
= std::max(T(0), width
- aMargin
.LeftRight());
205 height
= std::max(T(0), height
- aMargin
.TopBottom());
207 void Deflate(const SizeT
& aSize
) { Deflate(aSize
.width
, aSize
.height
); }
209 // Return true if the rectangles contain the same set of points, including
210 // points on the edges.
211 // Use when we care about the exact x/y/width/height values being
212 // equal (i.e. we care about differences in empty rectangles).
213 bool IsEqualEdges(const Sub
& aRect
) const
215 return x
== aRect
.x
&& y
== aRect
.y
&&
216 width
== aRect
.width
&& height
== aRect
.height
;
218 // Return true if the rectangles contain the same area of the plane.
219 // Use when we do not care about differences in empty rectangles.
220 bool IsEqualInterior(const Sub
& aRect
) const
222 return IsEqualEdges(aRect
) || (IsEmpty() && aRect
.IsEmpty());
225 Sub
operator+(const Point
& aPoint
) const
227 return Sub(x
+ aPoint
.x
, y
+ aPoint
.y
, width
, height
);
229 Sub
operator-(const Point
& aPoint
) const
231 return Sub(x
- aPoint
.x
, y
- aPoint
.y
, width
, height
);
233 Sub
& operator+=(const Point
& aPoint
)
236 return *static_cast<Sub
*>(this);
238 Sub
& operator-=(const Point
& aPoint
)
241 return *static_cast<Sub
*>(this);
244 // Find difference as a Margin
245 Margin
operator-(const Sub
& aRect
) const
247 return Margin(aRect
.y
- y
,
248 XMost() - aRect
.XMost(),
249 YMost() - aRect
.YMost(),
253 // Helpers for accessing the vertices
254 Point
TopLeft() const { return Point(x
, y
); }
255 Point
TopRight() const { return Point(XMost(), y
); }
256 Point
BottomLeft() const { return Point(x
, YMost()); }
257 Point
BottomRight() const { return Point(XMost(), YMost()); }
258 Point
Center() const { return Point(x
, y
) + Point(width
, height
)/2; }
259 SizeT
Size() const { return SizeT(width
, height
); }
261 // Helper methods for computing the extents
262 T
X() const { return x
; }
263 T
Y() const { return y
; }
264 T
Width() const { return width
; }
265 T
Height() const { return height
; }
266 T
XMost() const { return x
+ width
; }
267 T
YMost() const { return y
+ height
; }
269 // Moves one edge of the rect without moving the opposite edge.
270 void SetLeftEdge(T aX
) {
271 MOZ_ASSERT(aX
<= XMost());
272 width
= XMost() - aX
;
275 void SetRightEdge(T aXMost
) {
276 MOZ_ASSERT(aXMost
>= x
);
279 void SetTopEdge(T aY
) {
280 MOZ_ASSERT(aY
<= YMost());
281 height
= YMost() - aY
;
284 void SetBottomEdge(T aYMost
) {
285 MOZ_ASSERT(aYMost
>= y
);
289 // Round the rectangle edges to integer coordinates, such that the rounded
290 // rectangle has the same set of pixel centers as the original rectangle.
291 // Edges at offset 0.5 round up.
292 // Suitable for most places where integral device coordinates
293 // are needed, but note that any translation should be applied first to
294 // avoid pixel rounding errors.
295 // Note that this is *not* rounding to nearest integer if the values are negative.
296 // They are always rounding as floor(n + 0.5).
297 // See https://bugzilla.mozilla.org/show_bug.cgi?id=410748#c14
298 // If you need similar method which is using NS_round(), you should create
299 // new |RoundAwayFromZero()| method.
302 T x0
= static_cast<T
>(floor(T(X()) + 0.5));
303 T y0
= static_cast<T
>(floor(T(Y()) + 0.5));
304 T x1
= static_cast<T
>(floor(T(XMost()) + 0.5));
305 T y1
= static_cast<T
>(floor(T(YMost()) + 0.5));
314 // Snap the rectangle edges to integer coordinates, such that the
315 // original rectangle contains the resulting rectangle.
318 T x0
= static_cast<T
>(ceil(T(X())));
319 T y0
= static_cast<T
>(ceil(T(Y())));
320 T x1
= static_cast<T
>(floor(T(XMost())));
321 T y1
= static_cast<T
>(floor(T(YMost())));
330 // Snap the rectangle edges to integer coordinates, such that the
331 // resulting rectangle contains the original rectangle.
334 T x0
= static_cast<T
>(floor(T(X())));
335 T y0
= static_cast<T
>(floor(T(Y())));
336 T x1
= static_cast<T
>(ceil(T(XMost())));
337 T y1
= static_cast<T
>(ceil(T(YMost())));
346 // Scale 'this' by aScale, converting coordinates to integers so that the result is
347 // the smallest integer-coordinate rectangle containing the unrounded result.
348 // Note: this can turn an empty rectangle into a non-empty rectangle
349 void ScaleRoundOut(double aScale
) { ScaleRoundOut(aScale
, aScale
); }
350 // Scale 'this' by aXScale and aYScale, converting coordinates to integers so
351 // that the result is the smallest integer-coordinate rectangle containing the
353 // Note: this can turn an empty rectangle into a non-empty rectangle
354 void ScaleRoundOut(double aXScale
, double aYScale
)
356 T right
= static_cast<T
>(ceil(double(XMost()) * aXScale
));
357 T bottom
= static_cast<T
>(ceil(double(YMost()) * aYScale
));
358 x
= static_cast<T
>(floor(double(x
) * aXScale
));
359 y
= static_cast<T
>(floor(double(y
) * aYScale
));
363 // Scale 'this' by aScale, converting coordinates to integers so that the result is
364 // the largest integer-coordinate rectangle contained by the unrounded result.
365 void ScaleRoundIn(double aScale
) { ScaleRoundIn(aScale
, aScale
); }
366 // Scale 'this' by aXScale and aYScale, converting coordinates to integers so
367 // that the result is the largest integer-coordinate rectangle contained by the
369 void ScaleRoundIn(double aXScale
, double aYScale
)
371 T right
= static_cast<T
>(floor(double(XMost()) * aXScale
));
372 T bottom
= static_cast<T
>(floor(double(YMost()) * aYScale
));
373 x
= static_cast<T
>(ceil(double(x
) * aXScale
));
374 y
= static_cast<T
>(ceil(double(y
) * aYScale
));
375 width
= std::max
<T
>(0, right
- x
);
376 height
= std::max
<T
>(0, bottom
- y
);
378 // Scale 'this' by 1/aScale, converting coordinates to integers so that the result is
379 // the smallest integer-coordinate rectangle containing the unrounded result.
380 // Note: this can turn an empty rectangle into a non-empty rectangle
381 void ScaleInverseRoundOut(double aScale
) { ScaleInverseRoundOut(aScale
, aScale
); }
382 // Scale 'this' by 1/aXScale and 1/aYScale, converting coordinates to integers so
383 // that the result is the smallest integer-coordinate rectangle containing the
385 // Note: this can turn an empty rectangle into a non-empty rectangle
386 void ScaleInverseRoundOut(double aXScale
, double aYScale
)
388 T right
= static_cast<T
>(ceil(double(XMost()) / aXScale
));
389 T bottom
= static_cast<T
>(ceil(double(YMost()) / aYScale
));
390 x
= static_cast<T
>(floor(double(x
) / aXScale
));
391 y
= static_cast<T
>(floor(double(y
) / aYScale
));
395 // Scale 'this' by 1/aScale, converting coordinates to integers so that the result is
396 // the largest integer-coordinate rectangle contained by the unrounded result.
397 void ScaleInverseRoundIn(double aScale
) { ScaleInverseRoundIn(aScale
, aScale
); }
398 // Scale 'this' by 1/aXScale and 1/aYScale, converting coordinates to integers so
399 // that the result is the largest integer-coordinate rectangle contained by the
401 void ScaleInverseRoundIn(double aXScale
, double aYScale
)
403 T right
= static_cast<T
>(floor(double(XMost()) / aXScale
));
404 T bottom
= static_cast<T
>(floor(double(YMost()) / aYScale
));
405 x
= static_cast<T
>(ceil(double(x
) / aXScale
));
406 y
= static_cast<T
>(ceil(double(y
) / aYScale
));
407 width
= std::max
<T
>(0, right
- x
);
408 height
= std::max
<T
>(0, bottom
- y
);
412 * Clamp aPoint to this rectangle. It is allowed to end up on any
413 * edge of the rectangle.
415 Point
ClampPoint(const Point
& aPoint
) const
417 return Point(std::max(x
, std::min(XMost(), aPoint
.x
)),
418 std::max(y
, std::min(YMost(), aPoint
.y
)));
422 // Do not use the default operator== or operator!= !
423 // Use IsEqualEdges or IsEqualInterior explicitly.
424 bool operator==(const Sub
& aRect
) const { return false; }
425 bool operator!=(const Sub
& aRect
) const { return false; }
431 #endif /* MOZILLA_GFX_BASERECT_H_ */