--- a/layout/svg/nsSVGUtils.cpp
+++ b/layout/svg/nsSVGUtils.cpp
@@ -575,34 +575,28 @@ nsSVGUtils::PaintFrameWithEffects(nsIFra
aFrame->PresContext()->IsGlyph(),
"If display lists are enabled, only painting of non-display "
"SVG should take this code path");
nsISVGChildFrame *svgChildFrame = do_QueryFrame(aFrame);
if (!svgChildFrame)
return DrawResult::SUCCESS;
- float opacity = aFrame->StyleEffects()->mOpacity;
- if (opacity == 0.0f)
+ MaskUsage maskUsage;
+ DetermineMaskUsage(aFrame, true, maskUsage);
+ if (maskUsage.opacity == 0.0f) {
return DrawResult::SUCCESS;
+ }
const nsIContent* content = aFrame->GetContent();
if (content->IsSVGElement() &&
!static_cast<const nsSVGElement*>(content)->HasValidDimensions()) {
return DrawResult::SUCCESS;
}
- /* Properties are added lazily and may have been removed by a restyle,
- so make sure all applicable ones are set again. */
-
- nsSVGEffects::EffectProperties effectProperties =
- nsSVGEffects::GetEffectProperties(aFrame);
-
- bool isOK = effectProperties.HasNoFilterOrHasValidFilter();
-
if (aDirtyRect &&
!(aFrame->GetStateBits() & NS_FRAME_IS_NONDISPLAY)) {
// Here we convert aFrame's paint bounds to outer-<svg> device space,
// compare it to aDirtyRect, and return early if they don't intersect.
// We don't do this optimization for nondisplay SVG since nondisplay
// SVG doesn't maintain bounds/overflow rects.
nsRect overflowRect = aFrame->GetVisualOverflowRectRelativeToSelf();
if (aFrame->IsFrameOfType(nsIFrame::eSVGGeometry) ||
@@ -643,69 +637,69 @@ nsSVGUtils::PaintFrameWithEffects(nsIFra
* We follow this, but perform a couple of optimizations:
*
* + Use cairo's clipPath when representable natively (single object
* clip region).
*f
* + Merge opacity and masking if both used together.
*/
- if (opacity != 1.0f && CanOptimizeOpacity(aFrame))
- opacity = 1.0f;
-
- DrawTarget* drawTarget = aContext.GetDrawTarget();
- bool complexEffects = false;
-
+ /* Properties are added lazily and may have been removed by a restyle,
+ so make sure all applicable ones are set again. */
+ nsSVGEffects::EffectProperties effectProperties =
+ nsSVGEffects::GetEffectProperties(aFrame);
+ bool isOK = effectProperties.HasNoFilterOrHasValidFilter();
nsSVGClipPathFrame *clipPathFrame = effectProperties.GetClipPathFrame(&isOK);
nsSVGMaskFrame *maskFrame = effectProperties.GetFirstMaskFrame(&isOK);
-
- bool isTrivialClip = clipPathFrame ? clipPathFrame->IsTrivial() : true;
-
if (!isOK) {
// Some resource is invalid. We shouldn't paint anything.
return DrawResult::SUCCESS;
}
// 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 || maskFrame || (clipPathFrame && !isTrivialClip)
- || aFrame->StyleEffects()->mMixBlendMode != NS_STYLE_BLEND_NORMAL) {
- complexEffects = true;
+ bool shouldGenerateMask = (maskUsage.opacity != 1.0f ||
+ maskUsage.shouldGenerateClipMaskLayer ||
+ maskUsage.shouldGenerateMaskLayer ||
+ aFrame->StyleEffects()->mMixBlendMode != NS_STYLE_BLEND_NORMAL);
+ if (shouldGenerateMask) {
Matrix maskTransform;
- RefPtr<SourceSurface> maskSurface =
- maskFrame ? maskFrame->GetMaskForMaskedFrame(&aContext,
- aFrame, aTransform, opacity, &maskTransform)
- : nullptr;
+ RefPtr<SourceSurface> maskSurface;
- if (maskFrame && !maskSurface) {
- // Entire surface is clipped out.
- return DrawResult::SUCCESS;
+ if (maskUsage.shouldGenerateMaskLayer) {
+ maskSurface =
+ maskFrame->GetMaskForMaskedFrame(&aContext, aFrame, aTransform,
+ maskUsage.opacity, &maskTransform);
+
+ if (!maskSurface) {
+ // Entire surface is clipped out.
+ return DrawResult::SUCCESS;
+ }
}
- aContext.Save();
if (!(aFrame->GetStateBits() & NS_FRAME_IS_NONDISPLAY)) {
// aFrame has a valid visual overflow rect, so clip to it before calling
// PushGroup() to minimize the size of the surfaces we'll composite:
gfxContextMatrixAutoSaveRestore matrixAutoSaveRestore(&aContext);
aContext.Multiply(aTransform);
nsRect overflowRect = aFrame->GetVisualOverflowRectRelativeToSelf();
if (aFrame->IsFrameOfType(nsIFrame::eSVGGeometry) ||
aFrame->IsSVGText()) {
// Unlike containers, leaf frames do not include GetPosition() in
// GetCanvasTM().
overflowRect = overflowRect + aFrame->GetPosition();
}
aContext.Clip(NSRectToSnappedRect(overflowRect,
aFrame->PresContext()->AppUnitsPerDevPixel(),
- *drawTarget));
+ *aContext.GetDrawTarget()));
}
if (aFrame->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(&aContext);
@@ -721,42 +715,45 @@ nsSVGUtils::PaintFrameWithEffects(nsIFra
if (!target) {
gfxDevCrash(LogReason::InvalidContext) << "SVGPaintWithEffects context problem " << gfx::hexa(targetDT);
return DrawResult::TEMPORARY_ERROR;
}
target->SetMatrix(aContext.CurrentMatrix() * gfxMatrix::Translation(-drawRect.TopLeft()));
targetOffset = drawRect.TopLeft();
}
- if (clipPathFrame && !isTrivialClip) {
+ if (maskUsage.shouldGenerateClipMaskLayer) {
Matrix clippedMaskTransform;
- RefPtr<SourceSurface> clipMaskSurface = clipPathFrame->GetClipMask(aContext, aFrame, aTransform,
- &clippedMaskTransform, maskSurface, maskTransform);
+ RefPtr<SourceSurface> clipMaskSurface =
+ clipPathFrame->GetClipMask(aContext, aFrame, aTransform,
+ &clippedMaskTransform, maskSurface,
+ maskTransform);
if (clipMaskSurface) {
maskSurface = clipMaskSurface;
maskTransform = clippedMaskTransform;
}
}
- if (maskFrame) {
- target->PushGroupForBlendBack(gfxContentType::COLOR_ALPHA, 1.0,
- maskSurface, maskTransform);
- } else if (opacity != 1.0f || (clipPathFrame && !isTrivialClip)) {
- target->PushGroupForBlendBack(gfxContentType::COLOR_ALPHA, opacity,
- maskSurface, maskTransform);
- }
+ // SVG mask multiply opacity into maskSurface already, so we do not bother
+ // to apply opacity again.
+ float opacity = maskFrame ? 1.0 : maskUsage.opacity;
+ 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, aTransform);
+ if (maskUsage.shouldApplyClipPath || maskUsage.shouldApplyBasicShape) {
+ if (maskUsage.shouldApplyClipPath) {
+ clipPathFrame->ApplyClipPath(aContext, aFrame, aTransform);
+ } else {
+ nsCSSClipPathInstance::ApplyBasicShapeClip(aContext, aFrame);
+ }
}
DrawResult result = DrawResult::SUCCESS;
/* Paint the child */
if (effectProperties.HasValidFilter()) {
nsRegion* dirtyRegion = nullptr;
nsRegion tmpDirtyRegion;
@@ -781,40 +778,35 @@ nsSVGUtils::PaintFrameWithEffects(nsIFra
SVGPaintCallback paintCallback;
nsFilterInstance::PaintFilteredFrame(aFrame, target->GetDrawTarget(),
aTransform, &paintCallback,
dirtyRegion);
} else {
result = svgChildFrame->PaintSVG(*target, aTransform, aDirtyRect);
}
- if (clipPathFrame && isTrivialClip) {
- aContext.Restore();
+ if (maskUsage.shouldApplyClipPath || maskUsage.shouldApplyBasicShape) {
+ aContext.PopClip();
}
- /* No more effects, we're done. */
- if (!complexEffects)
- return result;
-
- if (opacity != 1.0f || maskFrame || (clipPathFrame && !isTrivialClip)) {
+ if (shouldGenerateMask) {
target->PopGroupAndBlend();
}
if (aFrame->StyleEffects()->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();
return result;
}
bool
nsSVGUtils::HitTestClip(nsIFrame *aFrame, const gfxPoint &aPoint)
{
nsSVGEffects::EffectProperties props =
nsSVGEffects::GetEffectProperties(aFrame);