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/nsCSSClipPathInstance.cpp | 116 +--------------------------- layout/svg/nsCSSClipPathInstance.h | 4 - layout/svg/nsSVGIntegrationUtils.cpp | 144 ++++++++++++++++++++++++----------- layout/svg/nsSVGUtils.cpp | 21 +---- 4 files changed, 103 insertions(+), 182 deletions(-) (limited to 'layout/svg') diff --git a/layout/svg/nsCSSClipPathInstance.cpp b/layout/svg/nsCSSClipPathInstance.cpp index 828b10eac..01f7de248 100644 --- a/layout/svg/nsCSSClipPathInstance.cpp +++ b/layout/svg/nsCSSClipPathInstance.cpp @@ -62,122 +62,12 @@ nsCSSClipPathInstance::HitTestBasicShapeClip(nsIFrame* aFrame, return path->ContainsPoint(ToPoint(aPoint) * pixelRatio, Matrix()); } -nsRect -nsCSSClipPathInstance::ComputeSVGReferenceRect() -{ - MOZ_ASSERT(mTargetFrame->GetContent()->IsSVGElement()); - nsRect r; - - // For SVG elements without associated CSS layout box, the used value for - // content-box, padding-box, border-box and margin-box is fill-box. - switch (mClipPathStyle.GetReferenceBox()) { - case StyleClipPathGeometryBox::Stroke: { - // XXX Bug 1299876 - // The size of srtoke-box is not correct if this graphic element has - // specific stroke-linejoin or stroke-linecap. - gfxRect bbox = nsSVGUtils::GetBBox(mTargetFrame, - nsSVGUtils::eBBoxIncludeFill | nsSVGUtils::eBBoxIncludeStroke); - r = nsLayoutUtils::RoundGfxRectToAppRect(bbox, - nsPresContext::AppUnitsPerCSSPixel()); - break; - } - case StyleClipPathGeometryBox::View: { - nsIContent* content = mTargetFrame->GetContent(); - nsSVGElement* element = static_cast(content); - SVGSVGElement* svgElement = element->GetCtx(); - MOZ_ASSERT(svgElement); - - if (svgElement && svgElement->HasViewBoxRect()) { - // If a ‘viewBox‘ attribute is specified for the SVG viewport creating - // element: - // 1. The reference box is positioned at the origin of the coordinate - // system established by the ‘viewBox‘ attribute. - // 2. The dimension of the reference box is set to the width and height - // values of the ‘viewBox‘ attribute. - nsSVGViewBox* viewBox = svgElement->GetViewBox(); - const nsSVGViewBoxRect& value = viewBox->GetAnimValue(); - r = nsRect(nsPresContext::CSSPixelsToAppUnits(value.x), - nsPresContext::CSSPixelsToAppUnits(value.y), - nsPresContext::CSSPixelsToAppUnits(value.width), - nsPresContext::CSSPixelsToAppUnits(value.height)); - } else { - // No viewBox is specified, uses the nearest SVG viewport as reference - // box. - svgFloatSize viewportSize = svgElement->GetViewportSize(); - r = nsRect(0, 0, - nsPresContext::CSSPixelsToAppUnits(viewportSize.width), - nsPresContext::CSSPixelsToAppUnits(viewportSize.height)); - } - - break; - } - case StyleClipPathGeometryBox::NoBox: - case StyleClipPathGeometryBox::Border: - case StyleClipPathGeometryBox::Content: - case StyleClipPathGeometryBox::Padding: - case StyleClipPathGeometryBox::Margin: - case StyleClipPathGeometryBox::Fill: { - gfxRect bbox = nsSVGUtils::GetBBox(mTargetFrame, - nsSVGUtils::eBBoxIncludeFill); - r = nsLayoutUtils::RoundGfxRectToAppRect(bbox, - nsPresContext::AppUnitsPerCSSPixel()); - break; - } - default:{ - MOZ_ASSERT_UNREACHABLE("unknown StyleClipPathGeometryBox type"); - gfxRect bbox = nsSVGUtils::GetBBox(mTargetFrame, - nsSVGUtils::eBBoxIncludeFill); - r = nsLayoutUtils::RoundGfxRectToAppRect(bbox, - nsPresContext::AppUnitsPerCSSPixel()); - break; - } - } - - return r; -} - -nsRect -nsCSSClipPathInstance::ComputeHTMLReferenceRect() -{ - nsRect r; - - // For elements with associated CSS layout box, the used value for fill-box, - // stroke-box and view-box is border-box. - switch (mClipPathStyle.GetReferenceBox()) { - case StyleClipPathGeometryBox::Content: - r = mTargetFrame->GetContentRectRelativeToSelf(); - break; - case StyleClipPathGeometryBox::Padding: - r = mTargetFrame->GetPaddingRectRelativeToSelf(); - break; - case StyleClipPathGeometryBox::Margin: - r = mTargetFrame->GetMarginRectRelativeToSelf(); - break; - case StyleClipPathGeometryBox::NoBox: - case StyleClipPathGeometryBox::Border: - case StyleClipPathGeometryBox::Fill: - case StyleClipPathGeometryBox::Stroke: - case StyleClipPathGeometryBox::View: - r = mTargetFrame->GetRectRelativeToSelf(); - break; - default: - MOZ_ASSERT_UNREACHABLE("unknown StyleClipPathGeometryBox type"); - r = mTargetFrame->GetRectRelativeToSelf(); - break; - } - - return r; -} - already_AddRefed nsCSSClipPathInstance::CreateClipPath(DrawTarget* aDrawTarget) { - // We use ComputeSVGReferenceRect for all SVG elements, except - // element, which does have an associated CSS layout box. In this case we - // should still use ComputeHTMLReferenceRect for region computing. - nsRect r = mTargetFrame->IsFrameOfType(nsIFrame::eSVG) && - (mTargetFrame->GetType() != nsGkAtoms::svgOuterSVGFrame) - ? ComputeSVGReferenceRect() : ComputeHTMLReferenceRect(); + nsRect r = + nsLayoutUtils::ComputeGeometryBox(mTargetFrame, + mClipPathStyle.GetReferenceBox()); if (mClipPathStyle.GetType() != StyleShapeSourceType::Shape) { // TODO Clip to border-radius/reference box if no shape diff --git a/layout/svg/nsCSSClipPathInstance.h b/layout/svg/nsCSSClipPathInstance.h index 3b0724dbd..33d92e738 100644 --- a/layout/svg/nsCSSClipPathInstance.h +++ b/layout/svg/nsCSSClipPathInstance.h @@ -48,10 +48,6 @@ private: already_AddRefed CreateClipPathInset(DrawTarget* aDrawTarget, const nsRect& aRefBox); - - nsRect ComputeHTMLReferenceRect(); - nsRect ComputeSVGReferenceRect(); - /** * The frame for the element that is currently being clipped. */ 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(); } diff --git a/layout/svg/nsSVGUtils.cpp b/layout/svg/nsSVGUtils.cpp index 344ebf645..0bded21ff 100644 --- a/layout/svg/nsSVGUtils.cpp +++ b/layout/svg/nsSVGUtils.cpp @@ -514,26 +514,7 @@ nsSVGUtils::DetermineMaskUsage(nsIFrame* aFrame, bool aHandleOpacity, nsTArray maskFrames = effectProperties.GetMaskFrames(); -#ifdef MOZ_ENABLE_MASK_AS_SHORTHAND - // 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 , we still need to create a - // transparent black mask layer under this condition. - // For a SVG doc: - // SVG 1.1 say that if we fail to resolve a mask, we should draw the - // object unmasked. - aUsage.shouldGenerateMaskLayer = - (aFrame->GetStateBits() & NS_FRAME_SVG_LAYOUT) - ? maskFrames.Length() == 1 && maskFrames[0] - : maskFrames.Length() > 0; -#else - // Since we do not support image mask so far, we should treat any - // unresolvable mask as no mask. Otherwise, any object with a valid image - // mask, e.g. url("xxx.png"), will become invisible just because we can not - // handle image mask correctly. (See bug 1294171) - aUsage.shouldGenerateMaskLayer = maskFrames.Length() == 1 && maskFrames[0]; -#endif + aUsage.shouldGenerateMaskLayer = (maskFrames.Length() > 0); bool isOK = effectProperties.HasNoFilterOrHasValidFilter(); nsSVGClipPathFrame *clipPathFrame = effectProperties.GetClipPathFrame(&isOK); -- cgit v1.2.3