Make SkShadowUtils tessellations ref counted in preparation for caching them.

Change-Id: I60133fcc4101a27bcc3e7ad38e7348ad9147b8a9
Reviewed-on: https://skia-review.googlesource.com/7784
Reviewed-by: Jim Van Verth <jvanverth@google.com>
Commit-Queue: Brian Salomon <bsalomon@google.com>
diff --git a/include/private/SkTDArray.h b/include/private/SkTDArray.h
index f71d357..16f8474 100644
--- a/include/private/SkTDArray.h
+++ b/include/private/SkTDArray.h
@@ -72,6 +72,12 @@
         SkTSwap(fCount, other.fCount);
     }
 
+    // The deleter that ought to be used for a std:: smart pointer that takes ownership from
+    // release().
+    struct Deleter {
+        void operator()(const void* p) { sk_free((void*)p); }
+    };
+
     /** Return a ptr to the array of data, to be freed with sk_free. This also
         resets the SkTDArray to be empty.
      */
diff --git a/src/utils/SkShadowTessellator.cpp b/src/utils/SkShadowTessellator.cpp
index ca8927b..147ff53 100755
--- a/src/utils/SkShadowTessellator.cpp
+++ b/src/utils/SkShadowTessellator.cpp
@@ -13,6 +13,57 @@
 #include "GrPathUtils.h"
 #endif
 
+template <typename T> using UniqueArray = SkShadowVertices::UniqueArray<T>;
+
+// TODO: derive the ambient and spot classes from a base class containing common elements
+
+class SkAmbientShadowTessellator {
+public:
+    SkAmbientShadowTessellator(const SkPath& path, SkScalar radius, SkColor umbraColor,
+                               SkColor penumbraColor, bool transparent);
+
+    int vertexCount() const { return fPositions.count(); }
+    int indexCount() const { return fIndices.count(); }
+
+    UniqueArray<SkPoint> releasePositions() { return UniqueArray<SkPoint>(fPositions.release()); }
+    UniqueArray<SkColor> releaseColors() { return UniqueArray<SkColor>(fColors.release()); }
+    UniqueArray<uint16_t> releaseIndices() { return UniqueArray<uint16_t>(fIndices.release()); }
+
+private:
+    void handleLine(const SkPoint& p);
+
+    void handleQuad(const SkPoint pts[3]);
+
+    void handleCubic(SkPoint pts[4]);
+
+    void handleConic(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                 fPrevInnerIndex;
+    SkVector            fPrevNormal;
+    int                 fFirstVertex;
+    SkVector            fFirstNormal;
+    SkScalar            fDirection;
+    int                 fCentroidCount;
+
+    // first three points
+    SkTDArray<SkPoint>  fInitPoints;
+    // temporary buffer
+    SkTDArray<SkPoint>  fPointBuffer;
+};
+
 static bool compute_normal(const SkPoint& p0, const SkPoint& p1, SkScalar radius, SkScalar dir,
                            SkVector* newNormal) {
     SkVector normal;
@@ -307,6 +358,63 @@
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
+class SkSpotShadowTessellator {
+public:
+    SkSpotShadowTessellator(const SkPath& path, 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(); }
+
+    UniqueArray<SkPoint> releasePositions() { return UniqueArray<SkPoint>(fPositions.release()); }
+    UniqueArray<SkColor> releaseColors() { return UniqueArray<SkColor>(fColors.release()); }
+    UniqueArray<uint16_t> releaseIndices() { return UniqueArray<uint16_t>(fIndices.release()); }
+
+private:
+    void computeClipBounds(const SkPath& path);
+
+    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 mapPoints(SkScalar scale, const SkVector& xlate, SkPoint* pts, int count);
+    void addInnerPoint(const SkPoint& pathPoint, SkColor umbraColor, SkScalar radiusSqd);
+    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;
+
+    SkTDArray<SkPoint>  fPositions;
+    SkTDArray<SkColor>  fColors;
+    SkTDArray<uint16_t> fIndices;
+
+    int                 fPrevInnerIndex;
+    SkPoint             fPrevPoint;
+    SkVector            fPrevNormal;
+    int                 fFirstVertex;
+    SkPoint             fFirstPoint;
+    SkVector            fFirstNormal;
+    SkScalar            fDirection;
+
+    SkPoint             fCentroid;
+    SkTDArray<SkPoint>  fClipPolygon;
+
+    // first three points
+    SkTDArray<SkPoint>  fInitPoints;
+    // temporary buffer
+    SkTDArray<SkPoint>  fPointBuffer;
+};
+
 SkSpotShadowTessellator::SkSpotShadowTessellator(const SkPath& path,
                                                  SkScalar scale, const SkVector& translate,
                                                  SkScalar radius,
@@ -645,3 +753,30 @@
     fPrevInnerIndex = fPositions.count() - 2;
     fPrevNormal = nextNormal;
 }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+sk_sp<SkShadowVertices> SkShadowVertices::MakeAmbient(const SkPath& path, SkScalar radius,
+                                                      SkColor umbraColor, SkColor penumbraColor,
+                                                      bool transparent) {
+    SkAmbientShadowTessellator ambientTess(path, radius, umbraColor, penumbraColor, transparent);
+    int vcount = ambientTess.vertexCount();
+    int icount = ambientTess.indexCount();
+    return sk_sp<SkShadowVertices>(new SkShadowVertices(ambientTess.releasePositions(),
+                                                        ambientTess.releaseColors(),
+                                                        ambientTess.releaseIndices(), vcount,
+                                                        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);
+    int vcount = spotTess.vertexCount();
+    int icount = spotTess.indexCount();
+    return sk_sp<SkShadowVertices>(new SkShadowVertices(spotTess.releasePositions(),
+                                                        spotTess.releaseColors(),
+                                                        spotTess.releaseIndices(), vcount, icount));
+}
diff --git a/src/utils/SkShadowTessellator.h b/src/utils/SkShadowTessellator.h
index ababba7..301b684 100755
--- a/src/utils/SkShadowTessellator.h
+++ b/src/utils/SkShadowTessellator.h
@@ -9,6 +9,7 @@
 #define SkShadowTessellator_DEFINED
 
 #include "SkTDArray.h"
+#include "SkRefCnt.h"
 #include "SkPoint.h"
 
 #include "SkColor.h"
@@ -16,118 +17,53 @@
 class SkMatrix;
 class SkPath;
 
-// TODO: derive these two classes from a base class containing common elements
-
-/**
- * This class generates an ambient shadow for a path by walking the path, outsetting by 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.
- */
-class SkAmbientShadowTessellator {
+class SkShadowVertices : public SkRefCnt {
 public:
-    SkAmbientShadowTessellator(const SkPath& path, SkScalar radius, SkColor umbraColor,
-                               SkColor penumbraColor, bool transparent);
+    /**
+     * This function generates an ambient shadow mesh for a path by walking the path, outsetting by
+     * 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);
 
-    int      vertexCount() { return fPositions.count(); }
-    SkPoint* positions() { return fPositions.begin(); }
-    SkColor* colors() { return fColors.begin(); }
-    int      indexCount() { return fIndices.count(); }
-    uint16_t* indices() { return fIndices.begin(); }
+    /**
+     * 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);
+
+    int vertexCount() const { return fVertexCnt; }
+    const SkPoint* positions() const { return fPositions.get(); }
+    const SkColor* colors() const { return fColors.get(); }
+
+    int indexCount() const { return fIndexCnt; }
+    const uint16_t* indices() const { return fIndices.get(); }
 
 private:
-    void handleLine(const SkPoint& p);
+    template<typename T> using Deleter = SkTDArray<SkPoint>::Deleter;
+    template<typename T> using UniqueArray = std::unique_ptr<const T[], Deleter<T>>;
 
-    void handleQuad(const SkPoint pts[3]);
+    SkShadowVertices(UniqueArray<SkPoint>&& positions, UniqueArray<SkColor>&& colors,
+                     UniqueArray<uint16_t>&& indices, int vertexCnt, int indexCnt)
+            : fVertexCnt(vertexCnt)
+            , fIndexCnt(indexCnt)
+            , fPositions(std::move(positions))
+            , fColors(std::move(colors))
+            , fIndices(std::move(indices)) {
+        SkASSERT(SkToBool(indices) == SkToBool(indexCnt));
+    }
 
-    void handleCubic(SkPoint pts[4]);
-
-    void handleConic(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                 fPrevInnerIndex;
-    SkVector            fPrevNormal;
-    int                 fFirstVertex;
-    SkVector            fFirstNormal;
-    SkScalar            fDirection;
-    int                 fCentroidCount;
-
-    // first three points
-    SkTDArray<SkPoint>  fInitPoints;
-    // temporary buffer
-    SkTDArray<SkPoint>  fPointBuffer;
-};
-
-/**
- * This class generates an spot shadow 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.
- */
-class SkSpotShadowTessellator {
-public:
-    SkSpotShadowTessellator(const SkPath& path, SkScalar scale, const SkVector& translate,
-                            SkScalar radius, SkColor umbraColor, SkColor penumbraColor,
-                            bool transparent);
-
-    int      vertexCount() { return fPositions.count(); }
-    SkPoint* positions() { return fPositions.begin(); }
-    SkColor* colors() { return fColors.begin(); }
-    int      indexCount() { return fIndices.count(); }
-    uint16_t* indices() { return fIndices.begin(); }
-
-private:
-    void computeClipBounds(const SkPath& path);
-
-    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 mapPoints(SkScalar scale, const SkVector& xlate, SkPoint* pts, int count);
-    void addInnerPoint(const SkPoint& pathPoint, SkColor umbraColor, SkScalar radiusSqd);
-    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;
-
-    SkTDArray<SkPoint>  fPositions;
-    SkTDArray<SkColor>  fColors;
-    SkTDArray<uint16_t> fIndices;
-
-    int                 fPrevInnerIndex;
-    SkPoint             fPrevPoint;
-    SkVector            fPrevNormal;
-    int                 fFirstVertex;
-    SkPoint             fFirstPoint;
-    SkVector            fFirstNormal;
-    SkScalar            fDirection;
-
-    SkPoint             fCentroid;
-    SkTDArray<SkPoint>  fClipPolygon;
-
-    // first three points
-    SkTDArray<SkPoint>  fInitPoints;
-    // temporary buffer
-    SkTDArray<SkPoint>  fPointBuffer;
+    int fVertexCnt;
+    int fIndexCnt;
+    UniqueArray<SkPoint> fPositions;
+    UniqueArray<SkColor> fColors;
+    UniqueArray<uint16_t> fIndices;
 };
 
 #endif
diff --git a/src/utils/SkShadowUtils.cpp b/src/utils/SkShadowUtils.cpp
index 5c92d66..6a08965 100755
--- a/src/utils/SkShadowUtils.cpp
+++ b/src/utils/SkShadowUtils.cpp
@@ -87,6 +87,8 @@
     canvas->save();
     canvas->resetMatrix();
 
+    bool transparent = SkToBool(flags & SkShadowFlags::kTransparentOccluder_ShadowFlag);
+
     if (ambientAlpha > 0) {
         SkScalar radius = occluderHeight * kHeightFactor * kGeomFactor;
         SkScalar umbraAlpha = SkScalarInvert((1.0f + SkTMax(occluderHeight*kHeightFactor, 0.0f)));
@@ -97,14 +99,15 @@
         SkColor  umbraColor = SkColorSetARGB(255, 0, ambientAlpha*255.9999f, umbraAlpha*255.9999f);
         SkColor  penumbraColor = SkColorSetARGB(255, 0, ambientAlpha*255.9999f, 0);
 
-        SkAmbientShadowTessellator tess(xformedPath, radius, umbraColor, penumbraColor,
-                                 SkToBool(flags & SkShadowFlags::kTransparentOccluder_ShadowFlag));
-
+        sk_sp<SkShadowVertices> vertices =
+                SkShadowVertices::MakeAmbient(xformedPath, radius, umbraColor, penumbraColor,
+                                              transparent);
         SkPaint paint;
         paint.setColor(color);
         paint.setColorFilter(SkGaussianColorFilter::Make());
-        canvas->drawVertices(SkCanvas::kTriangles_VertexMode, tess.vertexCount(), tess.positions(),
-                             nullptr, tess.colors(), tess.indices(), tess.indexCount(), paint);
+        canvas->drawVertices(SkCanvas::kTriangles_VertexMode, vertices->vertexCount(),
+                             vertices->positions(), nullptr, vertices->colors(),
+                             vertices->indices(), vertices->indexCount(), paint);
     }
 
     if (spotAlpha > 0) {
@@ -120,15 +123,15 @@
 
         SkColor  umbraColor = SkColorSetARGB(255, 0, spotAlpha*255.9999f, 255);
         SkColor  penumbraColor = SkColorSetARGB(255, 0, spotAlpha*255.9999f, 0);
-        SkSpotShadowTessellator tess(xformedPath, scale, spotOffset, radius,
-                                     umbraColor, penumbraColor,
-                                 SkToBool(flags & SkShadowFlags::kTransparentOccluder_ShadowFlag));
-
+        sk_sp<SkShadowVertices> vertices =
+                SkShadowVertices::MakeSpot(xformedPath, scale, spotOffset, radius, umbraColor,
+                                           penumbraColor, transparent);
         SkPaint paint;
         paint.setColor(color);
         paint.setColorFilter(SkGaussianColorFilter::Make());
-        canvas->drawVertices(SkCanvas::kTriangles_VertexMode, tess.vertexCount(), tess.positions(),
-                             nullptr, tess.colors(), tess.indices(), tess.indexCount(), paint);
+        canvas->drawVertices(SkCanvas::kTriangles_VertexMode, vertices->vertexCount(),
+                             vertices->positions(), nullptr, vertices->colors(),
+                             vertices->indices(), vertices->indexCount(), paint);
     }
 
     canvas->restore();