Bumping gaia.json for 2 gaia revision(s) a=gaia-bump
[gecko.git] / gfx / 2d / BaseRect.h
blob4285da10678b612f065ccdd8a1f9b8a230c70963
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 <algorithm>
10 #include <cmath>
11 #include <ostream>
13 #include "mozilla/Assertions.h"
14 #include "mozilla/FloatingPoint.h"
15 #include "mozilla/TypeTraits.h"
17 namespace mozilla {
18 namespace gfx {
20 /**
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
24 * differ:
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
38 * methods.
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>
44 struct BaseRect {
45 T x, y, width, height;
47 // Constructors
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
59 // is <= 0
60 bool IsEmpty() const { return height <= 0 || width <= 0; }
61 void SetEmpty() { width = height = 0; }
63 // "Finite" means not inf and not NaN
64 bool IsFinite() const
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
110 Sub result;
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) {
116 result.SizeTo(0, 0);
118 return result;
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);
129 return !IsEmpty();
132 // Returns the smallest rectangle that contains both the area of both
133 // this and aRect2.
134 // Thus, empty input rectangles are ignored.
135 // If both rectangles are empty, returns this.
136 Sub Union(const Sub& aRect) const
138 if (IsEmpty()) {
139 return aRect;
140 } else if (aRect.IsEmpty()) {
141 return *static_cast<const Sub*>(this);
142 } else {
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
151 Sub result;
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;
156 return result;
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)
197 x -= aDx;
198 y -= aDy;
199 width += 2 * aDx;
200 height += 2 * aDy;
202 void Inflate(const MarginT& aMargin)
204 x -= aMargin.left;
205 y -= aMargin.top;
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)
213 T xMost = XMost();
214 T yMost = YMost();
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;
221 width = xMost - x;
222 height = yMost - y;
225 void Deflate(T aD) { Deflate(aD, aD); }
226 void Deflate(T aDx, T aDy)
228 x += aDx;
229 y += 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)
235 x += aMargin.left;
236 y += aMargin.top;
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)
260 aSub += aPoint;
261 return aSub;
263 friend Sub operator-(Sub aSub, const Point& aPoint)
265 aSub -= aPoint;
266 return aSub;
268 friend Sub operator+(Sub aSub, const SizeT& aSize)
270 aSub += aSize;
271 return aSub;
273 friend Sub operator-(Sub aSub, const SizeT& aSize)
275 aSub -= aSize;
276 return aSub;
278 Sub& operator+=(const Point& aPoint)
280 MoveBy(aPoint);
281 return *static_cast<Sub*>(this);
283 Sub& operator-=(const Point& aPoint)
285 MoveBy(-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(),
306 aRect.x - x);
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;
329 x = aX;
331 void SetRightEdge(T aXMost) {
332 MOZ_ASSERT(aXMost >= x);
333 width = aXMost - x;
335 void SetTopEdge(T aY) {
336 MOZ_ASSERT(aY <= YMost());
337 height = YMost() - aY;
338 y = aY;
340 void SetBottomEdge(T aYMost) {
341 MOZ_ASSERT(aYMost >= y);
342 height = 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.
356 void Round()
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));
363 x = x0;
364 y = y0;
366 width = x1 - x0;
367 height = y1 - y0;
370 // Snap the rectangle edges to integer coordinates, such that the
371 // original rectangle contains the resulting rectangle.
372 void RoundIn()
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())));
379 x = x0;
380 y = y0;
382 width = x1 - x0;
383 height = y1 - y0;
386 // Snap the rectangle edges to integer coordinates, such that the
387 // resulting rectangle contains the original rectangle.
388 void RoundOut()
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())));
395 x = x0;
396 y = y0;
398 width = x1 - x0;
399 height = y1 - y0;
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;
409 x = x * aXScale;
410 y = y * aYScale;
411 width = right - x;
412 height = bottom - y;
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
420 // unrounded result.
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));
428 width = right - x;
429 height = bottom - y;
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
436 // unrounded result.
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
452 // unrounded result.
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));
460 width = right - x;
461 height = bottom - y;
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
468 // unrounded result.
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;
502 return rect;
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 << ')';
511 private:
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_ */