gfx/2d/BaseRect.h

changeset 0
6474c204b198
     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_ */

mercurial