virtual void Paint(nsRenderingContext *aContext, nsIFrame *aTarget, const nsIntRect* aDirtyRect) { BasicLayerManager* basic = static_cast<BasicLayerManager*>(mLayerManager); basic->SetTarget(aContext->ThebesContext()); nsRenderingContext::AutoPushTranslation push(aContext, -mOffset); mLayerManager->EndTransaction(FrameLayerBuilder::DrawThebesLayer, mBuilder); }
nsBaseWidget::AutoLayerManagerSetup::~AutoLayerManagerSetup() { BasicLayerManager* manager = static_cast<BasicLayerManager*>(mWidget->GetLayerManager()); if (manager) { NS_ASSERTION(manager->GetBackendType() == LayerManager::LAYERS_BASIC, "AutoLayerManagerSetup instantiated for non-basic layer backend!"); manager->SetDefaultTarget(nsnull, BasicLayerManager::BUFFER_NONE); } }
nsBaseWidget::AutoLayerManagerSetup::~AutoLayerManagerSetup() { BasicLayerManager* manager = static_cast<BasicLayerManager*>(mWidget->GetLayerManager()); if (manager) { NS_ASSERTION(manager->GetBackendType() == LAYERS_BASIC, "AutoLayerManagerSetup instantiated for non-basic layer backend!"); manager->SetDefaultTarget(nullptr); manager->SetDefaultTargetConfiguration(mozilla::layers::BUFFER_NONE, ROTATION_0); } }
nsBaseWidget::AutoLayerManagerSetup::AutoLayerManagerSetup( nsBaseWidget* aWidget, gfxContext* aTarget, BasicLayerManager::BufferMode aDoubleBuffering) : mWidget(aWidget) { BasicLayerManager* manager = static_cast<BasicLayerManager*>(mWidget->GetLayerManager()); if (manager) { NS_ASSERTION(manager->GetBackendType() == LayerManager::LAYERS_BASIC, "AutoLayerManagerSetup instantiated for non-basic layer backend!"); manager->SetDefaultTarget(aTarget, aDoubleBuffering); } }
virtual void Paint(gfxContext& aContext, nsIFrame *aTarget, const gfxMatrix& aTransform, const nsIntRect* aDirtyRect, imgDrawingParams& aImgParams) override { BasicLayerManager* basic = mLayerManager->AsBasicLayerManager(); RefPtr<gfxContext> oldCtx = basic->GetTarget(); basic->SetTarget(&aContext); gfxContextMatrixAutoSaveRestore autoSR(&aContext); aContext.SetMatrixDouble(aContext.CurrentMatrixDouble().PreTranslate(-mUserSpaceToFrameSpaceOffset)); mLayerManager->EndTransaction(FrameLayerBuilder::DrawPaintedLayer, mBuilder); basic->SetTarget(oldCtx); }
virtual void Paint(gfxContext& aContext, nsIFrame *aTarget, const gfxMatrix& aTransform, const nsIntRect* aDirtyRect) override { BasicLayerManager* basic = static_cast<BasicLayerManager*>(mLayerManager); basic->SetTarget(&aContext); gfxPoint devPixelOffset = nsLayoutUtils::PointToGfxPoint(-mOffset, aTarget->PresContext()->AppUnitsPerDevPixel()); gfxContextMatrixAutoSaveRestore autoSR(&aContext); aContext.SetMatrix(aContext.CurrentMatrix().Translate(devPixelOffset)); mLayerManager->EndTransaction(FrameLayerBuilder::DrawPaintedLayer, mBuilder); }
LayerManager * nsWindow::GetLayerManager(PLayerTransactionChild* aShadowManager, LayersBackend aBackendHint, LayerManagerPersistence aPersistence, bool* aAllowRetaining) { if (aAllowRetaining) *aAllowRetaining = true; if (mLayerManager) { // This layer manager might be used for painting outside of DoDraw(), so we need // to set the correct rotation on it. if (mLayerManager->GetBackendType() == LAYERS_BASIC) { BasicLayerManager* manager = static_cast<BasicLayerManager*>(mLayerManager.get()); manager->SetDefaultTargetConfiguration(mozilla::layers::BUFFER_NONE, ScreenRotation(EffectiveScreenRotation())); } else if (mLayerManager->GetBackendType() == LAYERS_CLIENT) { ClientLayerManager* manager = static_cast<ClientLayerManager*>(mLayerManager.get()); manager->SetDefaultTargetConfiguration(mozilla::layers::BUFFER_NONE, ScreenRotation(EffectiveScreenRotation())); } return mLayerManager; } // Set mUseLayersAcceleration here to make it consistent with // nsBaseWidget::GetLayerManager mUseLayersAcceleration = ComputeShouldAccelerate(mUseLayersAcceleration); nsWindow *topWindow = sTopWindows[0]; if (!topWindow) { LOGW(" -- no topwindow\n"); return nullptr; } if (sUsingOMTC) { CreateCompositor(); if (mLayerManager) return mLayerManager; } if (mUseLayersAcceleration) { DebugOnly<nsIntRect> fbBounds = gScreenBounds; if (!sGLContext) { sGLContext = GLContextProvider::CreateForWindow(this); } MOZ_ASSERT(fbBounds.value == gScreenBounds); if (sGLContext) { nsRefPtr<LayerManagerOGL> layerManager = new LayerManagerOGL(this); if (layerManager->Initialize(sGLContext)) { mLayerManager = layerManager; return mLayerManager; } else { LOGW("Could not create OGL LayerManager"); } } else { LOGW("GL context was not created"); } } // Fall back to software rendering. sFramebufferOpen = Framebuffer::Open(); if (sFramebufferOpen) { LOG("Falling back to framebuffer software rendering"); } else { LOGE("Failed to mmap fb(?!?), aborting ..."); NS_RUNTIMEABORT("Can't open GL context and can't fall back on /dev/graphics/fb0 ..."); } mLayerManager = new ClientLayerManager(this); mUseLayersAcceleration = false; return mLayerManager; }
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(); }
void nsSVGIntegrationUtils::PaintFramesWithEffects(const PaintFramesParams& aParams) { #ifdef DEBUG NS_ASSERTION(!(aParams.frame->GetStateBits() & NS_FRAME_SVG_LAYOUT) || (NS_SVGDisplayListPaintingEnabled() && !(aParams.frame->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. */ nsIFrame* frame = aParams.frame; const nsIContent* content = frame->GetContent(); bool hasSVGLayout = (frame->GetStateBits() & NS_FRAME_SVG_LAYOUT); if (hasSVGLayout) { nsISVGChildFrame *svgChildFrame = do_QueryFrame(frame); if (!svgChildFrame || !frame->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 = frame->StyleEffects()->mOpacity; if (opacity == 0.0f) { return; } if (opacity != 1.0f && (nsSVGUtils::CanOptimizeOpacity(frame) || aParams.callerPaintsOpacity)) { opacity = 1.0f; } MOZ_ASSERT(!nsSVGUtils::CanOptimizeOpacity(frame) || !aParams.callerPaintsOpacity, "How can we be optimizing the opacity into the svg as well as having the caller paint it?"); /* 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(frame); nsSVGEffects::EffectProperties effectProperties = nsSVGEffects::GetEffectProperties(firstFrame); bool isOK = effectProperties.HasNoFilterOrHasValidFilter(); nsSVGClipPathFrame *clipPathFrame = effectProperties.GetClipPathFrame(&isOK); bool isTrivialClip = clipPathFrame ? clipPathFrame->IsTrivial() : true; gfxContext& context = aParams.ctx; DrawTarget* drawTarget = context.GetDrawTarget(); gfxContextMatrixAutoSaveRestore matrixAutoSaveRestore(&context); nsPoint firstFrameOffset = GetOffsetToBoundingBox(firstFrame); nsPoint offsetToBoundingBox = aParams.builder->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( frame->PresContext()->RoundAppUnitsToNearestDevPixels(offsetToBoundingBox.x), frame->PresContext()->RoundAppUnitsToNearestDevPixels(offsetToBoundingBox.y)); } // After applying only "offsetToBoundingBox", aCtx would have its origin at // the top left corner of frame'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(frame); 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, frame->PresContext()->AppUnitsPerDevPixel()); context.SetMatrix(context.CurrentMatrix().Translate(devPixelOffsetToUserSpace)); gfxMatrix cssPxToDevPxMatrix = GetCSSPxToDevPxMatrix(frame); const nsStyleSVGReset *svgReset = firstFrame->StyleSVGReset(); nsTArray<nsSVGMaskFrame *> maskFrames = effectProperties.GetMaskFrames(); // 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 <mask-sources> or <images>, 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. nsIDocument* currentDoc = frame->PresContext()->Document(); bool shouldGenerateMaskLayer = currentDoc->IsSVGDocument() ? maskFrames.Length() == 1 && maskFrames[0] : maskFrames.Length() > 0; // These are used if we require a temporary surface for a custom blend mode. RefPtr<gfxContext> target = &aParams.ctx; IntPoint targetOffset; bool complexEffects = false; /* 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) || frame->StyleEffects()->mMixBlendMode != NS_STYLE_BLEND_NORMAL || shouldGenerateMaskLayer) { complexEffects = true; context.Save(); nsRect clipRect = frame->GetVisualOverflowRectRelativeToSelf() + toUserSpace; context.Clip(NSRectToSnappedRect(clipRect, frame->PresContext()->AppUnitsPerDevPixel(), *drawTarget)); Matrix maskTransform; RefPtr<SourceSurface> maskSurface; if (shouldGenerateMaskLayer) { GenerateMaskSurface(aParams, opacity, firstFrame->StyleContext(), maskFrames, offsetToUserSpace, maskTransform, maskSurface); } if (shouldGenerateMaskLayer && !maskSurface) { // Entire surface is clipped out. context.Restore(); return; } if (frame->StyleEffects()->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(&context); context.SetMatrix(gfxMatrix()); clipRect = context.GetClipExtents(); } IntRect drawRect = RoundedOut(ToRect(clipRect)); RefPtr<DrawTarget> targetDT = context.GetDrawTarget()->CreateSimilarDrawTarget(drawRect.Size(), SurfaceFormat::B8G8R8A8); if (!targetDT || !targetDT->IsValid()) { context.Restore(); return; } target = gfxContext::CreateOrNull(targetDT); MOZ_ASSERT(target); // already checked the draw target above target->SetMatrix(context.CurrentMatrix() * gfxMatrix::Translation(-drawRect.TopLeft())); targetOffset = drawRect.TopLeft(); } if (clipPathFrame && !isTrivialClip) { Matrix clippedMaskTransform; RefPtr<SourceSurface> clipMaskSurface = clipPathFrame->GetClipMask(context, frame, cssPxToDevPxMatrix, &clippedMaskTransform, maskSurface, maskTransform); if (clipMaskSurface) { maskSurface = clipMaskSurface; maskTransform = clippedMaskTransform; } } if (opacity != 1.0f || shouldGenerateMaskLayer || (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) { context.Save(); clipPathFrame->ApplyClipPath(context, frame, cssPxToDevPxMatrix); } else if (!clipPathFrame && svgReset->HasClipPath()) { context.Save(); nsCSSClipPathInstance::ApplyBasicShapeClip(context, frame); } /* Paint the child */ if (effectProperties.HasValidFilter()) { RegularFramePaintCallback callback(aParams.builder, aParams.layerManager, offsetToUserSpace); nsRegion dirtyRegion = aParams.dirtyRect - offsetToBoundingBox; gfxMatrix tm = nsSVGIntegrationUtils::GetCSSPxToDevPxMatrix(frame); nsFilterInstance::PaintFilteredFrame(frame, target->GetDrawTarget(), tm, &callback, &dirtyRegion); } else { target->SetMatrix(matrixAutoSaveRestore.Matrix()); BasicLayerManager* basic = static_cast<BasicLayerManager*>(aParams.layerManager); RefPtr<gfxContext> oldCtx = basic->GetTarget(); basic->SetTarget(target); aParams.layerManager->EndTransaction(FrameLayerBuilder::DrawPaintedLayer, aParams.builder); basic->SetTarget(oldCtx); } if ((clipPathFrame && isTrivialClip) || (!clipPathFrame && svgReset->HasClipPath())) { context.Restore(); } /* No more effects, we're done. */ if (!complexEffects) { return; } if (opacity != 1.0f || shouldGenerateMaskLayer || (clipPathFrame && !isTrivialClip)) { target->PopGroupAndBlend(); } if (frame->StyleEffects()->mMixBlendMode != NS_STYLE_BLEND_NORMAL) { RefPtr<DrawTarget> targetDT = target->GetDrawTarget(); target = nullptr; RefPtr<SourceSurface> targetSurf = targetDT->Snapshot(); context.SetMatrix(gfxMatrix()); // This will be restored right after. RefPtr<gfxPattern> pattern = new gfxPattern(targetSurf, Matrix::Translation(targetOffset.x, targetOffset.y)); context.SetPattern(pattern); context.Paint(); } context.Restore(); }
void nsSVGIntegrationUtils::PaintMaskAndClipPath(const PaintFramesParams& aParams) { MOZ_ASSERT(UsingMaskOrClipPathForFrame(aParams.frame), "Should not use this method when no mask or clipPath effect" "on this frame"); /* SVG defines the following rendering model: * * 1. Render geometry * 2. Apply filter * 3. Apply clipping, masking, group opacity * * We handle #3 here and perform a couple of optimizations: * * + Use cairo's clipPath when representable natively (single object * clip region). * * + Merge opacity and masking if both used together. */ nsIFrame* frame = aParams.frame; if (!ValidateSVGFrame(frame)) { return; } nsSVGUtils::MaskUsage maskUsage; nsSVGUtils::DetermineMaskUsage(aParams.frame, aParams.handleOpacity, maskUsage); if (maskUsage.opacity == 0.0f) { return; } gfxContext& context = aParams.ctx; gfxContextMatrixAutoSaveRestore matrixAutoSaveRestore(&context); /* 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(frame); SVGObserverUtils::EffectProperties effectProperties = SVGObserverUtils::GetEffectProperties(firstFrame); nsSVGClipPathFrame *clipPathFrame = effectProperties.GetClipPathFrame(); gfxMatrix cssPxToDevPxMatrix = nsSVGUtils::GetCSSPxToDevPxMatrix(frame); nsTArray<nsSVGMaskFrame*> maskFrames = effectProperties.GetMaskFrames(); bool shouldGenerateMask = (maskUsage.opacity != 1.0f || 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) { gfxContextMatrixAutoSaveRestore matSR; Matrix maskTransform; RefPtr<SourceSurface> maskSurface; bool opacityApplied = false; if (maskUsage.shouldGenerateMaskLayer) { matSR.SetContext(&context); // For css-mask, we want to generate a mask for each continuation frame, // so we setup context matrix by the position of the current frame, // instead of the first continuation frame. EffectOffsets offsets = MoveContextOriginToUserSpace(frame, aParams); MaskPaintResult paintResult = CreateAndPaintMaskSurface(aParams, maskUsage.opacity, firstFrame->StyleContext(), maskFrames, offsets.offsetToUserSpace); if (paintResult.transparentBlackMask) { return; } maskSurface = paintResult.maskSurface; if (maskSurface) { shouldPushMask = true; maskTransform = paintResult.maskTransform; opacityApplied = paintResult.opacityApplied; } } if (maskUsage.shouldGenerateClipMaskLayer) { matSR.Restore(); matSR.SetContext(&context); MoveContextOriginToUserSpace(firstFrame, aParams); Matrix clipMaskTransform; RefPtr<SourceSurface> clipMaskSurface = clipPathFrame->GetClipMask(context, frame, cssPxToDevPxMatrix, &clipMaskTransform, maskSurface, maskTransform); if (clipMaskSurface) { maskSurface = clipMaskSurface; maskTransform = clipMaskTransform; } else { // Either entire surface is clipped out, or gfx buffer allocation // failure in nsSVGClipPathFrame::GetClipMask. return; } shouldPushMask = true; } // opacity != 1.0f. if (!maskUsage.shouldGenerateClipMaskLayer && !maskUsage.shouldGenerateMaskLayer) { MOZ_ASSERT(maskUsage.opacity != 1.0f); matSR.SetContext(&context); MoveContextOriginToUserSpace(firstFrame, aParams); shouldPushMask = true; } 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); } } } /* 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 (maskUsage.shouldApplyClipPath || maskUsage.shouldApplyBasicShape) { gfxContextMatrixAutoSaveRestore matSR(&context); MoveContextOriginToUserSpace(firstFrame, aParams); MOZ_ASSERT(!maskUsage.shouldApplyClipPath || !maskUsage.shouldApplyBasicShape); if (maskUsage.shouldApplyClipPath) { clipPathFrame->ApplyClipPath(context, frame, cssPxToDevPxMatrix); } else { nsCSSClipPathInstance::ApplyBasicShapeClip(context, frame); } } /* Paint the child */ context.SetMatrix(matrixAutoSaveRestore.Matrix()); BasicLayerManager* basic = aParams.layerManager->AsBasicLayerManager(); RefPtr<gfxContext> oldCtx = basic->GetTarget(); basic->SetTarget(&context); aParams.layerManager->EndTransaction(FrameLayerBuilder::DrawPaintedLayer, aParams.builder); basic->SetTarget(oldCtx); if (gfxPrefs::DrawMaskLayer()) { gfxContextAutoSaveRestore saver(&context); context.NewPath(); gfxRect drawingRect = nsLayoutUtils::RectToGfxRect(aParams.borderArea, frame->PresContext()->AppUnitsPerDevPixel()); context.Rectangle(drawingRect, true); Color overlayColor(0.0f, 0.0f, 0.0f, 0.8f); if (maskUsage.shouldGenerateMaskLayer) { overlayColor.r = 1.0f; // red represents css positioned mask. } if (maskUsage.shouldApplyClipPath || maskUsage.shouldGenerateClipMaskLayer) { overlayColor.g = 1.0f; // green represents clip-path:<clip-source>. } if (maskUsage.shouldApplyBasicShape) { overlayColor.b = 1.0f; // blue represents // clip-path:<basic-shape>||<geometry-box>. } context.SetColor(overlayColor); context.Fill(); } if (maskUsage.shouldApplyClipPath || maskUsage.shouldApplyBasicShape) { context.PopClip(); } if (shouldPushMask) { context.PopGroupAndBlend(); } }