/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #ifndef nsRegion_h__ #define nsRegion_h__ #include // for size_t #include // for uint32_t, uint64_t #include // for int32_t #include // for std::ostream #include "nsCoord.h" // for nscoord #include "nsError.h" // for nsresult #include "nsPoint.h" // for nsIntPoint, nsPoint #include "nsRect.h" // for mozilla::gfx::IntRect, nsRect #include "nsMargin.h" // for nsIntMargin #include "nsRegionFwd.h" // for nsIntRegion #include "nsStringGlue.h" // for nsCString #include "xpcom-config.h" // for CPP_THROW_NEW #include "mozilla/ArrayView.h" // for ArrayView #include "mozilla/Move.h" // for mozilla::Move #include "mozilla/gfx/MatrixFwd.h" // for mozilla::gfx::Matrix4x4 #include "pixman.h" /* For information on the internal representation look at pixman-region.c * * This replaces an older homebrew implementation of nsRegion. The * representation used here may use more rectangles than nsRegion however, the * representation is canonical. This means that there's no need for an * Optimize() method because for a paticular region there is only one * representation. This means that nsIntRegion will have more predictable * performance characteristics than the old nsRegion and should not become * degenerate. * * The pixman region code originates from X11 which has spread to a variety of * projects including Qt, Gtk, Wine. It should perform reasonably well. */ enum class VisitSide { TOP, BOTTOM, LEFT, RIGHT }; class nsRegion { public: typedef nsRect RectType; typedef nsPoint PointType; typedef nsMargin MarginType; nsRegion () { pixman_region32_init(&mImpl); } MOZ_IMPLICIT nsRegion (const nsRect& aRect) { pixman_region32_init_rect(&mImpl, aRect.x, aRect.y, aRect.width, aRect.height); } explicit nsRegion (mozilla::gfx::ArrayView aRects) { pixman_region32_init_rects(&mImpl, aRects.Data(), aRects.Length()); } nsRegion (const nsRegion& aRegion) { pixman_region32_init(&mImpl); pixman_region32_copy(&mImpl,aRegion.Impl()); } nsRegion (nsRegion&& aRegion) { mImpl = aRegion.mImpl; pixman_region32_init(&aRegion.mImpl); } nsRegion& operator = (nsRegion&& aRegion) { pixman_region32_fini(&mImpl); mImpl = aRegion.mImpl; pixman_region32_init(&aRegion.mImpl); return *this; } ~nsRegion () { pixman_region32_fini(&mImpl); } nsRegion& operator = (const nsRect& aRect) { Copy (aRect); return *this; } nsRegion& operator = (const nsRegion& aRegion) { Copy (aRegion); return *this; } bool operator==(const nsRegion& aRgn) const { return IsEqual(aRgn); } bool operator!=(const nsRegion& aRgn) const { return !(*this == aRgn); } friend std::ostream& operator<<(std::ostream& stream, const nsRegion& m); void Swap(nsRegion* aOther) { pixman_region32_t tmp = mImpl; mImpl = aOther->mImpl; aOther->mImpl = tmp; } static nsresult InitStatic() { return NS_OK; } static void ShutdownStatic() {} void AndWith(const nsRegion& aOther) { And(*this, aOther); } void AndWith(const nsRect& aOther) { And(*this, aOther); } nsRegion& And(const nsRegion& aRgn1, const nsRegion& aRgn2) { pixman_region32_intersect(&mImpl, aRgn1.Impl(), aRgn2.Impl()); return *this; } nsRegion& And(const nsRect& aRect, const nsRegion& aRegion) { return And(aRegion, aRect); } nsRegion& And(const nsRegion& aRegion, const nsRect& aRect) { pixman_region32_intersect_rect(&mImpl, aRegion.Impl(), aRect.x, aRect.y, aRect.width, aRect.height); return *this; } nsRegion& And(const nsRect& aRect1, const nsRect& aRect2) { nsRect TmpRect; TmpRect.IntersectRect(aRect1, aRect2); return Copy(TmpRect); } nsRegion& OrWith(const nsRegion& aOther) { return Or(*this, aOther); } nsRegion& OrWith(const nsRect& aOther) { return Or(*this, aOther); } nsRegion& Or(const nsRegion& aRgn1, const nsRegion& aRgn2) { pixman_region32_union(&mImpl, aRgn1.Impl(), aRgn2.Impl()); return *this; } nsRegion& Or(const nsRegion& aRegion, const nsRect& aRect) { pixman_region32_union_rect(&mImpl, aRegion.Impl(), aRect.x, aRect.y, aRect.width, aRect.height); return *this; } nsRegion& Or(const nsRect& aRect, const nsRegion& aRegion) { return Or(aRegion, aRect); } nsRegion& Or(const nsRect& aRect1, const nsRect& aRect2) { Copy (aRect1); return Or (*this, aRect2); } nsRegion& XorWith(const nsRegion& aOther) { return Xor(*this, aOther); } nsRegion& XorWith(const nsRect& aOther) { return Xor(*this, aOther); } nsRegion& Xor(const nsRegion& aRgn1, const nsRegion& aRgn2) { // this could be implemented better if pixman had direct // support for xoring regions. nsRegion p; p.Sub(aRgn1, aRgn2); nsRegion q; q.Sub(aRgn2, aRgn1); return Or(p, q); } nsRegion& Xor(const nsRegion& aRegion, const nsRect& aRect) { return Xor(aRegion, nsRegion(aRect)); } nsRegion& Xor(const nsRect& aRect, const nsRegion& aRegion) { return Xor(nsRegion(aRect), aRegion); } nsRegion& Xor(const nsRect& aRect1, const nsRect& aRect2) { return Xor(nsRegion(aRect1), nsRegion(aRect2)); } nsRegion ToAppUnits (nscoord aAppUnitsPerPixel) const; nsRegion& SubOut(const nsRegion& aOther) { return Sub(*this, aOther); } nsRegion& SubOut(const nsRect& aOther) { return Sub(*this, aOther); } nsRegion& Sub(const nsRegion& aRgn1, const nsRegion& aRgn2) { pixman_region32_subtract(&mImpl, aRgn1.Impl(), aRgn2.Impl()); return *this; } nsRegion& Sub(const nsRegion& aRegion, const nsRect& aRect) { return Sub(aRegion, nsRegion(aRect)); } nsRegion& Sub(const nsRect& aRect, const nsRegion& aRegion) { return Sub(nsRegion(aRect), aRegion); } nsRegion& Sub(const nsRect& aRect1, const nsRect& aRect2) { Copy(aRect1); return Sub(*this, aRect2); } /** * Returns true iff the given point is inside the region. A region * created from a rect (x=0, y=0, w=100, h=100) will NOT contain * the point x=100, y=100. */ bool Contains (int aX, int aY) const { return pixman_region32_contains_point(Impl(), aX, aY, nullptr); } bool Contains (const nsRect& aRect) const { pixman_box32_t box = RectToBox(aRect); return pixman_region32_contains_rectangle(Impl(), &box) == PIXMAN_REGION_IN; } bool Contains (const nsRegion& aRgn) const; bool Intersects (const nsRect& aRect) const; void MoveBy (int32_t aXOffset, int32_t aYOffset) { MoveBy (nsPoint (aXOffset, aYOffset)); } void MoveBy (nsPoint aPt) { pixman_region32_translate(&mImpl, aPt.x, aPt.y); } void SetEmpty () { pixman_region32_clear(&mImpl); } nsRegion MovedBy(int32_t aXOffset, int32_t aYOffset) const { return MovedBy(nsPoint(aXOffset, aYOffset)); } nsRegion MovedBy(const nsPoint& aPt) const { nsRegion copy(*this); copy.MoveBy(aPt); return copy; } nsRegion Intersect(const nsRegion& aOther) const { nsRegion intersection; intersection.And(*this, aOther); return intersection; } void Inflate(const nsMargin& aMargin); nsRegion Inflated(const nsMargin& aMargin) const { nsRegion copy(*this); copy.Inflate(aMargin); return copy; } bool IsEmpty () const { return !pixman_region32_not_empty(Impl()); } bool IsComplex () const { return GetNumRects() > 1; } bool IsEqual (const nsRegion& aRegion) const { return pixman_region32_equal(Impl(), aRegion.Impl()); } uint32_t GetNumRects () const { // Work around pixman bug. Sometimes pixman creates regions with 1 rect // that's empty. uint32_t result = pixman_region32_n_rects(Impl()); return (result == 1 && GetBounds().IsEmpty()) ? 0 : result; } const nsRect GetBounds () const { return BoxToRect(mImpl.extents); } uint64_t Area () const; /** * Return this region scaled to a different appunits per pixel (APP) ratio. * This applies nsRect::ScaleToOtherAppUnitsRoundOut/In to each rect of the region. * @param aFromAPP the APP to scale from * @param aToAPP the APP to scale to * @note this can turn an empty region into a non-empty region */ MOZ_MUST_USE nsRegion ScaleToOtherAppUnitsRoundOut (int32_t aFromAPP, int32_t aToAPP) const; MOZ_MUST_USE nsRegion ScaleToOtherAppUnitsRoundIn (int32_t aFromAPP, int32_t aToAPP) const; nsRegion& ScaleRoundOut(float aXScale, float aYScale); nsRegion& ScaleInverseRoundOut(float aXScale, float aYScale); nsRegion& Transform (const mozilla::gfx::Matrix4x4 &aTransform); nsIntRegion ScaleToOutsidePixels (float aXScale, float aYScale, nscoord aAppUnitsPerPixel) const; nsIntRegion ScaleToInsidePixels (float aXScale, float aYScale, nscoord aAppUnitsPerPixel) const; nsIntRegion ScaleToNearestPixels (float aXScale, float aYScale, nscoord aAppUnitsPerPixel) const; nsIntRegion ToOutsidePixels (nscoord aAppUnitsPerPixel) const; nsIntRegion ToNearestPixels (nscoord aAppUnitsPerPixel) const; /** * Gets the largest rectangle contained in the region. * @param aContainingRect if non-empty, we choose a rectangle that * maximizes the area intersecting with aContainingRect (and break ties by * then choosing the largest rectangle overall) */ nsRect GetLargestRectangle (const nsRect& aContainingRect = nsRect()) const; /** * Make sure the region has at most aMaxRects by adding area to it * if necessary. The simplified region will be a superset of the * original region. The simplified region's bounding box will be * the same as for the current region. */ void SimplifyOutward (uint32_t aMaxRects); /** * Simplify the region by adding at most aThreshold area between spans of * rects. The simplified region will be a superset of the original region. * The simplified region's bounding box will be the same as for the current * region. */ void SimplifyOutwardByArea(uint32_t aThreshold); /** * Make sure the region has at most aMaxRects by removing area from * it if necessary. The simplified region will be a subset of the * original region. */ void SimplifyInward (uint32_t aMaxRects); /** * VisitEdges is a weird kind of function that we use for padding * out surfaces to prevent texture filtering artifacts. * It calls the visitFn callback for each of the exterior edges of * the regions. The top and bottom edges will be expanded 1 pixel * to the left and right if there's an outside corner. The order * the edges are visited is not guaranteed. * * visitFn has a side parameter that can be TOP,BOTTOM,LEFT,RIGHT * and specifies which kind of edge is being visited. x1, y1, x2, y2 * are the coordinates of the line. (x1 == x2) || (y1 == y2) */ typedef void (*visitFn)(void *closure, VisitSide side, int x1, int y1, int x2, int y2); void VisitEdges(visitFn, void *closure); nsCString ToString() const; class RectIterator { int mCurrent; // Index of the current entry int mLimit; // Index one past the final entry. mutable nsRect mTmp; // The most recently gotten rectangle. pixman_box32_t *mBoxes; public: explicit RectIterator(const nsRegion& aRegion) { mCurrent = 0; mBoxes = pixman_region32_rectangles(aRegion.Impl(), &mLimit); // Work around pixman bug. Sometimes pixman creates regions with 1 rect // that's empty. if (mLimit == 1 && nsRegion::BoxToRect(mBoxes[0]).IsEmpty()) { mLimit = 0; } } bool Done() const { return mCurrent == mLimit; } const nsRect& Get() const { MOZ_ASSERT(!Done()); mTmp = nsRegion::BoxToRect(mBoxes[mCurrent]); NS_ASSERTION(!mTmp.IsEmpty(), "Shouldn't return empty rect"); return mTmp; } void Next() { MOZ_ASSERT(!Done()); mCurrent++; } }; RectIterator RectIter() const { return RectIterator(*this); } private: pixman_region32_t mImpl; #ifndef MOZ_TREE_PIXMAN // For compatibility with pixman versions older than 0.25.2. static inline void pixman_region32_clear(pixman_region32_t *region) { pixman_region32_fini(region); pixman_region32_init(region); } #endif nsIntRegion ToPixels(nscoord aAppUnitsPerPixel, bool aOutsidePixels) const; nsRegion& Copy (const nsRegion& aRegion) { pixman_region32_copy(&mImpl, aRegion.Impl()); return *this; } nsRegion& Copy (const nsRect& aRect) { // pixman needs to distinguish between an empty region and a region // with one rect so that it can return a different number of rectangles. // Empty rect: data = empty_box // 1 rect: data = null // >1 rect: data = rects if (aRect.IsEmpty()) { pixman_region32_clear(&mImpl); } else { pixman_box32_t box = RectToBox(aRect); pixman_region32_reset(&mImpl, &box); } return *this; } static inline pixman_box32_t RectToBox(const nsRect &aRect) { pixman_box32_t box = { aRect.x, aRect.y, aRect.XMost(), aRect.YMost() }; return box; } static inline pixman_box32_t RectToBox(const mozilla::gfx::IntRect &aRect) { pixman_box32_t box = { aRect.x, aRect.y, aRect.XMost(), aRect.YMost() }; return box; } static inline nsRect BoxToRect(const pixman_box32_t &aBox) { return nsRect(aBox.x1, aBox.y1, aBox.x2 - aBox.x1, aBox.y2 - aBox.y1); } pixman_region32_t* Impl() const { return const_cast(&mImpl); } }; namespace mozilla { namespace gfx { /** * BaseIntRegions use int32_t coordinates. */ template class BaseIntRegion { friend class ::nsRegion; // Give access to all specializations of IntRegionTyped, not just ones that // derive from this specialization of BaseIntRegion. template friend class IntRegionTyped; public: typedef Rect RectType; typedef Point PointType; typedef Margin MarginType; BaseIntRegion () {} MOZ_IMPLICIT BaseIntRegion (const Rect& aRect) : mImpl (ToRect(aRect)) {} explicit BaseIntRegion (mozilla::gfx::ArrayView aRects) : mImpl (aRects) {} BaseIntRegion (const BaseIntRegion& aRegion) : mImpl (aRegion.mImpl) {} BaseIntRegion (BaseIntRegion&& aRegion) : mImpl (mozilla::Move(aRegion.mImpl)) {} Derived& operator = (const Rect& aRect) { mImpl = ToRect (aRect); return This(); } Derived& operator = (const Derived& aRegion) { mImpl = aRegion.mImpl; return This(); } Derived& operator = (Derived&& aRegion) { mImpl = mozilla::Move(aRegion.mImpl); return This(); } bool operator==(const Derived& aRgn) const { return IsEqual(aRgn); } bool operator!=(const Derived& aRgn) const { return !(*this == aRgn); } friend std::ostream& operator<<(std::ostream& stream, const Derived& m) { return stream << m.mImpl; } void Swap(Derived* aOther) { mImpl.Swap(&aOther->mImpl); } void AndWith(const Derived& aOther) { And(This(), aOther); } void AndWith(const Rect& aOther) { And(This(), aOther); } Derived& And (const Derived& aRgn1, const Derived& aRgn2) { mImpl.And (aRgn1.mImpl, aRgn2.mImpl); return This(); } Derived& And (const Derived& aRegion, const Rect& aRect) { mImpl.And (aRegion.mImpl, ToRect (aRect)); return This(); } Derived& And (const Rect& aRect, const Derived& aRegion) { return And (aRegion, aRect); } Derived& And (const Rect& aRect1, const Rect& aRect2) { Rect TmpRect; TmpRect.IntersectRect (aRect1, aRect2); mImpl = ToRect (TmpRect); return This(); } Derived& OrWith(const Derived& aOther) { return Or(This(), aOther); } Derived& OrWith(const Rect& aOther) { return Or(This(), aOther); } Derived& Or (const Derived& aRgn1, const Derived& aRgn2) { mImpl.Or (aRgn1.mImpl, aRgn2.mImpl); return This(); } Derived& Or (const Derived& aRegion, const Rect& aRect) { mImpl.Or (aRegion.mImpl, ToRect (aRect)); return This(); } Derived& Or (const Rect& aRect, const Derived& aRegion) { return Or (aRegion, aRect); } Derived& Or (const Rect& aRect1, const Rect& aRect2) { mImpl = ToRect (aRect1); return Or (This(), aRect2); } Derived& XorWith(const Derived& aOther) { return Xor(This(), aOther); } Derived& XorWith(const Rect& aOther) { return Xor(This(), aOther); } Derived& Xor (const Derived& aRgn1, const Derived& aRgn2) { mImpl.Xor (aRgn1.mImpl, aRgn2.mImpl); return This(); } Derived& Xor (const Derived& aRegion, const Rect& aRect) { mImpl.Xor (aRegion.mImpl, ToRect (aRect)); return This(); } Derived& Xor (const Rect& aRect, const Derived& aRegion) { return Xor (aRegion, aRect); } Derived& Xor (const Rect& aRect1, const Rect& aRect2) { mImpl = ToRect (aRect1); return Xor (This(), aRect2); } Derived& SubOut(const Derived& aOther) { return Sub(This(), aOther); } Derived& SubOut(const Rect& aOther) { return Sub(This(), aOther); } Derived& Sub (const Derived& aRgn1, const Derived& aRgn2) { mImpl.Sub (aRgn1.mImpl, aRgn2.mImpl); return This(); } Derived& Sub (const Derived& aRegion, const Rect& aRect) { mImpl.Sub (aRegion.mImpl, ToRect (aRect)); return This(); } Derived& Sub (const Rect& aRect, const Derived& aRegion) { return Sub (Derived (aRect), aRegion); } Derived& Sub (const Rect& aRect1, const Rect& aRect2) { mImpl = ToRect (aRect1); return Sub (This(), aRect2); } /** * Returns true iff the given point is inside the region. A region * created from a rect (x=0, y=0, w=100, h=100) will NOT contain * the point x=100, y=100. */ bool Contains (int aX, int aY) const { return mImpl.Contains(aX, aY); } bool Contains (const Rect& aRect) const { return mImpl.Contains (ToRect (aRect)); } bool Contains (const Derived& aRgn) const { return mImpl.Contains (aRgn.mImpl); } bool Intersects (const Rect& aRect) const { return mImpl.Intersects (ToRect (aRect)); } void MoveBy (int32_t aXOffset, int32_t aYOffset) { MoveBy (Point (aXOffset, aYOffset)); } void MoveBy (Point aPt) { mImpl.MoveBy (aPt.x, aPt.y); } Derived MovedBy(int32_t aXOffset, int32_t aYOffset) const { return MovedBy(Point(aXOffset, aYOffset)); } Derived MovedBy(const Point& aPt) const { Derived copy(This()); copy.MoveBy(aPt); return copy; } Derived Intersect(const Derived& aOther) const { Derived intersection; intersection.And(This(), aOther); return intersection; } void Inflate(const Margin& aMargin) { mImpl.Inflate(nsMargin(aMargin.top, aMargin.right, aMargin.bottom, aMargin.left)); } Derived Inflated(const Margin& aMargin) const { Derived copy(This()); copy.Inflate(aMargin); return copy; } void SetEmpty () { mImpl.SetEmpty (); } bool IsEmpty () const { return mImpl.IsEmpty (); } bool IsComplex () const { return mImpl.IsComplex (); } bool IsEqual (const Derived& aRegion) const { return mImpl.IsEqual (aRegion.mImpl); } uint32_t GetNumRects () const { return mImpl.GetNumRects (); } Rect GetBounds () const { return FromRect (mImpl.GetBounds ()); } uint64_t Area () const { return mImpl.Area(); } nsRegion ToAppUnits (nscoord aAppUnitsPerPixel) const { nsRegion result; for (auto iter = RectIter(); !iter.Done(); iter.Next()) { nsRect appRect = ::ToAppUnits(iter.Get(), aAppUnitsPerPixel); result.Or(result, appRect); } return result; } Rect GetLargestRectangle (const Rect& aContainingRect = Rect()) const { return FromRect (mImpl.GetLargestRectangle( ToRect(aContainingRect) )); } Derived& ScaleRoundOut (float aXScale, float aYScale) { mImpl.ScaleRoundOut(aXScale, aYScale); return This(); } Derived& ScaleInverseRoundOut (float aXScale, float aYScale) { mImpl.ScaleInverseRoundOut(aXScale, aYScale); return This(); } // Prefer using TransformBy(matrix, region) from UnitTransforms.h, // as applying the transform should typically change the unit system. // TODO(botond): Move this to IntRegionTyped and disable it for // unit != UnknownUnits. Derived& Transform (const mozilla::gfx::Matrix4x4 &aTransform) { mImpl.Transform(aTransform); return This(); } /** * Make sure the region has at most aMaxRects by adding area to it * if necessary. The simplified region will be a superset of the * original region. The simplified region's bounding box will be * the same as for the current region. */ void SimplifyOutward (uint32_t aMaxRects) { mImpl.SimplifyOutward (aMaxRects); } void SimplifyOutwardByArea (uint32_t aThreshold) { mImpl.SimplifyOutwardByArea (aThreshold); } /** * Make sure the region has at most aMaxRects by removing area from * it if necessary. The simplified region will be a subset of the * original region. */ void SimplifyInward (uint32_t aMaxRects) { mImpl.SimplifyInward (aMaxRects); } typedef void (*visitFn)(void *closure, VisitSide side, int x1, int y1, int x2, int y2); void VisitEdges (visitFn visit, void *closure) { mImpl.VisitEdges (visit, closure); } nsCString ToString() const { return mImpl.ToString(); } class RectIterator { nsRegion::RectIterator mImpl; // The underlying iterator. mutable Rect mTmp; // The most recently gotten rectangle. public: explicit RectIterator(const BaseIntRegion& aRegion) : mImpl(aRegion.mImpl) {} bool Done() const { return mImpl.Done(); } const Rect& Get() const { mTmp = FromRect(mImpl.Get()); return mTmp; } void Next() { mImpl.Next(); } }; RectIterator RectIter() const { return RectIterator(*this); } protected: // Expose enough to derived classes from them to define conversions // between different types of BaseIntRegions. explicit BaseIntRegion(const nsRegion& aImpl) : mImpl(aImpl) {} const nsRegion& Impl() const { return mImpl; } private: nsRegion mImpl; static nsRect ToRect(const Rect& aRect) { return nsRect (aRect.x, aRect.y, aRect.width, aRect.height); } static Rect FromRect(const nsRect& aRect) { return Rect (aRect.x, aRect.y, aRect.width, aRect.height); } Derived& This() { return *static_cast(this); } const Derived& This() const { return *static_cast(this); } }; template class IntRegionTyped : public BaseIntRegion, IntRectTyped, IntPointTyped, IntMarginTyped> { typedef BaseIntRegion, IntRectTyped, IntPointTyped, IntMarginTyped> Super; // Make other specializations of IntRegionTyped friends. template friend class IntRegionTyped; static_assert(IsPixel::value, "'units' must be a coordinate system tag"); public: typedef IntRectTyped RectType; typedef IntPointTyped PointType; typedef IntMarginTyped MarginType; // Forward constructors. IntRegionTyped() {} MOZ_IMPLICIT IntRegionTyped(const IntRectTyped& aRect) : Super(aRect) {} IntRegionTyped(const IntRegionTyped& aRegion) : Super(aRegion) {} explicit IntRegionTyped(mozilla::gfx::ArrayView aRects) : Super(aRects) {} IntRegionTyped(IntRegionTyped&& aRegion) : Super(mozilla::Move(aRegion)) {} // Assignment operators need to be forwarded as well, otherwise the compiler // will declare deleted ones. IntRegionTyped& operator=(const IntRegionTyped& aRegion) { return Super::operator=(aRegion); } IntRegionTyped& operator=(IntRegionTyped&& aRegion) { return Super::operator=(mozilla::Move(aRegion)); } static IntRegionTyped FromUnknownRegion(const IntRegion& aRegion) { return IntRegionTyped(aRegion.Impl()); } IntRegion ToUnknownRegion() const { // Need |this->| because Impl() is defined in a dependent base class. return IntRegion(this->Impl()); } private: // This is deliberately private, so calling code uses FromUnknownRegion(). explicit IntRegionTyped(const nsRegion& aRegion) : Super(aRegion) {} }; } // namespace gfx } // namespace mozilla typedef mozilla::gfx::IntRegion nsIntRegion; #endif