/* 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 "gfxCore.h" // for NS_GFX #include "nsCoord.h" // for nscoord #include "nsError.h" // for nsresult #include "nsPoint.h" // for nsIntPoint, nsPoint #include "nsRect.h" // for nsIntRect, nsRect #include "nsMargin.h" // for nsIntMargin #include "nsStringGlue.h" // for nsCString #include "xpcom-config.h" // for CPP_THROW_NEW class nsIntRegion; class gfx3DMatrix; #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. */ class nsRegionRectIterator; class nsRegion { friend class nsRegionRectIterator; public: nsRegion () { pixman_region32_init(&mImpl); } nsRegion (const nsRect& aRect) { pixman_region32_init_rect(&mImpl, aRect.x, aRect.y, aRect.width, aRect.height); } nsRegion (const nsRegion& aRegion) { pixman_region32_init(&mImpl); pixman_region32_copy(&mImpl,aRegion.Impl()); } ~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); } void Swap(nsRegion* aOther) { pixman_region32_t tmp = mImpl; mImpl = aOther->mImpl; aOther->mImpl = tmp; } static nsresult InitStatic() { return NS_OK; } static void ShutdownStatic() {} 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& 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& 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& 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); } 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 { return pixman_region32_n_rects(Impl()); } const nsRect GetBounds () const { return BoxToRect(mImpl.extents); } uint64_t Area () const; // Converts this region from aFromAPP, an appunits per pixel ratio, to // aToAPP. This applies nsRect::ConvertAppUnitsRoundOut/In to each rect of // the region. nsRegion ConvertAppUnitsRoundOut (int32_t aFromAPP, int32_t aToAPP) const; nsRegion ConvertAppUnitsRoundIn (int32_t aFromAPP, int32_t aToAPP) const; nsRegion& ScaleRoundOut(float aXScale, float aYScale); nsRegion& ScaleInverseRoundOut(float aXScale, float aYScale); nsRegion& Transform (const gfx3DMatrix &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); nsCString ToString() const; 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 nsIntRect &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); } }; class NS_GFX nsRegionRectIterator { const nsRegion* mRegion; int i; int n; nsRect rect; pixman_box32_t *boxes; public: nsRegionRectIterator (const nsRegion& aRegion) { mRegion = &aRegion; i = 0; boxes = pixman_region32_rectangles(aRegion.Impl(), &n); } const nsRect* Next () { if (i == n) return nullptr; rect = nsRegion::BoxToRect(boxes[i]); i++; return ▭ } const nsRect* Prev () { if (i == -1) return nullptr; rect = nsRegion::BoxToRect(boxes[i]); i--; return ▭ } void Reset () { i = 0; } }; /** * nsIntRegions use int32_t coordinates and nsIntRects. */ class NS_GFX nsIntRegion { friend class nsIntRegionRectIterator; friend class nsRegion; public: nsIntRegion () {} nsIntRegion (const nsIntRect& aRect) : mImpl (ToRect(aRect)) {} nsIntRegion (const nsIntRegion& aRegion) : mImpl (aRegion.mImpl) {} nsIntRegion& operator = (const nsIntRect& aRect) { mImpl = ToRect (aRect); return *this; } nsIntRegion& operator = (const nsIntRegion& aRegion) { mImpl = aRegion.mImpl; return *this; } bool operator==(const nsIntRegion& aRgn) const { return IsEqual(aRgn); } void Swap(nsIntRegion* aOther) { mImpl.Swap(&aOther->mImpl); } nsIntRegion& And (const nsIntRegion& aRgn1, const nsIntRegion& aRgn2) { mImpl.And (aRgn1.mImpl, aRgn2.mImpl); return *this; } nsIntRegion& And (const nsIntRegion& aRegion, const nsIntRect& aRect) { mImpl.And (aRegion.mImpl, ToRect (aRect)); return *this; } nsIntRegion& And (const nsIntRect& aRect, const nsIntRegion& aRegion) { return And (aRegion, aRect); } nsIntRegion& And (const nsIntRect& aRect1, const nsIntRect& aRect2) { nsIntRect TmpRect; TmpRect.IntersectRect (aRect1, aRect2); mImpl = ToRect (TmpRect); return *this; } nsIntRegion& Or (const nsIntRegion& aRgn1, const nsIntRegion& aRgn2) { mImpl.Or (aRgn1.mImpl, aRgn2.mImpl); return *this; } nsIntRegion& Or (const nsIntRegion& aRegion, const nsIntRect& aRect) { mImpl.Or (aRegion.mImpl, ToRect (aRect)); return *this; } nsIntRegion& Or (const nsIntRect& aRect, const nsIntRegion& aRegion) { return Or (aRegion, aRect); } nsIntRegion& Or (const nsIntRect& aRect1, const nsIntRect& aRect2) { mImpl = ToRect (aRect1); return Or (*this, aRect2); } nsIntRegion& Xor (const nsIntRegion& aRgn1, const nsIntRegion& aRgn2) { mImpl.Xor (aRgn1.mImpl, aRgn2.mImpl); return *this; } nsIntRegion& Xor (const nsIntRegion& aRegion, const nsIntRect& aRect) { mImpl.Xor (aRegion.mImpl, ToRect (aRect)); return *this; } nsIntRegion& Xor (const nsIntRect& aRect, const nsIntRegion& aRegion) { return Xor (aRegion, aRect); } nsIntRegion& Xor (const nsIntRect& aRect1, const nsIntRect& aRect2) { mImpl = ToRect (aRect1); return Xor (*this, aRect2); } nsIntRegion& Sub (const nsIntRegion& aRgn1, const nsIntRegion& aRgn2) { mImpl.Sub (aRgn1.mImpl, aRgn2.mImpl); return *this; } nsIntRegion& Sub (const nsIntRegion& aRegion, const nsIntRect& aRect) { mImpl.Sub (aRegion.mImpl, ToRect (aRect)); return *this; } nsIntRegion& Sub (const nsIntRect& aRect, const nsIntRegion& aRegion) { return Sub (nsIntRegion (aRect), aRegion); } nsIntRegion& Sub (const nsIntRect& aRect1, const nsIntRect& aRect2) { mImpl = ToRect (aRect1); return Sub (*this, aRect2); } bool Contains (const nsIntRect& aRect) const { return mImpl.Contains (ToRect (aRect)); } bool Contains (const nsIntRegion& aRgn) const { return mImpl.Contains (aRgn.mImpl); } bool Intersects (const nsIntRect& aRect) const { return mImpl.Intersects (ToRect (aRect)); } void MoveBy (int32_t aXOffset, int32_t aYOffset) { MoveBy (nsIntPoint (aXOffset, aYOffset)); } void MoveBy (nsIntPoint aPt) { mImpl.MoveBy (aPt.x, aPt.y); } nsIntRegion MovedBy(int32_t aXOffset, int32_t aYOffset) const { return MovedBy(nsIntPoint(aXOffset, aYOffset)); } nsIntRegion MovedBy(const nsIntPoint& aPt) const { nsIntRegion copy(*this); copy.MoveBy(aPt); return copy; } nsIntRegion Intersect(const nsIntRegion& aOther) const { nsIntRegion intersection; intersection.And(*this, aOther); return intersection; } void Inflate(const nsIntMargin& aMargin) { mImpl.Inflate(nsMargin(aMargin.top, aMargin.right, aMargin.bottom, aMargin.left)); } nsIntRegion Inflated(const nsIntMargin& aMargin) const { nsIntRegion 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 nsIntRegion& aRegion) const { return mImpl.IsEqual (aRegion.mImpl); } uint32_t GetNumRects () const { return mImpl.GetNumRects (); } nsIntRect GetBounds () const { return FromRect (mImpl.GetBounds ()); } uint64_t Area () const { return mImpl.Area(); } nsRegion ToAppUnits (nscoord aAppUnitsPerPixel) const; nsIntRect GetLargestRectangle (const nsIntRect& aContainingRect = nsIntRect()) const { return FromRect (mImpl.GetLargestRectangle( ToRect(aContainingRect) )); } nsIntRegion& ScaleRoundOut (float aXScale, float aYScale) { mImpl.ScaleRoundOut(aXScale, aYScale); return *this; } nsIntRegion& Transform (const gfx3DMatrix &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); } nsCString ToString() const { return mImpl.ToString(); } private: nsRegion mImpl; static nsRect ToRect(const nsIntRect& aRect) { return nsRect (aRect.x, aRect.y, aRect.width, aRect.height); } static nsIntRect FromRect(const nsRect& aRect) { return nsIntRect (aRect.x, aRect.y, aRect.width, aRect.height); } }; class NS_GFX nsIntRegionRectIterator { nsRegionRectIterator mImpl; nsIntRect mTmp; public: nsIntRegionRectIterator (const nsIntRegion& aRegion) : mImpl (aRegion.mImpl) {} const nsIntRect* Next () { const nsRect* r = mImpl.Next(); if (!r) return nullptr; mTmp = nsIntRegion::FromRect (*r); return &mTmp; } const nsIntRect* Prev () { const nsRect* r = mImpl.Prev(); if (!r) return nullptr; mTmp = nsIntRegion::FromRect (*r); return &mTmp; } void Reset () { mImpl.Reset (); } }; #endif