From b241a84d884293e1ce49a79c75e362c02c0899f9 Mon Sep 17 00:00:00 2001 From: yami <34216515+kn-yami@users.noreply.github.com> Date: Sun, 16 Dec 2018 16:17:20 +0100 Subject: issue #908 - implement missing parts of CSS mask --- layout/svg/nsSVGIntegrationUtils.cpp | 144 ++++++++++++++++++++++++----------- 1 file changed, 99 insertions(+), 45 deletions(-) (limited to 'layout/svg/nsSVGIntegrationUtils.cpp') diff --git a/layout/svg/nsSVGIntegrationUtils.cpp b/layout/svg/nsSVGIntegrationUtils.cpp index 498f69393..0003e1a73 100644 --- a/layout/svg/nsSVGIntegrationUtils.cpp +++ b/layout/svg/nsSVGIntegrationUtils.cpp @@ -509,71 +509,108 @@ PaintMaskSurface(const PaintFramesParams& aParams, return DrawResult::SUCCESS; } -static DrawResult +struct MaskPaintResult { + RefPtr maskSurface; + Matrix maskTransform; + DrawResult result; + bool transparentBlackMask; + bool opacityApplied; + + MaskPaintResult() + : result(DrawResult::SUCCESS), transparentBlackMask(false), + opacityApplied(false) + {} +}; + +static MaskPaintResult CreateAndPaintMaskSurface(const PaintFramesParams& aParams, float aOpacity, nsStyleContext* aSC, const nsTArray& aMaskFrames, - const nsPoint& aOffsetToUserSpace, - Matrix& aOutMaskTransform, - RefPtr& aOutMaskSurface, - bool& aOpacityApplied) + const nsPoint& aOffsetToUserSpace) { const nsStyleSVGReset *svgReset = aSC->StyleSVGReset(); MOZ_ASSERT(aMaskFrames.Length() > 0); + MaskPaintResult paintResult; gfxContext& ctx = aParams.ctx; - // There is only one SVG mask. + // Optimization for single SVG mask. if (((aMaskFrames.Length() == 1) && aMaskFrames[0])) { gfxMatrix cssPxToDevPxMatrix = nsSVGIntegrationUtils::GetCSSPxToDevPxMatrix(aParams.frame); + paintResult.opacityApplied = true; - aOpacityApplied = true; - aOutMaskSurface = + paintResult.maskSurface = aMaskFrames[0]->GetMaskForMaskedFrame(&ctx, aParams.frame, cssPxToDevPxMatrix, aOpacity, - &aOutMaskTransform, + &paintResult.maskTransform, svgReset->mMask.mLayers[0].mMaskMode); - return DrawResult::SUCCESS; + if (!paintResult.maskSurface) { + paintResult.transparentBlackMask = true; + } + + return paintResult; } const IntRect& maskSurfaceRect = aParams.maskRect; if (maskSurfaceRect.IsEmpty()) { - return DrawResult::SUCCESS; + paintResult.transparentBlackMask = true; + return paintResult; } RefPtr maskDT = ctx.GetDrawTarget()->CreateSimilarDrawTarget(maskSurfaceRect.Size(), SurfaceFormat::A8); if (!maskDT || !maskDT->IsValid()) { - return DrawResult::TEMPORARY_ERROR; + paintResult.result = DrawResult::TEMPORARY_ERROR; + return paintResult; } // Set aAppliedOpacity as true only if all mask layers are svg mask. // In this case, we will apply opacity into the final mask surface, so the // caller does not need to apply it again. - aOpacityApplied = !HasNonSVGMask(aMaskFrames); + paintResult.opacityApplied = !HasNonSVGMask(aMaskFrames); // Set context's matrix on maskContext, offset by the maskSurfaceRect's // position. This makes sure that we combine the masks in device space. gfxMatrix maskSurfaceMatrix = ctx.CurrentMatrix() * gfxMatrix::Translation(-aParams.maskRect.TopLeft()); - DrawResult result = PaintMaskSurface(aParams, maskDT, - aOpacityApplied ? aOpacity : 1.0, - aSC, aMaskFrames, maskSurfaceMatrix, - aOffsetToUserSpace); - if (result != DrawResult::SUCCESS) { - return result; - } - - aOutMaskTransform = ToMatrix(maskSurfaceMatrix); - if (!aOutMaskTransform.Invert()) { - return DrawResult::SUCCESS; + paintResult.result = PaintMaskSurface(aParams, maskDT, + paintResult.opacityApplied + ? aOpacity : 1.0, + aSC, aMaskFrames, maskSurfaceMatrix, + aOffsetToUserSpace); + if (paintResult.result != DrawResult::SUCCESS) { + // Now we know the status of mask resource since we used it while painting. + // According to the return value of PaintMaskSurface, we know whether mask + // resource is resolvable or not. + // + // For a HTML doc: + // According to css-masking spec, always create a mask surface when + // we have any item in maskFrame even if all of those items are + // non-resolvable or . + // Set paintResult.transparentBlackMask as true, the caller should stop + // painting masked content as if this mask is a transparent black one. + // For a SVG doc: + // SVG 1.1 say that if we fail to resolve a mask, we should draw the + // object unmasked. + // Left patinResult.maskSurface empty, the caller should paint all + // masked content as if this mask is an opaque white one(no mask). + paintResult.transparentBlackMask = + !(aParams.frame->GetStateBits() & NS_FRAME_SVG_LAYOUT); + + MOZ_ASSERT(!paintResult.maskSurface); + return paintResult; + } + + paintResult.maskTransform = ToMatrix(maskSurfaceMatrix); + if (!paintResult.maskTransform.Invert()) { + return paintResult; } - aOutMaskSurface = maskDT->Snapshot(); - return DrawResult::SUCCESS; + paintResult.maskSurface = maskDT->Snapshot(); + return paintResult; } static bool @@ -783,6 +820,8 @@ nsSVGIntegrationUtils::PaintMaskAndClipPath(const PaintFramesParams& aParams) maskUsage.shouldGenerateClipMaskLayer || maskUsage.shouldGenerateMaskLayer); + bool shouldPushMask = false; + /* Check if we need to do additional operations on this child's * rendering, which necessitates rendering into another surface. */ if (shouldGenerateMask) { @@ -800,14 +839,23 @@ nsSVGIntegrationUtils::PaintMaskAndClipPath(const PaintFramesParams& aParams) // instead of the first continuation frame. SetupContextMatrix(frame, aParams, offsetToBoundingBox, offsetToUserSpace); - result = CreateAndPaintMaskSurface(aParams, maskUsage.opacity, - firstFrame->StyleContext(), - maskFrames, offsetToUserSpace, - maskTransform, maskSurface, - opacityApplied); - if (!maskSurface) { - // Entire surface is clipped out. - return result; + MaskPaintResult paintResult = + CreateAndPaintMaskSurface(aParams, maskUsage.opacity, + firstFrame->StyleContext(), + maskFrames, offsetToUserSpace); + + if (paintResult.transparentBlackMask) { + MOZ_ASSERT(paintResult.result != DrawResult::SUCCESS); + return paintResult.result; + } + + result &= paintResult.result; + maskSurface = paintResult.maskSurface; + if (maskSurface) { + MOZ_ASSERT(paintResult.result == DrawResult::SUCCESS); + shouldPushMask = true; + maskTransform = paintResult.maskTransform; + opacityApplied = paintResult.opacityApplied; } } @@ -831,6 +879,8 @@ nsSVGIntegrationUtils::PaintMaskAndClipPath(const PaintFramesParams& aParams) // failure in nsSVGClipPathFrame::GetClipMask. return result; } + + shouldPushMask = true; } // opacity != 1.0f. @@ -841,18 +891,22 @@ nsSVGIntegrationUtils::PaintMaskAndClipPath(const PaintFramesParams& aParams) matSR.SetContext(&context); SetupContextMatrix(firstFrame, aParams, offsetToBoundingBox, offsetToUserSpace); + shouldPushMask = true; } - if (aParams.layerManager->GetRoot()->GetContentFlags() & Layer::CONTENT_COMPONENT_ALPHA) { - context.PushGroupAndCopyBackground(gfxContentType::COLOR_ALPHA, - opacityApplied - ? 1.0 - : maskUsage.opacity, - maskSurface, maskTransform); - } else { - context.PushGroupForBlendBack(gfxContentType::COLOR_ALPHA, - opacityApplied ? 1.0 : maskUsage.opacity, - maskSurface, maskTransform); + if (shouldPushMask) { + if (aParams.layerManager->GetRoot()->GetContentFlags() & + Layer::CONTENT_COMPONENT_ALPHA) { + context.PushGroupAndCopyBackground(gfxContentType::COLOR_ALPHA, + opacityApplied + ? 1.0 + : maskUsage.opacity, + maskSurface, maskTransform); + } else { + context.PushGroupForBlendBack(gfxContentType::COLOR_ALPHA, + opacityApplied ? 1.0 : maskUsage.opacity, + maskSurface, maskTransform); + } } } @@ -899,7 +953,7 @@ nsSVGIntegrationUtils::PaintMaskAndClipPath(const PaintFramesParams& aParams) context.PopClip(); } - if (shouldGenerateMask) { + if (shouldPushMask) { context.PopGroupAndBlend(); } -- cgit v1.2.3