--- a/layout/svg/nsSVGClipPathFrame.cpp
+++ b/layout/svg/nsSVGClipPathFrame.cpp
@@ -76,98 +76,91 @@ nsSVGClipPathFrame::ApplyClipPath(gfxCon
aContext.Clip(clipPath);
} else {
// The spec says clip away everything if we have no children or the
// clipping path otherwise can't be resolved:
aContext.Clip(Rect());
}
}
-already_AddRefed<SourceSurface>
-nsSVGClipPathFrame::GetClipMask(gfxContext& aReferenceContext,
- nsIFrame* aClippedFrame,
- const gfxMatrix& aMatrix,
- Matrix* aMaskTransform,
- SourceSurface* aExtraMask,
- const Matrix& aExtraMasksTransform,
- DrawResult* aResult)
+already_AddRefed<DrawTarget>
+nsSVGClipPathFrame::CreateClipMask(gfxContext& aReferenceContext,
+ IntPoint& aOffset)
{
- MOZ_ASSERT(!IsTrivial(), "Caller needs to use ApplyClipPath");
+ gfxContextMatrixAutoSaveRestore autoRestoreMatrix(&aReferenceContext);
+
+ aReferenceContext.SetMatrix(gfxMatrix());
+ gfxRect rect = aReferenceContext.GetClipExtents();
+ IntRect bounds = RoundedOut(ToRect(rect));
+ if (bounds.IsEmpty()) {
+ // We don't need to create a mask surface, all drawing is clipped anyway.
+ return nullptr;
+ }
+
+ DrawTarget* referenceDT = aReferenceContext.GetDrawTarget();
+ RefPtr<DrawTarget> maskDT =
+ referenceDT->CreateSimilarDrawTarget(bounds.Size(), SurfaceFormat::A8);
- if (aResult) {
- *aResult = DrawResult::SUCCESS;
- }
- DrawTarget& aReferenceDT = *aReferenceContext.GetDrawTarget();
+ aOffset = bounds.TopLeft();
+
+ return maskDT.forget();
+}
+DrawResult
+nsSVGClipPathFrame::PaintClipMask(gfxContext& aMaskContext,
+ nsIFrame* aClippedFrame,
+ const gfxMatrix& aMatrix,
+ Matrix* aMaskTransform,
+ SourceSurface* aExtraMask,
+ const Matrix& aExtraMasksTransform)
+{
// A clipPath can reference another clipPath. We re-enter this method for
// each clipPath in a reference chain, so here we limit chain length:
static int16_t sRefChainLengthCounter = AutoReferenceLimiter::notReferencing;
AutoReferenceLimiter
refChainLengthLimiter(&sRefChainLengthCounter,
MAX_SVG_CLIP_PATH_REFERENCE_CHAIN_LENGTH);
if (!refChainLengthLimiter.Reference()) {
- return nullptr; // Reference chain is too long!
+ return DrawResult::SUCCESS; // Reference chain is too long!
}
// And to prevent reference loops we check that this clipPath only appears
// once in the reference chain (if any) that we're currently processing:
AutoReferenceLimiter refLoopDetector(&mReferencing, 1);
if (!refLoopDetector.Reference()) {
- return nullptr; // Reference loop!
- }
-
- IntRect devSpaceClipExtents;
- {
- gfxContextMatrixAutoSaveRestore autoRestoreMatrix(&aReferenceContext);
-
- aReferenceContext.SetMatrix(gfxMatrix());
- gfxRect rect = aReferenceContext.GetClipExtents();
- devSpaceClipExtents = RoundedOut(ToRect(rect));
- if (devSpaceClipExtents.IsEmpty()) {
- // We don't need to create a mask surface, all drawing is clipped anyway.
- return nullptr;
- }
+ return DrawResult::SUCCESS; // Reference loop!
}
- RefPtr<DrawTarget> maskDT =
- aReferenceDT.CreateSimilarDrawTarget(devSpaceClipExtents.Size(),
- SurfaceFormat::A8);
-
- gfxMatrix mat = aReferenceContext.CurrentMatrix() *
- gfxMatrix::Translation(-devSpaceClipExtents.TopLeft());
+ DrawResult result = DrawResult::SUCCESS;
+ DrawTarget* maskDT = aMaskContext.GetDrawTarget();
+ MOZ_ASSERT(maskDT->GetFormat() == SurfaceFormat::A8);
- // Paint this clipPath's contents into maskDT:
+ // Paint this clipPath's contents into aMaskDT:
{
- RefPtr<gfxContext> ctx = gfxContext::CreateOrNull(maskDT);
- if (!ctx) {
- gfxCriticalError() << "SVGClipPath context problem " << gfx::hexa(maskDT);
- return nullptr;
- }
- ctx->SetMatrix(mat);
-
// We need to set mMatrixForChildren here so that under the PaintSVG calls
// on our children (below) our GetCanvasTM() method will return the correct
// transform.
mMatrixForChildren = GetClipPathTransform(aClippedFrame) * aMatrix;
// Check if this clipPath is itself clipped by another clipPath:
nsSVGClipPathFrame* clipPathThatClipsClipPath =
nsSVGEffects::GetEffectProperties(this).GetClipPathFrame(nullptr);
bool clippingOfClipPathRequiredMasking;
if (clipPathThatClipsClipPath) {
- ctx->Save();
+ aMaskContext.Save();
clippingOfClipPathRequiredMasking = !clipPathThatClipsClipPath->IsTrivial();
if (!clippingOfClipPathRequiredMasking) {
- clipPathThatClipsClipPath->ApplyClipPath(*ctx, aClippedFrame, aMatrix);
+ clipPathThatClipsClipPath->ApplyClipPath(aMaskContext, aClippedFrame,
+ aMatrix);
} else {
Matrix maskTransform;
RefPtr<SourceSurface> mask =
- clipPathThatClipsClipPath->GetClipMask(*ctx, aClippedFrame,
+ clipPathThatClipsClipPath->GetClipMask(aMaskContext, aClippedFrame,
aMatrix, &maskTransform);
- ctx->PushGroupForBlendBack(gfxContentType::ALPHA, 1.0,
+ aMaskContext.PushGroupForBlendBack(gfxContentType::ALPHA, 1.0,
mask, maskTransform);
// The corresponding PopGroupAndBlend call below will mask the
// blend using |mask|.
}
}
// Paint our children into the mask:
for (nsIFrame* kid = mFrames.FirstChild(); kid;
@@ -184,25 +177,26 @@ nsSVGClipPathFrame::GetClipMask(gfxConte
if (!isOK) {
continue;
}
bool childsClipPathRequiresMasking;
if (clipPathThatClipsChild) {
childsClipPathRequiresMasking = !clipPathThatClipsChild->IsTrivial();
- ctx->Save();
+ aMaskContext.Save();
if (!childsClipPathRequiresMasking) {
- clipPathThatClipsChild->ApplyClipPath(*ctx, aClippedFrame, aMatrix);
+ clipPathThatClipsChild->ApplyClipPath(aMaskContext, aClippedFrame,
+ aMatrix);
} else {
Matrix maskTransform;
RefPtr<SourceSurface> mask =
- clipPathThatClipsChild->GetClipMask(*ctx, aClippedFrame,
+ clipPathThatClipsChild->GetClipMask(aMaskContext, aClippedFrame,
aMatrix, &maskTransform);
- ctx->PushGroupForBlendBack(gfxContentType::ALPHA, 1.0,
+ aMaskContext.PushGroupForBlendBack(gfxContentType::ALPHA, 1.0,
mask, maskTransform);
// The corresponding PopGroupAndBlend call below will mask the
// blend using |mask|.
}
}
gfxMatrix toChildsUserSpace = mMatrixForChildren;
nsIFrame* child = do_QueryFrame(SVGFrame);
@@ -211,60 +205,98 @@ nsSVGClipPathFrame::GetClipMask(gfxConte
toChildsUserSpace =
static_cast<const nsSVGElement*>(childContent)->
PrependLocalTransformsTo(mMatrixForChildren, eUserSpaceToParent);
}
// Our children have NS_STATE_SVG_CLIPPATH_CHILD set on them, and
// nsSVGPathGeometryFrame::Render checks for that state bit and paints
// only the geometry (opaque black) if set.
- DrawResult result = SVGFrame->PaintSVG(*ctx, toChildsUserSpace);
- if (aResult) {
- *aResult &= result;
- }
+ result &= SVGFrame->PaintSVG(aMaskContext, toChildsUserSpace);
if (clipPathThatClipsChild) {
if (childsClipPathRequiresMasking) {
- ctx->PopGroupAndBlend();
+ aMaskContext.PopGroupAndBlend();
}
- ctx->Restore();
+ aMaskContext.Restore();
}
}
}
if (clipPathThatClipsClipPath) {
if (clippingOfClipPathRequiredMasking) {
- ctx->PopGroupAndBlend();
+ aMaskContext.PopGroupAndBlend();
}
- ctx->Restore();
+ aMaskContext.Restore();
}
}
// Moz2D transforms in the opposite direction to Thebes
- mat.Invert();
+ gfxMatrix maskTransfrom = aMaskContext.CurrentMatrix();
+ maskTransfrom.Invert();
if (aExtraMask) {
// We could potentially due this more efficiently with OPERATOR_IN
// but that operator does not work well on CG or D2D
RefPtr<SourceSurface> currentMask = maskDT->Snapshot();
+ IntSize targetSize = maskDT->GetSize();
Matrix transform = maskDT->GetTransform();
maskDT->SetTransform(Matrix());
- maskDT->ClearRect(Rect(0, 0,
- devSpaceClipExtents.width,
- devSpaceClipExtents.height));
+ maskDT->ClearRect(Rect(0, 0, targetSize.width, targetSize.height));
maskDT->SetTransform(aExtraMasksTransform * transform);
// draw currentMask with the inverse of the transform that we just so that
// it ends up in the same spot with aExtraMask transformed by aExtraMasksTransform
- maskDT->MaskSurface(SurfacePattern(currentMask, ExtendMode::CLAMP, aExtraMasksTransform.Inverse() * ToMatrix(mat)),
+ maskDT->MaskSurface(SurfacePattern(currentMask, ExtendMode::CLAMP, aExtraMasksTransform.Inverse() * ToMatrix(maskTransfrom)),
aExtraMask,
Point(0, 0));
}
- *aMaskTransform = ToMatrix(mat);
+ *aMaskTransform = ToMatrix(maskTransfrom);
+ return result;
+}
+
+already_AddRefed<SourceSurface>
+nsSVGClipPathFrame::GetClipMask(gfxContext& aReferenceContext,
+ nsIFrame* aClippedFrame,
+ const gfxMatrix& aMatrix,
+ Matrix* aMaskTransform,
+ SourceSurface* aExtraMask,
+ const Matrix& aExtraMasksTransform,
+ DrawResult* aResult)
+{
+ MOZ_ASSERT(!IsTrivial(), "Caller needs to use ApplyClipPath");
+
+ IntPoint offset;
+ RefPtr<DrawTarget> maskDT = CreateClipMask(aReferenceContext, offset);
+ if (!maskDT) {
+ if (aResult) {
+ *aResult = DrawResult::SUCCESS;
+ }
+ return nullptr;
+ }
+
+ RefPtr<gfxContext> maskContext = gfxContext::CreateOrNull(maskDT);
+ if (!maskContext) {
+ gfxCriticalError() << "SVGClipPath context problem " << gfx::hexa(maskDT);
+ if (aResult) {
+ *aResult = DrawResult::TEMPORARY_ERROR;
+ }
+ return nullptr;
+ }
+ maskContext->SetMatrix(aReferenceContext.CurrentMatrix() *
+ gfxMatrix::Translation(-offset));
+
+ DrawResult result = PaintClipMask(*maskContext, aClippedFrame, aMatrix,
+ aMaskTransform, aExtraMask,
+ aExtraMasksTransform);
+ if (aResult) {
+ *aResult = result;
+ }
+
return maskDT->Snapshot();
}
bool
nsSVGClipPathFrame::PointIsInsideClipPath(nsIFrame* aClippedFrame,
const gfxPoint &aPoint)
{
// A clipPath can reference another clipPath. We re-enter this method for
@@ -315,16 +347,17 @@ nsSVGClipPathFrame::PointIsInsideClipPat
}
pointForChild = m.Transform(point);
}
if (SVGFrame->GetFrameForPoint(pointForChild)) {
return true;
}
}
}
+
return false;
}
bool
nsSVGClipPathFrame::IsTrivial(nsISVGChildFrame **aSingleChild)
{
// If the clip path is clipped then it's non-trivial
if (nsSVGEffects::GetEffectProperties(this).GetClipPathFrame(nullptr))
--- a/layout/svg/nsSVGClipPathFrame.h
+++ b/layout/svg/nsSVGClipPathFrame.h
@@ -82,16 +82,36 @@ public:
already_AddRefed<SourceSurface>
GetClipMask(gfxContext& aReferenceContext, nsIFrame* aClippedFrame,
const gfxMatrix& aMatrix, Matrix* aMaskTransform,
SourceSurface* aExtraMask = nullptr,
const Matrix& aExtraMasksTransform = Matrix(),
DrawResult* aResult = nullptr);
/**
+ * Paint mask directly onto a given context(aMaskContext).
+ *
+ * @param aMaskContext The target of mask been painting on.
+ * @param aClippedFrame The/an nsIFrame of the element that references this
+ * clipPath that is currently being processed.
+ * @param aMatrix The transform from aClippedFrame's user space to
+ * current transform.
+ * @param [out] aMaskTransform The transform to use with the returned
+ * surface.
+ * @param [in, optional] aExtraMask An extra surface that the returned
+ * surface should be masked with.
+ * @param [in, optional] aExtraMasksTransform The transform to use with
+ * aExtraMask. Should be passed when aExtraMask is passed.
+ */
+ DrawResult
+ PaintClipMask(gfxContext& aMaskContext, nsIFrame* aClippedFrame,
+ const gfxMatrix& aMatrix, Matrix* aMaskTransform,
+ SourceSurface* aExtraMask, const Matrix& aExtraMasksTransform);
+
+ /**
* aPoint is expected to be in aClippedFrame's SVG user space.
*/
bool PointIsInsideClipPath(nsIFrame* aClippedFrame, const gfxPoint &aPoint);
// Check if this clipPath is made up of more than one geometry object.
// If so, the clipping API in cairo isn't enough and we need to use
// mask based clipping.
bool IsTrivial(nsISVGChildFrame **aSingleChild = nullptr);
@@ -132,16 +152,19 @@ public:
*/
gfxMatrix GetClipPathTransform(nsIFrame* aClippedFrame);
private:
// nsSVGContainerFrame methods:
virtual gfxMatrix GetCanvasTM() override;
+ already_AddRefed<DrawTarget>
+ CreateClipMask(gfxContext& aReferenceContext, IntPoint& aOffset);
+
// Set, during a GetClipMask() call, to the transform that still needs to be
// concatenated to the transform of the DrawTarget that was passed to
// GetClipMask in order to establish the coordinate space that the clipPath
// establishes for its contents (i.e. including applying 'clipPathUnits' and
// any 'transform' attribute set on the clipPath) specifically for clipping
// the frame that was passed to GetClipMask at that moment in time. This is
// set so that if our GetCanvasTM method is called while GetClipMask is
// painting its children, the returned matrix will include the transforms