Add ear-clipping code to triangulate simple polygons.

Use this to fill concave shadows.

Bug: skia:7971
Change-Id: I63dc1ed845f9fa3fcd86f1ad13b03da23cae0313
Reviewed-on: https://skia-review.googlesource.com/135200
Commit-Queue: Jim Van Verth <jvanverth@google.com>
Reviewed-by: Robert Phillips <robertphillips@google.com>
diff --git a/gm/convex_all_line_paths.cpp b/gm/convex_all_line_paths.cpp
index b1907bc..e21711b 100644
--- a/gm/convex_all_line_paths.cpp
+++ b/gm/convex_all_line_paths.cpp
@@ -6,7 +6,7 @@
  */
 
 #include "gm.h"
-#include "SkOffsetPolygon.h"
+#include "SkPolyUtils.h"
 #include "SkPathPriv.h"
 
 static void create_ngon(int n, SkPoint* pts, SkScalar width, SkScalar height) {
diff --git a/gm/polygonoffset.cpp b/gm/polygonoffset.cpp
index 48b6d1a..44a313b 100644
--- a/gm/polygonoffset.cpp
+++ b/gm/polygonoffset.cpp
@@ -7,7 +7,7 @@
 
 #include "gm.h"
 #include "sk_tool_utils.h"
-#include "SkOffsetPolygon.h"
+#include "SkPolyUtils.h"
 #include "SkPathPriv.h"
 
 static void create_ngon(int n, SkPoint* pts, SkScalar w, SkScalar h, SkPath::Direction dir) {
diff --git a/gn/utils.gni b/gn/utils.gni
index 63b063b..95c4e93 100644
--- a/gn/utils.gni
+++ b/gn/utils.gni
@@ -47,8 +47,6 @@
   "$_src/utils/SkMultiPictureDocument.cpp",
   "$_src/utils/SkNWayCanvas.cpp",
   "$_src/utils/SkNullCanvas.cpp",
-  "$_src/utils/SkOffsetPolygon.cpp",
-  "$_src/utils/SkOffsetPolygon.h",
   "$_src/utils/SkOSPath.cpp",
   "$_src/utils/SkOSPath.h",
   "$_src/utils/SkPaintFilterCanvas.cpp",
@@ -57,6 +55,8 @@
   "$_src/utils/SkParsePath.cpp",
   "$_src/utils/SkPatchUtils.cpp",
   "$_src/utils/SkPatchUtils.h",
+  "$_src/utils/SkPolyUtils.cpp",
+  "$_src/utils/SkPolyUtils.h",
   "$_src/utils/SkShadowTessellator.cpp",
   "$_src/utils/SkShadowTessellator.h",
   "$_src/utils/SkShadowUtils.cpp",
diff --git a/src/utils/SkOffsetPolygon.cpp b/src/utils/SkPolyUtils.cpp
similarity index 75%
rename from src/utils/SkOffsetPolygon.cpp
rename to src/utils/SkPolyUtils.cpp
index c72f7d4..ea9d649 100755
--- a/src/utils/SkOffsetPolygon.cpp
+++ b/src/utils/SkPolyUtils.cpp
@@ -5,12 +5,16 @@
  * found in the LICENSE file.
  */
 
-#include "SkOffsetPolygon.h"
+#include "SkPolyUtils.h"
 
 #include "SkPointPriv.h"
 #include "SkTArray.h"
 #include "SkTemplates.h"
 #include "SkTDPQueue.h"
+#include "SkTInternalLList.h"
+
+//////////////////////////////////////////////////////////////////////////////////
+// Helper data structures and functions
 
 struct OffsetSegment {
     SkPoint fP0;
@@ -33,23 +37,17 @@
 
 // returns 1 for ccw, -1 for cw and 0 if degenerate
 static int get_winding(const SkPoint* polygonVerts, int polygonSize) {
-    SkPoint p0 = polygonVerts[0];
-    SkPoint p1 = polygonVerts[1];
-
-    for (int i = 2; i < polygonSize; ++i) {
-        SkPoint p2 = polygonVerts[i];
-
-        // determine if cw or ccw
-        int side = compute_side(p0, p1, p2);
-        if (0 != side) {
-            return ((side > 0) ? 1 : -1);
-        }
-
-        // if nearly collinear, treat as straight line and continue
-        p1 = p2;
+    // compute area and use sign to determine winding
+    SkScalar quadArea = 0;
+    for (int curr = 0; curr < polygonSize; ++curr) {
+        int next = (curr + 1) % polygonSize;
+        quadArea += polygonVerts[curr].cross(polygonVerts[next]);
     }
-
-    return 0;
+    if (SkScalarNearlyZero(quadArea)) {
+        return 0;
+    }
+    // 1 == ccw, -1 == cw
+    return (quadArea > 0) ? 1 : -1;
 }
 
 // Helper function to compute the individual vector for non-equal offsets
@@ -261,6 +259,8 @@
     }
 };
 
+//////////////////////////////////////////////////////////////////////////////////
+
 // The objective here is to inset all of the edges by the given distance, and then
 // remove any invalid inset edges by detecting right-hand turns. In a ccw polygon,
 // we should only be making left-hand turns (for cw polygons, we use the winding
@@ -280,6 +280,7 @@
         return false;
     }
 
+    // get winding direction
     int winding = get_winding(inputPolygonVerts, inputPolygonSize);
     if (0 == winding) {
         return false;
@@ -397,9 +398,11 @@
     return (insetPolygon->count() >= 3 && is_convex(*insetPolygon));
 }
 
-// compute the number of points needed for a circular join when offsetting a  reflex vertex
-static void compute_radial_steps(const SkVector& v1, const SkVector& v2, SkScalar r,
-                                 SkScalar* rotSin, SkScalar* rotCos, int* n) {
+///////////////////////////////////////////////////////////////////////////////////////////
+
+// compute the number of points needed for a circular join when offsetting a reflex vertex
+void SkComputeRadialSteps(const SkVector& v1, const SkVector& v2, SkScalar r,
+                          SkScalar* rotSin, SkScalar* rotCos, int* n) {
     const SkScalar kRecipPixelsPerArcSegment = 0.25f;
 
     SkScalar rCos = v1.dot(v2);
@@ -529,10 +532,10 @@
 
         // if we intersect with the edge above or below us
         // then we know this polygon is not simple, so don't remove, just fail
-        if (removeIndex > 0 && fEdges[removeIndex].intersect(fEdges[removeIndex-1])) {
+        if (removeIndex > 0 && fEdges[removeIndex].intersect(fEdges[removeIndex - 1])) {
             return false;
         }
-        if (removeIndex < fEdges.count()-1) {
+        if (removeIndex < fEdges.count() - 1) {
             if (fEdges[removeIndex].intersect(fEdges[removeIndex + 1])) {
                 return false;
             }
@@ -555,7 +558,7 @@
 // Then as we pop the vertices from the queue we generate events which indicate that an edge
 // should be added or removed from an edge list. If any intersections are detected in the edge
 // list, then we know the polygon is self-intersecting and hence not simple.
-static bool is_simple_polygon(const SkPoint* polygon, int polygonSize) {
+bool SkIsSimplePolygon(const SkPoint* polygon, int polygonSize) {
     SkTDPQueue <Vertex, Vertex::Left> vertexQueue;
     EdgeList sweepLine;
 
@@ -613,7 +616,8 @@
     return (vertexQueue.count() == 0);
 }
 
-// TODO: assuming a constant offset here -- do we want to support variable offset?
+///////////////////////////////////////////////////////////////////////////////////////////
+
 bool SkOffsetSimplePolygon(const SkPoint* inputPolygonVerts, int inputPolygonSize,
                            std::function<SkScalar(const SkPoint&)> offsetDistanceFunc,
                            SkTDArray<SkPoint>* offsetPolygon, SkTDArray<int>* polygonIndices) {
@@ -621,22 +625,12 @@
         return false;
     }
 
-    if (!is_simple_polygon(inputPolygonVerts, inputPolygonSize)) {
+    // get winding direction
+    int winding = get_winding(inputPolygonVerts, inputPolygonSize);
+    if (0 == winding) {
         return false;
     }
 
-    // compute area and use sign to determine winding
-    SkScalar quadArea = 0;
-    for (int curr = 0; curr < inputPolygonSize; ++curr) {
-        int next = (curr + 1) % inputPolygonSize;
-        quadArea += inputPolygonVerts[curr].cross(inputPolygonVerts[next]);
-    }
-    if (SkScalarNearlyZero(quadArea)) {
-        return false;
-    }
-    // 1 == ccw, -1 == cw
-    int winding = (quadArea > 0) ? 1 : -1;
-
     // build normals
     SkAutoSTMalloc<64, SkVector> normal0(inputPolygonSize);
     SkAutoSTMalloc<64, SkVector> normal1(inputPolygonSize);
@@ -667,7 +661,7 @@
             SkScalar rotSin, rotCos;
             int numSteps;
             SkVector prevNormal = normal1[currIndex];
-            compute_radial_steps(prevNormal, normal0[currIndex], SkScalarAbs(offset),
+            SkComputeRadialSteps(prevNormal, normal0[currIndex], SkScalarAbs(offset),
                                  &rotSin, &rotCos, &numSteps);
             for (int i = 0; i < numSteps - 1; ++i) {
                 SkVector currNormal = SkVector::Make(prevNormal.fX*rotCos - prevNormal.fY*rotSin,
@@ -792,14 +786,211 @@
         }
     }
 
-    // compute signed area to check winding (it should be same as the original polygon)
-    quadArea = 0;
-    for (int curr = 0; curr < offsetPolygon->count(); ++curr) {
-        int next = (curr + 1) % offsetPolygon->count();
-        quadArea += (*offsetPolygon)[curr].cross((*offsetPolygon)[next]);
-    }
+    // check winding of offset polygon (it should be same as the original polygon)
+    SkScalar offsetWinding = get_winding(offsetPolygon->begin(), offsetPolygon->count());
 
-    return (winding*quadArea > 0 &&
-            is_simple_polygon(offsetPolygon->begin(), offsetPolygon->count()));
+    return (winding*offsetWinding > 0 &&
+            SkIsSimplePolygon(offsetPolygon->begin(), offsetPolygon->count()));
 }
 
+//////////////////////////////////////////////////////////////////////////////////////////
+
+struct TriangulationVertex {
+    SK_DECLARE_INTERNAL_LLIST_INTERFACE(TriangulationVertex);
+
+    enum class VertexType { kConvex, kReflex };
+
+    SkPoint    fPosition;
+    VertexType fVertexType;
+    uint16_t   fIndex;
+    uint16_t   fPrevIndex;
+    uint16_t   fNextIndex;
+};
+
+// test to see if point p is in triangle p0p1p2.
+// for now assuming strictly inside -- if on the edge it's outside
+static bool point_in_triangle(const SkPoint& p0, const SkPoint& p1, const SkPoint& p2,
+                              const SkPoint& p) {
+    SkVector v0 = p1 - p0;
+    SkVector v1 = p2 - p1;
+    SkScalar n = v0.cross(v1);
+
+    SkVector w0 = p - p0;
+    if (n*v0.cross(w0) < SK_ScalarNearlyZero) {
+        return false;
+    }
+
+    SkVector w1 = p - p1;
+    if (n*v1.cross(w1) < SK_ScalarNearlyZero) {
+        return false;
+    }
+
+    SkVector v2 = p0 - p2;
+    SkVector w2 = p - p2;
+    if (n*v2.cross(w2) < SK_ScalarNearlyZero) {
+        return false;
+    }
+
+    return true;
+}
+
+// Data structure to track reflex vertices and check whether any are inside a given triangle
+class ReflexHash {
+public:
+    void add(TriangulationVertex* v) {
+        fReflexList.addToTail(v);
+    }
+
+    void remove(TriangulationVertex* v) {
+        fReflexList.remove(v);
+    }
+
+    bool checkTriangle(const SkPoint& p0, const SkPoint& p1, const SkPoint& p2) {
+        for (SkTInternalLList<TriangulationVertex>::Iter reflexIter = fReflexList.begin();
+             reflexIter != fReflexList.end(); ++reflexIter) {
+            TriangulationVertex* reflexVertex = *reflexIter;
+            if (point_in_triangle(p0, p1, p2, reflexVertex->fPosition)) {
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+private:
+    // TODO: switch to an actual spatial hash
+    SkTInternalLList<TriangulationVertex> fReflexList;
+};
+
+// Check to see if a reflex vertex has become a convex vertex after clipping an ear
+static void reclassify_vertex(TriangulationVertex* p, const SkPoint* polygonVerts,
+                              int winding, ReflexHash* reflexHash,
+                              SkTInternalLList<TriangulationVertex>* convexList) {
+    if (TriangulationVertex::VertexType::kReflex == p->fVertexType) {
+        SkVector v0 = p->fPosition - polygonVerts[p->fPrevIndex];
+        SkVector v1 = polygonVerts[p->fNextIndex] - p->fPosition;
+        if (winding*v0.cross(v1) > SK_ScalarNearlyZero) {
+            p->fVertexType = TriangulationVertex::VertexType::kConvex;
+            reflexHash->remove(p);
+            p->fPrev = p->fNext = nullptr;
+            convexList->addToTail(p);
+        }
+    }
+}
+
+bool SkTriangulateSimplePolygon(const SkPoint* polygonVerts, uint16_t* indexMap, int polygonSize,
+                                SkTDArray<uint16_t>* triangleIndices) {
+    if (polygonSize < 3) {
+        return false;
+    }
+    // need to be able to represent all the vertices in the 16-bit indices
+    if (polygonSize >= (1 << 16)) {
+        return false;
+    }
+
+    // get winding direction
+    // TODO: we do this for all the polygon routines -- might be better to have the client
+    // compute it and pass it in
+    int winding = get_winding(polygonVerts, polygonSize);
+    if (0 == winding) {
+        return false;
+    }
+
+    // Classify initial vertices into a list of convex vertices and a hash of reflex vertices
+    // TODO: possibly sort the convexList in some way to get better triangles
+    SkTInternalLList<TriangulationVertex> convexList;
+    ReflexHash reflexHash;
+    SkAutoSTMalloc<64, TriangulationVertex> triangulationVertices(polygonSize);
+    int prevIndex = polygonSize - 1;
+    int currIndex = 0;
+    int nextIndex = 1;
+    SkVector v0 = polygonVerts[currIndex] - polygonVerts[prevIndex];
+    SkVector v1 = polygonVerts[nextIndex] - polygonVerts[currIndex];
+    for (int i = 0; i < polygonSize; ++i) {
+        SkDEBUGCODE(memset(&triangulationVertices[currIndex], 0, sizeof(TriangulationVertex)));
+        triangulationVertices[currIndex].fPosition = polygonVerts[currIndex];
+        triangulationVertices[currIndex].fIndex = currIndex;
+        triangulationVertices[currIndex].fPrevIndex = prevIndex;
+        triangulationVertices[currIndex].fNextIndex = nextIndex;
+        if (winding*v0.cross(v1) > SK_ScalarNearlyZero) {
+            triangulationVertices[currIndex].fVertexType = TriangulationVertex::VertexType::kConvex;
+            convexList.addToTail(&triangulationVertices[currIndex]);
+        } else {
+            // We treat near collinear vertices as reflex
+            triangulationVertices[currIndex].fVertexType = TriangulationVertex::VertexType::kReflex;
+            reflexHash.add(&triangulationVertices[currIndex]);
+        }
+
+        prevIndex = currIndex;
+        currIndex = nextIndex;
+        nextIndex = (currIndex + 1) % polygonSize;
+        v0 = v1;
+        v1 = polygonVerts[nextIndex] - polygonVerts[currIndex];
+    }
+
+    // The general concept: We are trying to find three neighboring vertices where
+    // no other vertex lies inside the triangle (an "ear"). If we find one, we clip
+    // that ear off, and then repeat on the new polygon. Once we get down to three vertices
+    // we have triangulated the entire polygon.
+    // In the worst case this is an n^2 algorithm. We can cut down the search space somewhat by
+    // noting that only convex vertices can be potential ears, and we only need to check whether
+    // any reflex vertices lie inside the ear.
+    triangleIndices->setReserve(triangleIndices->count() + 3 * (polygonSize - 2));
+    int vertexCount = polygonSize;
+    while (vertexCount > 3) {
+        bool success = false;
+        TriangulationVertex* earVertex = nullptr;
+        TriangulationVertex* p0 = nullptr;
+        TriangulationVertex* p2 = nullptr;
+        // find a convex vertex to clip
+        for (SkTInternalLList<TriangulationVertex>::Iter convexIter = convexList.begin();
+             convexIter != convexList.end(); ++convexIter) {
+            earVertex = *convexIter;
+            SkASSERT(TriangulationVertex::VertexType::kReflex != earVertex->fVertexType);
+
+            p0 = &triangulationVertices[earVertex->fPrevIndex];
+            p2 = &triangulationVertices[earVertex->fNextIndex];
+
+            // see if any reflex vertices are inside the ear
+            bool failed = reflexHash.checkTriangle(p0->fPosition, earVertex->fPosition,
+                                                   p2->fPosition);
+            if (failed) {
+                continue;
+            }
+
+            // found one we can clip
+            success = true;
+            break;
+        }
+        // If we can't find any ears to clip, this probably isn't a simple polygon
+        if (!success) {
+            return false;
+        }
+
+        // add indices
+        auto indices = triangleIndices->append(3);
+        indices[0] = indexMap[p0->fIndex];
+        indices[1] = indexMap[earVertex->fIndex];
+        indices[2] = indexMap[p2->fIndex];
+
+        // clip the ear
+        convexList.remove(earVertex);
+        --vertexCount;
+
+        // reclassify reflex verts
+        p0->fNextIndex = earVertex->fNextIndex;
+        reclassify_vertex(p0, polygonVerts, winding, &reflexHash, &convexList);
+
+        p2->fPrevIndex = earVertex->fPrevIndex;
+        reclassify_vertex(p2, polygonVerts, winding, &reflexHash, &convexList);
+    }
+
+    // output indices
+    for (SkTInternalLList<TriangulationVertex>::Iter vertexIter = convexList.begin();
+         vertexIter != convexList.end(); ++vertexIter) {
+        TriangulationVertex* vertex = *vertexIter;
+        *triangleIndices->push() = indexMap[vertex->fIndex];
+    }
+
+    return true;
+}
diff --git a/src/utils/SkOffsetPolygon.h b/src/utils/SkPolyUtils.h
similarity index 71%
rename from src/utils/SkOffsetPolygon.h
rename to src/utils/SkPolyUtils.h
index 4c98ac0..ab5ebb8 100755
--- a/src/utils/SkOffsetPolygon.h
+++ b/src/utils/SkPolyUtils.h
@@ -49,6 +49,7 @@
 /**
  * Generates a simple polygon (if possible) that is offset a variable distance (controlled by
  * offsetDistanceFunc) from the boundary of a given simple polygon.
+ * The input polygon must be simple and have no coincident vertices or collinear edges.
  *
  * @param inputPolygonVerts  Array of points representing the vertices of the original polygon.
  * @param inputPolygonSize  Number of vertices in the original polygon.
@@ -66,6 +67,7 @@
 /**
  * Generates a simple polygon (if possible) that is offset a constant distance from the boundary
  * of a given simple polygon.
+ * The input polygon must be simple and have no coincident vertices or collinear edges.
  *
  * @param inputPolygonVerts  Array of points representing the vertices of the original polygon.
  * @param inputPolygonSize  Number of vertices in the original polygon.
@@ -100,4 +102,42 @@
 bool SkOffsetSegment(const SkPoint& p0, const SkPoint& p1, SkScalar d0, SkScalar d1,
                      int side, SkPoint* offset0, SkPoint* offset1);
 
+/**
+ * Compute the number of points needed for a circular join when offsetting a vertex.
+ * The lengths of offset0 and offset1 don't have to equal r -- only the direction matters.
+ * The segment lengths will be approximately four pixels.
+ *
+ * @param offset0  Starting offset vector direction.
+ * @param offset1  Ending offset vector direction.
+ * @param r  Length of offset.
+ * @param rotSin  Sine of rotation delta per step.
+ * @param rotCos  Cosine of rotation delta per step.
+ * @param n  Number of steps to fill out the arc.
+ */
+void SkComputeRadialSteps(const SkVector& offset0, const SkVector& offset1, SkScalar r,
+                          SkScalar* rotSin, SkScalar* rotCos, int* n);
+
+/**
+ * Determine whether a polygon is simple (i.e., not self-intersecting) or not.
+ *
+ * @param polygonVerts  Array of points representing the vertices of the polygon.
+ * @param polygonSize  Number of vertices in the polygon.
+ * @return true if the polygon is simple, false otherwise.
+ */
+ bool SkIsSimplePolygon(const SkPoint* polygonVerts, int polygonSize);
+
+ /**
+  * Compute indices to triangulate the given polygon.
+  * The input polygon must be simple (i.e. it is not self-intersecting)
+  * and have no coincident vertices or collinear edges.
+  *
+  * @param polygonVerts  Array of points representing the vertices of the polygon.
+  * @param indexMap Mapping from index in the given array to the final index in the triangulation.
+  * @param polygonSize  Number of vertices in the polygon.
+  * @param triangleIndices  Indices of the resulting triangulation.
+  * @return true if successful, false otherwise.
+  */
+ bool SkTriangulateSimplePolygon(const SkPoint* polygonVerts, uint16_t* indexMap, int polygonSize,
+                                 SkTDArray<uint16_t>* triangleIndices);
+
 #endif
diff --git a/src/utils/SkShadowTessellator.cpp b/src/utils/SkShadowTessellator.cpp
index b454e5f..3d84e8f 100755
--- a/src/utils/SkShadowTessellator.cpp
+++ b/src/utils/SkShadowTessellator.cpp
@@ -9,7 +9,7 @@
 #include "SkColorData.h"
 #include "SkDrawShadowInfo.h"
 #include "SkGeometry.h"
-#include "SkOffsetPolygon.h"
+#include "SkPolyUtils.h"
 #include "SkPath.h"
 #include "SkPoint3.h"
 #include "SkPointPriv.h"
@@ -127,21 +127,6 @@
     return true;
 }
 
-static void compute_radial_steps(const SkVector& v1, const SkVector& v2, SkScalar r,
-                                 SkScalar* rotSin, SkScalar* rotCos, int* n) {
-    const SkScalar kRecipPixelsPerArcSegment = 0.125f;
-
-    SkScalar rCos = v1.dot(v2);
-    SkScalar rSin = v1.cross(v2);
-    SkScalar theta = SkScalarATan2(rSin, rCos);
-
-    int steps = SkScalarRoundToInt(SkScalarAbs(r*theta*kRecipPixelsPerArcSegment));
-
-    SkScalar dTheta = theta / steps;
-    *rotSin = SkScalarSinCos(dTheta, rotCos);
-    *n = steps;
-}
-
 static bool duplicate_pt(const SkPoint& p0, const SkPoint& p1) {
     static constexpr SkScalar kClose = (SK_Scalar1 / 16);
     static constexpr SkScalar kCloseSqd = kClose * kClose;
@@ -269,6 +254,9 @@
                                                  SkTDArray<int>* umbraIndices,
                                                  const SkTDArray<SkPoint>& penumbraPolygon,
                                                  SkTDArray<int>* penumbraIndices) {
+    // TODO: only create and fill indexMap when fTransparent is true?
+    SkAutoSTMalloc<64, uint16_t> indexMap(umbraPolygon.count());
+
     // find minimum indices
     int minIndex = 0;
     int min = (*penumbraIndices)[0];
@@ -311,6 +299,7 @@
     *fPositions.push() = umbraPolygon[currUmbra];
     *fColors.push() = fUmbraColor;
     fPrevUmbraIndex = 1;
+    indexMap[currUmbra] = 1;
 
     int nextPenumbra = (currPenumbra + 1) % penumbraPolygon.count();
     int nextUmbra = (currUmbra + 1) % umbraPolygon.count();
@@ -326,6 +315,7 @@
             *fPositions.push() = umbraPolygon[nextUmbra];
             *fColors.push() = fUmbraColor;
             int currUmbraIndex = fPositions.count() - 1;
+            indexMap[nextUmbra] = currUmbraIndex;
 
             this->appendQuad(prevPenumbraIndex, currPenumbraIndex,
                              fPrevUmbraIndex, currUmbraIndex);
@@ -363,6 +353,7 @@
             *fPositions.push() = umbraPolygon[nextUmbra];
             *fColors.push() = fUmbraColor;
             int currUmbraIndex = fPositions.count() - 1;
+            indexMap[nextUmbra] = currUmbraIndex;
 
             this->appendTriangle(fPrevUmbraIndex, prevPenumbraIndex, currUmbraIndex);
 
@@ -381,12 +372,14 @@
     *fPositions.push() = umbraPolygon[nextUmbra];
     *fColors.push() = fUmbraColor;
     int currUmbraIndex = fPositions.count() - 1;
+    indexMap[nextUmbra] = currUmbraIndex;
 
     this->appendQuad(prevPenumbraIndex, currPenumbraIndex,
                      fPrevUmbraIndex, currUmbraIndex);
 
     if (fTransparent) {
-        // TODO: fill penumbra
+        SkTriangulateSimplePolygon(umbraPolygon.begin(), indexMap, umbraPolygon.count(),
+                                   &fIndices);
     }
 }
 
@@ -508,7 +501,7 @@
     // fill in fan from previous quad
     SkScalar rotSin, rotCos;
     int numSteps;
-    compute_radial_steps(fPrevOutset, nextNormal, fRadius, &rotSin, &rotCos, &numSteps);
+    SkComputeRadialSteps(fPrevOutset, nextNormal, fRadius, &rotSin, &rotCos, &numSteps);
     SkVector prevNormal = fPrevOutset;
     for (int i = 0; i < numSteps-1; ++i) {
         SkVector currNormal;
@@ -801,8 +794,7 @@
 }
 
 bool SkAmbientShadowTessellator::computeConcaveShadow() {
-    // TODO: remove when we support filling the penumbra
-    if (fTransparent) {
+    if (!SkIsSimplePolygon(&fPathPolygon[0], fPathPolygon.count())) {
         return false;
     }
 
@@ -1418,8 +1410,7 @@
 }
 
 bool SkSpotShadowTessellator::computeConcaveShadow(SkScalar radius) {
-    // TODO: remove when we support filling the penumbra
-    if (fTransparent) {
+    if (!SkIsSimplePolygon(&fPathPolygon[0], fPathPolygon.count())) {
         return false;
     }
 
diff --git a/src/utils/SkShadowUtils.cpp b/src/utils/SkShadowUtils.cpp
index b4fba5f..6d5b7e1 100644
--- a/src/utils/SkShadowUtils.cpp
+++ b/src/utils/SkShadowUtils.cpp
@@ -542,9 +542,11 @@
 void SkBaseDevice::drawShadow(const SkPath& path, const SkDrawShadowRec& rec) {
     auto drawVertsProc = [this](const SkVertices* vertices, SkBlendMode mode, const SkPaint& paint,
                                 SkScalar tx, SkScalar ty) {
-        SkAutoDeviceCTMRestore adr(this, SkMatrix::Concat(this->ctm(),
-                                                          SkMatrix::MakeTrans(tx, ty)));
-        this->drawVertices(vertices, mode, paint);
+        if (vertices->vertexCount()) {
+            SkAutoDeviceCTMRestore adr(this, SkMatrix::Concat(this->ctm(),
+                                                              SkMatrix::MakeTrans(tx, ty)));
+            this->drawVertices(vertices, mode, paint);
+        }
     };
 
     if (!validate_rec(rec)) {
diff --git a/tests/InsetConvexPolyTest.cpp b/tests/InsetConvexPolyTest.cpp
index 433cf23..022060c 100644
--- a/tests/InsetConvexPolyTest.cpp
+++ b/tests/InsetConvexPolyTest.cpp
@@ -5,7 +5,7 @@
  * found in the LICENSE file.
  */
 #include "Test.h"
-#include "SkOffsetPolygon.h"
+#include "SkPolyUtils.h"
 
 static bool is_convex(const SkTDArray<SkPoint>& poly) {
     if (poly.count() < 3) {
diff --git a/tests/OffsetSimplePolyTest.cpp b/tests/OffsetSimplePolyTest.cpp
index de720b5..547dc9e 100644
--- a/tests/OffsetSimplePolyTest.cpp
+++ b/tests/OffsetSimplePolyTest.cpp
@@ -5,7 +5,7 @@
  * found in the LICENSE file.
  */
 #include "Test.h"
-#include "SkOffsetPolygon.h"
+#include "SkPolyUtils.h"
 
 static bool is_convex(const SkTDArray<SkPoint>& poly) {
     if (poly.count() < 3) {
@@ -200,10 +200,7 @@
     *intersectingPoly.push() = SkPoint::Make(-43.30f, -25.0f);
     *intersectingPoly.push() = SkPoint::Make(-14.43f, -25.0f);
 
-    result = SkOffsetSimplePolygon(&intersectingPoly[0], intersectingPoly.count(), -100,
-                                   &offsetPoly);
+    // SkOffsetSimplePolygon now assumes that the input is simple, so we'll just check for that
+    result = SkIsSimplePolygon(&intersectingPoly[0], intersectingPoly.count());
     REPORTER_ASSERT(reporter, !result);
-
-
-
 }