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 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 DrawResult::SUCCESS; // Reference loop! } DrawResult result = DrawResult::SUCCESS; DrawTarget* maskDT = aMaskContext.GetDrawTarget(); MOZ_ASSERT(maskDT->GetFormat() == SurfaceFormat::A8); // Paint this clipPath's contents into aMaskDT: // 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(); nsSVGUtils::MaskUsage maskUsage; nsSVGUtils::DetermineMaskUsage(this, true, maskUsage); if (maskUsage.shouldApplyClipPath) { clipPathThatClipsClipPath->ApplyClipPath(aMaskContext, aClippedFrame, aMatrix); } else if (maskUsage.shouldGenerateClipMaskLayer) { Matrix maskTransform; RefPtr<SourceSurface> maskSurface; Tie(result, maskSurface) = clipPathThatClipsClipPath->GetClipMask(aMaskContext, aClippedFrame, aMatrix, &maskTransform); aMaskContext.PushGroupForBlendBack(gfxContentType::ALPHA, 1.0, maskSurface, maskTransform); // The corresponding PopGroupAndBlend call below will mask the // blend using |maskSurface|. } // Paint our children into the mask: for (nsIFrame* kid = mFrames.FirstChild(); kid; kid = kid->GetNextSibling()) { result &= PaintFrameIntoMask(kid, aClippedFrame, aMaskContext, aMatrix); } if (maskUsage.shouldGenerateClipMaskLayer) { aMaskContext.PopGroupAndBlend(); } else if (maskUsage.shouldApplyClipPath) { aMaskContext.PopClip(); } // Moz2D transforms in the opposite direction to Thebes gfxMatrix maskTransfrom = aMaskContext.CurrentMatrix(); maskTransfrom.Invert(); if (aExtraMask) { ComposeExtraMask(maskDT, maskTransfrom, aExtraMask, aExtraMasksTransform); } *aMaskTransform = ToMatrix(maskTransfrom); return result; }
DrawResult nsSVGClipPathFrame::PaintFrameIntoMask(nsIFrame *aFrame, nsIFrame* aClippedFrame, gfxContext& aTarget, const gfxMatrix& aMatrix) { nsISVGChildFrame* frame = do_QueryFrame(aFrame); if (!frame) { return DrawResult::SUCCESS; } // The CTM of each frame referencing us can be different. frame->NotifySVGChanged(nsISVGChildFrame::TRANSFORM_CHANGED); // Children of this clipPath may themselves be clipped. nsSVGEffects::EffectProperties effectProperties = nsSVGEffects::GetEffectProperties(aFrame); if (effectProperties.HasInvalidClipPath()) { return DrawResult::SUCCESS; } nsSVGClipPathFrame *clipPathThatClipsChild = effectProperties.GetClipPathFrame(); nsSVGUtils::MaskUsage maskUsage; nsSVGUtils::DetermineMaskUsage(aFrame, true, maskUsage); DrawResult result = DrawResult::SUCCESS; if (maskUsage.shouldApplyClipPath) { clipPathThatClipsChild->ApplyClipPath(aTarget, aClippedFrame, aMatrix); } else if (maskUsage.shouldGenerateClipMaskLayer) { Matrix maskTransform; RefPtr<SourceSurface> maskSurface; Tie(result, maskSurface) = clipPathThatClipsChild->GetClipMask(aTarget, aClippedFrame, aMatrix, &maskTransform); aTarget.PushGroupForBlendBack(gfxContentType::ALPHA, 1.0, maskSurface, maskTransform); // The corresponding PopGroupAndBlend call below will mask the // blend using |maskSurface|. } gfxMatrix toChildsUserSpace = mMatrixForChildren; nsIFrame* child = do_QueryFrame(frame); nsIContent* childContent = child->GetContent(); if (childContent->IsSVGElement()) { toChildsUserSpace = static_cast<const nsSVGElement*>(childContent)-> PrependLocalTransformsTo(mMatrixForChildren, eUserSpaceToParent); } // Our children have NS_STATE_SVG_CLIPPATH_CHILD set on them, and // SVGGeometryFrame::Render checks for that state bit and paints // only the geometry (opaque black) if set. result &= frame->PaintSVG(aTarget, toChildsUserSpace); if (maskUsage.shouldGenerateClipMaskLayer) { aTarget.PopGroupAndBlend(); } else if (maskUsage.shouldApplyClipPath) { aTarget.PopClip(); } return result; }
//---------------------------------------------------------------------- // nsISVGChildFrame methods: nsresult nsSVGImageFrame::PaintSVG(gfxContext& aContext, const gfxMatrix& aTransform, const nsIntRect *aDirtyRect) { nsresult rv = NS_OK; if (!StyleVisibility()->IsVisible()) return NS_OK; float x, y, width, height; SVGImageElement *imgElem = static_cast<SVGImageElement*>(mContent); imgElem->GetAnimatedLengthValues(&x, &y, &width, &height, nullptr); NS_ASSERTION(width > 0 && height > 0, "Should only be painting things with valid width/height"); if (!mImageContainer) { nsCOMPtr<imgIRequest> currentRequest; nsCOMPtr<nsIImageLoadingContent> imageLoader = do_QueryInterface(mContent); if (imageLoader) imageLoader->GetRequest(nsIImageLoadingContent::CURRENT_REQUEST, getter_AddRefs(currentRequest)); if (currentRequest) currentRequest->GetImage(getter_AddRefs(mImageContainer)); } if (mImageContainer) { gfxContextAutoSaveRestore autoRestorer(&aContext); if (StyleDisplay()->IsScrollableOverflow()) { gfxRect clipRect = nsSVGUtils::GetClipRectForFrame(this, x, y, width, height); nsSVGUtils::SetClipRect(&aContext, aTransform, clipRect); } if (!TransformContextForPainting(&aContext, aTransform)) { return NS_ERROR_FAILURE; } // fill-opacity doesn't affect <image>, so if we're allowed to // optimize group opacity, the opacity used for compositing the // image into the current canvas is just the group opacity. float opacity = 1.0f; if (nsSVGUtils::CanOptimizeOpacity(this)) { opacity = StyleEffects()->mOpacity; } if (opacity != 1.0f || StyleEffects()->mMixBlendMode != NS_STYLE_BLEND_NORMAL) { aContext.PushGroupForBlendBack(gfxContentType::COLOR_ALPHA, opacity); } nscoord appUnitsPerDevPx = PresContext()->AppUnitsPerDevPixel(); nsRect dirtyRect; // only used if aDirtyRect is non-null if (aDirtyRect) { NS_ASSERTION(!NS_SVGDisplayListPaintingEnabled() || (mState & NS_FRAME_IS_NONDISPLAY), "Display lists handle dirty rect intersection test"); dirtyRect = ToAppUnits(*aDirtyRect, appUnitsPerDevPx); // Adjust dirtyRect to match our local coordinate system. nsRect rootRect = nsSVGUtils::TransformFrameRectToOuterSVG(mRect, aTransform, PresContext()); dirtyRect.MoveBy(-rootRect.TopLeft()); } // XXXbholley - I don't think huge images in SVGs are common enough to // warrant worrying about the responsiveness impact of doing synchronous // decodes. The extra code complexity of determinining when we want to // force sync probably just isn't worth it, so always pass FLAG_SYNC_DECODE uint32_t drawFlags = imgIContainer::FLAG_SYNC_DECODE; if (mImageContainer->GetType() == imgIContainer::TYPE_VECTOR) { // Package up the attributes of this image element which can override the // attributes of mImageContainer's internal SVG document. The 'width' & // 'height' values we're passing in here are in CSS units (though they // come from width/height *attributes* in SVG). They influence the region // of the SVG image's internal document that is visible, in combination // with preserveAspectRatio and viewBox. SVGImageContext context(CSSIntSize(width, height), Some(imgElem->mPreserveAspectRatio.GetAnimValue())); // For the actual draw operation to draw crisply (and at the right size), // our destination rect needs to be |width|x|height|, *in dev pixels*. LayoutDeviceSize devPxSize(width, height); nsRect destRect(nsPoint(), LayoutDevicePixel::ToAppUnits(devPxSize, appUnitsPerDevPx)); // Note: Can't use DrawSingleUnscaledImage for the TYPE_VECTOR case. // That method needs our image to have a fixed native width & height, // and that's not always true for TYPE_VECTOR images. // FIXME We should use the return value, see bug 1258510. Unused << nsLayoutUtils::DrawSingleImage( aContext, PresContext(), mImageContainer, nsLayoutUtils::GetGraphicsFilterForFrame(this), destRect, aDirtyRect ? dirtyRect : destRect, &context, drawFlags); } else { // mImageContainer->GetType() == TYPE_RASTER // FIXME We should use the return value, see bug 1258510. Unused << nsLayoutUtils::DrawSingleUnscaledImage( aContext, PresContext(), mImageContainer, nsLayoutUtils::GetGraphicsFilterForFrame(this), nsPoint(0, 0), aDirtyRect ? &dirtyRect : nullptr, drawFlags); } if (opacity != 1.0f || StyleEffects()->mMixBlendMode != NS_STYLE_BLEND_NORMAL) { aContext.PopGroupAndBlend(); } // gfxContextAutoSaveRestore goes out of scope & cleans up our gfxContext } return rv; }