void nsSVGClipPathFrame::ApplyClipPath(gfxContext& aContext, nsIFrame* aClippedFrame, const gfxMatrix& aMatrix) { MOZ_ASSERT(IsTrivial(), "Caller needs to use GetClipMask"); DrawTarget& aDrawTarget = *aContext.GetDrawTarget(); // No need for AutoReferenceLimiter since simple clip paths can't create // a reference loop (they don't reference other clip paths). // Restore current transform after applying clip path: gfxContextMatrixAutoSaveRestore autoRestore(&aContext); RefPtr<Path> clipPath; nsISVGChildFrame* singleClipPathChild = nullptr; IsTrivial(&singleClipPathChild); if (singleClipPathChild) { nsSVGPathGeometryFrame* pathFrame = do_QueryFrame(singleClipPathChild); if (pathFrame) { nsSVGPathGeometryElement* pathElement = static_cast<nsSVGPathGeometryElement*>(pathFrame->GetContent()); gfxMatrix toChildsUserSpace = pathElement-> PrependLocalTransformsTo(GetClipPathTransform(aClippedFrame) * aMatrix, eUserSpaceToParent); gfxMatrix newMatrix = aContext.CurrentMatrix().PreMultiply(toChildsUserSpace).NudgeToIntegers(); if (!newMatrix.IsSingular()) { aContext.SetMatrix(newMatrix); FillRule clipRule = nsSVGUtils::ToFillRule(pathFrame->StyleSVG()->mClipRule); clipPath = pathElement->GetOrBuildPath(aDrawTarget, clipRule); } } } if (clipPath) { 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()); } }
/* static*/ void nsCSSClipPathInstance::ApplyBasicShapeClip(gfxContext& aContext, nsIFrame* aFrame) { auto& clipPathStyle = aFrame->StyleSVGReset()->mClipPath; #ifdef DEBUG StyleShapeSourceType type = clipPathStyle.GetType(); MOZ_ASSERT(type == StyleShapeSourceType::Shape || type == StyleShapeSourceType::Box, "This function is used with basic-shape and geometry-box only."); #endif nsCSSClipPathInstance instance(aFrame, clipPathStyle); aContext.NewPath(); RefPtr<Path> path = instance.CreateClipPath(aContext.GetDrawTarget()); aContext.SetPath(path); aContext.Clip(); }
void nsSVGIntegrationUtils::PaintFramesWithEffects(gfxContext& aContext, nsIFrame* aFrame, const nsRect& aDirtyRect, const nsRect& aBorderArea, nsDisplayListBuilder* aBuilder, LayerManager *aLayerManager) { #ifdef DEBUG NS_ASSERTION(!(aFrame->GetStateBits() & NS_FRAME_SVG_LAYOUT) || (NS_SVGDisplayListPaintingEnabled() && !(aFrame->GetStateBits() & NS_FRAME_IS_NONDISPLAY)), "Should not use nsSVGIntegrationUtils on this SVG frame"); #endif /* SVG defines the following rendering model: * * 1. Render geometry * 2. Apply filter * 3. Apply clipping, masking, group opacity * * We follow this, but perform a couple of optimizations: * * + Use cairo's clipPath when representable natively (single object * clip region). * * + Merge opacity and masking if both used together. */ const nsIContent* content = aFrame->GetContent(); bool hasSVGLayout = (aFrame->GetStateBits() & NS_FRAME_SVG_LAYOUT); if (hasSVGLayout) { nsISVGChildFrame *svgChildFrame = do_QueryFrame(aFrame); if (!svgChildFrame || !aFrame->GetContent()->IsSVGElement()) { NS_ASSERTION(false, "why?"); return; } if (!static_cast<const nsSVGElement*>(content)->HasValidDimensions()) { return; // The SVG spec says not to draw _anything_ } } float opacity = aFrame->StyleDisplay()->mOpacity; if (opacity == 0.0f) { return; } if (opacity != 1.0f && hasSVGLayout && nsSVGUtils::CanOptimizeOpacity(aFrame)) { opacity = 1.0f; } /* Properties are added lazily and may have been removed by a restyle, so make sure all applicable ones are set again. */ nsIFrame* firstFrame = nsLayoutUtils::FirstContinuationOrIBSplitSibling(aFrame); nsSVGEffects::EffectProperties effectProperties = nsSVGEffects::GetEffectProperties(firstFrame); bool isOK = effectProperties.HasNoFilterOrHasValidFilter(); nsSVGClipPathFrame *clipPathFrame = effectProperties.GetClipPathFrame(&isOK); bool isTrivialClip = clipPathFrame ? clipPathFrame->IsTrivial() : true; DrawTarget* drawTarget = aContext.GetDrawTarget(); gfxContextMatrixAutoSaveRestore matrixAutoSaveRestore(&aContext); nsPoint firstFrameOffset = GetOffsetToBoundingBox(firstFrame); nsPoint offsetToBoundingBox = aBuilder->ToReferenceFrame(firstFrame) - firstFrameOffset; if (!firstFrame->IsFrameOfType(nsIFrame::eSVG)) { /* Snap the offset if the reference frame is not a SVG frame, * since other frames will be snapped to pixel when rendering. */ offsetToBoundingBox = nsPoint( aFrame->PresContext()->RoundAppUnitsToNearestDevPixels(offsetToBoundingBox.x), aFrame->PresContext()->RoundAppUnitsToNearestDevPixels(offsetToBoundingBox.y)); } // After applying only "offsetToBoundingBox", aCtx would have its origin at // the top left corner of aFrame's bounding box (over all continuations). // However, SVG painting needs the origin to be located at the origin of the // SVG frame's "user space", i.e. the space in which, for example, the // frame's BBox lives. // SVG geometry frames and foreignObject frames apply their own offsets, so // their position is relative to their user space. So for these frame types, // if we want aCtx to be in user space, we first need to subtract the // frame's position so that SVG painting can later add it again and the // frame is painted in the right place. gfxPoint toUserSpaceGfx = nsSVGUtils::FrameSpaceInCSSPxToUserSpaceOffset(aFrame); nsPoint toUserSpace(nsPresContext::CSSPixelsToAppUnits(float(toUserSpaceGfx.x)), nsPresContext::CSSPixelsToAppUnits(float(toUserSpaceGfx.y))); nsPoint offsetToUserSpace = offsetToBoundingBox - toUserSpace; NS_ASSERTION(hasSVGLayout || offsetToBoundingBox == offsetToUserSpace, "For non-SVG frames there shouldn't be any additional offset"); gfxPoint devPixelOffsetToUserSpace = nsLayoutUtils::PointToGfxPoint(offsetToUserSpace, aFrame->PresContext()->AppUnitsPerDevPixel()); aContext.SetMatrix(aContext.CurrentMatrix().Translate(devPixelOffsetToUserSpace)); gfxMatrix cssPxToDevPxMatrix = GetCSSPxToDevPxMatrix(aFrame); const nsStyleSVGReset *svgReset = firstFrame->StyleSVGReset(); // Keep moving forward even if svgMaskFrame is nullptr or isOK is false. // This source is not a svg mask, but it still can be a correct mask image. nsSVGMaskFrame *svgMaskFrame = effectProperties.GetMaskFrame(&isOK); bool complexEffects = false; bool hasValidLayers = svgReset->mMask.HasLayerWithImage(); // These are used if we require a temporary surface for a custom blend mode. RefPtr<gfxContext> target = &aContext; IntPoint targetOffset; /* Check if we need to do additional operations on this child's * rendering, which necessitates rendering into another surface. */ if (opacity != 1.0f || (clipPathFrame && !isTrivialClip) || aFrame->StyleDisplay()->mMixBlendMode != NS_STYLE_BLEND_NORMAL || svgMaskFrame || hasValidLayers) { complexEffects = true; aContext.Save(); nsRect clipRect = aFrame->GetVisualOverflowRectRelativeToSelf() + toUserSpace; aContext.Clip(NSRectToSnappedRect(clipRect, aFrame->PresContext()->AppUnitsPerDevPixel(), *drawTarget)); Matrix maskTransform; RefPtr<SourceSurface> maskSurface; if (svgMaskFrame) { maskSurface = svgMaskFrame->GetMaskForMaskedFrame(&aContext, aFrame, cssPxToDevPxMatrix, opacity, &maskTransform); } else if (hasValidLayers) { gfxRect clipRect = aContext.GetClipExtents(); { gfxContextMatrixAutoSaveRestore matRestore(&aContext); aContext.SetMatrix(gfxMatrix()); clipRect = aContext.GetClipExtents(); } IntRect drawRect = RoundedOut(ToRect(clipRect)); RefPtr<DrawTarget> targetDT = aContext.GetDrawTarget()->CreateSimilarDrawTarget(drawRect.Size(), SurfaceFormat::A8); if (!targetDT) { aContext.Restore(); return; } RefPtr<gfxContext> target = new gfxContext(targetDT); target->SetMatrix(matrixAutoSaveRestore.Matrix() * gfxMatrix::Translation(-drawRect.TopLeft())); // Generate mask surface. uint32_t flags = aBuilder->GetBackgroundPaintFlags() | nsCSSRendering::PAINTBG_MASK_IMAGE; nsRenderingContext rc(target); nsCSSRendering::PaintBackgroundWithSC(aFrame->PresContext(), rc, aFrame, aDirtyRect, aBorderArea, firstFrame->StyleContext(), *aFrame->StyleBorder(), flags, nullptr, -1); maskSurface = targetDT->Snapshot(); // Compute mask transform. Matrix mat = ToMatrix(aContext.CurrentMatrix()); mat.Invert(); maskTransform = Matrix::Translation(drawRect.x, drawRect.y) * mat; } if ((svgMaskFrame || hasValidLayers) && !maskSurface) { // Entire surface is clipped out. aContext.Restore(); return; } if (aFrame->StyleDisplay()->mMixBlendMode != NS_STYLE_BLEND_NORMAL) { // Create a temporary context to draw to so we can blend it back with // another operator. gfxRect clipRect; { gfxContextMatrixAutoSaveRestore matRestore(&aContext); aContext.SetMatrix(gfxMatrix()); clipRect = aContext.GetClipExtents(); } IntRect drawRect = RoundedOut(ToRect(clipRect)); RefPtr<DrawTarget> targetDT = aContext.GetDrawTarget()->CreateSimilarDrawTarget(drawRect.Size(), SurfaceFormat::B8G8R8A8); if (!targetDT) { aContext.Restore(); return; } target = new gfxContext(targetDT); target->SetMatrix(aContext.CurrentMatrix() * gfxMatrix::Translation(-drawRect.TopLeft())); targetOffset = drawRect.TopLeft(); } if (clipPathFrame && !isTrivialClip) { Matrix clippedMaskTransform; RefPtr<SourceSurface> clipMaskSurface = clipPathFrame->GetClipMask(aContext, aFrame, cssPxToDevPxMatrix, &clippedMaskTransform, maskSurface, maskTransform); if (clipMaskSurface) { maskSurface = clipMaskSurface; maskTransform = clippedMaskTransform; } } if (opacity != 1.0f || svgMaskFrame || hasValidLayers || (clipPathFrame && !isTrivialClip)) { target->PushGroupForBlendBack(gfxContentType::COLOR_ALPHA, opacity, maskSurface, maskTransform); } } /* If this frame has only a trivial clipPath, set up cairo's clipping now so * we can just do normal painting and get it clipped appropriately. */ if (clipPathFrame && isTrivialClip) { aContext.Save(); clipPathFrame->ApplyClipPath(aContext, aFrame, cssPxToDevPxMatrix); } /* Paint the child */ if (effectProperties.HasValidFilter()) { RegularFramePaintCallback callback(aBuilder, aLayerManager, offsetToUserSpace); nsRegion dirtyRegion = aDirtyRect - offsetToBoundingBox; gfxMatrix tm = nsSVGIntegrationUtils::GetCSSPxToDevPxMatrix(aFrame); nsFilterInstance::PaintFilteredFrame(aFrame, target->GetDrawTarget(), tm, &callback, &dirtyRegion); } else { target->SetMatrix(matrixAutoSaveRestore.Matrix()); BasicLayerManager* basic = static_cast<BasicLayerManager*>(aLayerManager); RefPtr<gfxContext> oldCtx = basic->GetTarget(); basic->SetTarget(target); aLayerManager->EndTransaction(FrameLayerBuilder::DrawPaintedLayer, aBuilder); basic->SetTarget(oldCtx); } if (clipPathFrame && isTrivialClip) { aContext.Restore(); } /* No more effects, we're done. */ if (!complexEffects) { return; } if (opacity != 1.0f || svgMaskFrame || hasValidLayers || (clipPathFrame && !isTrivialClip)) { target->PopGroupAndBlend(); } if (aFrame->StyleDisplay()->mMixBlendMode != NS_STYLE_BLEND_NORMAL) { RefPtr<DrawTarget> targetDT = target->GetDrawTarget(); target = nullptr; RefPtr<SourceSurface> targetSurf = targetDT->Snapshot(); aContext.SetMatrix(gfxMatrix()); // This will be restored right after. RefPtr<gfxPattern> pattern = new gfxPattern(targetSurf, Matrix::Translation(targetOffset.x, targetOffset.y)); aContext.SetPattern(pattern); aContext.Paint(); } aContext.Restore(); }