Indexed drawing for paths with multiple contours

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



git-svn-id: http://skia.googlecode.com/svn/trunk@1800 2bbb7eff-a529-9590-31e7-b0007b416f81
diff --git a/gpu/src/GrPathRenderer.cpp b/gpu/src/GrPathRenderer.cpp
index e82774d..0f28fa9 100644
--- a/gpu/src/GrPathRenderer.cpp
+++ b/gpu/src/GrPathRenderer.cpp
@@ -38,6 +38,7 @@
 void GrPathRenderer::clearPath() {
     this->pathWillClear();
     fTarget->resetVertexSource();
+    fTarget->resetIndexSource();
     fTarget = NULL;
     fPath = NULL;
 }
@@ -229,18 +230,40 @@
     fPreviousStages = -1;
 }
 
-void GrDefaultPathRenderer::createGeom(GrScalar srcSpaceTol, 
+static inline void append_countour_edge_indices(GrPathFill fillType,
+                                                uint16_t fanCenterIdx,
+                                                uint16_t edgeV0Idx,
+                                                uint16_t** indices) {
+    // when drawing lines we're appending line segments along
+    // the contour. When applying the other fill rules we're
+    // drawing triangle fans around fanCenterIdx.
+    if (kHairLine_PathFill != fillType) {
+        *((*indices)++) = fanCenterIdx;
+    }
+    *((*indices)++) = edgeV0Idx;
+    *((*indices)++) = edgeV0Idx + 1;
+}
+
+bool GrDefaultPathRenderer::createGeom(GrScalar srcSpaceTol, 
                                        GrDrawTarget::StageBitfield stages) {
     {
     SK_TRACE_EVENT0("GrDefaultPathRenderer::createGeom");
 
-    fPreviousSrcTol = srcSpaceTol;
-    fPreviousStages = stages;
-
     GrScalar srcSpaceTolSqd = GrMul(srcSpaceTol, srcSpaceTol);
     int maxPts = GrPathUtils::worstCasePointCount(*fPath, &fSubpathCount,
                                                   srcSpaceTol);
 
+    if (maxPts <= 0) {
+        return false;
+    }
+    if (maxPts > ((int)SK_MaxU16 + 1)) {
+        GrPrintf("Path not rendered, too many verts (%d)\n", maxPts);
+        return false;
+    }
+
+    fPreviousSrcTol = srcSpaceTol;
+    fPreviousStages = stages;
+
     GrVertexLayout layout = 0;
     for (int s = 0; s < GrDrawTarget::kNumStages; ++s) {
         if ((1 << s) & stages) {
@@ -248,12 +271,36 @@
         }
     }
 
-    // add 4 to hold the bounding rect
-    GrPoint* base;
-    fTarget->reserveVertexSpace(layout, maxPts + 4, (void**)&base);
+    fUseIndexedDraw = fSubpathCount > 1;
 
+    int maxIdxs = 0;
+    if (kHairLine_PathFill == fFill) {
+        if (fUseIndexedDraw) {
+            maxIdxs = 2 * maxPts;
+            fPrimitiveType = kLines_PrimitiveType;
+        } else {
+            fPrimitiveType = kLineStrip_PrimitiveType;
+        }
+    } else {
+        if (fUseIndexedDraw) {
+            maxIdxs = 3 * maxPts;
+            fPrimitiveType = kTriangles_PrimitiveType;
+        } else {
+            fPrimitiveType = kTriangleFan_PrimitiveType;
+        }
+    }
+
+    GrPoint* base;
+    fTarget->reserveVertexSpace(layout, maxPts, (void**)&base);
     GrPoint* vert = base;
-    GrPoint* subpathBase = base;
+
+    uint16_t* idxBase = NULL;
+    uint16_t* idx = NULL;
+    uint16_t subpathIdxStart = 0;
+    if (fUseIndexedDraw) {
+        fTarget->reserveIndexSpace(maxIdxs, (void**)&idxBase);
+        idx = idxBase;
+    }
 
     fSubpathVertCount.realloc(fSubpathCount);
 
@@ -269,41 +316,68 @@
         switch (cmd) {
             case kMove_PathCmd:
                 if (!first) {
-                    fSubpathVertCount[subpath] = vert-subpathBase;
-                    subpathBase = vert;
+                    uint16_t currIdx = (uint16_t) (vert - base);
+                    fSubpathVertCount[subpath] = currIdx - subpathIdxStart;
+                    subpathIdxStart = currIdx;
                     ++subpath;
                 }
                 *vert = pts[0];
                 vert++;
                 break;
             case kLine_PathCmd:
-                *vert = pts[1];
-                vert++;
+                if (fUseIndexedDraw) {
+                    uint16_t prevIdx = (uint16_t)(vert - base) - 1;
+                    append_countour_edge_indices(fFill, subpathIdxStart,
+                                                 prevIdx, &idx);
+                }
+                *(vert++) = pts[1];
                 break;
             case kQuadratic_PathCmd: {
-                GrPathUtils::generateQuadraticPoints(pts[0], pts[1], pts[2],
-                                                     srcSpaceTolSqd, &vert,
-                                                     GrPathUtils::quadraticPointCount(pts, srcSpaceTol));
+                // first pt of quad is the pt we ended on in previous step
+                uint16_t firstQPtIdx = (uint16_t)(vert - base) - 1;
+                uint16_t numPts =  (uint16_t) 
+                    GrPathUtils::generateQuadraticPoints(
+                            pts[0], pts[1], pts[2],
+                            srcSpaceTolSqd, &vert,
+                            GrPathUtils::quadraticPointCount(pts, srcSpaceTol));
+                if (fUseIndexedDraw) {
+                    for (uint16_t i = 0; i < numPts; ++i) {
+                        append_countour_edge_indices(fFill, subpathIdxStart,
+                                                     firstQPtIdx + i, &idx);
+                    }
+                }
                 break;
             }
             case kCubic_PathCmd: {
-                GrPathUtils::generateCubicPoints(pts[0], pts[1], pts[2], pts[3],
-                                                 srcSpaceTolSqd, &vert,
-                                                 GrPathUtils::cubicPointCount(pts, srcSpaceTol));
+                // first pt of cubic is the pt we ended on in previous step
+                uint16_t firstCPtIdx = (uint16_t)(vert - base) - 1;
+                uint16_t numPts = (uint16_t) GrPathUtils::generateCubicPoints(
+                                pts[0], pts[1], pts[2], pts[3],
+                                srcSpaceTolSqd, &vert,
+                                GrPathUtils::cubicPointCount(pts, srcSpaceTol));
+                if (fUseIndexedDraw) {
+                    for (uint16_t i = 0; i < numPts; ++i) {
+                        append_countour_edge_indices(fFill, subpathIdxStart,
+                                                     firstCPtIdx + i, &idx);
+                    }
+                }
                 break;
             }
             case kClose_PathCmd:
                 break;
             case kEnd_PathCmd:
-                fSubpathVertCount[subpath] = vert-subpathBase;
-                ++subpath; // this could be only in debug
+                uint16_t currIdx = (uint16_t) (vert - base);
+                fSubpathVertCount[subpath] = currIdx - subpathIdxStart;
                 goto FINISHED;
         }
         first = false;
     }
 FINISHED:
-    GrAssert(subpath == fSubpathCount);
     GrAssert((vert - base) <= maxPts);
+    GrAssert((idx - idxBase) <= maxIdxs);
+
+    fVertexCnt = vert - base;
+    fIndexCnt = idx - idxBase;
 
     if (fTranslate.fX || fTranslate.fY) {
         int count = vert - base;
@@ -312,6 +386,7 @@
         }
     }
     }
+    return true;
 }
 
 void GrDefaultPathRenderer::onDrawPath(GrDrawTarget::StageBitfield stages,
@@ -343,7 +418,9 @@
     // it is a silly limitation of the GrDrawTarget design that should be fixed.
     if (tol != fPreviousSrcTol ||
         stages != fPreviousStages) {
-        this->createGeom(tol, stages);
+        if (!this->createGeom(tol, stages)) {
+            return;
+        }
     }
 
     GrAssert(NULL != fTarget);
@@ -352,7 +429,6 @@
     // face culling doesn't make sense here
     GrAssert(GrDrawTarget::kBoth_DrawFace == fTarget->getDrawFace());
 
-    GrPrimitiveType             type;
     int                         passCount = 0;
     const GrStencilSettings*    passes[3];
     GrDrawTarget::DrawFace      drawFace[3];
@@ -360,7 +436,6 @@
     bool                        lastPassIsBounds;
 
     if (kHairLine_PathFill == fFill) {
-        type = kLineStrip_PrimitiveType;
         passCount = 1;
         if (stencilOnly) {
             passes[0] = &gDirectToStencil;
@@ -370,7 +445,6 @@
         lastPassIsBounds = false;
         drawFace[0] = GrDrawTarget::kBoth_DrawFace;
     } else {
-        type = kTriangleFan_PrimitiveType;
         if (single_pass_path(*fTarget, *fPath, fFill)) {
             passCount = 1;
             if (stencilOnly) {
@@ -481,11 +555,16 @@
             if (passCount > 1) {
                 fTarget->enableState(GrDrawTarget::kNoColorWrites_StateBit);
             }
-            int baseVertex = 0;
-            for (int sp = 0; sp < fSubpathCount; ++sp) {
-                fTarget->drawNonIndexed(type, baseVertex, 
-                                        fSubpathVertCount[sp]);
-                baseVertex += fSubpathVertCount[sp];
+            if (fUseIndexedDraw) {
+                fTarget->drawIndexed(fPrimitiveType, 0, 0, 
+                                     fVertexCnt, fIndexCnt);
+            } else {
+                int baseVertex = 0;
+                for (int sp = 0; sp < fSubpathCount; ++sp) {
+                    fTarget->drawNonIndexed(fPrimitiveType, baseVertex,
+                                            fSubpathVertCount[sp]);
+                    baseVertex += fSubpathVertCount[sp];
+                }
             }
         }
     }