Have ShadowTessellators transform path; add SkShadowTessellator base class

BUG=skia:6119

Change-Id: I37639ebab43c9f32f48d2d7dbb8d4619efb9b09e
Reviewed-on: https://skia-review.googlesource.com/8061
Commit-Queue: Jim Van Verth <jvanverth@google.com>
Reviewed-by: Robert Phillips <robertphillips@google.com>
diff --git a/src/utils/SkShadowTessellator.cpp b/src/utils/SkShadowTessellator.cpp
index a0a011f..b721f38 100755
--- a/src/utils/SkShadowTessellator.cpp
+++ b/src/utils/SkShadowTessellator.cpp
@@ -16,12 +16,14 @@
 
 template <typename T> using UniqueArray = SkShadowVertices::UniqueArray<T>;
 
-// TODO: derive the ambient and spot classes from a base class containing common elements
-
-class SkAmbientShadowTessellator {
+/**
+ * Base class
+ */
+class SkShadowTessellator {
 public:
-    SkAmbientShadowTessellator(const SkPath& path, SkScalar radius, SkColor umbraColor,
-                               SkColor penumbraColor, bool transparent);
+    SkShadowTessellator(SkScalar radius, SkColor umbraColor,
+                        SkColor penumbraColor, bool transparent);
+    virtual ~SkShadowTessellator() {}
 
     int vertexCount() const { return fPositions.count(); }
     int indexCount() const { return fIndices.count(); }
@@ -40,41 +42,45 @@
         return UniqueArray<uint16_t>(static_cast<const uint16_t*>(fIndices.release()));
     }
 
-private:
-    void handleLine(const SkPoint& p);
+protected:
+    virtual void handleLine(const SkPoint& p) = 0;
+    void handleLine(const SkMatrix& m, SkPoint* p);
 
     void handleQuad(const SkPoint pts[3]);
+    void handleQuad(const SkMatrix& m, SkPoint pts[3]);
 
-    void handleCubic(SkPoint pts[4]);
+    void handleCubic(const SkMatrix& m, SkPoint pts[4]);
 
-    void handleConic(SkPoint pts[3], SkScalar w);
+    void handleConic(const SkMatrix& m, SkPoint pts[3], SkScalar w);
 
     void addArc(const SkVector& nextNormal);
     void finishArcAndAddEdge(const SkVector& nextPoint, const SkVector& nextNormal);
-    void addEdge(const SkVector& nextPoint, const SkVector& nextNormal);
-
-    SkScalar            fRadius;
-    SkColor             fUmbraColor;
-    SkColor             fPenumbraColor;
-    bool                fTransparent;
-
-    SkTDArray<SkPoint>  fPositions;
-    SkTDArray<SkColor>  fColors;
-    SkTDArray<uint16_t> fIndices;
-
-    int                 fPrevUmbraIndex;
-    SkVector            fPrevNormal;
-    int                 fFirstVertex;
-    SkVector            fFirstNormal;
-    SkScalar            fDirection;
-    int                 fCentroidCount;
+    virtual void addEdge(const SkVector& nextPoint, const SkVector& nextNormal) = 0;
 
     // first three points
     SkTDArray<SkPoint>  fInitPoints;
     // temporary buffer
     SkTDArray<SkPoint>  fPointBuffer;
 
+    SkTDArray<SkPoint>  fPositions;
+    SkTDArray<SkColor>  fColors;
+    SkTDArray<uint16_t> fIndices;
+
+    int                 fFirstVertex;
+    SkVector            fFirstNormal;
+    SkPoint             fFirstPoint;
+
     bool                fSucceeded;
+    bool                fTransparent;
+
+    SkColor             fUmbraColor;
+    SkColor             fPenumbraColor;
+
+    SkScalar            fRadius;
+    SkScalar            fDirection;
+    int                 fPrevUmbraIndex;
+    SkVector            fPrevNormal;
+    SkPoint             fPrevPoint;
 };
 
 static bool compute_normal(const SkPoint& p0, const SkPoint& p1, SkScalar radius, SkScalar dir,
@@ -106,17 +112,151 @@
     *n = SkScalarFloorToInt(steps);
 }
 
+SkShadowTessellator::SkShadowTessellator(SkScalar radius, SkColor umbraColor,
+                                         SkColor penumbraColor, bool transparent)
+    : fFirstVertex(-1)
+    , fSucceeded(false)
+    , fTransparent(transparent)
+    , fUmbraColor(umbraColor)
+    , fPenumbraColor(penumbraColor)
+    , fRadius(radius)
+    , fDirection(1)
+    , fPrevUmbraIndex(-1) {
+
+    fInitPoints.setReserve(3);
+
+    // child classes will set reserve for positions, colors and indices
+}
+
+// tesselation tolerance values, in device space pixels
+static const SkScalar kQuadTolerance = 0.2f;
+static const SkScalar kCubicTolerance = 0.2f;
+static const SkScalar kConicTolerance = 0.5f;
+
+void SkShadowTessellator::handleLine(const SkMatrix& m, SkPoint* p) {
+    m.mapPoints(p, 1);
+    this->handleLine(*p);
+}
+
+void SkShadowTessellator::handleQuad(const SkPoint pts[3]) {
+#if SK_SUPPORT_GPU
+    // TODO: Pull PathUtils out of Ganesh?
+    int maxCount = GrPathUtils::quadraticPointCount(pts, kQuadTolerance);
+    fPointBuffer.setReserve(maxCount);
+    SkPoint* target = fPointBuffer.begin();
+    int count = GrPathUtils::generateQuadraticPoints(pts[0], pts[1], pts[2],
+                                                     kQuadTolerance, &target, maxCount);
+    fPointBuffer.setCount(count);
+    for (int i = 0; i < count; i++) {
+        this->handleLine(fPointBuffer[i]);
+    }
+#else
+    // for now, just to draw something
+    this->handleLine(pts[1]);
+    this->handleLine(pts[2]);
+#endif
+}
+
+void SkShadowTessellator::handleQuad(const SkMatrix& m, SkPoint pts[3]) {
+    m.mapPoints(pts, 3);
+    this->handleQuad(pts);
+}
+
+void SkShadowTessellator::handleCubic(const SkMatrix& m, SkPoint pts[4]) {
+    m.mapPoints(pts, 4);
+#if SK_SUPPORT_GPU
+    // TODO: Pull PathUtils out of Ganesh?
+    int maxCount = GrPathUtils::cubicPointCount(pts, kCubicTolerance);
+    fPointBuffer.setReserve(maxCount);
+    SkPoint* target = fPointBuffer.begin();
+    int count = GrPathUtils::generateCubicPoints(pts[0], pts[1], pts[2], pts[3],
+                                                 kCubicTolerance, &target, maxCount);
+    fPointBuffer.setCount(count);
+    for (int i = 0; i < count; i++) {
+        this->handleLine(fPointBuffer[i]);
+    }
+#else
+    // for now, just to draw something
+    this->handleLine(pts[1]);
+    this->handleLine(pts[2]);
+    this->handleLine(pts[3]);
+#endif
+}
+
+void SkShadowTessellator::handleConic(const SkMatrix& m, SkPoint pts[3], SkScalar w) {
+    m.mapPoints(pts, 3);
+    SkAutoConicToQuads quadder;
+    const SkPoint* quads = quadder.computeQuads(pts, w, kConicTolerance);
+    SkPoint lastPoint = *(quads++);
+    int count = quadder.countQuads();
+    for (int i = 0; i < count; ++i) {
+        SkPoint quadPts[3];
+        quadPts[0] = lastPoint;
+        quadPts[1] = quads[0];
+        quadPts[2] = i == count - 1 ? pts[2] : quads[1];
+        this->handleQuad(quadPts);
+        lastPoint = quadPts[2];
+        quads += 2;
+    }
+}
+
+void SkShadowTessellator::addArc(const SkVector& nextNormal) {
+    // fill in fan from previous quad
+    SkScalar rotSin, rotCos;
+    int numSteps;
+    compute_radial_steps(fPrevNormal, nextNormal, fRadius, &rotSin, &rotCos, &numSteps);
+    SkVector prevNormal = fPrevNormal;
+    for (int i = 0; i < numSteps; ++i) {
+        SkVector nextNormal;
+        nextNormal.fX = prevNormal.fX*rotCos - prevNormal.fY*rotSin;
+        nextNormal.fY = prevNormal.fY*rotCos + prevNormal.fX*rotSin;
+        *fPositions.push() = fPrevPoint + nextNormal;
+        *fColors.push() = fPenumbraColor;
+        *fIndices.push() = fPrevUmbraIndex;
+        *fIndices.push() = fPositions.count() - 2;
+        *fIndices.push() = fPositions.count() - 1;
+
+        prevNormal = nextNormal;
+    }
+}
+
+void SkShadowTessellator::finishArcAndAddEdge(const SkPoint& nextPoint,
+                                              const SkVector& nextNormal) {
+    // close out previous arc
+    *fPositions.push() = fPrevPoint + nextNormal;
+    *fColors.push() = fPenumbraColor;
+    *fIndices.push() = fPrevUmbraIndex;
+    *fIndices.push() = fPositions.count() - 2;
+    *fIndices.push() = fPositions.count() - 1;
+
+    this->addEdge(nextPoint, nextNormal);
+}
+
+
+//////////////////////////////////////////////////////////////////////////////////////////////////
+
+class SkAmbientShadowTessellator : public SkShadowTessellator {
+public:
+    SkAmbientShadowTessellator(const SkPath& path, const SkMatrix& ctm,
+                               SkScalar radius, SkColor umbraColor,
+                               SkColor penumbraColor, bool transparent);
+
+private:
+    void handleLine(const SkPoint& p) override;
+    void addEdge(const SkVector& nextPoint, const SkVector& nextNormal) override;
+
+    int                 fCentroidCount;
+
+    typedef SkShadowTessellator INHERITED;
+};
+
 SkAmbientShadowTessellator::SkAmbientShadowTessellator(const SkPath& path,
+                                                       const SkMatrix& ctm,
                                                        SkScalar radius,
                                                        SkColor umbraColor,
                                                        SkColor penumbraColor,
                                                        bool transparent)
-        : fRadius(radius)
-        , fUmbraColor(umbraColor)
-        , fPenumbraColor(penumbraColor)
-        , fTransparent(transparent)
-        , fPrevUmbraIndex(-1)
-        , fSucceeded(false) {
+        : INHERITED(radius, umbraColor, penumbraColor, transparent) {
     // Outer ring: 3*numPts
     // Middle ring: numPts
     fPositions.setReserve(4 * path.countPoints());
@@ -125,8 +265,6 @@
     // Middle ring: 0
     fIndices.setReserve(12 * path.countPoints());
 
-    fInitPoints.setReserve(3);
-
     // walk around the path, tessellate and generate outer ring
     // if original path is transparent, will accumulate sum of points for centroid
     SkPath::Iter iter(path, true);
@@ -140,16 +278,16 @@
     while ((verb = iter.next(pts)) != SkPath::kDone_Verb) {
         switch (verb) {
             case SkPath::kLine_Verb:
-                this->handleLine(pts[1]);
+                this->INHERITED::handleLine(ctm, &pts[1]);
                 break;
             case SkPath::kQuad_Verb:
-                this->handleQuad(pts);
+                this->handleQuad(ctm, pts);
                 break;
             case SkPath::kCubic_Verb:
-                this->handleCubic(pts);
+                this->handleCubic(ctm, pts);
                 break;
             case SkPath::kConic_Verb:
-                this->handleConic(pts, iter.conicWeight());
+                this->handleConic(ctm, pts, iter.conicWeight());
                 break;
             case SkPath::kMove_Verb:
             case SkPath::kClose_Verb:
@@ -163,19 +301,19 @@
     }
 
     SkVector normal;
-    if (compute_normal(fPositions[fPrevUmbraIndex], fPositions[fFirstVertex], fRadius, fDirection,
+    if (compute_normal(fPrevPoint, fFirstPoint, fRadius, fDirection,
                        &normal)) {
         this->addArc(normal);
 
         // close out previous arc
-        *fPositions.push() = fPositions[fPrevUmbraIndex] + normal;
+        *fPositions.push() = fPrevPoint + normal;
         *fColors.push() = fPenumbraColor;
         *fIndices.push() = fPrevUmbraIndex;
         *fIndices.push() = fPositions.count() - 2;
         *fIndices.push() = fPositions.count() - 1;
 
         // add final edge
-        *fPositions.push() = fPositions[fFirstVertex] + normal;
+        *fPositions.push() = fFirstPoint + normal;
         *fColors.push() = fPenumbraColor;
 
         *fIndices.push() = fPrevUmbraIndex;
@@ -200,6 +338,7 @@
     if (fPositions.count() >= 3) {
         fPrevUmbraIndex = fFirstVertex;
         fPrevNormal = normal;
+        fPrevPoint = fFirstPoint;
         this->addArc(fFirstNormal);
 
         *fIndices.push() = fFirstVertex;
@@ -209,11 +348,6 @@
     fSucceeded = true;
 }
 
-// tesselation tolerance values, in device space pixels
-static const SkScalar kQuadTolerance = 0.2f;
-static const SkScalar kCubicTolerance = 0.2f;
-static const SkScalar kConicTolerance = 0.5f;
-
 void SkAmbientShadowTessellator::handleLine(const SkPoint& p)  {
     if (fInitPoints.count() < 2) {
         *fInitPoints.push() = p;
@@ -242,8 +376,10 @@
             return;
         }
 
+        fFirstPoint = fInitPoints[0];
         fFirstVertex = fPositions.count();
         fPrevNormal = fFirstNormal;
+        fPrevPoint = fFirstPoint;
         fPrevUmbraIndex = fFirstVertex;
 
         *fPositions.push() = fInitPoints[0];
@@ -267,84 +403,6 @@
     }
 }
 
-void SkAmbientShadowTessellator::handleQuad(const SkPoint pts[3]) {
-#if SK_SUPPORT_GPU
-    // TODO: Pull PathUtils out of Ganesh?
-    int maxCount = GrPathUtils::quadraticPointCount(pts, kQuadTolerance);
-    fPointBuffer.setReserve(maxCount);
-    SkPoint* target = fPointBuffer.begin();
-    int count = GrPathUtils::generateQuadraticPoints(pts[0], pts[1], pts[2],
-                                                     kQuadTolerance, &target, maxCount);
-    fPointBuffer.setCount(count);
-    for (int i = 0; i < count; i++) {
-        this->handleLine(fPointBuffer[i]);
-    }
-#endif
-}
-
-void SkAmbientShadowTessellator::handleCubic(SkPoint pts[4]) {
-#if SK_SUPPORT_GPU
-    // TODO: Pull PathUtils out of Ganesh?
-    int maxCount = GrPathUtils::cubicPointCount(pts, kCubicTolerance);
-    fPointBuffer.setReserve(maxCount);
-    SkPoint* target = fPointBuffer.begin();
-    int count = GrPathUtils::generateCubicPoints(pts[0], pts[1], pts[2], pts[3],
-                                                 kCubicTolerance, &target, maxCount);
-    fPointBuffer.setCount(count);
-    for (int i = 0; i < count; i++) {
-        this->handleLine(fPointBuffer[i]);
-    }
-#endif
-}
-
-void SkAmbientShadowTessellator::handleConic(SkPoint pts[3], SkScalar w) {
-    SkAutoConicToQuads quadder;
-    const SkPoint* quads = quadder.computeQuads(pts, w, kConicTolerance);
-    SkPoint lastPoint = *(quads++);
-    int count = quadder.countQuads();
-    for (int i = 0; i < count; ++i) {
-        SkPoint quadPts[3];
-        quadPts[0] = lastPoint;
-        quadPts[1] = quads[0];
-        quadPts[2] = i == count - 1 ? pts[2] : quads[1];
-        this->handleQuad(quadPts);
-        lastPoint = quadPts[2];
-        quads += 2;
-    }
-}
-
-void SkAmbientShadowTessellator::addArc(const SkVector& nextNormal) {
-    // fill in fan from previous quad
-    SkScalar rotSin, rotCos;
-    int numSteps;
-    compute_radial_steps(fPrevNormal, nextNormal, fRadius, &rotSin, &rotCos, &numSteps);
-    SkVector prevNormal = fPrevNormal;
-    for (int i = 0; i < numSteps; ++i) {
-        SkVector nextNormal;
-        nextNormal.fX = prevNormal.fX*rotCos - prevNormal.fY*rotSin;
-        nextNormal.fY = prevNormal.fY*rotCos + prevNormal.fX*rotSin;
-        *fPositions.push() = fPositions[fPrevUmbraIndex] + nextNormal;
-        *fColors.push() = fPenumbraColor;
-        *fIndices.push() = fPrevUmbraIndex;
-        *fIndices.push() = fPositions.count() - 2;
-        *fIndices.push() = fPositions.count() - 1;
-
-        prevNormal = nextNormal;
-    }
-}
-
-void SkAmbientShadowTessellator::finishArcAndAddEdge(const SkPoint& nextPoint,
-                                                     const SkVector& nextNormal) {
-    // close out previous arc
-    *fPositions.push() = fPositions[fPrevUmbraIndex] + nextNormal;
-    *fColors.push() = fPenumbraColor;
-    *fIndices.push() = fPrevUmbraIndex;
-    *fIndices.push() = fPositions.count() - 2;
-    *fIndices.push() = fPositions.count() - 1;
-
-    this->addEdge(nextPoint, nextNormal);
-}
-
 void SkAmbientShadowTessellator::addEdge(const SkPoint& nextPoint, const SkVector& nextNormal) {
     // add next quad
     *fPositions.push() = nextPoint;
@@ -372,106 +430,52 @@
 
     fPrevUmbraIndex = fPositions.count() - 2;
     fPrevNormal = nextNormal;
+    fPrevPoint = nextPoint;
 }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-class SkSpotShadowTessellator {
+class SkSpotShadowTessellator : public SkShadowTessellator {
 public:
-    SkSpotShadowTessellator(const SkPath& path, SkScalar scale, const SkVector& translate,
+    SkSpotShadowTessellator(const SkPath& path, const SkMatrix& ctm,
+                            SkScalar scale, const SkVector& translate,
                             SkScalar radius, SkColor umbraColor, SkColor penumbraColor,
                             bool transparent);
 
-    int vertexCount() const { return fPositions.count(); }
-    int indexCount() const { return fIndices.count(); }
-
-    // The casts are needed to work around an older GCC issue where the fact that the pointers are
-    // T* and not const T* causes calls to a deleted unique_ptr constructor.
-    UniqueArray<SkPoint> releasePositions() {
-        return UniqueArray<SkPoint>(static_cast<const SkPoint*>(fPositions.release()));
-    }
-    UniqueArray<SkColor> releaseColors() {
-        return UniqueArray<SkColor>(static_cast<const SkColor*>(fColors.release()));
-    }
-    UniqueArray<uint16_t> releaseIndices() {
-        return UniqueArray<uint16_t>(static_cast<const uint16_t*>(fIndices.release()));
-    }
-
-    bool succeeded() const { return fSucceeded; }
-
 private:
-    void computeClipBounds(const SkPath& path);
+    void computeClipBounds(const SkPath& path, const SkMatrix& ctm, SkPath* devPath);
     void checkUmbraAndTransformCentroid(SkScalar scale, const SkVector& xlate,
                                         bool useDistanceToPoint);
     bool clipUmbraPoint(const SkPoint& umbraPoint, const SkPoint& centroid, SkPoint* clipPoint);
 
-    void handleLine(const SkPoint& p);
-    void handleLine(SkScalar scale, const SkVector& xlate, SkPoint p);
-
-    void handleQuad(const SkPoint pts[3]);
-    void handleQuad(SkScalar scale, const SkVector& xlate, SkPoint pts[3]);
-
-    void handleCubic(SkScalar scale, const SkVector& xlate, SkPoint pts[4]);
-
-    void handleConic(SkScalar scale, const SkVector& xlate, SkPoint pts[3], SkScalar w);
+    void handleLine(const SkPoint& p) override;
 
     void mapPoints(SkScalar scale, const SkVector& xlate, SkPoint* pts, int count);
     void addInnerPoint(const SkPoint& pathPoint);
-    void addArc(const SkVector& nextNormal);
-    void finishArcAndAddEdge(const SkVector& nextPoint, const SkVector& nextNormal);
-    void addEdge(const SkVector& nextPoint, const SkVector& nextNormal);
+    void addEdge(const SkVector& nextPoint, const SkVector& nextNormal) override;
 
-    SkScalar            fRadius;
-    SkColor             fUmbraColor;
-    SkColor             fPenumbraColor;
-    bool                fTransparent;
-    bool                fValidUmbra;
-
-    SkTDArray<SkPoint>  fPositions;
-    SkTDArray<SkColor>  fColors;
-    SkTDArray<uint16_t> fIndices;
-
-    int                 fPrevUmbraIndex;
-    SkPoint             fPrevPoint;
-    SkVector            fPrevNormal;
-    int                 fFirstVertex;
-    SkPoint             fFirstPoint;
-    SkVector            fFirstNormal;
-    SkScalar            fDirection;
-
-    SkPoint             fCentroid;
     SkTDArray<SkPoint>  fClipPolygon;
     SkTDArray<SkVector> fClipVectors;
+    SkPoint             fCentroid;
+
     int                 fCurrPolyPoint;
     bool                fPrevUmbraOutside;
     bool                fFirstUmbraOutside;
+    bool                fValidUmbra;
 
-    // first three points
-    SkTDArray<SkPoint>  fInitPoints;
-    // temporary buffer
-    SkTDArray<SkPoint>  fPointBuffer;
-
-    bool                fSucceeded;
+    typedef SkShadowTessellator INHERITED;
 };
 
-
-
-SkSpotShadowTessellator::SkSpotShadowTessellator(const SkPath& path,
+SkSpotShadowTessellator::SkSpotShadowTessellator(const SkPath& path, const SkMatrix& ctm,
                                                  SkScalar scale, const SkVector& translate,
-                                                 SkScalar radius,
-                                                 SkColor umbraColor, SkColor penumbraColor,
-                                                 bool transparent)
-        : fRadius(radius)
-        , fUmbraColor(umbraColor)
-        , fPenumbraColor(penumbraColor)
-        , fTransparent(transparent)
-        , fValidUmbra(true)
-        , fPrevUmbraIndex(-1)
+                                                 SkScalar radius, SkColor umbraColor,
+                                                 SkColor penumbraColor, bool transparent)
+        : INHERITED(radius, umbraColor, penumbraColor, transparent)
         , fCurrPolyPoint(0)
         , fPrevUmbraOutside(false)
         , fFirstUmbraOutside(false)
-        , fSucceeded(false) {
-
+        , fValidUmbra(true) {
+    
     // TODO: calculate these better
     // Penumbra ring: 3*numPts
     // Umbra ring: numPts
@@ -482,11 +486,10 @@
     // Umbra ring: 3*numPts
     fIndices.setReserve(15 * path.countPoints());
 
-    fInitPoints.setReserve(3);
-
     fClipPolygon.setReserve(path.countPoints());
     // compute rough clip bounds for umbra, plus centroid
-    this->computeClipBounds(path);
+    SkPath devPath;
+    this->computeClipBounds(path, ctm, &devPath);
     if (fClipPolygon.count() < 3) {
         return;
     }
@@ -500,26 +503,28 @@
     this->checkUmbraAndTransformCentroid(scale, translate, usePointCheck);
 
     // walk around the path, tessellate and generate inner and outer rings
-    SkPath::Iter iter(path, true);
+    SkPath::Iter iter(devPath, true);
     SkPoint pts[4];
     SkPath::Verb verb;
     if (fTransparent) {
         *fPositions.push() = fCentroid;
         *fColors.push() = fUmbraColor;
     }
+    SkMatrix shadowTransform;
+    shadowTransform.setScaleTranslate(scale, scale, xlate.fX, xlate.fY);
     while ((verb = iter.next(pts)) != SkPath::kDone_Verb) {
         switch (verb) {
             case SkPath::kLine_Verb:
-                this->handleLine(scale, xlate, pts[1]);
+                this->INHERITED::handleLine(shadowTransform, &pts[1]);
                 break;
             case SkPath::kQuad_Verb:
-                this->handleQuad(scale, xlate, pts);
+                this->handleQuad(shadowTransform, pts);
                 break;
             case SkPath::kCubic_Verb:
-                this->handleCubic(scale, xlate, pts);
+                this->handleCubic(shadowTransform, pts);
                 break;
             case SkPath::kConic_Verb:
-                this->handleConic(scale, xlate, pts, iter.conicWeight());
+                this->handleConic(shadowTransform, pts, iter.conicWeight());
                 break;
             case SkPath::kMove_Verb:
             case SkPath::kClose_Verb:
@@ -600,7 +605,8 @@
     fSucceeded = true;
 }
 
-void SkSpotShadowTessellator::computeClipBounds(const SkPath& path) {
+void SkSpotShadowTessellator::computeClipBounds(const SkPath& path, const SkMatrix& ctm,
+                                                SkPath* devPath) {
     // walk around the path and compute clip polygon
     // if original path is transparent, will accumulate sum of points for centroid
     // for Bezier curves, we compute additional interior points on curve
@@ -623,13 +629,19 @@
     while ((verb = iter.next(pts)) != SkPath::kDone_Verb) {
         switch (verb) {
             case SkPath::kMove_Verb:
+                ctm.mapPoints(&pts[0], 1);
+                devPath->moveTo(pts[0]);
                 break;
             case SkPath::kLine_Verb:
+                ctm.mapPoints(&pts[1], 1);
+                devPath->lineTo(pts[1]);
                 fCentroid += pts[1];
                 centroidCount++;
                 *fClipPolygon.push() = pts[1];
                 break;
             case SkPath::kQuad_Verb:
+                ctm.mapPoints(pts, 3);
+                devPath->quadTo(pts[1], pts[2]);
                 // point at t = 1/2
                 curvePoint.fX = 0.25f*pts[0].fX + 0.5f*pts[1].fX + 0.25f*pts[2].fX;
                 curvePoint.fY = 0.25f*pts[0].fY + 0.5f*pts[1].fY + 0.25f*pts[2].fY;
@@ -640,8 +652,10 @@
                 centroidCount += 2;
                 break;
             case SkPath::kConic_Verb:
-                // point at t = 1/2
+                ctm.mapPoints(pts, 3);
                 w = iter.conicWeight();
+                devPath->conicTo(pts[1], pts[2], w);
+                // point at t = 1/2
                 curvePoint.fX = 0.25f*pts[0].fX + w*0.5f*pts[1].fX + 0.25f*pts[2].fX;
                 curvePoint.fY = 0.25f*pts[0].fY + w*0.5f*pts[1].fY + 0.25f*pts[2].fY;
                 curvePoint *= SkScalarInvert(0.5f + 0.5f*w);
@@ -652,6 +666,8 @@
                 centroidCount += 2;
                 break;
             case SkPath::kCubic_Verb:
+                ctm.mapPoints(pts, 4);
+                devPath->cubicTo(pts[1], pts[2], pts[3]);
                 // point at t = 5/16
                 curvePoint.fX = kA*pts[0].fX + kB*pts[1].fX + kC*pts[2].fX + kD*pts[3].fX;
                 curvePoint.fY = kA*pts[0].fY + kB*pts[1].fY + kC*pts[2].fY + kD*pts[3].fY;
@@ -667,6 +683,7 @@
                 centroidCount += 3;
                 break;
             case SkPath::kClose_Verb:
+                devPath->close();
                 break;
             default:
                 SkDEBUGFAIL("unknown verb");
@@ -855,65 +872,6 @@
     }
 }
 
-void SkSpotShadowTessellator::handleLine(SkScalar scale, const SkVector& xlate, SkPoint p) {
-    this->mapPoints(scale, xlate, &p, 1);
-    this->handleLine(p);
-}
-
-void SkSpotShadowTessellator::handleQuad(const SkPoint pts[3]) {
-#if SK_SUPPORT_GPU
-    // TODO: Pull PathUtils out of Ganesh?
-    int maxCount = GrPathUtils::quadraticPointCount(pts, kQuadTolerance);
-    fPointBuffer.setReserve(maxCount);
-    SkPoint* target = fPointBuffer.begin();
-    int count = GrPathUtils::generateQuadraticPoints(pts[0], pts[1], pts[2],
-                                                     kQuadTolerance, &target, maxCount);
-    fPointBuffer.setCount(count);
-    for (int i = 0; i < count; i++) {
-        this->handleLine(fPointBuffer[i]);
-    }
-#endif
-}
-
-void SkSpotShadowTessellator::handleQuad(SkScalar scale, const SkVector& xlate, SkPoint pts[3]) {
-    this->mapPoints(scale, xlate, pts, 3);
-    this->handleQuad(pts);
-}
-
-void SkSpotShadowTessellator::handleCubic(SkScalar scale, const SkVector& xlate, SkPoint pts[4]) {
-#if SK_SUPPORT_GPU
-    // TODO: Pull PathUtils out of Ganesh?
-    this->mapPoints(scale, xlate, pts, 4);
-    int maxCount = GrPathUtils::cubicPointCount(pts, kCubicTolerance);
-    fPointBuffer.setReserve(maxCount);
-    SkPoint* target = fPointBuffer.begin();
-    int count = GrPathUtils::generateCubicPoints(pts[0], pts[1], pts[2], pts[3],
-                                                 kCubicTolerance, &target, maxCount);
-    fPointBuffer.setCount(count);
-    for (int i = 0; i < count; i++) {
-        this->handleLine(fPointBuffer[i]);
-    }
-#endif
-}
-
-void SkSpotShadowTessellator::handleConic(SkScalar scale, const SkVector& xlate,
-                                          SkPoint pts[3], SkScalar w) {
-    this->mapPoints(scale, xlate, pts, 3);
-    SkAutoConicToQuads quadder;
-    const SkPoint* quads = quadder.computeQuads(pts, w, kConicTolerance);
-    SkPoint lastPoint = *(quads++);
-    int count = quadder.countQuads();
-    for (int i = 0; i < count; ++i) {
-        SkPoint quadPts[3];
-        quadPts[0] = lastPoint;
-        quadPts[1] = quads[0];
-        quadPts[2] = i == count - 1 ? pts[2] : quads[1];
-        this->handleQuad(quadPts);
-        lastPoint = quadPts[2];
-        quads += 2;
-    }
-}
-
 void SkSpotShadowTessellator::addInnerPoint(const SkPoint& pathPoint) {
     SkVector v = fCentroid - pathPoint;
     SkScalar distance = v.length();
@@ -932,39 +890,6 @@
     fPrevPoint = pathPoint;
 }
 
-void SkSpotShadowTessellator::addArc(const SkVector& nextNormal) {
-    // fill in fan from previous quad
-    SkScalar rotSin, rotCos;
-    int numSteps;
-    compute_radial_steps(fPrevNormal, nextNormal, fRadius, &rotSin, &rotCos, &numSteps);
-    SkVector prevNormal = fPrevNormal;
-    for (int i = 0; i < numSteps; ++i) {
-        SkVector nextNormal;
-        nextNormal.fX = prevNormal.fX*rotCos - prevNormal.fY*rotSin;
-        nextNormal.fY = prevNormal.fY*rotCos + prevNormal.fX*rotSin;
-        *fPositions.push() = fPrevPoint + nextNormal;
-        *fColors.push() = fPenumbraColor;
-        *fIndices.push() = fPrevUmbraIndex;
-        *fIndices.push() = fPositions.count() - 2;
-        *fIndices.push() = fPositions.count() - 1;
-
-        prevNormal = nextNormal;
-    }
-}
-
-void SkSpotShadowTessellator::finishArcAndAddEdge(const SkPoint& nextPoint,
-                                                  const SkVector& nextNormal) {
-    // close out previous arc
-    SkPoint newPoint = fPrevPoint + nextNormal;
-    *fPositions.push() = newPoint;
-    *fColors.push() = fPenumbraColor;
-    *fIndices.push() = fPrevUmbraIndex;
-    *fIndices.push() = fPositions.count() - 2;
-    *fIndices.push() = fPositions.count() - 1;
-
-    this->addEdge(nextPoint, nextNormal);
-}
-
 void SkSpotShadowTessellator::addEdge(const SkPoint& nextPoint, const SkVector& nextNormal) {
     // add next umbra point
     this->addInnerPoint(nextPoint);
@@ -1023,10 +948,10 @@
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-sk_sp<SkShadowVertices> SkShadowVertices::MakeAmbient(const SkPath& path, SkScalar radius,
-                                                      SkColor umbraColor, SkColor penumbraColor,
-                                                      bool transparent) {
-    SkAmbientShadowTessellator ambientTess(path, radius, umbraColor, penumbraColor, transparent);
+sk_sp<SkShadowVertices> SkShadowVertices::MakeAmbient(const SkPath& path, const SkMatrix& ctm,
+                                                      SkScalar radius, SkColor umbra,
+                                                      SkColor penumbra, bool transparent) {
+    SkAmbientShadowTessellator ambientTess(path, ctm, radius, umbra, penumbra, transparent);
     if (!ambientTess.succeeded()) {
         return nullptr;
     }
@@ -1038,12 +963,12 @@
                                                         icount));
 }
 
-sk_sp<SkShadowVertices> SkShadowVertices::MakeSpot(const SkPath& path, SkScalar scale,
-                                                   const SkVector& translate, SkScalar radius,
-                                                   SkColor umbraColor, SkColor penumbraColor,
-                                                   bool transparent) {
-    SkSpotShadowTessellator spotTess(path, scale, translate, radius, umbraColor, penumbraColor,
-                                     transparent);
+sk_sp<SkShadowVertices> SkShadowVertices::MakeSpot(const SkPath& path, const SkMatrix& ctm,
+                                                   SkScalar scale, const SkVector& translate,
+                                                   SkScalar radius, SkColor umbraColor,
+                                                   SkColor penumbraColor, bool transparent) {
+    SkSpotShadowTessellator spotTess(path, ctm, scale, translate, radius, umbraColor,
+                                     penumbraColor, transparent);
     if (!spotTess.succeeded()) {
         return nullptr;
     }
diff --git a/src/utils/SkShadowTessellator.h b/src/utils/SkShadowTessellator.h
index c9abd72..875bb76 100755
--- a/src/utils/SkShadowTessellator.h
+++ b/src/utils/SkShadowTessellator.h
@@ -24,19 +24,19 @@
      * the radius, and setting inner and outer colors to umbraColor and penumbraColor, respectively.
      * If transparent is true, then the center of the ambient shadow will be filled in.
      */
-    static sk_sp<SkShadowVertices> MakeAmbient(const SkPath& path, SkScalar radius,
-                                               SkColor umbraColor, SkColor penumbraColor,
-                                               bool transparent);
+    static sk_sp<SkShadowVertices> MakeAmbient(const SkPath& path, const SkMatrix& ctm,
+                                               SkScalar radius, SkColor umbraColor,
+                                               SkColor penumbraColor, bool transparent);
 
     /**
      * This function generates a spot shadow mesh for a path by walking the transformed path,
      * further transforming by the scale and translation, and outsetting and insetting by a radius.
      * The center will be clipped against the original path unless transparent is true.
      */
-    static sk_sp<SkShadowVertices> MakeSpot(const SkPath& path, SkScalar scale,
-                                            const SkVector& translate, SkScalar radius,
-                                            SkColor umbraColor, SkColor penumbraColor,
-                                            bool transparent);
+    static sk_sp<SkShadowVertices> MakeSpot(const SkPath& path, const SkMatrix& ctm,
+                                            SkScalar scale, const SkVector& translate,
+                                            SkScalar radius, SkColor umbraColor,
+                                            SkColor penumbraColor, bool transparent);
 
     int vertexCount() const { return fVertexCnt; }
     const SkPoint* positions() const { return fPositions.get(); }
diff --git a/src/utils/SkShadowUtils.cpp b/src/utils/SkShadowUtils.cpp
index c79f991..ba4decc 100755
--- a/src/utils/SkShadowUtils.cpp
+++ b/src/utils/SkShadowUtils.cpp
@@ -98,8 +98,8 @@
         return true;
     }
 
-    sk_sp<SkShadowVertices> makeVertices(const SkPath& devPath) const {
-        return SkShadowVertices::MakeAmbient(devPath, fRadius, fUmbraColor, fPenumbraColor,
+    sk_sp<SkShadowVertices> makeVertices(const SkPath& path, const SkMatrix& ctm) const {
+        return SkShadowVertices::MakeAmbient(path, ctm, fRadius, fUmbraColor, fPenumbraColor,
                                              fTransparent);
     }
 };
@@ -148,9 +148,9 @@
         return false;
     }
 
-    sk_sp<SkShadowVertices> makeVertices(const SkPath& devPath) const {
+    sk_sp<SkShadowVertices> makeVertices(const SkPath& path, const SkMatrix& ctm) const {
         bool transparent = OccluderType::kTransparent == fOccluderType;
-        return SkShadowVertices::MakeSpot(devPath, fScale, fOffset, fRadius, fUmbraColor,
+        return SkShadowVertices::MakeSpot(path, ctm, fScale, fOffset, fRadius, fUmbraColor,
                                           fPenumbraColor, transparent);
     }
 };
@@ -216,7 +216,7 @@
 
         sk_sp<SkShadowVertices> add(const SkPath& devPath, const FACTORY& factory,
                                     const SkMatrix& matrix) {
-            sk_sp<SkShadowVertices> vertices = factory.makeVertices(devPath);
+            sk_sp<SkShadowVertices> vertices = factory.makeVertices(devPath, matrix);
             if (!vertices) {
                 return nullptr;
             }
@@ -333,20 +333,14 @@
 class ShadowedPath {
 public:
     ShadowedPath(const SkPath* path, const SkMatrix* viewMatrix)
-            : fOriginalPath(path)
+            : fPath(path)
             , fViewMatrix(viewMatrix)
 #if SK_SUPPORT_GPU
             , fShapeForKey(*path, GrStyle::SimpleFill())
 #endif
     {}
 
-    const SkPath& transformedPath() {
-        if (!fTransformedPath.isValid()) {
-            fOriginalPath->transform(*fViewMatrix, fTransformedPath.init());
-        }
-        return *fTransformedPath.get();
-    }
-
+    const SkPath& path() const { return *fPath; }
     const SkMatrix& viewMatrix() const { return *fViewMatrix; }
 #if SK_SUPPORT_GPU
     /** Negative means the vertices should not be cached for this path. */
@@ -362,12 +356,11 @@
 #endif
 
 private:
-    const SkPath* fOriginalPath;
+    const SkPath* fPath;
     const SkMatrix* fViewMatrix;
 #if SK_SUPPORT_GPU
     GrShape fShapeForKey;
 #endif
-    SkTLazy<SkPath> fTransformedPath;
 };
 
 // This creates a domain of keys in SkResourceCache used by this file.
@@ -409,13 +402,13 @@
             } else {
                 tessellations.reset(new CachedTessellations());
             }
-            vertices = tessellations->add(path.transformedPath(), factory, path.viewMatrix());
+            vertices = tessellations->add(path.path(), factory, path.viewMatrix());
             if (!vertices) {
                 return;
             }
             SkResourceCache::Add(new CachedTessellationsRec(*key, std::move(tessellations)));
         } else {
-            vertices = factory.makeVertices(path.transformedPath());
+            vertices = factory.makeVertices(path.path(), path.viewMatrix());
             if (!vertices) {
                 return;
             }
@@ -521,6 +514,9 @@
                 }
             }
         }
+        if (factory.fOccluderType == SpotVerticesFactory::OccluderType::kOpaque) {
+            factory.fOccluderType = SpotVerticesFactory::OccluderType::kTransparent;
+        }
         draw_shadow(factory, canvas, shadowedPath, color);
     }
 }