Bug 849918 - Initial support for PannerNode's 3D positional audio (equalpower panning...
[gecko.git] / gfx / 2d / BaseRect.h
blobc8735043c7ea9bbebe5b4815281da10e700abb27
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_
9 #include <cmath>
10 #include <mozilla/Assertions.h>
11 #include <algorithm>
13 namespace mozilla {
14 namespace gfx {
16 /**
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
20 * differ:
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
34 * methods.
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>
40 struct BaseRect {
41 T x, y, width, height;
43 // Constructors
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
55 // is <= 0
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
91 Sub result;
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) {
97 result.SizeTo(0, 0);
99 return result;
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);
110 return !IsEmpty();
113 // Returns the smallest rectangle that contains both the area of both
114 // this and aRect2.
115 // Thus, empty input rectangles are ignored.
116 // If both rectangles are empty, returns this.
117 Sub Union(const Sub& aRect) const
119 if (IsEmpty()) {
120 return aRect;
121 } else if (aRect.IsEmpty()) {
122 return *static_cast<const Sub*>(this);
123 } else {
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
132 Sub result;
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;
137 return result;
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)
178 x -= aDx;
179 y -= aDy;
180 width += 2 * aDx;
181 height += 2 * aDy;
183 void Inflate(const Margin& aMargin)
185 x -= aMargin.left;
186 y -= aMargin.top;
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)
195 x += aDx;
196 y += 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)
202 x += aMargin.left;
203 y += aMargin.top;
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)
235 MoveBy(aPoint);
236 return *static_cast<Sub*>(this);
238 Sub& operator-=(const Point& aPoint)
240 MoveBy(-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(),
250 aRect.x - x);
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;
273 x = aX;
275 void SetRightEdge(T aXMost) {
276 MOZ_ASSERT(aXMost >= x);
277 width = aXMost - x;
279 void SetTopEdge(T aY) {
280 MOZ_ASSERT(aY <= YMost());
281 height = YMost() - aY;
282 y = aY;
284 void SetBottomEdge(T aYMost) {
285 MOZ_ASSERT(aYMost >= y);
286 height = 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.
300 void Round()
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));
307 x = x0;
308 y = y0;
310 width = x1 - x0;
311 height = y1 - y0;
314 // Snap the rectangle edges to integer coordinates, such that the
315 // original rectangle contains the resulting rectangle.
316 void RoundIn()
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())));
323 x = x0;
324 y = y0;
326 width = x1 - x0;
327 height = y1 - y0;
330 // Snap the rectangle edges to integer coordinates, such that the
331 // resulting rectangle contains the original rectangle.
332 void RoundOut()
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())));
339 x = x0;
340 y = y0;
342 width = x1 - x0;
343 height = y1 - y0;
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
352 // unrounded result.
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));
360 width = right - x;
361 height = bottom - y;
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
368 // unrounded result.
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
384 // unrounded result.
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));
392 width = right - x;
393 height = bottom - y;
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
400 // unrounded result.
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)));
421 private:
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_ */