1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/gfx/2d/BaseRect.h Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,472 @@ 1.4 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- 1.5 + * This Source Code Form is subject to the terms of the Mozilla Public 1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.8 + 1.9 +#ifndef MOZILLA_GFX_BASERECT_H_ 1.10 +#define MOZILLA_GFX_BASERECT_H_ 1.11 + 1.12 +#include <algorithm> 1.13 +#include <cmath> 1.14 + 1.15 +#include "mozilla/Assertions.h" 1.16 +#include "mozilla/FloatingPoint.h" 1.17 +#include "mozilla/TypeTraits.h" 1.18 + 1.19 +namespace mozilla { 1.20 +namespace gfx { 1.21 + 1.22 +/** 1.23 + * Rectangles have two interpretations: a set of (zero-size) points, 1.24 + * and a rectangular area of the plane. Most rectangle operations behave 1.25 + * the same no matter what interpretation is being used, but some operations 1.26 + * differ: 1.27 + * -- Equality tests behave differently. When a rectangle represents an area, 1.28 + * all zero-width and zero-height rectangles are equal to each other since they 1.29 + * represent the empty area. But when a rectangle represents a set of 1.30 + * mathematical points, zero-width and zero-height rectangles can be unequal. 1.31 + * -- The union operation can behave differently. When rectangles represent 1.32 + * areas, taking the union of a zero-width or zero-height rectangle with 1.33 + * another rectangle can just ignore the empty rectangle. But when rectangles 1.34 + * represent sets of mathematical points, we may need to extend the latter 1.35 + * rectangle to include the points of a zero-width or zero-height rectangle. 1.36 + * 1.37 + * To ensure that these interpretations are explicitly disambiguated, we 1.38 + * deny access to the == and != operators and require use of IsEqualEdges and 1.39 + * IsEqualInterior instead. Similarly we provide separate Union and UnionEdges 1.40 + * methods. 1.41 + * 1.42 + * Do not use this class directly. Subclass it, pass that subclass as the 1.43 + * Sub parameter, and only use that subclass. 1.44 + */ 1.45 +template <class T, class Sub, class Point, class SizeT, class MarginT> 1.46 +struct BaseRect { 1.47 + T x, y, width, height; 1.48 + 1.49 + // Constructors 1.50 + BaseRect() : x(0), y(0), width(0), height(0) {} 1.51 + BaseRect(const Point& aOrigin, const SizeT &aSize) : 1.52 + x(aOrigin.x), y(aOrigin.y), width(aSize.width), height(aSize.height) 1.53 + { 1.54 + } 1.55 + BaseRect(T aX, T aY, T aWidth, T aHeight) : 1.56 + x(aX), y(aY), width(aWidth), height(aHeight) 1.57 + { 1.58 + } 1.59 + 1.60 + // Emptiness. An empty rect is one that has no area, i.e. its height or width 1.61 + // is <= 0 1.62 + bool IsEmpty() const { return height <= 0 || width <= 0; } 1.63 + void SetEmpty() { width = height = 0; } 1.64 + 1.65 + // "Finite" means not inf and not NaN 1.66 + bool IsFinite() const 1.67 + { 1.68 + typedef typename mozilla::Conditional<mozilla::IsSame<T, float>::value, float, double>::Type FloatType; 1.69 + return (mozilla::IsFinite(FloatType(x)) && 1.70 + mozilla::IsFinite(FloatType(y)) && 1.71 + mozilla::IsFinite(FloatType(width)) && 1.72 + mozilla::IsFinite(FloatType(height))); 1.73 + } 1.74 + 1.75 + // Returns true if this rectangle contains the interior of aRect. Always 1.76 + // returns true if aRect is empty, and always returns false is aRect is 1.77 + // nonempty but this rect is empty. 1.78 + bool Contains(const Sub& aRect) const 1.79 + { 1.80 + return aRect.IsEmpty() || 1.81 + (x <= aRect.x && aRect.XMost() <= XMost() && 1.82 + y <= aRect.y && aRect.YMost() <= YMost()); 1.83 + } 1.84 + // Returns true if this rectangle contains the rectangle (aX,aY,1,1). 1.85 + bool Contains(T aX, T aY) const 1.86 + { 1.87 + return x <= aX && aX + 1 <= XMost() && 1.88 + y <= aY && aY + 1 <= YMost(); 1.89 + } 1.90 + // Returns true if this rectangle contains the rectangle (aPoint.x,aPoint.y,1,1). 1.91 + bool Contains(const Point& aPoint) const { return Contains(aPoint.x, aPoint.y); } 1.92 + 1.93 + // Intersection. Returns TRUE if the receiver's area has non-empty 1.94 + // intersection with aRect's area, and FALSE otherwise. 1.95 + // Always returns false if aRect is empty or 'this' is empty. 1.96 + bool Intersects(const Sub& aRect) const 1.97 + { 1.98 + return x < aRect.XMost() && aRect.x < XMost() && 1.99 + y < aRect.YMost() && aRect.y < YMost(); 1.100 + } 1.101 + // Returns the rectangle containing the intersection of the points 1.102 + // (including edges) of *this and aRect. If there are no points in that 1.103 + // intersection, returns an empty rectangle with x/y set to the std::max of the x/y 1.104 + // of *this and aRect. 1.105 + Sub Intersect(const Sub& aRect) const 1.106 + { 1.107 + Sub result; 1.108 + result.x = std::max<T>(x, aRect.x); 1.109 + result.y = std::max<T>(y, aRect.y); 1.110 + result.width = std::min<T>(XMost(), aRect.XMost()) - result.x; 1.111 + result.height = std::min<T>(YMost(), aRect.YMost()) - result.y; 1.112 + if (result.width < 0 || result.height < 0) { 1.113 + result.SizeTo(0, 0); 1.114 + } 1.115 + return result; 1.116 + } 1.117 + // Sets *this to be the rectangle containing the intersection of the points 1.118 + // (including edges) of *this and aRect. If there are no points in that 1.119 + // intersection, sets *this to be an empty rectangle with x/y set to the std::max 1.120 + // of the x/y of *this and aRect. 1.121 + // 1.122 + // 'this' can be the same object as either aRect1 or aRect2 1.123 + bool IntersectRect(const Sub& aRect1, const Sub& aRect2) 1.124 + { 1.125 + *static_cast<Sub*>(this) = aRect1.Intersect(aRect2); 1.126 + return !IsEmpty(); 1.127 + } 1.128 + 1.129 + // Returns the smallest rectangle that contains both the area of both 1.130 + // this and aRect2. 1.131 + // Thus, empty input rectangles are ignored. 1.132 + // If both rectangles are empty, returns this. 1.133 + Sub Union(const Sub& aRect) const 1.134 + { 1.135 + if (IsEmpty()) { 1.136 + return aRect; 1.137 + } else if (aRect.IsEmpty()) { 1.138 + return *static_cast<const Sub*>(this); 1.139 + } else { 1.140 + return UnionEdges(aRect); 1.141 + } 1.142 + } 1.143 + // Returns the smallest rectangle that contains both the points (including 1.144 + // edges) of both aRect1 and aRect2. 1.145 + // Thus, empty input rectangles are allowed to affect the result. 1.146 + Sub UnionEdges(const Sub& aRect) const 1.147 + { 1.148 + Sub result; 1.149 + result.x = std::min(x, aRect.x); 1.150 + result.y = std::min(y, aRect.y); 1.151 + result.width = std::max(XMost(), aRect.XMost()) - result.x; 1.152 + result.height = std::max(YMost(), aRect.YMost()) - result.y; 1.153 + return result; 1.154 + } 1.155 + // Computes the smallest rectangle that contains both the area of both 1.156 + // aRect1 and aRect2, and fills 'this' with the result. 1.157 + // Thus, empty input rectangles are ignored. 1.158 + // If both rectangles are empty, sets 'this' to aRect2. 1.159 + // 1.160 + // 'this' can be the same object as either aRect1 or aRect2 1.161 + void UnionRect(const Sub& aRect1, const Sub& aRect2) 1.162 + { 1.163 + *static_cast<Sub*>(this) = aRect1.Union(aRect2); 1.164 + } 1.165 + 1.166 + // Computes the smallest rectangle that contains both the points (including 1.167 + // edges) of both aRect1 and aRect2. 1.168 + // Thus, empty input rectangles are allowed to affect the result. 1.169 + // 1.170 + // 'this' can be the same object as either aRect1 or aRect2 1.171 + void UnionRectEdges(const Sub& aRect1, const Sub& aRect2) 1.172 + { 1.173 + *static_cast<Sub*>(this) = aRect1.UnionEdges(aRect2); 1.174 + } 1.175 + 1.176 + void SetRect(T aX, T aY, T aWidth, T aHeight) 1.177 + { 1.178 + x = aX; y = aY; width = aWidth; height = aHeight; 1.179 + } 1.180 + void SetRect(const Point& aPt, const SizeT& aSize) 1.181 + { 1.182 + SetRect(aPt.x, aPt.y, aSize.width, aSize.height); 1.183 + } 1.184 + void MoveTo(T aX, T aY) { x = aX; y = aY; } 1.185 + void MoveTo(const Point& aPoint) { x = aPoint.x; y = aPoint.y; } 1.186 + void MoveBy(T aDx, T aDy) { x += aDx; y += aDy; } 1.187 + void MoveBy(const Point& aPoint) { x += aPoint.x; y += aPoint.y; } 1.188 + void SizeTo(T aWidth, T aHeight) { width = aWidth; height = aHeight; } 1.189 + void SizeTo(const SizeT& aSize) { width = aSize.width; height = aSize.height; } 1.190 + 1.191 + void Inflate(T aD) { Inflate(aD, aD); } 1.192 + void Inflate(T aDx, T aDy) 1.193 + { 1.194 + x -= aDx; 1.195 + y -= aDy; 1.196 + width += 2 * aDx; 1.197 + height += 2 * aDy; 1.198 + } 1.199 + void Inflate(const MarginT& aMargin) 1.200 + { 1.201 + x -= aMargin.left; 1.202 + y -= aMargin.top; 1.203 + width += aMargin.LeftRight(); 1.204 + height += aMargin.TopBottom(); 1.205 + } 1.206 + void Inflate(const SizeT& aSize) { Inflate(aSize.width, aSize.height); } 1.207 + 1.208 + void Deflate(T aD) { Deflate(aD, aD); } 1.209 + void Deflate(T aDx, T aDy) 1.210 + { 1.211 + x += aDx; 1.212 + y += aDy; 1.213 + width = std::max(T(0), width - 2 * aDx); 1.214 + height = std::max(T(0), height - 2 * aDy); 1.215 + } 1.216 + void Deflate(const MarginT& aMargin) 1.217 + { 1.218 + x += aMargin.left; 1.219 + y += aMargin.top; 1.220 + width = std::max(T(0), width - aMargin.LeftRight()); 1.221 + height = std::max(T(0), height - aMargin.TopBottom()); 1.222 + } 1.223 + void Deflate(const SizeT& aSize) { Deflate(aSize.width, aSize.height); } 1.224 + 1.225 + // Return true if the rectangles contain the same set of points, including 1.226 + // points on the edges. 1.227 + // Use when we care about the exact x/y/width/height values being 1.228 + // equal (i.e. we care about differences in empty rectangles). 1.229 + bool IsEqualEdges(const Sub& aRect) const 1.230 + { 1.231 + return x == aRect.x && y == aRect.y && 1.232 + width == aRect.width && height == aRect.height; 1.233 + } 1.234 + // Return true if the rectangles contain the same area of the plane. 1.235 + // Use when we do not care about differences in empty rectangles. 1.236 + bool IsEqualInterior(const Sub& aRect) const 1.237 + { 1.238 + return IsEqualEdges(aRect) || (IsEmpty() && aRect.IsEmpty()); 1.239 + } 1.240 + 1.241 + Sub operator+(const Point& aPoint) const 1.242 + { 1.243 + return Sub(x + aPoint.x, y + aPoint.y, width, height); 1.244 + } 1.245 + Sub operator-(const Point& aPoint) const 1.246 + { 1.247 + return Sub(x - aPoint.x, y - aPoint.y, width, height); 1.248 + } 1.249 + Sub& operator+=(const Point& aPoint) 1.250 + { 1.251 + MoveBy(aPoint); 1.252 + return *static_cast<Sub*>(this); 1.253 + } 1.254 + Sub& operator-=(const Point& aPoint) 1.255 + { 1.256 + MoveBy(-aPoint); 1.257 + return *static_cast<Sub*>(this); 1.258 + } 1.259 + 1.260 + // Find difference as a Margin 1.261 + MarginT operator-(const Sub& aRect) const 1.262 + { 1.263 + return MarginT(aRect.y - y, 1.264 + XMost() - aRect.XMost(), 1.265 + YMost() - aRect.YMost(), 1.266 + aRect.x - x); 1.267 + } 1.268 + 1.269 + // Helpers for accessing the vertices 1.270 + Point TopLeft() const { return Point(x, y); } 1.271 + Point TopRight() const { return Point(XMost(), y); } 1.272 + Point BottomLeft() const { return Point(x, YMost()); } 1.273 + Point BottomRight() const { return Point(XMost(), YMost()); } 1.274 + Point Center() const { return Point(x, y) + Point(width, height)/2; } 1.275 + SizeT Size() const { return SizeT(width, height); } 1.276 + 1.277 + // Helper methods for computing the extents 1.278 + T X() const { return x; } 1.279 + T Y() const { return y; } 1.280 + T Width() const { return width; } 1.281 + T Height() const { return height; } 1.282 + T XMost() const { return x + width; } 1.283 + T YMost() const { return y + height; } 1.284 + 1.285 + // Moves one edge of the rect without moving the opposite edge. 1.286 + void SetLeftEdge(T aX) { 1.287 + MOZ_ASSERT(aX <= XMost()); 1.288 + width = XMost() - aX; 1.289 + x = aX; 1.290 + } 1.291 + void SetRightEdge(T aXMost) { 1.292 + MOZ_ASSERT(aXMost >= x); 1.293 + width = aXMost - x; 1.294 + } 1.295 + void SetTopEdge(T aY) { 1.296 + MOZ_ASSERT(aY <= YMost()); 1.297 + height = YMost() - aY; 1.298 + y = aY; 1.299 + } 1.300 + void SetBottomEdge(T aYMost) { 1.301 + MOZ_ASSERT(aYMost >= y); 1.302 + height = aYMost - y; 1.303 + } 1.304 + 1.305 + // Round the rectangle edges to integer coordinates, such that the rounded 1.306 + // rectangle has the same set of pixel centers as the original rectangle. 1.307 + // Edges at offset 0.5 round up. 1.308 + // Suitable for most places where integral device coordinates 1.309 + // are needed, but note that any translation should be applied first to 1.310 + // avoid pixel rounding errors. 1.311 + // Note that this is *not* rounding to nearest integer if the values are negative. 1.312 + // They are always rounding as floor(n + 0.5). 1.313 + // See https://bugzilla.mozilla.org/show_bug.cgi?id=410748#c14 1.314 + // If you need similar method which is using NS_round(), you should create 1.315 + // new |RoundAwayFromZero()| method. 1.316 + void Round() 1.317 + { 1.318 + T x0 = static_cast<T>(floor(T(X()) + 0.5)); 1.319 + T y0 = static_cast<T>(floor(T(Y()) + 0.5)); 1.320 + T x1 = static_cast<T>(floor(T(XMost()) + 0.5)); 1.321 + T y1 = static_cast<T>(floor(T(YMost()) + 0.5)); 1.322 + 1.323 + x = x0; 1.324 + y = y0; 1.325 + 1.326 + width = x1 - x0; 1.327 + height = y1 - y0; 1.328 + } 1.329 + 1.330 + // Snap the rectangle edges to integer coordinates, such that the 1.331 + // original rectangle contains the resulting rectangle. 1.332 + void RoundIn() 1.333 + { 1.334 + T x0 = static_cast<T>(ceil(T(X()))); 1.335 + T y0 = static_cast<T>(ceil(T(Y()))); 1.336 + T x1 = static_cast<T>(floor(T(XMost()))); 1.337 + T y1 = static_cast<T>(floor(T(YMost()))); 1.338 + 1.339 + x = x0; 1.340 + y = y0; 1.341 + 1.342 + width = x1 - x0; 1.343 + height = y1 - y0; 1.344 + } 1.345 + 1.346 + // Snap the rectangle edges to integer coordinates, such that the 1.347 + // resulting rectangle contains the original rectangle. 1.348 + void RoundOut() 1.349 + { 1.350 + T x0 = static_cast<T>(floor(T(X()))); 1.351 + T y0 = static_cast<T>(floor(T(Y()))); 1.352 + T x1 = static_cast<T>(ceil(T(XMost()))); 1.353 + T y1 = static_cast<T>(ceil(T(YMost()))); 1.354 + 1.355 + x = x0; 1.356 + y = y0; 1.357 + 1.358 + width = x1 - x0; 1.359 + height = y1 - y0; 1.360 + } 1.361 + 1.362 + // Scale 'this' by aScale without doing any rounding. 1.363 + void Scale(T aScale) { Scale(aScale, aScale); } 1.364 + // Scale 'this' by aXScale and aYScale, without doing any rounding. 1.365 + void Scale(T aXScale, T aYScale) 1.366 + { 1.367 + T right = XMost() * aXScale; 1.368 + T bottom = YMost() * aYScale; 1.369 + x = x * aXScale; 1.370 + y = y * aYScale; 1.371 + width = right - x; 1.372 + height = bottom - y; 1.373 + } 1.374 + // Scale 'this' by aScale, converting coordinates to integers so that the result is 1.375 + // the smallest integer-coordinate rectangle containing the unrounded result. 1.376 + // Note: this can turn an empty rectangle into a non-empty rectangle 1.377 + void ScaleRoundOut(double aScale) { ScaleRoundOut(aScale, aScale); } 1.378 + // Scale 'this' by aXScale and aYScale, converting coordinates to integers so 1.379 + // that the result is the smallest integer-coordinate rectangle containing the 1.380 + // unrounded result. 1.381 + // Note: this can turn an empty rectangle into a non-empty rectangle 1.382 + void ScaleRoundOut(double aXScale, double aYScale) 1.383 + { 1.384 + T right = static_cast<T>(ceil(double(XMost()) * aXScale)); 1.385 + T bottom = static_cast<T>(ceil(double(YMost()) * aYScale)); 1.386 + x = static_cast<T>(floor(double(x) * aXScale)); 1.387 + y = static_cast<T>(floor(double(y) * aYScale)); 1.388 + width = right - x; 1.389 + height = bottom - y; 1.390 + } 1.391 + // Scale 'this' by aScale, converting coordinates to integers so that the result is 1.392 + // the largest integer-coordinate rectangle contained by the unrounded result. 1.393 + void ScaleRoundIn(double aScale) { ScaleRoundIn(aScale, aScale); } 1.394 + // Scale 'this' by aXScale and aYScale, converting coordinates to integers so 1.395 + // that the result is the largest integer-coordinate rectangle contained by the 1.396 + // unrounded result. 1.397 + void ScaleRoundIn(double aXScale, double aYScale) 1.398 + { 1.399 + T right = static_cast<T>(floor(double(XMost()) * aXScale)); 1.400 + T bottom = static_cast<T>(floor(double(YMost()) * aYScale)); 1.401 + x = static_cast<T>(ceil(double(x) * aXScale)); 1.402 + y = static_cast<T>(ceil(double(y) * aYScale)); 1.403 + width = std::max<T>(0, right - x); 1.404 + height = std::max<T>(0, bottom - y); 1.405 + } 1.406 + // Scale 'this' by 1/aScale, converting coordinates to integers so that the result is 1.407 + // the smallest integer-coordinate rectangle containing the unrounded result. 1.408 + // Note: this can turn an empty rectangle into a non-empty rectangle 1.409 + void ScaleInverseRoundOut(double aScale) { ScaleInverseRoundOut(aScale, aScale); } 1.410 + // Scale 'this' by 1/aXScale and 1/aYScale, converting coordinates to integers so 1.411 + // that the result is the smallest integer-coordinate rectangle containing the 1.412 + // unrounded result. 1.413 + // Note: this can turn an empty rectangle into a non-empty rectangle 1.414 + void ScaleInverseRoundOut(double aXScale, double aYScale) 1.415 + { 1.416 + T right = static_cast<T>(ceil(double(XMost()) / aXScale)); 1.417 + T bottom = static_cast<T>(ceil(double(YMost()) / aYScale)); 1.418 + x = static_cast<T>(floor(double(x) / aXScale)); 1.419 + y = static_cast<T>(floor(double(y) / aYScale)); 1.420 + width = right - x; 1.421 + height = bottom - y; 1.422 + } 1.423 + // Scale 'this' by 1/aScale, converting coordinates to integers so that the result is 1.424 + // the largest integer-coordinate rectangle contained by the unrounded result. 1.425 + void ScaleInverseRoundIn(double aScale) { ScaleInverseRoundIn(aScale, aScale); } 1.426 + // Scale 'this' by 1/aXScale and 1/aYScale, converting coordinates to integers so 1.427 + // that the result is the largest integer-coordinate rectangle contained by the 1.428 + // unrounded result. 1.429 + void ScaleInverseRoundIn(double aXScale, double aYScale) 1.430 + { 1.431 + T right = static_cast<T>(floor(double(XMost()) / aXScale)); 1.432 + T bottom = static_cast<T>(floor(double(YMost()) / aYScale)); 1.433 + x = static_cast<T>(ceil(double(x) / aXScale)); 1.434 + y = static_cast<T>(ceil(double(y) / aYScale)); 1.435 + width = std::max<T>(0, right - x); 1.436 + height = std::max<T>(0, bottom - y); 1.437 + } 1.438 + 1.439 + /** 1.440 + * Clamp aPoint to this rectangle. It is allowed to end up on any 1.441 + * edge of the rectangle. 1.442 + */ 1.443 + Point ClampPoint(const Point& aPoint) const 1.444 + { 1.445 + return Point(std::max(x, std::min(XMost(), aPoint.x)), 1.446 + std::max(y, std::min(YMost(), aPoint.y))); 1.447 + } 1.448 + 1.449 + /** 1.450 + * Clamp this rectangle to be inside aRect. The function returns a copy of 1.451 + * this rect after it is forced inside the bounds of aRect. It will attempt to 1.452 + * retain the size but will shrink the dimensions that don't fit. 1.453 + */ 1.454 + Sub ForceInside(const Sub& aRect) const 1.455 + { 1.456 + Sub rect(std::max(aRect.x, x), 1.457 + std::max(aRect.y, y), 1.458 + std::min(aRect.width, width), 1.459 + std::min(aRect.height, height)); 1.460 + rect.x = std::min(rect.XMost(), aRect.XMost()) - rect.width; 1.461 + rect.y = std::min(rect.YMost(), aRect.YMost()) - rect.height; 1.462 + return rect; 1.463 + } 1.464 + 1.465 +private: 1.466 + // Do not use the default operator== or operator!= ! 1.467 + // Use IsEqualEdges or IsEqualInterior explicitly. 1.468 + bool operator==(const Sub& aRect) const { return false; } 1.469 + bool operator!=(const Sub& aRect) const { return false; } 1.470 +}; 1.471 + 1.472 +} 1.473 +} 1.474 + 1.475 +#endif /* MOZILLA_GFX_BASERECT_H_ */