gfx/2d/BaseRect.h

Tue, 06 Jan 2015 21:39:09 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Tue, 06 Jan 2015 21:39:09 +0100
branch
TOR_BUG_9701
changeset 8
97036ab72558
permissions
-rw-r--r--

Conditionally force memory storage according to privacy.thirdparty.isolate;
This solves Tor bug #9701, complying with disk avoidance documented in
https://www.torproject.org/projects/torbrowser/design/#disk-avoidance.

     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>
    12 #include "mozilla/Assertions.h"
    13 #include "mozilla/FloatingPoint.h"
    14 #include "mozilla/TypeTraits.h"
    16 namespace mozilla {
    17 namespace gfx {
    19 /**
    20  * Rectangles have two interpretations: a set of (zero-size) points,
    21  * and a rectangular area of the plane. Most rectangle operations behave
    22  * the same no matter what interpretation is being used, but some operations
    23  * differ:
    24  * -- Equality tests behave differently. When a rectangle represents an area,
    25  * all zero-width and zero-height rectangles are equal to each other since they
    26  * represent the empty area. But when a rectangle represents a set of
    27  * mathematical points, zero-width and zero-height rectangles can be unequal.
    28  * -- The union operation can behave differently. When rectangles represent
    29  * areas, taking the union of a zero-width or zero-height rectangle with
    30  * another rectangle can just ignore the empty rectangle. But when rectangles
    31  * represent sets of mathematical points, we may need to extend the latter
    32  * rectangle to include the points of a zero-width or zero-height rectangle.
    33  *
    34  * To ensure that these interpretations are explicitly disambiguated, we
    35  * deny access to the == and != operators and require use of IsEqualEdges and
    36  * IsEqualInterior instead. Similarly we provide separate Union and UnionEdges
    37  * methods.
    38  *
    39  * Do not use this class directly. Subclass it, pass that subclass as the
    40  * Sub parameter, and only use that subclass.
    41  */
    42 template <class T, class Sub, class Point, class SizeT, class MarginT>
    43 struct BaseRect {
    44   T x, y, width, height;
    46   // Constructors
    47   BaseRect() : x(0), y(0), width(0), height(0) {}
    48   BaseRect(const Point& aOrigin, const SizeT &aSize) :
    49       x(aOrigin.x), y(aOrigin.y), width(aSize.width), height(aSize.height)
    50   {
    51   }
    52   BaseRect(T aX, T aY, T aWidth, T aHeight) :
    53       x(aX), y(aY), width(aWidth), height(aHeight)
    54   {
    55   }
    57   // Emptiness. An empty rect is one that has no area, i.e. its height or width
    58   // is <= 0
    59   bool IsEmpty() const { return height <= 0 || width <= 0; }
    60   void SetEmpty() { width = height = 0; }
    62   // "Finite" means not inf and not NaN
    63   bool IsFinite() const
    64   {
    65     typedef typename mozilla::Conditional<mozilla::IsSame<T, float>::value, float, double>::Type FloatType;
    66     return (mozilla::IsFinite(FloatType(x)) &&
    67             mozilla::IsFinite(FloatType(y)) &&
    68             mozilla::IsFinite(FloatType(width)) &&
    69             mozilla::IsFinite(FloatType(height)));
    70   }
    72   // Returns true if this rectangle contains the interior of aRect. Always
    73   // returns true if aRect is empty, and always returns false is aRect is
    74   // nonempty but this rect is empty.
    75   bool Contains(const Sub& aRect) const
    76   {
    77     return aRect.IsEmpty() ||
    78            (x <= aRect.x && aRect.XMost() <= XMost() &&
    79             y <= aRect.y && aRect.YMost() <= YMost());
    80   }
    81   // Returns true if this rectangle contains the rectangle (aX,aY,1,1).
    82   bool Contains(T aX, T aY) const
    83   {
    84     return x <= aX && aX + 1 <= XMost() &&
    85            y <= aY && aY + 1 <= YMost();
    86   }
    87   // Returns true if this rectangle contains the rectangle (aPoint.x,aPoint.y,1,1).
    88   bool Contains(const Point& aPoint) const { return Contains(aPoint.x, aPoint.y); }
    90   // Intersection. Returns TRUE if the receiver's area has non-empty
    91   // intersection with aRect's area, and FALSE otherwise.
    92   // Always returns false if aRect is empty or 'this' is empty.
    93   bool Intersects(const Sub& aRect) const
    94   {
    95     return x < aRect.XMost() && aRect.x < XMost() &&
    96            y < aRect.YMost() && aRect.y < YMost();
    97   }
    98   // Returns the rectangle containing the intersection of the points
    99   // (including edges) of *this and aRect. If there are no points in that
   100   // intersection, returns an empty rectangle with x/y set to the std::max of the x/y
   101   // of *this and aRect.
   102   Sub Intersect(const Sub& aRect) const
   103   {
   104     Sub result;
   105     result.x = std::max<T>(x, aRect.x);
   106     result.y = std::max<T>(y, aRect.y);
   107     result.width = std::min<T>(XMost(), aRect.XMost()) - result.x;
   108     result.height = std::min<T>(YMost(), aRect.YMost()) - result.y;
   109     if (result.width < 0 || result.height < 0) {
   110       result.SizeTo(0, 0);
   111     }
   112     return result;
   113   }
   114   // Sets *this to be the rectangle containing the intersection of the points
   115   // (including edges) of *this and aRect. If there are no points in that
   116   // intersection, sets *this to be an empty rectangle with x/y set to the std::max
   117   // of the x/y of *this and aRect.
   118   //
   119   // 'this' can be the same object as either aRect1 or aRect2
   120   bool IntersectRect(const Sub& aRect1, const Sub& aRect2)
   121   {
   122     *static_cast<Sub*>(this) = aRect1.Intersect(aRect2);
   123     return !IsEmpty();
   124   }
   126   // Returns the smallest rectangle that contains both the area of both
   127   // this and aRect2.
   128   // Thus, empty input rectangles are ignored.
   129   // If both rectangles are empty, returns this.
   130   Sub Union(const Sub& aRect) const
   131   {
   132     if (IsEmpty()) {
   133       return aRect;
   134     } else if (aRect.IsEmpty()) {
   135       return *static_cast<const Sub*>(this);
   136     } else {
   137       return UnionEdges(aRect);
   138     }
   139   }
   140   // Returns the smallest rectangle that contains both the points (including
   141   // edges) of both aRect1 and aRect2.
   142   // Thus, empty input rectangles are allowed to affect the result.
   143   Sub UnionEdges(const Sub& aRect) const
   144   {
   145     Sub result;
   146     result.x = std::min(x, aRect.x);
   147     result.y = std::min(y, aRect.y);
   148     result.width = std::max(XMost(), aRect.XMost()) - result.x;
   149     result.height = std::max(YMost(), aRect.YMost()) - result.y;
   150     return result;
   151   }
   152   // Computes the smallest rectangle that contains both the area of both
   153   // aRect1 and aRect2, and fills 'this' with the result.
   154   // Thus, empty input rectangles are ignored.
   155   // If both rectangles are empty, sets 'this' to aRect2.
   156   //
   157   // 'this' can be the same object as either aRect1 or aRect2
   158   void UnionRect(const Sub& aRect1, const Sub& aRect2)
   159   {
   160     *static_cast<Sub*>(this) = aRect1.Union(aRect2);
   161   }
   163   // Computes the smallest rectangle that contains both the points (including
   164   // edges) of both aRect1 and aRect2.
   165   // Thus, empty input rectangles are allowed to affect the result.
   166   //
   167   // 'this' can be the same object as either aRect1 or aRect2
   168   void UnionRectEdges(const Sub& aRect1, const Sub& aRect2)
   169   {
   170     *static_cast<Sub*>(this) = aRect1.UnionEdges(aRect2);
   171   }
   173   void SetRect(T aX, T aY, T aWidth, T aHeight)
   174   {
   175     x = aX; y = aY; width = aWidth; height = aHeight;
   176   }
   177   void SetRect(const Point& aPt, const SizeT& aSize)
   178   {
   179     SetRect(aPt.x, aPt.y, aSize.width, aSize.height);
   180   }
   181   void MoveTo(T aX, T aY) { x = aX; y = aY; }
   182   void MoveTo(const Point& aPoint) { x = aPoint.x; y = aPoint.y; }
   183   void MoveBy(T aDx, T aDy) { x += aDx; y += aDy; }
   184   void MoveBy(const Point& aPoint) { x += aPoint.x; y += aPoint.y; }
   185   void SizeTo(T aWidth, T aHeight) { width = aWidth; height = aHeight; }
   186   void SizeTo(const SizeT& aSize) { width = aSize.width; height = aSize.height; }
   188   void Inflate(T aD) { Inflate(aD, aD); }
   189   void Inflate(T aDx, T aDy)
   190   {
   191     x -= aDx;
   192     y -= aDy;
   193     width += 2 * aDx;
   194     height += 2 * aDy;
   195   }
   196   void Inflate(const MarginT& aMargin)
   197   {
   198     x -= aMargin.left;
   199     y -= aMargin.top;
   200     width += aMargin.LeftRight();
   201     height += aMargin.TopBottom();
   202   }
   203   void Inflate(const SizeT& aSize) { Inflate(aSize.width, aSize.height); }
   205   void Deflate(T aD) { Deflate(aD, aD); }
   206   void Deflate(T aDx, T aDy)
   207   {
   208     x += aDx;
   209     y += aDy;
   210     width = std::max(T(0), width - 2 * aDx);
   211     height = std::max(T(0), height - 2 * aDy);
   212   }
   213   void Deflate(const MarginT& aMargin)
   214   {
   215     x += aMargin.left;
   216     y += aMargin.top;
   217     width = std::max(T(0), width - aMargin.LeftRight());
   218     height = std::max(T(0), height - aMargin.TopBottom());
   219   }
   220   void Deflate(const SizeT& aSize) { Deflate(aSize.width, aSize.height); }
   222   // Return true if the rectangles contain the same set of points, including
   223   // points on the edges.
   224   // Use when we care about the exact x/y/width/height values being
   225   // equal (i.e. we care about differences in empty rectangles).
   226   bool IsEqualEdges(const Sub& aRect) const
   227   {
   228     return x == aRect.x && y == aRect.y &&
   229            width == aRect.width && height == aRect.height;
   230   }
   231   // Return true if the rectangles contain the same area of the plane.
   232   // Use when we do not care about differences in empty rectangles.
   233   bool IsEqualInterior(const Sub& aRect) const
   234   {
   235     return IsEqualEdges(aRect) || (IsEmpty() && aRect.IsEmpty());
   236   }
   238   Sub operator+(const Point& aPoint) const
   239   {
   240     return Sub(x + aPoint.x, y + aPoint.y, width, height);
   241   }
   242   Sub operator-(const Point& aPoint) const
   243   {
   244     return Sub(x - aPoint.x, y - aPoint.y, width, height);
   245   }
   246   Sub& operator+=(const Point& aPoint)
   247   {
   248     MoveBy(aPoint);
   249     return *static_cast<Sub*>(this);
   250   }
   251   Sub& operator-=(const Point& aPoint)
   252   {
   253     MoveBy(-aPoint);
   254     return *static_cast<Sub*>(this);
   255   }
   257   // Find difference as a Margin
   258   MarginT operator-(const Sub& aRect) const
   259   {
   260     return MarginT(aRect.y - y,
   261                    XMost() - aRect.XMost(),
   262                    YMost() - aRect.YMost(),
   263                    aRect.x - x);
   264   }
   266   // Helpers for accessing the vertices
   267   Point TopLeft() const { return Point(x, y); }
   268   Point TopRight() const { return Point(XMost(), y); }
   269   Point BottomLeft() const { return Point(x, YMost()); }
   270   Point BottomRight() const { return Point(XMost(), YMost()); }
   271   Point Center() const { return Point(x, y) + Point(width, height)/2; }
   272   SizeT Size() const { return SizeT(width, height); }
   274   // Helper methods for computing the extents
   275   T X() const { return x; }
   276   T Y() const { return y; }
   277   T Width() const { return width; }
   278   T Height() const { return height; }
   279   T XMost() const { return x + width; }
   280   T YMost() const { return y + height; }
   282   // Moves one edge of the rect without moving the opposite edge.
   283   void SetLeftEdge(T aX) {
   284     MOZ_ASSERT(aX <= XMost());
   285     width = XMost() - aX;
   286     x = aX;
   287   }
   288   void SetRightEdge(T aXMost) { 
   289     MOZ_ASSERT(aXMost >= x);
   290     width = aXMost - x; 
   291   }
   292   void SetTopEdge(T aY) {
   293     MOZ_ASSERT(aY <= YMost());
   294     height = YMost() - aY;
   295     y = aY;
   296   }
   297   void SetBottomEdge(T aYMost) { 
   298     MOZ_ASSERT(aYMost >= y);
   299     height = aYMost - y; 
   300   }
   302   // Round the rectangle edges to integer coordinates, such that the rounded
   303   // rectangle has the same set of pixel centers as the original rectangle.
   304   // Edges at offset 0.5 round up.
   305   // Suitable for most places where integral device coordinates
   306   // are needed, but note that any translation should be applied first to
   307   // avoid pixel rounding errors.
   308   // Note that this is *not* rounding to nearest integer if the values are negative.
   309   // They are always rounding as floor(n + 0.5).
   310   // See https://bugzilla.mozilla.org/show_bug.cgi?id=410748#c14
   311   // If you need similar method which is using NS_round(), you should create
   312   // new |RoundAwayFromZero()| method.
   313   void Round()
   314   {
   315     T x0 = static_cast<T>(floor(T(X()) + 0.5));
   316     T y0 = static_cast<T>(floor(T(Y()) + 0.5));
   317     T x1 = static_cast<T>(floor(T(XMost()) + 0.5));
   318     T y1 = static_cast<T>(floor(T(YMost()) + 0.5));
   320     x = x0;
   321     y = y0;
   323     width = x1 - x0;
   324     height = y1 - y0;
   325   }
   327   // Snap the rectangle edges to integer coordinates, such that the
   328   // original rectangle contains the resulting rectangle.
   329   void RoundIn()
   330   {
   331     T x0 = static_cast<T>(ceil(T(X())));
   332     T y0 = static_cast<T>(ceil(T(Y())));
   333     T x1 = static_cast<T>(floor(T(XMost())));
   334     T y1 = static_cast<T>(floor(T(YMost())));
   336     x = x0;
   337     y = y0;
   339     width = x1 - x0;
   340     height = y1 - y0;
   341   }
   343   // Snap the rectangle edges to integer coordinates, such that the
   344   // resulting rectangle contains the original rectangle.
   345   void RoundOut()
   346   {
   347     T x0 = static_cast<T>(floor(T(X())));
   348     T y0 = static_cast<T>(floor(T(Y())));
   349     T x1 = static_cast<T>(ceil(T(XMost())));
   350     T y1 = static_cast<T>(ceil(T(YMost())));
   352     x = x0;
   353     y = y0;
   355     width = x1 - x0;
   356     height = y1 - y0;
   357   }
   359   // Scale 'this' by aScale without doing any rounding.
   360   void Scale(T aScale) { Scale(aScale, aScale); }
   361   // Scale 'this' by aXScale and aYScale, without doing any rounding.
   362   void Scale(T aXScale, T aYScale)
   363   {
   364     T right = XMost() * aXScale;
   365     T bottom = YMost() * aYScale;
   366     x = x * aXScale;
   367     y = y * aYScale;
   368     width = right - x;
   369     height = bottom - y;
   370   }
   371   // Scale 'this' by aScale, converting coordinates to integers so that the result is
   372   // the smallest integer-coordinate rectangle containing the unrounded result.
   373   // Note: this can turn an empty rectangle into a non-empty rectangle
   374   void ScaleRoundOut(double aScale) { ScaleRoundOut(aScale, aScale); }
   375   // Scale 'this' by aXScale and aYScale, converting coordinates to integers so
   376   // that the result is the smallest integer-coordinate rectangle containing the
   377   // unrounded result.
   378   // Note: this can turn an empty rectangle into a non-empty rectangle
   379   void ScaleRoundOut(double aXScale, double aYScale)
   380   {
   381     T right = static_cast<T>(ceil(double(XMost()) * aXScale));
   382     T bottom = static_cast<T>(ceil(double(YMost()) * aYScale));
   383     x = static_cast<T>(floor(double(x) * aXScale));
   384     y = static_cast<T>(floor(double(y) * aYScale));
   385     width = right - x;
   386     height = bottom - y;
   387   }
   388   // Scale 'this' by aScale, converting coordinates to integers so that the result is
   389   // the largest integer-coordinate rectangle contained by the unrounded result.
   390   void ScaleRoundIn(double aScale) { ScaleRoundIn(aScale, aScale); }
   391   // Scale 'this' by aXScale and aYScale, converting coordinates to integers so
   392   // that the result is the largest integer-coordinate rectangle contained by the
   393   // unrounded result.
   394   void ScaleRoundIn(double aXScale, double aYScale)
   395   {
   396     T right = static_cast<T>(floor(double(XMost()) * aXScale));
   397     T bottom = static_cast<T>(floor(double(YMost()) * aYScale));
   398     x = static_cast<T>(ceil(double(x) * aXScale));
   399     y = static_cast<T>(ceil(double(y) * aYScale));
   400     width = std::max<T>(0, right - x);
   401     height = std::max<T>(0, bottom - y);
   402   }
   403   // Scale 'this' by 1/aScale, converting coordinates to integers so that the result is
   404   // the smallest integer-coordinate rectangle containing the unrounded result.
   405   // Note: this can turn an empty rectangle into a non-empty rectangle
   406   void ScaleInverseRoundOut(double aScale) { ScaleInverseRoundOut(aScale, aScale); }
   407   // Scale 'this' by 1/aXScale and 1/aYScale, converting coordinates to integers so
   408   // that the result is the smallest integer-coordinate rectangle containing the
   409   // unrounded result.
   410   // Note: this can turn an empty rectangle into a non-empty rectangle
   411   void ScaleInverseRoundOut(double aXScale, double aYScale)
   412   {
   413     T right = static_cast<T>(ceil(double(XMost()) / aXScale));
   414     T bottom = static_cast<T>(ceil(double(YMost()) / aYScale));
   415     x = static_cast<T>(floor(double(x) / aXScale));
   416     y = static_cast<T>(floor(double(y) / aYScale));
   417     width = right - x;
   418     height = bottom - y;
   419   }
   420   // Scale 'this' by 1/aScale, converting coordinates to integers so that the result is
   421   // the largest integer-coordinate rectangle contained by the unrounded result.
   422   void ScaleInverseRoundIn(double aScale) { ScaleInverseRoundIn(aScale, aScale); }
   423   // Scale 'this' by 1/aXScale and 1/aYScale, converting coordinates to integers so
   424   // that the result is the largest integer-coordinate rectangle contained by the
   425   // unrounded result.
   426   void ScaleInverseRoundIn(double aXScale, double aYScale)
   427   {
   428     T right = static_cast<T>(floor(double(XMost()) / aXScale));
   429     T bottom = static_cast<T>(floor(double(YMost()) / aYScale));
   430     x = static_cast<T>(ceil(double(x) / aXScale));
   431     y = static_cast<T>(ceil(double(y) / aYScale));
   432     width = std::max<T>(0, right - x);
   433     height = std::max<T>(0, bottom - y);
   434   }
   436   /**
   437    * Clamp aPoint to this rectangle. It is allowed to end up on any
   438    * edge of the rectangle.
   439    */
   440   Point ClampPoint(const Point& aPoint) const
   441   {
   442     return Point(std::max(x, std::min(XMost(), aPoint.x)),
   443                  std::max(y, std::min(YMost(), aPoint.y)));
   444   }
   446   /**
   447    * Clamp this rectangle to be inside aRect. The function returns a copy of
   448    * this rect after it is forced inside the bounds of aRect. It will attempt to
   449    * retain the size but will shrink the dimensions that don't fit.
   450    */
   451   Sub ForceInside(const Sub& aRect) const
   452   {
   453     Sub rect(std::max(aRect.x, x),
   454              std::max(aRect.y, y),
   455              std::min(aRect.width, width),
   456              std::min(aRect.height, height));
   457     rect.x = std::min(rect.XMost(), aRect.XMost()) - rect.width;
   458     rect.y = std::min(rect.YMost(), aRect.YMost()) - rect.height;
   459     return rect;
   460   }
   462 private:
   463   // Do not use the default operator== or operator!= !
   464   // Use IsEqualEdges or IsEqualInterior explicitly.
   465   bool operator==(const Sub& aRect) const { return false; }
   466   bool operator!=(const Sub& aRect) const { return false; }
   467 };
   469 }
   470 }
   472 #endif /* MOZILLA_GFX_BASERECT_H_ */

mercurial