diff --git a/layout/svg/base/src/nsSVGGeometryFrame.cpp b/layout/svg/base/src/nsSVGGeometryFrame.cpp index f32cb1c6912..6ed093c153f 100644 --- a/layout/svg/base/src/nsSVGGeometryFrame.cpp +++ b/layout/svg/base/src/nsSVGGeometryFrame.cpp @@ -333,6 +333,15 @@ SetupCairoColor(cairo_t *aCtx, nscolor aRGB, float aOpacity) aOpacity); } +float +nsSVGGeometryFrame::MaybeOptimizeOpacity(float aOpacity) +{ + if (nsSVGUtils::CanOptimizeOpacity(this)) { + aOpacity *= GetStyleDisplay()->mOpacity; + } + return aOpacity; +} + nsresult nsSVGGeometryFrame::SetupCairoFill(gfxContext *aContext, void **aClosure) @@ -345,21 +354,19 @@ nsSVGGeometryFrame::SetupCairoFill(gfxContext *aContext, else cairo_set_fill_rule(ctx, CAIRO_FILL_RULE_WINDING); + float opacity = MaybeOptimizeOpacity(GetStyleSVG()->mFillOpacity); + if (GetStateBits() & NS_STATE_SVG_FILL_PSERVER) { nsSVGPaintServerFrame *ps = NS_STATIC_CAST(nsSVGPaintServerFrame*, GetProperty(nsGkAtoms::fill)); - return ps->SetupPaintServer(ctx, this, - GetStyleSVG()->mFillOpacity, - aClosure); + return ps->SetupPaintServer(ctx, this, opacity, aClosure); } else if (GetStyleSVG()->mFill.mType == eStyleSVGPaintType_Server) { // should have a paint server but something has gone wrong configuring it. - SetupCairoColor(ctx, - sInvalidPaintColour, - GetStyleSVG()->mFillOpacity); + SetupCairoColor(ctx, sInvalidPaintColour, opacity); } else SetupCairoColor(ctx, GetStyleSVG()->mFill.mPaint.mColor, - GetStyleSVG()->mFillOpacity); + opacity); return NS_OK; } @@ -428,16 +435,16 @@ nsSVGGeometryFrame::SetupCairoStroke(gfxContext *aContext, SetupCairoStrokeHitGeometry(aCtx); + float opacity = MaybeOptimizeOpacity(GetStyleSVG()->mStrokeOpacity); + if (GetStateBits() & NS_STATE_SVG_STROKE_PSERVER) { nsSVGPaintServerFrame *ps = NS_STATIC_CAST(nsSVGPaintServerFrame*, GetProperty(nsGkAtoms::stroke)); - return ps->SetupPaintServer(aCtx, this, - GetStyleSVG()->mStrokeOpacity, - aClosure); + return ps->SetupPaintServer(aCtx, this, opacity, aClosure); } else SetupCairoColor(aCtx, GetStyleSVG()->mStroke.mPaint.mColor, - GetStyleSVG()->mStrokeOpacity); + opacity); return NS_OK; } diff --git a/layout/svg/base/src/nsSVGGeometryFrame.h b/layout/svg/base/src/nsSVGGeometryFrame.h index a70d6182865..fb94ee6bd88 100644 --- a/layout/svg/base/src/nsSVGGeometryFrame.h +++ b/layout/svg/base/src/nsSVGGeometryFrame.h @@ -115,6 +115,14 @@ private: nsresult GetStrokeDashArray(double **arr, PRUint32 *count); float GetStrokeDashoffset(); void RemovePaintServerProperties(); + + // Returns opacity that should be used in rendering this primitive. + // In the general case the return value is just the passed opacity. + // If we can avoid the expense of a specified group opacity, we + // multiply the passed opacity by the value of the 'opacity' + // property, and elsewhere pretend the 'opacity' property has a + // value of 1. + float MaybeOptimizeOpacity(float aOpacity); }; #endif // __NS_SVGGEOMETRYFRAME_H__ diff --git a/layout/svg/base/src/nsSVGImageFrame.cpp b/layout/svg/base/src/nsSVGImageFrame.cpp index 2776a2f69ee..4a94f2c6c11 100644 --- a/layout/svg/base/src/nsSVGImageFrame.cpp +++ b/layout/svg/base/src/nsSVGImageFrame.cpp @@ -291,8 +291,15 @@ nsSVGImageFrame::PaintSVG(nsSVGRenderState *aContext, nsRect *aDirtyRect) nsSVGUtils::SetClipRect(gfx, ctm, x, y, width, height); } - nsSVGUtils::CompositeSurfaceMatrix(gfx, thebesSurface, fini, - mStyleContext->GetStyleDisplay()->mOpacity); + // fill-opacity doesn't affect , 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 = GetStyleDisplay()->mOpacity; + } + + nsSVGUtils::CompositeSurfaceMatrix(gfx, thebesSurface, fini, opacity); if (GetStyleDisplay()->IsScrollableOverflow()) gfx->Restore(); diff --git a/layout/svg/base/src/nsSVGUtils.cpp b/layout/svg/base/src/nsSVGUtils.cpp index d61b354e6be..b29590e4425 100644 --- a/layout/svg/base/src/nsSVGUtils.cpp +++ b/layout/svg/base/src/nsSVGUtils.cpp @@ -626,6 +626,8 @@ nsSVGUtils::PaintChildWithEffects(nsSVGRenderState *aContext, return; float opacity = aFrame->GetStyleDisplay()->mOpacity; + if (opacity == 0.0f) + return; /* Properties are added lazily and may have been removed by a restyle, so make sure all applicable ones are set again. */ @@ -658,6 +660,9 @@ nsSVGUtils::PaintChildWithEffects(nsSVGRenderState *aContext, * + Merge opacity and masking if both used together. */ + if (opacity != 1.0 && nsSVGUtils::CanOptimizeOpacity(aFrame)) + opacity = 1.0; + gfxContext *gfx = aContext->GetGfxContext(); cairo_t *ctx = nsnull; @@ -1039,6 +1044,22 @@ nsSVGUtils::SetClipRect(gfxContext *aContext, aContext->SetMatrix(oldMatrix); } +PRBool +nsSVGUtils::CanOptimizeOpacity(nsIFrame *aFrame) +{ + if (!(aFrame->GetStateBits() & NS_STATE_SVG_FILTERED)) { + nsIAtom *type = aFrame->GetType(); + if (type == nsGkAtoms::svgImageFrame) + return PR_TRUE; + if (type == nsGkAtoms::svgPathGeometryFrame) { + nsSVGGeometryFrame *geom = NS_STATIC_CAST(nsSVGGeometryFrame*, aFrame); + if (!(geom->HasFill() && geom->HasStroke())) + return PR_TRUE; + } + } + return PR_FALSE; +} + // ---------------------------------------------------------------------- nsSVGRenderState::nsSVGRenderState(nsIRenderingContext *aContext) : diff --git a/layout/svg/base/src/nsSVGUtils.h b/layout/svg/base/src/nsSVGUtils.h index 2bd672cc9bc..470d9d25aea 100644 --- a/layout/svg/base/src/nsSVGUtils.h +++ b/layout/svg/base/src/nsSVGUtils.h @@ -323,6 +323,14 @@ public: nsIDOMSVGMatrix *aCTM, float aX, float aY, float aWidth, float aHeight); + /* Using group opacity instead of fill or stroke opacity on a + * geometry object seems to be a common authoring mistake. If we're + * not applying filters and not both stroking and filling, we can + * generate the same result without going through the overhead of a + * push/pop group. */ + static PRBool + CanOptimizeOpacity(nsIFrame *aFrame); + private: /* Computational (nil) surfaces */ static cairo_surface_t *mCairoComputationalSurface;