/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set ts=8 sts=2 et sw=2 tw=80: */ /* 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 DISPLAYITEMCLIP_H_ #define DISPLAYITEMCLIP_H_ #include "mozilla/RefPtr.h" #include "nsRect.h" #include "nsTArray.h" class gfxContext; class nsPresContext; class nsRegion; namespace mozilla { namespace gfx { class DrawTarget; class Path; } // namespace gfx namespace layers { class StackingContextHelper; } // namespace layers namespace wr { struct ComplexClipRegion; } // namespace wr } // namespace mozilla namespace mozilla { /** * An DisplayItemClip represents the intersection of an optional rectangle * with a list of rounded rectangles (which is often empty), all in appunits. * It can represent everything CSS clipping can do to an element (except for * SVG clip-path), including no clipping at all. */ class DisplayItemClip { typedef mozilla::gfx::DeviceColor DeviceColor; typedef mozilla::gfx::DrawTarget DrawTarget; typedef mozilla::gfx::Path Path; public: struct RoundedRect { nsRect mRect; // Indices into mRadii are the HalfCorner values in gfx/2d/Types.h nscoord mRadii[8]; RoundedRect operator+(const nsPoint& aOffset) const { RoundedRect r = *this; r.mRect += aOffset; return r; } bool operator==(const RoundedRect& aOther) const { if (!mRect.IsEqualInterior(aOther.mRect)) { return false; } for (const auto corner : mozilla::AllPhysicalHalfCorners()) { if (mRadii[corner] != aOther.mRadii[corner]) { return false; } } return true; } bool operator!=(const RoundedRect& aOther) const { return !(*this == aOther); } }; // Constructs a DisplayItemClip that does no clipping at all. DisplayItemClip() : mHaveClipRect(false) {} void SetTo(const nsRect& aRect); void SetTo(const nsRect& aRect, const nscoord* aRadii); void SetTo(const nsRect& aRect, const nsRect& aRoundedRect, const nscoord* aRadii); void IntersectWith(const DisplayItemClip& aOther); // Apply this |DisplayItemClip| to the given gfxContext. Any saving of state // or clearing of other clips must be done by the caller. // See aBegin/aEnd note on ApplyRoundedRectsTo. void ApplyTo(gfxContext* aContext, int32_t A2D) const; void ApplyRectTo(gfxContext* aContext, int32_t A2D) const; // Applies the rounded rects in this Clip to aContext // Will only apply rounded rects from aBegin (inclusive) to aEnd // (exclusive) or the number of rounded rects, whichever is smaller. void ApplyRoundedRectClipsTo(gfxContext* aContext, int32_t A2DPRInt32, uint32_t aBegin, uint32_t aEnd) const; // Draw (fill) the rounded rects in this clip to aContext void FillIntersectionOfRoundedRectClips(gfxContext* aContext, const DeviceColor& aColor, int32_t aAppUnitsPerDevPixel) const; // 'Draw' (create as a path, does not stroke or fill) aRoundRect to aContext already_AddRefed MakeRoundedRectPath( DrawTarget& aDrawTarget, int32_t A2D, const RoundedRect& aRoundRect) const; // Returns true if the intersection of aRect and this clip region is // non-empty. This is precise for DisplayItemClips with at most one // rounded rectangle. When multiple rounded rectangles are present, we just // check that the rectangle intersects all of them (but possibly in different // places). So it may return true when the correct answer is false. bool MayIntersect(const nsRect& aRect) const; // Return a rectangle contained in the intersection of aRect with this // clip region. Tries to return the largest possible rectangle, but may // not succeed. nsRect ApproximateIntersectInward(const nsRect& aRect) const; /* * Computes a region which contains the clipped area of this DisplayItemClip, * or if aOldClip is non-null, the union of the clipped area of this * DisplayItemClip with the clipped area of aOldClip translated by aShift. * The result is stored in aCombined. If the result would be infinite * (because one or both of the clips does no clipping), returns false. */ bool ComputeRegionInClips(const DisplayItemClip* aOldClip, const nsPoint& aShift, nsRegion* aCombined) const; // Returns false if aRect is definitely not clipped by a rounded corner in // this clip. Returns true if aRect is clipped by a rounded corner in this // clip or it can not be quickly determined that it is not clipped by a // rounded corner in this clip. bool IsRectClippedByRoundedCorner(const nsRect& aRect) const; // Returns false if aRect is definitely not clipped by anything in this clip. // Fast but not necessarily accurate. bool IsRectAffectedByClip(const nsRect& aRect) const; bool IsRectAffectedByClip(const nsIntRect& aRect, float aXScale, float aYScale, int32_t A2D) const; // Intersection of all rects in this clip ignoring any rounded corners. nsRect NonRoundedIntersection() const; // Intersect the given rects with all rects in this clip, ignoring any // rounded corners. nsRect ApplyNonRoundedIntersection(const nsRect& aRect) const; // Gets rid of any rounded corners in this clip. void RemoveRoundedCorners(); // Adds the difference between Intersect(*this + aPoint, aBounds) and // Intersect(aOther, aOtherBounds) to aDifference (or a bounding-box thereof). void AddOffsetAndComputeDifference(const nsPoint& aPoint, const nsRect& aBounds, const DisplayItemClip& aOther, const nsRect& aOtherBounds, nsRegion* aDifference); bool operator==(const DisplayItemClip& aOther) const { return mHaveClipRect == aOther.mHaveClipRect && (!mHaveClipRect || mClipRect.IsEqualInterior(aOther.mClipRect)) && mRoundedClipRects == aOther.mRoundedClipRects; } bool operator!=(const DisplayItemClip& aOther) const { return !(*this == aOther); } bool HasClip() const { return mHaveClipRect; } const nsRect& GetClipRect() const { NS_ASSERTION(HasClip(), "No clip rect!"); return mClipRect; } void MoveBy(const nsPoint& aPoint); nsCString ToString() const; uint32_t GetRoundedRectCount() const { return mRoundedClipRects.Length(); } void AppendRoundedRects(nsTArray* aArray) const; void ToComplexClipRegions(int32_t aAppUnitsPerDevPixel, nsTArray& aOutArray) const; static const DisplayItemClip& NoClip(); static void Shutdown(); private: nsRect mClipRect; CopyableTArray mRoundedClipRects; // If mHaveClipRect is false then this object represents no clipping at all // and mRoundedClipRects must be empty. bool mHaveClipRect; }; } // namespace mozilla #endif /* DISPLAYITEMCLIP_H_ */