Bug 1074294, part 2 - Convert nsSVGPathGeometryFrame::Render() to render directly using the Moz2D DrawTarget. r=longsonr
authorJonathan Watt <jwatt@jwatt.org>
Tue, 30 Sep 2014 18:08:13 +0100
changeset 207964 cd87b4cb26c105ba4305f5e5430f3812de50bee0
parent 207963 7879734700ce9d78f2f2d3099ec99e11032bb8df
child 207965 9647b50b12afb14a8e640d661a5a8dd0cf3b3099
push id49822
push userjwatt@jwatt.org
push dateTue, 30 Sep 2014 17:08:54 +0000
treeherdermozilla-inbound@0ade54570f25 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerslongsonr
bugs1074294
milestone35.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 1074294, part 2 - Convert nsSVGPathGeometryFrame::Render() to render directly using the Moz2D DrawTarget. r=longsonr
content/svg/content/src/SVGContentUtils.cpp
content/svg/content/src/SVGContentUtils.h
layout/reftests/svg/reftest.list
layout/svg/nsSVGPathGeometryFrame.cpp
layout/svg/nsSVGPathGeometryFrame.h
--- a/content/svg/content/src/SVGContentUtils.cpp
+++ b/content/svg/content/src/SVGContentUtils.cpp
@@ -178,19 +178,19 @@ SVGContentUtils::GetStrokeOptions(AutoSt
   DashState dashState =
     GetStrokeDashData(aStrokeOptions, aElement, styleSVG, aContextPaint);
 
   if (dashState == eNoStroke) {
     // Hopefully this will shortcircuit any stroke operations:
     aStrokeOptions->mLineWidth = 0;
     return;
   }
-  if (dashState == eContinuousStroke) {
-    // Prevent our caller from wasting time looking at the dash array:
-    aStrokeOptions->mDashLength = 0;
+  if (dashState == eContinuousStroke && aStrokeOptions->mDashPattern) {
+    // Prevent our caller from wasting time looking at a pattern without gaps:
+    aStrokeOptions->DiscardDashPattern();
   }
 
   aStrokeOptions->mLineWidth =
     GetStrokeWidth(aElement, styleContext, aContextPaint);
 
   aStrokeOptions->mMiterLimit = Float(styleSVG->mStrokeMiterlimit);
 
   switch (styleSVG->mStrokeLinejoin) {
--- a/content/svg/content/src/SVGContentUtils.h
+++ b/content/svg/content/src/SVGContentUtils.h
@@ -111,16 +111,23 @@ public:
         mDashPattern = mSmallArray;
         return mSmallArray;
       }
       static const mozilla::fallible_t fallible = mozilla::fallible_t();
       Float* nonConstArray = new (fallible) Float[aDashCount];
       mDashPattern = nonConstArray;
       return nonConstArray;
     }
+    void DiscardDashPattern() {
+      if (mDashPattern && mDashPattern != mSmallArray) {
+        delete [] mDashPattern;
+      }
+      mDashLength = 0;
+      mDashPattern = nullptr;
+    }
   private:
     // Most dasharrays will fit in this and save us allocating
     Float mSmallArray[16];
   };
 
   static void GetStrokeOptions(AutoStrokeOptions* aStrokeOptions,
                                nsSVGElement* aElement,
                                nsStyleContext* aStyleContext,
--- a/layout/reftests/svg/reftest.list
+++ b/layout/reftests/svg/reftest.list
@@ -321,17 +321,17 @@ fuzzy-if(OSX==10.7,6,2) fuzzy-if(OSX==10
 == text-layout-05.svg text-layout-05-ref.svg
 fuzzy-if(cocoaWidget&&layersGPUAccelerated,1,3) == text-layout-06.svg text-layout-06-ref.svg
 == text-layout-07.svg text-layout-07-ref.svg
 == text-layout-08.svg text-layout-08-ref.svg
 == text-scale-01.svg text-scale-01-ref.svg
 HTTP(..) == text-scale-02.svg text-scale-02-ref.svg
 HTTP(..) == text-scale-03.svg text-scale-03-ref.svg
 == text-stroke-scaling-01.svg text-stroke-scaling-01-ref.svg
-fails-if(OSX==10.8) == stroke-dasharray-01.svg stroke-dasharray-01-ref.svg # bug 896487
+== stroke-dasharray-01.svg stroke-dasharray-01-ref.svg
 == stroke-dasharray-02.svg pass.svg
 == stroke-dasharray-and-pathLength-01.svg pass.svg
 == stroke-dasharray-and-text-01.svg stroke-dasharray-and-text-01-ref.svg
 == stroke-dashoffset-01.svg pass.svg
 == stroke-linecap-square-w-zero-length-segs-01.svg pass.svg
 == stroke-linecap-square-w-zero-length-segs-02.svg pass.svg
 == textPath-01.svg textPath-01-ref.svg
 == textPath-02.svg pass.svg
--- a/layout/svg/nsSVGPathGeometryFrame.cpp
+++ b/layout/svg/nsSVGPathGeometryFrame.cpp
@@ -7,16 +7,17 @@
 #include "nsSVGPathGeometryFrame.h"
 
 // Keep others in (case-insensitive) order:
 #include "gfx2DGlue.h"
 #include "gfxContext.h"
 #include "gfxPlatform.h"
 #include "gfxSVGGlyphs.h"
 #include "mozilla/gfx/2D.h"
+#include "mozilla/gfx/Helpers.h"
 #include "mozilla/RefPtr.h"
 #include "nsDisplayList.h"
 #include "nsGkAtoms.h"
 #include "nsLayoutUtils.h"
 #include "nsRenderingContext.h"
 #include "nsSVGEffects.h"
 #include "nsSVGIntegrationUtils.h"
 #include "nsSVGMarkerFrame.h"
@@ -102,21 +103,19 @@ nsDisplaySVGPathGeometry::Paint(nsDispla
   // ToReferenceFrame includes our mRect offset, but painting takes
   // account of that too. To avoid double counting, we subtract that
   // here.
   nsPoint offset = ToReferenceFrame() - mFrame->GetPosition();
 
   gfxPoint devPixelOffset =
     nsLayoutUtils::PointToGfxPoint(offset, appUnitsPerDevPixel);
 
-  aCtx->ThebesContext()->Save();
   gfxMatrix tm = nsSVGIntegrationUtils::GetCSSPxToDevPxMatrix(mFrame) *
                    gfxMatrix::Translation(devPixelOffset);
   static_cast<nsSVGPathGeometryFrame*>(mFrame)->PaintSVG(aCtx, tm);
-  aCtx->ThebesContext()->Restore();
 }
 
 //----------------------------------------------------------------------
 // nsIFrame methods
 
 void
 nsSVGPathGeometryFrame::Init(nsIContent*       aContent,
                              nsContainerFrame* aParent,
@@ -214,30 +213,39 @@ nsSVGPathGeometryFrame::BuildDisplayList
 nsresult
 nsSVGPathGeometryFrame::PaintSVG(nsRenderingContext *aContext,
                                  const gfxMatrix& aTransform,
                                  const nsIntRect* aDirtyRect)
 {
   if (!StyleVisibility()->IsVisible())
     return NS_OK;
 
+  gfxContext* gfx = aContext->ThebesContext();
+
+  // Matrix to the geometry's user space:
+  gfxMatrix newMatrix =
+    gfx->CurrentMatrix().PreMultiply(aTransform).NudgeToIntegers();
+  if (newMatrix.IsSingular()) {
+    return NS_OK;
+  }
+
   uint32_t paintOrder = StyleSVG()->mPaintOrder;
   if (paintOrder == NS_STYLE_PAINT_ORDER_NORMAL) {
-    Render(aContext, eRenderFill | eRenderStroke, aTransform);
+    Render(gfx, eRenderFill | eRenderStroke, newMatrix);
     PaintMarkers(aContext, aTransform);
   } else {
     while (paintOrder) {
       uint32_t component =
         paintOrder & ((1 << NS_STYLE_PAINT_ORDER_BITWIDTH) - 1);
       switch (component) {
         case NS_STYLE_PAINT_ORDER_FILL:
-          Render(aContext, eRenderFill, aTransform);
+          Render(gfx, eRenderFill, newMatrix);
           break;
         case NS_STYLE_PAINT_ORDER_STROKE:
-          Render(aContext, eRenderStroke, aTransform);
+          Render(gfx, eRenderStroke, newMatrix);
           break;
         case NS_STYLE_PAINT_ORDER_MARKERS:
           PaintMarkers(aContext, aTransform);
           break;
       }
       paintOrder >>= NS_STYLE_PAINT_ORDER_BITWIDTH;
     }
   }
@@ -603,101 +611,103 @@ nsSVGPathGeometryFrame::MarkerProperties
 {
   if (!mMarkerEnd)
     return nullptr;
   return static_cast<nsSVGMarkerFrame *>
     (mMarkerEnd->GetReferencedFrame(nsGkAtoms::svgMarkerFrame, nullptr));
 }
 
 void
-nsSVGPathGeometryFrame::Render(nsRenderingContext *aContext,
+nsSVGPathGeometryFrame::Render(gfxContext* aContext,
                                uint32_t aRenderComponents,
-                               const gfxMatrix& aTransform)
+                               const gfxMatrix& aNewTransform)
 {
-  gfxContext *gfx = aContext->ThebesContext();
+  MOZ_ASSERT(!aNewTransform.IsSingular());
+
+  DrawTarget* drawTarget = aContext->GetDrawTarget();
 
-  gfxMatrix newMatrix =
-    gfx->CurrentMatrix().PreMultiply(aTransform).NudgeToIntegers();
-  if (newMatrix.IsSingular()) {
-    return;
-  }
+  uint16_t renderMode = SVGAutoRenderState::GetRenderMode(drawTarget);
+  MOZ_ASSERT(renderMode == SVGAutoRenderState::NORMAL ||
+             renderMode == SVGAutoRenderState::CLIP_MASK,
+             "Unknown render mode");
 
-  uint16_t renderMode = SVGAutoRenderState::GetRenderMode(aContext->GetDrawTarget());
   FillRule fillRule =
-    nsSVGUtils::ToFillRule(renderMode == SVGAutoRenderState::CLIP_MASK ?
-                             StyleSVG()->mClipRule : StyleSVG()->mFillRule);
+    nsSVGUtils::ToFillRule(renderMode == SVGAutoRenderState::NORMAL ?
+                             StyleSVG()->mFillRule : StyleSVG()->mClipRule);
 
-  RefPtr<PathBuilder> builder =
-    aContext->GetDrawTarget()->CreatePathBuilder(fillRule);
+  RefPtr<PathBuilder> builder = drawTarget->CreatePathBuilder(fillRule);
   if (!builder) {
     return;
   }
 
   RefPtr<Path> path =
     static_cast<nsSVGPathGeometryElement*>(mContent)->BuildPath(builder);
   if (!path) {
     return;
   }
 
-  switch (StyleSVG()->mShapeRendering) {
-  case NS_STYLE_SHAPE_RENDERING_OPTIMIZESPEED:
-  case NS_STYLE_SHAPE_RENDERING_CRISPEDGES:
-    gfx->SetAntialiasMode(AntialiasMode::NONE);
-    break;
-  default:
-    gfx->SetAntialiasMode(AntialiasMode::SUBPIXEL);
-    break;
-  }
+  AntialiasMode aaMode =
+    (StyleSVG()->mShapeRendering == NS_STYLE_SHAPE_RENDERING_OPTIMIZESPEED ||
+     StyleSVG()->mShapeRendering == NS_STYLE_SHAPE_RENDERING_CRISPEDGES) ?
+    AntialiasMode::NONE : AntialiasMode::SUBPIXEL;
+
+  // We wait as late as possible before setting the transform so that we don't
+  // set it unnecessarily if we return early (it's an expensive operation for
+  // some backends).
+  gfxContextMatrixAutoSaveRestore autoRestoreTransform(aContext);
+  aContext->SetMatrix(aNewTransform);
 
   if (renderMode == SVGAutoRenderState::CLIP_MASK) {
-    FillRule oldFillRule = gfx->CurrentFillRule();
-    gfxContextMatrixAutoSaveRestore autoSaveRestore(gfx);
-
-    gfx->SetMatrix(newMatrix);
-    gfx->SetFillRule(fillRule);
-    gfx->SetColor(gfxRGBA(1.0f, 1.0f, 1.0f, 1.0f));
-    gfx->SetPath(path);
-    gfx->Fill();
-
-    gfx->SetFillRule(oldFillRule);
-    gfx->NewPath();
+    drawTarget->Fill(path, ColorPattern(Color(1.0f, 1.0f, 1.0f, 1.0f)),
+                     DrawOptions(1.0f, CompositionOp::OP_OVER, aaMode));
     return;
   }
 
-  NS_ABORT_IF_FALSE(renderMode == SVGAutoRenderState::NORMAL,
-                    "Unknown render mode");
-
-  gfxContextAutoSaveRestore autoSaveRestore(gfx);
-  gfx->SetMatrix(newMatrix);
+  gfxTextContextPaint *contextPaint =
+    (gfxTextContextPaint*)drawTarget->
+      GetUserData(&gfxTextContextPaint::sUserDataKey);
 
-  gfxTextContextPaint *contextPaint =
-    (gfxTextContextPaint*)aContext->GetDrawTarget()->GetUserData(&gfxTextContextPaint::sUserDataKey);
-
-  if ((aRenderComponents & eRenderFill)) {
+  if (aRenderComponents & eRenderFill) {
     GeneralPattern fillPattern;
-    nsSVGUtils::MakeFillPatternFor(this, gfx, &fillPattern, contextPaint);
+    nsSVGUtils::MakeFillPatternFor(this, aContext, &fillPattern, contextPaint);
     if (fillPattern.GetPattern()) {
-      gfx->SetPath(path);
-      gfx->SetFillRule(fillRule);
-      gfx->Fill(fillPattern);
+      drawTarget->Fill(path, fillPattern,
+                       DrawOptions(1.0f, CompositionOp::OP_OVER, aaMode));
     }
   }
 
   if ((aRenderComponents & eRenderStroke) &&
       nsSVGUtils::HasStroke(this, contextPaint)) {
+    // Account for vector-effect:non-scaling-stroke:
+    gfxMatrix strokeTransform = nsSVGUtils::GetStrokeTransform(this);
+    if (!strokeTransform.IsIdentity()) {
+      // We need to transform the path back into the appropriate ancestor
+      // coordinate system, and paint it it that coordinate system, in order
+      // for non-scaled stroke to paint correctly.
+      aContext->Multiply(strokeTransform);
+      Matrix moz2DstrokeTransform = ToMatrix(strokeTransform); // inverse of Thebes
+      moz2DstrokeTransform.Invert();
+      builder = path->TransformedCopyToBuilder(moz2DstrokeTransform, fillRule);
+      path = builder->Finish();
+    }
     GeneralPattern strokePattern;
-    nsSVGUtils::MakeStrokePatternFor(this, gfx, &strokePattern, contextPaint);
+    nsSVGUtils::MakeStrokePatternFor(this, aContext, &strokePattern, contextPaint);
     if (strokePattern.GetPattern()) {
-      gfx->SetPath(path);
-      nsSVGUtils::SetupCairoStrokeGeometry(this, gfx, contextPaint);
-      gfx->Stroke(strokePattern);
+      SVGContentUtils::AutoStrokeOptions strokeOptions;
+      SVGContentUtils::GetStrokeOptions(&strokeOptions,
+                                        static_cast<nsSVGElement*>(mContent),
+                                        StyleContext(), contextPaint);
+      // GetStrokeOptions may set the line width to zero as an optimization
+      if (strokeOptions.mLineWidth <= 0) {
+        return;
+      }
+      drawTarget->Stroke(path, strokePattern, strokeOptions,
+                         DrawOptions(1.0f, CompositionOp::OP_OVER, aaMode));
     }
   }
-
-  gfx->NewPath();
 }
 
 void
 nsSVGPathGeometryFrame::GeneratePath(gfxContext* aContext,
                                      const Matrix &aTransform)
 {
   if (aTransform.IsSingular()) {
     aContext->SetMatrix(gfxMatrix());
--- a/layout/svg/nsSVGPathGeometryFrame.h
+++ b/layout/svg/nsSVGPathGeometryFrame.h
@@ -10,16 +10,22 @@
 #include "gfxMatrix.h"
 #include "gfxRect.h"
 #include "nsFrame.h"
 #include "nsISVGChildFrame.h"
 #include "nsLiteralString.h"
 #include "nsQueryFrame.h"
 #include "nsSVGUtils.h"
 
+namespace mozilla {
+namespace gfx {
+class DrawTarget;
+}
+}
+
 class gfxContext;
 class nsDisplaySVGPathGeometry;
 class nsIAtom;
 class nsIFrame;
 class nsIPresShell;
 class nsRenderingContext;
 class nsStyleContext;
 class nsSVGMarkerFrame;
@@ -29,16 +35,18 @@ struct nsPoint;
 struct nsRect;
 struct nsIntRect;
 
 typedef nsFrame nsSVGPathGeometryFrameBase;
 
 class nsSVGPathGeometryFrame : public nsSVGPathGeometryFrameBase,
                                public nsISVGChildFrame
 {
+  typedef mozilla::gfx::DrawTarget DrawTarget;
+
   friend nsIFrame*
   NS_NewSVGPathGeometryFrame(nsIPresShell* aPresShell, nsStyleContext* aContext);
 
   friend class nsDisplaySVGPathGeometry;
 
 protected:
   explicit nsSVGPathGeometryFrame(nsStyleContext* aContext)
     : nsSVGPathGeometryFrameBase(aContext)
@@ -108,17 +116,17 @@ protected:
    * This function returns a set of bit flags indicating which parts of the
    * element (fill, stroke, bounds) should intercept pointer events. It takes
    * into account the type of element and the value of the 'pointer-events'
    * property on the element.
    */
   virtual uint16_t GetHitTestFlags();
 private:
   enum { eRenderFill = 1, eRenderStroke = 2 };
-  void Render(nsRenderingContext *aContext, uint32_t aRenderComponents,
+  void Render(gfxContext* aContext, uint32_t aRenderComponents,
               const gfxMatrix& aTransform);
 
   /**
    * @param aMatrix The transform that must be multiplied onto aContext to
    *   establish this frame's SVG user space.
    */
   void PaintMarkers(nsRenderingContext *aContext, const gfxMatrix& aMatrix);