Implement edge AA for concave polys in the tesselated path renderer.

Review URL:  http://codereview.appspot.com/4571072/



git-svn-id: http://skia.googlecode.com/svn/trunk@1600 2bbb7eff-a529-9590-31e7-b0007b416f81
diff --git a/gpu/include/GrDrawTarget.h b/gpu/include/GrDrawTarget.h
index d8d0498..d576d80 100644
--- a/gpu/include/GrDrawTarget.h
+++ b/gpu/include/GrDrawTarget.h
@@ -89,6 +89,10 @@
         kNoColorWrites_StateBit = 0x08, //<! If set it disables writing colors.
                                         //   Useful while performing stencil
                                         //   ops.
+        kEdgeAAConcave_StateBit =  0x10,//<! If set, edge AA will test edge
+                                        //   pairs for convexity while 
+                                        //   rasterizing.  Set this if the
+                                        //   source polygon is non-convex.
 
         // subclass may use additional bits internally
         kDummyStateBit,
diff --git a/gpu/src/GrGLProgram.cpp b/gpu/src/GrGLProgram.cpp
index ecb4753..b0d41aa 100644
--- a/gpu/src/GrGLProgram.cpp
+++ b/gpu/src/GrGLProgram.cpp
@@ -510,21 +510,39 @@
                 segments.fFSCode.appendS32(i);
                 segments.fFSCode.append("], pos), 0.0, 1.0);\n");
             }
-            segments.fFSCode.append("\tfloat edgeAlpha = ");
-            for (int i = 0; i < count - 1; i++) {
-                segments.fFSCode.append("min(a");
-                segments.fFSCode.appendS32(i);
-                segments.fFSCode.append(" * a");
-                segments.fFSCode.appendS32(i + 1);
-                segments.fFSCode.append(", ");
+            if (fProgramDesc.fEdgeAAConcave && (count & 0x01) == 0) {
+                // For concave polys, we consider the edges in pairs.
+                segments.fFSFunctions.append("float cross2(vec2 a, vec2 b) {\n");
+                segments.fFSFunctions.append("\treturn dot(a, vec2(b.y, -b.x));\n");
+                segments.fFSFunctions.append("}\n");
+                for (int i = 0; i < count; i += 2) {
+                    segments.fFSCode.appendf("\tfloat eb%d;\n", i / 2);
+                    segments.fFSCode.appendf("\tif (cross2(" EDGES_UNI_NAME "[%d].xy, " EDGES_UNI_NAME "[%d].xy) < 0.0) {\n", i, i + 1);
+                    segments.fFSCode.appendf("\t\teb%d = a%d * a%d;\n", i / 2, i, i + 1);
+                    segments.fFSCode.append("\t} else {\n");
+                    segments.fFSCode.appendf("\t\teb%d = a%d + a%d - a%d * a%d;\n", i / 2, i, i + 1, i, i + 1);
+                    segments.fFSCode.append("\t}\n");
+                }
+                segments.fFSCode.append("\tfloat edgeAlpha = ");
+                for (int i = 0; i < count / 2 - 1; i++) {
+                    segments.fFSCode.appendf("min(eb%d, ", i);
+                }
+                segments.fFSCode.appendf("eb%d", count / 2 - 1);
+                for (int i = 0; i < count / 2 - 1; i++) {
+                    segments.fFSCode.append(")");
+                }
+                segments.fFSCode.append(";\n");
+            } else {
+                segments.fFSCode.append("\tfloat edgeAlpha = ");
+                for (int i = 0; i < count - 1; i++) {
+                    segments.fFSCode.appendf("min(a%d * a%d, ", i, i + 1);
+                }
+                segments.fFSCode.appendf("a%d * a0", count - 1);
+                for (int i = 0; i < count - 1; i++) {
+                    segments.fFSCode.append(")");
+                }
+                segments.fFSCode.append(";\n");
             }
-            segments.fFSCode.append("a");
-            segments.fFSCode.appendS32(count - 1);
-            segments.fFSCode.append(" * a0");
-            for (int i = 0; i < count - 1; i++) {
-                segments.fFSCode.append(")");
-            }
-            segments.fFSCode.append(";\n");
             inCoverage = "edgeAlpha";
         }
 
@@ -702,6 +720,11 @@
         lengths[stringCnt] = segments.fFSOutputs.size();
         ++stringCnt;
     }
+    if (segments.fFSFunctions.size()) {
+        strings[stringCnt] = segments.fFSFunctions.c_str();
+        lengths[stringCnt] = segments.fFSFunctions.size();
+        ++stringCnt;
+    }
 
     GrAssert(segments.fFSCode.size());
     strings[stringCnt] = segments.fFSCode.c_str();
@@ -714,6 +737,7 @@
     GrPrintf(segments.fFSUnis.c_str());
     GrPrintf(segments.fVaryings.c_str());
     GrPrintf(segments.fFSOutputs.c_str());
+    GrPrintf(segments.fFSFunctions.c_str());
     GrPrintf(segments.fFSCode.c_str());
     GrPrintf("\n");
 #endif
diff --git a/gpu/src/GrGLProgram.h b/gpu/src/GrGLProgram.h
index 473bcb6..5c4727b 100644
--- a/gpu/src/GrGLProgram.h
+++ b/gpu/src/GrGLProgram.h
@@ -32,6 +32,7 @@
     GrStringBuilder fVaryings;
     GrStringBuilder fFSUnis;
     GrStringBuilder fFSOutputs;
+    GrStringBuilder fFSFunctions;
     GrStringBuilder fVSCode;
     GrStringBuilder fFSCode;
 };
@@ -161,11 +162,12 @@
         uint8_t fDualSrcOutput;  // casts to enum DualSrcOutput
         int8_t fFirstCoverageStage;
         SkBool8 fEmitsPointSize;
+        SkBool8 fEdgeAAConcave;
 
         int8_t fEdgeAANumEdges;
         uint8_t fColorFilterXfermode;  // casts to enum SkXfermode::Mode
 
-        uint8_t fPadTo32bLengthMultiple [2];
+        uint8_t fPadTo32bLengthMultiple [1];
 
     } fProgramDesc;
 
diff --git a/gpu/src/GrGpuGLShaders.cpp b/gpu/src/GrGpuGLShaders.cpp
index 0a933b5..689c1fc 100644
--- a/gpu/src/GrGpuGLShaders.cpp
+++ b/gpu/src/GrGpuGLShaders.cpp
@@ -199,6 +199,7 @@
         pdesc.fFirstCoverageStage = idx;
 
         pdesc.fEdgeAANumEdges = (random.nextF() * (getMaxEdges() + 1));
+        pdesc.fEdgeAAConcave = random.nextF() > .5f;
 
         if (fDualSourceBlendingSupport) {
             pdesc.fDualSrcOutput =
@@ -731,6 +732,7 @@
     }
 
     desc.fEdgeAANumEdges = fCurrDrawState.fEdgeAANumEdges;
+    desc.fEdgeAAConcave = desc.fEdgeAANumEdges > 0 && SkToBool(fCurrDrawState.fFlagBits & kEdgeAAConcave_StateBit);
 
     int lastEnabledStage = -1;
 
diff --git a/gpu/src/GrPathUtils.cpp b/gpu/src/GrPathUtils.cpp
index 8a72ba8..1fb043c 100644
--- a/gpu/src/GrPathUtils.cpp
+++ b/gpu/src/GrPathUtils.cpp
@@ -111,7 +111,7 @@
 
     bool first = true;
 
-    SkPath::Iter iter(path, true);
+    SkPath::Iter iter(path, false);
     GrPathCmd cmd;
 
     GrPoint pts[4];
diff --git a/gpu/src/GrTesselatedPathRenderer.cpp b/gpu/src/GrTesselatedPathRenderer.cpp
index 0e3389c..2783a9b 100644
--- a/gpu/src/GrTesselatedPathRenderer.cpp
+++ b/gpu/src/GrTesselatedPathRenderer.cpp
@@ -21,49 +21,256 @@
 #include "GrPoint.h"
 #include "GrTDArray.h"
 
+#include <limits.h>
 #include <sk_glu.h>
 
-struct PolygonData {
-    PolygonData(GrTDArray<GrPoint>* vertices, GrTDArray<short>* indices)
-      : fVertices(vertices)
-      , fIndices(indices)
-    {
+typedef GrTDArray<GrDrawTarget::Edge> GrEdgeArray;
+typedef GrTDArray<GrPoint> GrPointArray;
+typedef GrTDArray<uint16_t> GrIndexArray;
+typedef void (*TESSCB)();
+
+// limit the allowable vertex range to approximately half of the representable
+// IEEE exponent in order to avoid overflow when doing multiplies between
+// vertex components,
+const float kMaxVertexValue = 1e18;
+
+static inline GrDrawTarget::Edge computeEdge(const GrPoint& p,
+                                             const GrPoint& q,
+                                             float sign) {
+    GrVec tangent = GrVec::Make(p.fY - q.fY, q.fX - p.fX);
+    float scale = sign / tangent.length();
+    float cross2 = p.fX * q.fY - q.fX * p.fY;
+    return GrDrawTarget::Edge(tangent.fX * scale,
+                              tangent.fY * scale,
+                              cross2 * scale);
+}
+
+static inline GrPoint sanitizePoint(const GrPoint& pt) {
+    GrPoint r;
+    r.fX = SkScalarPin(pt.fX, -kMaxVertexValue, kMaxVertexValue);
+    r.fY = SkScalarPin(pt.fY, -kMaxVertexValue, kMaxVertexValue);
+    return r;
+}
+
+class GrTess {
+public:
+    GrTess(int count, unsigned winding_rule) {
+        fTess = Sk_gluNewTess();
+        Sk_gluTessProperty(fTess, GLU_TESS_WINDING_RULE, winding_rule);
+        Sk_gluTessNormal(fTess, 0.0f, 0.0f, 1.0f);
+        Sk_gluTessCallback(fTess, GLU_TESS_BEGIN_DATA, (TESSCB) &beginCB);
+        Sk_gluTessCallback(fTess, GLU_TESS_VERTEX_DATA, (TESSCB) &vertexCB);
+        Sk_gluTessCallback(fTess, GLU_TESS_END_DATA, (TESSCB) &endCB);
+        Sk_gluTessCallback(fTess, GLU_TESS_EDGE_FLAG_DATA, (TESSCB) &edgeFlagCB);
+        Sk_gluTessCallback(fTess, GLU_TESS_COMBINE_DATA, (TESSCB) &combineCB);
+        fInVertices = new double[count * 3];
     }
-    GrTDArray<GrPoint>* fVertices;
-    GrTDArray<short>* fIndices;
+    ~GrTess() {
+        Sk_gluDeleteTess(fTess);
+        delete[] fInVertices;
+    }
+    void addVertex(const GrPoint& pt, int index) {
+        if (index > USHRT_MAX) return;
+        double* inVertex = &fInVertices[index * 3];
+        inVertex[0] = pt.fX;
+        inVertex[1] = pt.fY;
+        inVertex[2] = 0.0;
+        *fVertices.append() = pt;
+        Sk_gluTessVertex(fTess, inVertex, reinterpret_cast<void*>(index));
+    }
+    void addVertices(const GrPoint* points, const uint16_t* contours, int numContours) {
+        Sk_gluTessBeginPolygon(fTess, this);
+        size_t i = 0;
+        for (int j = 0; j < numContours; ++j) {
+            Sk_gluTessBeginContour(fTess);
+            size_t end = i + contours[j];
+            for (; i < end; ++i) {
+                addVertex(points[i], i);
+            }
+            Sk_gluTessEndContour(fTess);
+        }
+        Sk_gluTessEndPolygon(fTess);
+    }
+    GLUtesselator* tess() { return fTess; }
+    const GrPointArray& vertices() const { return fVertices; }
+protected:
+    virtual void begin(GLenum type) = 0;
+    virtual void vertex(int index) = 0;
+    virtual void edgeFlag(bool flag) = 0;
+    virtual void end() = 0;
+    virtual int combine(GLdouble coords[3], int vertexIndices[4], 
+                         GLfloat weight[4]) = 0;
+    static void beginCB(GLenum type, void* data) {
+        static_cast<GrTess*>(data)->begin(type);
+    }
+    static void vertexCB(void* vertexData, void* data) {
+        static_cast<GrTess*>(data)->vertex(reinterpret_cast<long>(vertexData));
+    }
+    static void edgeFlagCB(GLboolean flag, void* data) {
+        static_cast<GrTess*>(data)->edgeFlag(flag != 0);
+    }
+    static void endCB(void* data) {
+        static_cast<GrTess*>(data)->end();
+    }
+    static void combineCB(GLdouble coords[3], void* vertexData[4],
+                          GLfloat weight[4], void **outData, void* data) {
+        int vertexIndex[4];
+        vertexIndex[0] = reinterpret_cast<long>(vertexData[0]);
+        vertexIndex[1] = reinterpret_cast<long>(vertexData[1]);
+        vertexIndex[2] = reinterpret_cast<long>(vertexData[2]);
+        vertexIndex[3] = reinterpret_cast<long>(vertexData[3]);
+        GrTess* tess = static_cast<GrTess*>(data);
+        int outIndex = tess->combine(coords, vertexIndex, weight);
+        *reinterpret_cast<long*>(outData) = outIndex;
+    }
+protected:
+    GLUtesselator* fTess;
+    GrPointArray fVertices;
+    double* fInVertices;
 };
 
-static void beginData(GLenum type, void* data)
-{
-    GR_DEBUGASSERT(type == GL_TRIANGLES);
+class GrPolygonTess : public GrTess {
+public:
+    GrPolygonTess(int count, unsigned winding_rule)
+      : GrTess(count, winding_rule) {
+    }
+    ~GrPolygonTess() {
+    }
+    const GrIndexArray& indices() const { return fIndices; }
+protected:
+    virtual void begin(GLenum type) {
+        GR_DEBUGASSERT(type == GL_TRIANGLES);
+    }
+    virtual void vertex(int index) {
+        *fIndices.append() = index;
+    }
+    virtual void edgeFlag(bool flag) {}
+    virtual void end() {}
+    virtual int combine(GLdouble coords[3], int vertexIndices[4],
+                         GLfloat weight[4]) {
+        int index = fVertices.count();
+        GrPoint p = GrPoint::Make(static_cast<float>(coords[0]),
+                                  static_cast<float>(coords[1]));
+        *fVertices.append() = p;
+        return index;
+    }
+protected:
+    GrIndexArray fIndices;
+};
+
+class GrEdgePolygonTess : public GrPolygonTess {
+public:
+    GrEdgePolygonTess(int count, unsigned winding_rule, const SkMatrix& matrix)
+      : GrPolygonTess(count, winding_rule),
+        fMatrix(matrix),
+        fEdgeFlag(false),
+        fEdgeVertex(-1),
+        fTriStartVertex(-1),
+        fEdges(NULL) {
+    }
+    ~GrEdgePolygonTess() {
+        delete[] fEdges;
+    }
+    const GrDrawTarget::Edge* edges() const { return fEdges; }
+private:
+    void addEdge(int index0, int index1) {
+        GrPoint p = fVertices[index0];
+        GrPoint q = fVertices[index1];
+        fMatrix.mapPoints(&p, 1);
+        fMatrix.mapPoints(&q, 1);
+        p = sanitizePoint(p);
+        q = sanitizePoint(q);
+        if (p == q) return;
+        GrDrawTarget::Edge edge = computeEdge(p, q, 1.0f);
+        fEdges[index0 * 2 + 1] = edge;
+        fEdges[index1 * 2] = edge;
+    }
+    virtual void begin(GLenum type) {
+        GR_DEBUGASSERT(type == GL_TRIANGLES);
+        int count = fVertices.count() * 2;
+        fEdges = new GrDrawTarget::Edge[count];
+        memset(fEdges, 0, count * sizeof(GrDrawTarget::Edge));
+    }
+    virtual void edgeFlag(bool flag) {
+        fEdgeFlag = flag;
+    }
+    virtual void vertex(int index) {
+        bool triStart = fIndices.count() % 3 == 0;
+        GrPolygonTess::vertex(index);
+        if (fEdgeVertex != -1) {
+            if (triStart) {
+                addEdge(fEdgeVertex, fTriStartVertex);
+            } else {
+                addEdge(fEdgeVertex, index);
+            }
+        }
+        if (triStart) {
+            fTriStartVertex = index;
+        }
+        if (fEdgeFlag) {
+            fEdgeVertex = index;
+        } else {
+            fEdgeVertex = -1;
+        }
+    }
+    virtual void end() {
+        if (fEdgeVertex != -1) {
+            addEdge(fEdgeVertex, fTriStartVertex);
+        }
+    }
+    GrMatrix fMatrix;
+    bool fEdgeFlag;
+    int fEdgeVertex, fTriStartVertex;
+    GrDrawTarget::Edge* fEdges;
+};
+
+class GrBoundaryTess : public GrTess {
+public:
+    GrBoundaryTess(int count, unsigned winding_rule)
+      : GrTess(count, winding_rule),
+        fContourStart(0) {
+        Sk_gluTessProperty(fTess, GLU_TESS_BOUNDARY_ONLY, 1);
+    }
+    ~GrBoundaryTess() {
+    }
+    GrPointArray& contourPoints() { return fContourPoints; }
+    const GrIndexArray& contours() const { return fContours; }
+private:
+    virtual void begin(GLenum type) {
+        fContourStart = fContourPoints.count();
+    }
+    virtual void vertex(int index) {
+        *fContourPoints.append() = fVertices.at(index);
+    }
+    virtual void edgeFlag(bool flag) {}
+    virtual void end() {
+        *fContours.append() = fContourPoints.count() - fContourStart;
+    }
+    virtual int combine(GLdouble coords[3], int vertexIndices[4],
+                        GLfloat weight[4]) {
+        int index = fVertices.count();
+        *fVertices.append() = GrPoint::Make(static_cast<float>(coords[0]),
+                                            static_cast<float>(coords[1]));
+        return index;
+    }
+    GrPointArray fContourPoints;
+    GrIndexArray fContours;
+    size_t fContourStart;
+};
+
+static bool nearlyEqual(float a, float b) {
+    return fabsf(a - b) < 0.0001f;
 }
 
-static void edgeFlagData(GLboolean flag, void* data)
-{
+static bool nearlyEqual(const GrPoint& a, const GrPoint& b) {
+    return nearlyEqual(a.fX, b.fX) && nearlyEqual(a.fY, b.fY);
 }
 
-static void vertexData(void* vertexData, void* data)
-{
-    short* end = static_cast<PolygonData*>(data)->fIndices->append();
-    *end = reinterpret_cast<long>(vertexData);
+static bool parallel(const GrDrawTarget::Edge& a, const GrDrawTarget::Edge& b) {
+    return (nearlyEqual(a.fX, b.fX) && nearlyEqual(a.fY, b.fY)) ||
+           (nearlyEqual(a.fX, -b.fX) && nearlyEqual(a.fY, -b.fY));
 }
 
-static void endData(void* data)
-{
-}
-
-static void combineData(GLdouble coords[3], void* vertexData[4],
-                                 GLfloat weight[4], void **outData, void* data)
-{
-    PolygonData* polygonData = static_cast<PolygonData*>(data);
-    int index = polygonData->fVertices->count();
-    *polygonData->fVertices->append() = GrPoint::Make(static_cast<float>(coords[0]),
-                                                      static_cast<float>(coords[1]));
-    *outData = reinterpret_cast<void*>(index);
-}
-
-typedef void (*TESSCB)();
-
 static unsigned fill_type_to_glu_winding_rule(GrPathFill fill) {
     switch (fill) {
         case kWinding_PathFill:
@@ -85,40 +292,59 @@
 GrTesselatedPathRenderer::GrTesselatedPathRenderer() {
 }
 
-typedef GrTDArray<GrDrawTarget::Edge> EdgeArray;
-
-bool isCCW(const GrPoint* pts)
-{
-    GrVec v1 = pts[1] - pts[0];
-    GrVec v2 = pts[2] - pts[1];
+static bool isCCW(const GrPoint* pts, int count) {
+    GrVec v1, v2;
+    do {
+        v1 = pts[1] - pts[0];
+        v2 = pts[2] - pts[1];
+        pts++;
+        count--;
+    } while (nearlyEqual(v1, v2) && count > 3);
     return v1.cross(v2) < 0;
 }
 
-static size_t computeEdgesAndOffsetVertices(const GrMatrix& matrix,
-                                            const GrMatrix& inverse,
-                                            GrPoint* vertices,
-                                            size_t numVertices,
-                                            EdgeArray* edges)
-{
+static bool validEdge(const GrDrawTarget::Edge& edge) {
+    return !(edge.fX == 0.0f && edge.fY == 0.0f && edge.fZ == 0.0f);
+}
+
+static size_t computeEdgesAndIntersect(const GrMatrix& matrix,
+                                       const GrMatrix& inverse,
+                                       GrPoint* vertices,
+                                       size_t numVertices,
+                                       GrEdgeArray* edges,
+                                       float sign) {
+    if (numVertices < 3) {
+        return 0;
+    }
     matrix.mapPoints(vertices, numVertices);
-    GrPoint p = vertices[numVertices - 1];
-    float sign = isCCW(vertices) ? -1.0f : 1.0f;
+    if (sign == 0.0f) {
+        sign = isCCW(vertices, numVertices) ? -1.0f : 1.0f;
+    }
+    GrPoint p = sanitizePoint(vertices[numVertices - 1]);
     for (size_t i = 0; i < numVertices; ++i) {
-        GrPoint q = vertices[i];
-        if (p == q) continue;
-        GrVec tangent = GrVec::Make(p.fY - q.fY, q.fX - p.fX);
-        float scale = sign / tangent.length();
-        float cross2 = p.fX * q.fY - q.fX * p.fY;
-        GrDrawTarget::Edge edge(tangent.fX * scale,
-                  tangent.fY * scale,
-                  cross2 * scale + 0.5f);
+        GrPoint q = sanitizePoint(vertices[i]);
+        if (p == q) {
+            continue;
+        }
+        GrDrawTarget::Edge edge = computeEdge(p, q, sign);
+        edge.fZ += 0.5f;    // Offset by half a pixel along the tangent.
         *edges->append() = edge;
         p = q;
     }
-    GrDrawTarget::Edge prev_edge = *edges->back();
-    for (int i = 0; i < edges->count(); ++i) {
-        GrDrawTarget::Edge edge = edges->at(i);
-        vertices[i] = prev_edge.intersect(edge);
+    int count = edges->count();
+    if (count == 0) {
+        return 0;
+    }
+    GrDrawTarget::Edge prev_edge = edges->at(0);
+    for (int i = 0; i < count; ++i) {
+        GrDrawTarget::Edge edge = edges->at(i < count - 1 ? i + 1 : 0);
+        if (parallel(edge, prev_edge)) {
+            // 3 points are collinear; offset by half the tangent instead
+            vertices[i].fX -= edge.fX * 0.5f;
+            vertices[i].fY -= edge.fY * 0.5f;
+        } else {
+            vertices[i] = prev_edge.intersect(edge);
+        }
         inverse.mapPoints(&vertices[i], 1);
         prev_edge = edge;
     }
@@ -164,14 +390,18 @@
         maxPts += 4;
         subpathCnt++;
     }
-    GrPoint* base = new GrPoint[maxPts];
+    if (maxPts > USHRT_MAX) {
+        return;
+    }
+    GrAutoSTMalloc<8, GrPoint> baseMem(maxPts);
+    GrPoint* base = (GrPoint*) baseMem;
     GrPoint* vert = base;
     GrPoint* subpathBase = base;
 
     GrAutoSTMalloc<8, uint16_t> subpathVertCount(subpathCnt);
 
     GrPoint pts[4];
-    SkPath::Iter iter(path, true);
+    SkPath::Iter iter(path, false);
 
     bool first = true;
     int subpath = 0;
@@ -242,18 +472,20 @@
     size_t count = vert - base;
 
     if (count < 3) {
-      delete[] base;
-      return;
+        return;
     }
 
     if (subpathCnt == 1 && !inverted && path.isConvex()) {
         if (target->isAntialiasState()) {
-            EdgeArray edges;
+            GrEdgeArray edges;
             GrMatrix inverse, matrix = target->getViewMatrix();
             target->getViewInverse(&inverse);
 
-            count = computeEdgesAndOffsetVertices(matrix, inverse, base, count, &edges);
+            count = computeEdgesAndIntersect(matrix, inverse, base, count, &edges, 0.0f);
             size_t maxEdges = target->getMaxEdges();
+            if (count == 0) {
+                return;
+            }
             if (count <= maxEdges) {
                 // All edges fit; upload all edges and draw all verts as a fan
                 target->setVertexSourceToArray(layout, base, count);
@@ -276,48 +508,96 @@
             target->setVertexSourceToArray(layout, base, count);
             target->drawNonIndexed(kTriangleFan_PrimitiveType, 0, count);
         }
-        delete[] base;
         return;
     }
 
-    // FIXME:  This copy could be removed if we had (templated?) versions of
-    // generate_*_point above that wrote directly into doubles.
-    double* inVertices = new double[count * 3];
-    for (size_t i = 0; i < count; ++i) {
-        inVertices[i * 3]     = base[i].fX;
-        inVertices[i * 3 + 1] = base[i].fY;
-        inVertices[i * 3 + 2] = 1.0;
-    }
+    if (target->isAntialiasState()) {
+        // Run the tesselator once to get the boundaries.
+        GrBoundaryTess btess(count, fill_type_to_glu_winding_rule(fill));
+        btess.addVertices(base, subpathVertCount, subpathCnt);
 
-    GLUtesselator* tess = Sk_gluNewTess();
-    unsigned windingRule = fill_type_to_glu_winding_rule(fill);
-    Sk_gluTessProperty(tess, GLU_TESS_WINDING_RULE, windingRule);
-    Sk_gluTessCallback(tess, GLU_TESS_BEGIN_DATA, (TESSCB) &beginData);
-    Sk_gluTessCallback(tess, GLU_TESS_VERTEX_DATA, (TESSCB) &vertexData);
-    Sk_gluTessCallback(tess, GLU_TESS_END_DATA, (TESSCB) &endData);
-    Sk_gluTessCallback(tess, GLU_TESS_EDGE_FLAG_DATA, (TESSCB) &edgeFlagData);
-    Sk_gluTessCallback(tess, GLU_TESS_COMBINE_DATA, (TESSCB) &combineData);
-    GrTDArray<short> indices;
-    GrTDArray<GrPoint> vertices;
-    PolygonData data(&vertices, &indices);
-
-    Sk_gluTessBeginPolygon(tess, &data);
-    size_t i = 0;
-    for (int sp = 0; sp < subpathCnt; ++sp) {
-        Sk_gluTessBeginContour(tess);
-        int start = i;
-        size_t end = start + subpathVertCount[sp];
-        for (; i < end; ++i) {
-            double* inVertex = &inVertices[i * 3];
-            *vertices.append() = GrPoint::Make(inVertex[0], inVertex[1]);
-            Sk_gluTessVertex(tess, inVertex, reinterpret_cast<void*>(i));
+        GrMatrix inverse, matrix = target->getViewMatrix();
+        if (!target->getViewInverse(&inverse)) {
+            return;
         }
-        Sk_gluTessEndContour(tess);
+
+        if (btess.vertices().count() > USHRT_MAX) {
+            return;
+        }
+
+        // Inflate the boundary, and run the tesselator again to generate
+        // interior polys.
+        const GrPointArray& contourPoints = btess.contourPoints();
+        const GrIndexArray& contours = btess.contours();
+        GrEdgePolygonTess ptess(contourPoints.count(), GLU_TESS_WINDING_NONZERO, matrix);
+
+        size_t i = 0;
+        Sk_gluTessBeginPolygon(ptess.tess(), &ptess);
+        for (int contour = 0; contour < contours.count(); ++contour) {
+            int count = contours[contour];
+            GrEdgeArray edges;
+            int newCount = computeEdgesAndIntersect(matrix, inverse, &btess.contourPoints()[i], count, &edges, 1.0f);
+            Sk_gluTessBeginContour(ptess.tess());
+            for (int j = 0; j < newCount; j++) {
+                ptess.addVertex(contourPoints[i + j], ptess.vertices().count());
+            }
+            i += count;
+            Sk_gluTessEndContour(ptess.tess());
+        }
+
+        Sk_gluTessEndPolygon(ptess.tess());
+
+        if (ptess.vertices().count() > USHRT_MAX) {
+            return;
+        }
+
+        // Draw the resulting polys and upload their edge data.
+        target->enableState(GrDrawTarget::kEdgeAAConcave_StateBit);
+        const GrPointArray& vertices = ptess.vertices();
+        const GrIndexArray& indices = ptess.indices();
+        const GrDrawTarget::Edge* edges = ptess.edges();
+        GR_DEBUGASSERT(indices.count() % 3 == 0);
+        for (int i = 0; i < indices.count(); i += 3) {
+            GrPoint tri_verts[3];
+            int index0 = indices[i];
+            int index1 = indices[i + 1];
+            int index2 = indices[i + 2];
+            tri_verts[0] = vertices[index0];
+            tri_verts[1] = vertices[index1];
+            tri_verts[2] = vertices[index2];
+            GrDrawTarget::Edge tri_edges[6];
+            int t = 0;
+            const GrDrawTarget::Edge& edge0 = edges[index0 * 2];
+            const GrDrawTarget::Edge& edge1 = edges[index0 * 2 + 1];
+            const GrDrawTarget::Edge& edge2 = edges[index1 * 2];
+            const GrDrawTarget::Edge& edge3 = edges[index1 * 2 + 1];
+            const GrDrawTarget::Edge& edge4 = edges[index2 * 2];
+            const GrDrawTarget::Edge& edge5 = edges[index2 * 2 + 1];
+            if (validEdge(edge0) && validEdge(edge1)) {
+                tri_edges[t++] = edge0;
+                tri_edges[t++] = edge1;
+            }
+            if (validEdge(edge2) && validEdge(edge3)) {
+                tri_edges[t++] = edge2;
+                tri_edges[t++] = edge3;
+            }
+            if (validEdge(edge4) && validEdge(edge5)) {
+                tri_edges[t++] = edge4;
+                tri_edges[t++] = edge5;
+            }
+            target->setEdgeAAData(&tri_edges[0], t);
+            target->setVertexSourceToArray(layout, &tri_verts[0], 3);
+            target->drawNonIndexed(kTriangles_PrimitiveType, 0, 3);
+        }
+        target->setEdgeAAData(NULL, 0);
+        target->disableState(GrDrawTarget::kEdgeAAConcave_StateBit);
+        return;
     }
 
-    Sk_gluTessEndPolygon(tess);
-    Sk_gluDeleteTess(tess);
-
+    GrPolygonTess ptess(count, fill_type_to_glu_winding_rule(fill));
+    ptess.addVertices(base, subpathVertCount, subpathCnt);
+    const GrPointArray& vertices = ptess.vertices();
+    const GrIndexArray& indices = ptess.indices();
     if (indices.count() > 0) {
         target->setVertexSourceToArray(layout, vertices.begin(), vertices.count());
         target->setIndexSourceToArray(indices.begin(), indices.count());
@@ -327,8 +607,6 @@
                             vertices.count(),
                             indices.count());
     }
-    delete[] inVertices;
-    delete[] base;
 }
 
 bool GrTesselatedPathRenderer::canDrawPath(const GrDrawTarget* target,
@@ -347,10 +625,5 @@
 bool GrTesselatedPathRenderer::supportsAA(GrDrawTarget* target,
                                                   const SkPath& path,
                                                   GrPathFill fill) {
-    int subpathCnt = 0;
-    int tol = GrPathUtils::gTolerance;
-    GrPathUtils::worstCasePointCount(path, &subpathCnt, tol);
-    return (subpathCnt == 1 &&
-            !IsFillInverted(fill) &&
-            path.isConvex());
+    return true;
 }
diff --git a/gyp/SampleApp.gyp b/gyp/SampleApp.gyp
index f0446d9..04e07d6 100644
--- a/gyp/SampleApp.gyp
+++ b/gyp/SampleApp.gyp
@@ -43,6 +43,7 @@
         '../samplecode/SampleCode.h',
         '../samplecode/SampleColorFilter.cpp',
         '../samplecode/SampleComplexClip.cpp',
+        '../samplecode/SampleConcavePaths.cpp',
         '../samplecode/SampleCull.cpp',
         '../samplecode/SampleDecode.cpp',
         '../samplecode/SampleDither.cpp',
diff --git a/samplecode/SampleConcavePaths.cpp b/samplecode/SampleConcavePaths.cpp
new file mode 100644
index 0000000..e084af2
--- /dev/null
+++ b/samplecode/SampleConcavePaths.cpp
@@ -0,0 +1,146 @@
+
+#include "SampleCode.h"
+#include "SkView.h"
+#include "SkCanvas.h"
+#include "SkGradientShader.h"
+#include "SkGraphics.h"
+#include "SkImageDecoder.h"
+#include "SkPath.h"
+#include "SkRegion.h"
+#include "SkShader.h"
+#include "SkUtils.h"
+#include "SkXfermode.h"
+#include "SkColorPriv.h"
+#include "SkColorFilter.h"
+#include "SkParsePath.h"
+#include "SkTime.h"
+#include "SkTypeface.h"
+
+#include "SkGeometry.h"
+
+class ConcavePathView : public SampleView {
+public:
+	ConcavePathView() {}
+    
+protected:
+    // overrides from SkEventSink
+    virtual bool onQuery(SkEvent* evt) {
+        if (SampleCode::TitleQ(*evt)) {
+            SampleCode::TitleR(evt, "ConcavePaths");
+            return true;
+        }
+        return this->INHERITED::onQuery(evt);
+    }
+    
+    virtual void onDrawContent(SkCanvas* canvas) {        
+        SkPaint paint;
+        
+        paint.setAntiAlias(true);
+        paint.setStyle(SkPaint::kFill_Style);
+
+        // Concave test
+        if (1) {
+            SkPath path;
+            canvas->translate(0, 0);
+            path.moveTo(SkIntToScalar(20), SkIntToScalar(20));
+            path.lineTo(SkIntToScalar(80), SkIntToScalar(20));
+            path.lineTo(SkIntToScalar(30), SkIntToScalar(30));
+            path.lineTo(SkIntToScalar(20), SkIntToScalar(80));
+            canvas->drawPath(path, paint);
+        }
+        // Reverse concave test
+        if (1) {
+            SkPath path;
+            canvas->save();
+            canvas->translate(100, 0);
+            path.moveTo(SkIntToScalar(20), SkIntToScalar(20));
+            path.lineTo(SkIntToScalar(20), SkIntToScalar(80));
+            path.lineTo(SkIntToScalar(30), SkIntToScalar(30));
+            path.lineTo(SkIntToScalar(80), SkIntToScalar(20));
+            canvas->drawPath(path, paint);
+            canvas->restore();
+        }
+        // Bowtie (intersection)
+        if (1) {
+            SkPath path;
+            canvas->save();
+            canvas->translate(200, 0);
+            path.moveTo(SkIntToScalar(20), SkIntToScalar(20));
+            path.lineTo(SkIntToScalar(80), SkIntToScalar(80));
+            path.lineTo(SkIntToScalar(80), SkIntToScalar(20));
+            path.lineTo(SkIntToScalar(20), SkIntToScalar(80));
+            canvas->drawPath(path, paint);
+            canvas->restore();
+        }
+        // "fake" bowtie (concave, but no intersection)
+        if (1) {
+            SkPath path;
+            canvas->save();
+            canvas->translate(300, 0);
+            path.moveTo(SkIntToScalar(20), SkIntToScalar(20));
+            path.lineTo(SkIntToScalar(50), SkIntToScalar(40));
+            path.lineTo(SkIntToScalar(80), SkIntToScalar(20));
+            path.lineTo(SkIntToScalar(80), SkIntToScalar(80));
+            path.lineTo(SkIntToScalar(50), SkIntToScalar(60));
+            path.lineTo(SkIntToScalar(20), SkIntToScalar(80));
+            canvas->drawPath(path, paint);
+            canvas->restore();
+        }
+        // Fish test (intersection/concave)
+        if (1) {
+            SkPath path;
+            canvas->save();
+            canvas->translate(0, 100);
+            path.moveTo(SkIntToScalar(20), SkIntToScalar(20));
+            path.lineTo(SkIntToScalar(80), SkIntToScalar(80));
+            path.lineTo(SkIntToScalar(70), SkIntToScalar(50));
+            path.lineTo(SkIntToScalar(80), SkIntToScalar(20));
+            path.lineTo(SkIntToScalar(20), SkIntToScalar(80));
+            path.lineTo(SkIntToScalar(0), SkIntToScalar(50));
+            canvas->drawPath(path, paint);
+            canvas->restore();
+        }
+        // Collinear test
+        if (1) {
+            SkPath path;
+            canvas->save();
+            canvas->translate(100, 100);
+            path.moveTo(SkIntToScalar(20), SkIntToScalar(20));
+            path.lineTo(SkIntToScalar(50), SkIntToScalar(20));
+            path.lineTo(SkIntToScalar(80), SkIntToScalar(20));
+            path.lineTo(SkIntToScalar(50), SkIntToScalar(80));
+            canvas->drawPath(path, paint);
+            canvas->restore();
+        }
+        // Hole test
+        if (1) {
+            SkPath path;
+            canvas->save();
+            canvas->translate(200, 100);
+            path.moveTo(SkIntToScalar(20), SkIntToScalar(20));
+            path.lineTo(SkIntToScalar(80), SkIntToScalar(20));
+            path.lineTo(SkIntToScalar(80), SkIntToScalar(80));
+            path.lineTo(SkIntToScalar(20), SkIntToScalar(80));
+            path.moveTo(SkIntToScalar(30), SkIntToScalar(30));
+            path.lineTo(SkIntToScalar(30), SkIntToScalar(70));
+            path.lineTo(SkIntToScalar(70), SkIntToScalar(70));
+            path.lineTo(SkIntToScalar(70), SkIntToScalar(30));
+            canvas->drawPath(path, paint);
+            canvas->restore();
+        }
+    }
+    
+    virtual SkView::Click* onFindClickHandler(SkScalar x, SkScalar y) {
+        this->inval(NULL);
+        return this->INHERITED::onFindClickHandler(x, y);
+    }
+    
+private:
+    typedef SampleView INHERITED;
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+static SkView* MyFactory() { return new ConcavePathView; }
+static SkViewRegister reg(MyFactory);
+