Bug 929364 - Avoid changing the current transform between emitting the path and clipping using that path in the SVGAutoRenderState::CLIP case. r=heycam
authorJonathan Watt <jwatt@jwatt.org>
Tue, 22 Oct 2013 23:45:51 +0200
changeset 151673 b011488de9e6e3c4fbf4acfd1fd582addcb7a9d1
parent 151672 ce0759a746fb526db7044b7ab1096e2c08823200
child 151674 a8680e083efa3aff99342f19b6bfbcf62c6ae0fb
push id35280
push userjwatt@jwatt.org
push dateTue, 22 Oct 2013 21:46:30 +0000
treeherdermozilla-inbound@39f4bb8c55d8 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersheycam
bugs929364
milestone27.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 929364 - Avoid changing the current transform between emitting the path and clipping using that path in the SVGAutoRenderState::CLIP case. r=heycam
layout/svg/nsSVGPathGeometryFrame.cpp
--- a/layout/svg/nsSVGPathGeometryFrame.cpp
+++ b/layout/svg/nsSVGPathGeometryFrame.cpp
@@ -580,57 +580,83 @@ nsSVGPathGeometryFrame::Render(nsRenderi
   case NS_STYLE_SHAPE_RENDERING_CRISPEDGES:
     gfx->SetAntialiasMode(gfxContext::MODE_ALIASED);
     break;
   default:
     gfx->SetAntialiasMode(gfxContext::MODE_COVERAGE);
     break;
   }
 
-  /* save/restore the state so we don't screw up the xform */
-  gfx->Save();
-
-  GeneratePath(gfx, GetCanvasTM(FOR_PAINTING, aTransformRoot));
-
   if (renderMode != SVGAutoRenderState::NORMAL) {
     NS_ABORT_IF_FALSE(renderMode == SVGAutoRenderState::CLIP ||
                       renderMode == SVGAutoRenderState::CLIP_MASK,
                       "Unknown render mode");
-    gfx->Restore();
+
+    // In the case that |renderMode == SVGAutoRenderState::CLIP| then we don't
+    // use the path we generate here until further up the call stack when
+    // nsSVGClipPathFrame::Clip calls gfxContext::Clip. That's a problem for
+    // Moz2D which emits paths in user space (unlike cairo which emits paths in
+    // device space). gfxContext has hacks to deal with code changing the
+    // transform then using the current path when it is backed by Moz2D, but
+    // Moz2D itself does not since that would fundamentally go against its API.
+    // Therefore we do not want to Save()/Restore() the gfxContext here in the
+    // SVGAutoRenderState::CLIP case since that would block us from killing off
+    // gfxContext and using Moz2D directly. Not bothering to Save()/Restore()
+    // is actually okay, since we know that doesn't matter in the
+    // SVGAutoRenderState::CLIP case (at least for the current implementation).
+    gfxContextMatrixAutoSaveRestore autoSaveRestore;
+    if (renderMode != SVGAutoRenderState::CLIP) {
+      autoSaveRestore.SetContext(gfx);
+    }
+
+    GeneratePath(gfx, GetCanvasTM(FOR_PAINTING, aTransformRoot));
+
+    // We used to call gfx->Restore() here, since for the
+    // SVGAutoRenderState::CLIP case it is important to leave the fill rule
+    // that we set below untouched so that the value is still set when return
+    // to gfxContext::Clip() further up the call stack. Since we no longer
+    // call gfx->Save() in the SVGAutoRenderState::CLIP case we don't need to
+    // worry that autoSaveRestore will delay the Restore() call for the
+    // CLIP_MASK case until we exit this function.
+
+    gfxContext::FillRule oldFillRull = gfx->CurrentFillRule();
 
     if (GetClipRule() == NS_STYLE_FILL_RULE_EVENODD)
       gfx->SetFillRule(gfxContext::FILL_RULE_EVEN_ODD);
     else
       gfx->SetFillRule(gfxContext::FILL_RULE_WINDING);
 
     if (renderMode == SVGAutoRenderState::CLIP_MASK) {
       gfx->SetColor(gfxRGBA(1.0f, 1.0f, 1.0f, 1.0f));
       gfx->Fill();
+      gfx->SetFillRule(oldFillRull); // restore, but only for CLIP_MASK
       gfx->NewPath();
     }
 
     return;
   }
 
+  gfxContextAutoSaveRestore autoSaveRestore(gfx);
+
+  GeneratePath(gfx, GetCanvasTM(FOR_PAINTING, aTransformRoot));
+
   gfxTextContextPaint *contextPaint =
     (gfxTextContextPaint*)aContext->GetUserData(&gfxTextContextPaint::sUserDataKey);
 
   if ((aRenderComponents & eRenderFill) &&
       nsSVGUtils::SetupCairoFillPaint(this, gfx, contextPaint)) {
     gfx->Fill();
   }
 
   if ((aRenderComponents & eRenderStroke) &&
        nsSVGUtils::SetupCairoStroke(this, gfx, contextPaint)) {
     gfx->Stroke();
   }
 
   gfx->NewPath();
-
-  gfx->Restore();
 }
 
 void
 nsSVGPathGeometryFrame::GeneratePath(gfxContext* aContext,
                                      const gfxMatrix &aTransform)
 {
   if (aTransform.IsSingular()) {
     aContext->IdentityMatrix();