Fixups for clipstack, convexity test for paths.

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

git-svn-id: http://skia.googlecode.com/svn/trunk@891 2bbb7eff-a529-9590-31e7-b0007b416f81
diff --git a/gpu/src/GrGpu.cpp b/gpu/src/GrGpu.cpp
index f42e316..4a46e98 100644
--- a/gpu/src/GrGpu.cpp
+++ b/gpu/src/GrGpu.cpp
@@ -74,7 +74,7 @@
                  fVertexPoolInUse(false),
                  fIndexPoolInUse(false) {
 #if GR_DEBUG
-//    gr_run_unittests();
+    //gr_run_unittests();
 #endif
     resetStats();
 }
@@ -338,9 +338,20 @@
             int clipBit = rt.stencilBits();
             clipBit = (1 << (clipBit-1));
 
+            // often we'll see the first two elements of the clip are
+            // the full rt size and another element intersected with it.
+            // We can skip the first full-size rect and save a big rect draw.
+            int firstElement = 0;
+            if (clip.getElementCount() > 1 && 
+                kRect_ClipType == clip.getElementType(0) &&
+                kIntersect_SetOp == clip.getOp(1)&&
+                clip.getRect(0).contains(bounds)) {
+                firstElement = 1;
+            }
+ 
             // walk through each clip element and perform its set op
             // with the existing clip.
-            for (int c = 0; c < count; ++c) {
+            for (int c = firstElement; c < count; ++c) {
                 GrPathFill fill;
                 // enabled at bottom of loop
                 this->disableState(kModifyStencilClip_StateBit);
@@ -351,15 +362,16 @@
                     fill = kEvenOdd_PathFill;
                 } else {
                     fill = clip.getPathFill(c);
-                    canDrawDirectToClip = getPathRenderer()->requiresStencilPass(clip.getPath(c));
+                    GrPathRenderer* pr = this->getPathRenderer();
+                    canDrawDirectToClip = pr->requiresStencilPass(this, clip.getPath(c), fill);
                 }
 
-                GrSetOp op = 0 == c ? kReplace_SetOp : clip.getOp(c);
+                GrSetOp op = firstElement == c ? kReplace_SetOp : clip.getOp(c);
                 int passes;
                 GrStencilSettings stencilSettings[GrStencilSettings::kMaxStencilClipPasses];
 
-                canDrawDirectToClip = GrStencilSettings::GetClipPasses(op,  canDrawDirectToClip,
-                                                                       clipBit, &fill,
+                canDrawDirectToClip = GrStencilSettings::GetClipPasses(op, canDrawDirectToClip,
+                                                                       clipBit, IsFillInverted(fill),
                                                                        &passes, stencilSettings);
 
                 // draw the element to the client stencil bits if necessary
@@ -378,7 +390,9 @@
                         this->drawSimpleRect(clip.getRect(c), NULL, 0);
                     } else {
                         SET_RANDOM_COLOR
-                        getPathRenderer()->drawPathToStencil(this, clip.getPath(c), fill, NULL);
+                        getPathRenderer()->drawPathToStencil(this, clip.getPath(c), 
+                                                             NonInvertedFill(fill), 
+                                                             NULL);
                     }
                 }
 
@@ -393,7 +407,9 @@
                             this->drawSimpleRect(clip.getRect(c), NULL, 0);
                         } else {
                             SET_RANDOM_COLOR
-                            getPathRenderer()->drawPath(this, 0, clip.getPath(c), fill, NULL);
+                            getPathRenderer()->drawPath(this, 0,
+                                                        clip.getPath(c), 
+                                                        fill, NULL);
                         }
                     } else {
                         SET_RANDOM_COLOR
diff --git a/gpu/src/GrGpuGL.cpp b/gpu/src/GrGpuGL.cpp
index 1e9d7bf..3ee524b 100644
--- a/gpu/src/GrGpuGL.cpp
+++ b/gpu/src/GrGpuGL.cpp
@@ -432,6 +432,10 @@
     fHWBlendDisabled = false;
     GR_GL(Enable(GL_BLEND));
 
+    // we don't use the zb at all
+    GR_GL(Disable(GL_DEPTH_TEST));
+    GR_GL(DepthMask(GL_FALSE));
+
     GR_GL(Disable(GL_CULL_FACE));
     GR_GL(FrontFace(GL_CCW));
     fHWDrawState.fDrawFace = kBoth_DrawFace;
@@ -1097,10 +1101,18 @@
 
 void GrGpuGL::eraseStencilClip(const GrIRect& rect) {
     GrAssert(NULL != fCurrDrawState.fRenderTarget);
+#if 0
     GLint stencilBitCount = fCurrDrawState.fRenderTarget->stencilBits();
     GrAssert(stencilBitCount > 0);
     GLint clipStencilMask  = (1 << (stencilBitCount - 1));
-
+#else
+    // we could just clear the clip bit but when we go through
+    // angle a partial stencil mask will cause clears to be
+    // turned into draws. Our contract on GrDrawTarget says that
+    // changing the clip between stencil passes may or may not
+    // zero the client's clip bits. So we just clear the whole thing.
+    static const GLint clipStencilMask  = ~0;
+#endif
     flushRenderTarget();
     flushScissor(&rect);
     GR_GL(StencilMask(clipStencilMask));
diff --git a/gpu/src/GrGpuGLShaders2.cpp b/gpu/src/GrGpuGLShaders2.cpp
index 14134e7..77847e9 100644
--- a/gpu/src/GrGpuGLShaders2.cpp
+++ b/gpu/src/GrGpuGLShaders2.cpp
@@ -1257,7 +1257,10 @@
         // invalidate the immediate mode color
         fHWDrawState.fColor = GrColor_ILLEGAL;
     } else {
-        if (fHWDrawState.fColor != fCurrDrawState.fColor) {
+        if (fHWDrawState.fColor != fCurrDrawState.fColor &&
+            (!GR_AGGRESSIVE_SHADER_OPTS || 0xffffffff != fCurrDrawState.fColor)) {
+            // avoid pushing the color attrib if the shader will optimize it out
+
             // OpenGL ES only supports the float varities of glVertexAttrib
             float c[] = {
                 GrColorUnpackR(fCurrDrawState.fColor) / 255.f,
diff --git a/gpu/src/GrPath.cpp b/gpu/src/GrPath.cpp
index ca5c43b..fc53ac2 100644
--- a/gpu/src/GrPath.cpp
+++ b/gpu/src/GrPath.cpp
@@ -1,6 +1,8 @@
 #include "GrPath.h"
 
-GrPath::GrPath() {}
+GrPath::GrPath() {
+    fConvexHint = kNone_ConvexHint;
+}
 
 GrPath::GrPath(const GrPath& src) : INHERITED() {
     GrPath::Iter iter(src);
@@ -15,13 +17,13 @@
 }
 
 bool GrPath::operator ==(const GrPath& path) const {
-    if (fVerbs.count() != path.fVerbs.count() ||
+    if (fCmds.count() != path.fCmds.count() ||
         fPts.count() != path.fPts.count()) {
         return false;
     }
 
-    for (int v = 0; v < fVerbs.count(); ++v) {
-        if (fVerbs[v] != path.fVerbs[v]) {
+    for (int v = 0; v < fCmds.count(); ++v) {
+        if (fCmds[v] != path.fCmds[v]) {
             return false;
         }
     }
@@ -35,31 +37,31 @@
 }
 
 void GrPath::ensureMoveTo() {
-    if (fVerbs.isEmpty() || this->wasLastVerb(kClose)) {
-        *fVerbs.append() = kMove;
+    if (fCmds.isEmpty() || this->wasLastVerb(kClose_PathCmd)) {
+        *fCmds.append() = kMove_PathCmd;
         fPts.append()->set(0, 0);
     }
 }
 
 void GrPath::moveTo(GrScalar x, GrScalar y) {
-    if (this->wasLastVerb(kMove)) {
+    if (this->wasLastVerb(kMove_PathCmd)) {
         // overwrite prev kMove value
         fPts[fPts.count() - 1].set(x, y);
     } else {
-        *fVerbs.append() = kMove;
+        *fCmds.append() = kMove_PathCmd;
         fPts.append()->set(x, y);
     }
 }
 
 void GrPath::lineTo(GrScalar x, GrScalar y) {
     this->ensureMoveTo();
-    *fVerbs.append() = kLine;
+    *fCmds.append() = kLine_PathCmd;
     fPts.append()->set(x, y);
 }
 
 void GrPath::quadTo(GrScalar x0, GrScalar y0, GrScalar x1, GrScalar y1) {
     this->ensureMoveTo();
-    *fVerbs.append() = kQuad;
+    *fCmds.append() = kQuadratic_PathCmd;
     fPts.append()->set(x0, y0);
     fPts.append()->set(x1, y1);
 }
@@ -67,100 +69,371 @@
 void GrPath::cubicTo(GrScalar x0, GrScalar y0, GrScalar x1, GrScalar y1,
                      GrScalar x2, GrScalar y2) {
     this->ensureMoveTo();
-    *fVerbs.append() = kCubic;
+    *fCmds.append() = kCubic_PathCmd;
     fPts.append()->set(x0, y0);
     fPts.append()->set(x1, y1);
     fPts.append()->set(x2, y2);
 }
 
 void GrPath::close() {
-    if (!fVerbs.isEmpty() && !this->wasLastVerb(kClose)) {
+    if (!fCmds.isEmpty() && !this->wasLastVerb(kClose_PathCmd)) {
         // should we allow kMove followed by kClose?
-        *fVerbs.append() = kClose;
+        *fCmds.append() = kClose_PathCmd;
     }
 }
 
 ///////////////////////////////////////////////////////////////////////////////
 
+static bool check_two_vecs(const GrVec& prevVec,
+                           const GrVec& currVec,
+                           GrScalar turnDir,
+                           int* xDir,
+                           int* yDir,
+                           int* flipX, 
+                           int* flipY) {
+    if (currVec.fX * *xDir < 0) {
+        ++*flipX;
+        if (*flipX > 2) {
+            return false;
+        }
+        *xDir = -*xDir;
+    }
+    if (currVec.fY * *yDir < 0) {
+        ++*flipY;
+        if (*flipY > 2) {
+            return false;
+        }
+        *yDir = -*yDir;
+    }
+    GrScalar d = prevVec.cross(currVec);
+    return (d * turnDir) >= 0;
+}
+
+static void init_from_two_vecs(const GrVec& firstVec,
+                               const GrVec& secondVec,
+                               GrScalar* turnDir,
+                               int* xDir, int* yDir) {
+    *turnDir = firstVec.cross(secondVec);
+    if (firstVec.fX > 0) {
+        *xDir = 1;
+    } else if (firstVec.fX < 0) {
+        *xDir = -1;
+    } else {
+        *xDir = 0;
+    }
+    if (firstVec.fY > 0) {
+        *yDir = 1;
+    } else if (firstVec.fY < 0) {
+        *yDir = -1;
+    } else {
+        *yDir = 0;
+    }
+}
+
 void GrPath::resetFromIter(GrPathIter* iter) {
     fPts.reset();
-    fVerbs.reset();
+    fCmds.reset();
 
+    fConvexHint = iter->convexHint();
+
+    // first point of the subpath
+    GrPoint firstPt;
+    // first edge of the subpath
+    GrVec firstVec;
+    // vec of most recently processed edge, that wasn't degenerate
+    GrVec previousVec;
+    // most recently processed point
+    GrPoint previousPt;
+
+    // sign indicates whether we're bending left or right
+    GrScalar turnDir;
+    // number of times the direction has flipped in x or y
+
+    // we track which direction we are moving in x/y and the
+    // number of times it changes.
+    int xDir;
+    int yDir;
+    int flipX;
+    int flipY;
+
+    // counts number of sub path pts that didn't add a degenerate edge.
+    int subPathPts = 0;
+
+    int numSubPaths = 0;
+    iter->rewind();
+    GrPathCmd cmd;
     GrPoint pts[4];
-    GrPathIter::Command cmd;
-
-    while ((cmd = iter->next(pts)) != GrPathIter::kEnd_Command) {
+    bool subPathClosed;
+    do {
+        cmd = iter->next(pts);
+        // If the convexity test is ever updated to handle multiple subpaths
+        // the loop has to be adjusted to handle moving to a new subpath without
+        // closing the previous one. Currently the implicit closing vectors for a
+        // filled path would never be examined.
         switch (cmd) {
-            case GrPathIter::kMove_Command:
+            case kMove_PathCmd:
                 this->moveTo(pts[0].fX, pts[0].fY);
+                subPathPts = 0;
+                subPathClosed = false;
                 break;
-            case GrPathIter::kLine_Command:
+            case kLine_PathCmd:
                 this->lineTo(pts[1].fX, pts[1].fY);
                 break;
-            case GrPathIter::kQuadratic_Command:
+            case kQuadratic_PathCmd:
                 this->quadTo(pts[1].fX, pts[1].fY, pts[2].fX, pts[2].fY);
                 break;
-            case GrPathIter::kCubic_Command:
+            case kCubic_PathCmd:
                 this->cubicTo(pts[1].fX, pts[1].fY, pts[2].fX, pts[2].fY,
                               pts[3].fX, pts[3].fY);
                 break;
-            case GrPathIter::kClose_Command:
+            case kClose_PathCmd:
                 this->close();
+                subPathClosed = true;
                 break;
-            case GrPathIter::kEnd_Command:
-                // never get here, but include it to avoid the warning
+            case kEnd_PathCmd:
                 break;
         }
+        int n = NumPathCmdPoints(cmd);
+        if (0 == subPathPts && n > 0) {
+            previousPt = pts[0];
+            firstPt = previousPt;
+            flipX = 0;
+            flipY = 0;
+            turnDir = 0;
+            subPathPts = 1;
+            ++numSubPaths;
+        }
+        // either we skip the first pt because it is redundant with
+        // last point of the previous subpath cmd or we just ate it
+        // in the above if.
+        int consumed = 1;
+        if (numSubPaths < 2 && kNone_ConvexHint == fConvexHint) {
+            while (consumed < n) {
+                GrAssert(pts[consumed-1] == previousPt);
+                GrVec vec;
+                vec.setBetween(previousPt, pts[consumed]);
+                if (vec.fX || vec.fY) {
+                    if (subPathPts >= 2) {
+                        if (0 == turnDir) { 
+                            firstVec = previousVec;
+                            init_from_two_vecs(firstVec, vec, 
+                                               &turnDir, &xDir, &yDir);
+                            // here we aren't checking whether the x/y dirs
+                            // change between the first and second edge. It
+                            // gets covered when the path is closed.
+                        } else {
+                            if (!check_two_vecs(previousVec, vec, turnDir,
+                                                &xDir, &yDir,
+                                                &flipX, &flipY)) {
+                                fConvexHint = kConcave_ConvexHint;
+                                break;
+                            }
+                        }
+                    }
+                    previousVec = vec;
+                    previousPt = pts[consumed];
+                    ++subPathPts;
+                }
+                ++consumed;
+            }
+            if (subPathPts > 2 && (kClose_PathCmd == cmd || 
+                        (!subPathClosed && kEnd_PathCmd == cmd ))) {
+                // if an additional vector is needed to close the loop check
+                // that it validates against the previous vector.
+                GrVec vec;
+                vec.setBetween(previousPt, firstPt);
+                if (vec.fX || vec.fY) {
+                    if (!check_two_vecs(previousVec, vec, turnDir, 
+                                        &xDir, &yDir, &flipX, &flipY)) {
+                        fConvexHint = kConcave_ConvexHint;
+                        break;
+                    }
+                    previousVec = vec;
+                }
+                // check that closing vector validates against the first vector.
+                if (!check_two_vecs(previousVec, firstVec, turnDir, 
+                                    &xDir, &yDir, &flipX, &flipY)) {
+                    fConvexHint = kConcave_ConvexHint;
+                    break;
+                }
+            }
+        }
+    } while (cmd != kEnd_PathCmd);
+    if (kNone_ConvexHint == fConvexHint && numSubPaths < 2) {
+        fConvexHint = kConvex_ConvexHint;
+    } else {
+        bool recurse = false;
+        if (recurse) {
+            this->resetFromIter(iter);
+        }
     }
-    fConvexHint = iter->convexHint();
 }
 
+void GrPath::ConvexUnitTest() {
+    GrPath testPath;
+    GrPath::Iter testIter;
+
+    GrPath pt;
+    pt.moveTo(0, 0);
+    pt.close();
+
+    testIter.reset(pt);
+    testPath.resetFromIter(&testIter);
+    GrAssert(kConvex_ConvexHint == testPath.getConvexHint());
+
+    GrPath line;
+    line.moveTo(GrIntToScalar(12), GrIntToScalar(20));
+    line.lineTo(GrIntToScalar(-12), GrIntToScalar(-20));
+    line.close();
+
+    testIter.reset(line);
+    testPath.resetFromIter(&testIter);
+    GrAssert(kConvex_ConvexHint == testPath.getConvexHint());
+
+    GrPath triLeft;
+    triLeft.moveTo(0, 0);
+    triLeft.lineTo(1, 0);
+    triLeft.lineTo(1, 1);
+    triLeft.close();
+
+    testIter.reset(triLeft);
+    testPath.resetFromIter(&testIter);
+    GrAssert(kConvex_ConvexHint == testPath.getConvexHint());
+
+    GrPath triRight;
+    triRight.moveTo(0, 0);
+    triRight.lineTo(-1, 0);
+    triRight.lineTo(1, 1);
+    triRight.close();
+
+    testIter.reset(triRight);
+    testPath.resetFromIter(&testIter);
+    GrAssert(kConvex_ConvexHint == testPath.getConvexHint());
+    
+    GrPath square;
+    square.moveTo(0, 0);
+    square.lineTo(1, 0);
+    square.lineTo(1, 1);
+    square.lineTo(0, 1);
+    square.close();
+
+    testIter.reset(square);
+    testPath.resetFromIter(&testIter);
+    GrAssert(kConvex_ConvexHint == testPath.getConvexHint());
+
+    GrPath redundantSquare;
+    square.moveTo(0, 0);
+    square.lineTo(0, 0);
+    square.lineTo(0, 0);
+    square.lineTo(1, 0);
+    square.lineTo(1, 0);
+    square.lineTo(1, 0);
+    square.lineTo(1, 1);
+    square.lineTo(1, 1);
+    square.lineTo(1, 1);
+    square.lineTo(0, 1);
+    square.lineTo(0, 1);
+    square.lineTo(0, 1);
+    square.close();
+    
+    testIter.reset(redundantSquare);
+    testPath.resetFromIter(&testIter);
+    GrAssert(kConvex_ConvexHint == testPath.getConvexHint());
+
+    GrPath bowTie;
+    bowTie.moveTo(0, 0);
+    bowTie.lineTo(0, 0);
+    bowTie.lineTo(0, 0);
+    bowTie.lineTo(1, 1);
+    bowTie.lineTo(1, 1);
+    bowTie.lineTo(1, 1);
+    bowTie.lineTo(1, 0);
+    bowTie.lineTo(1, 0);
+    bowTie.lineTo(1, 0);
+    bowTie.lineTo(0, 1);
+    bowTie.lineTo(0, 1);
+    bowTie.lineTo(0, 1);
+    bowTie.close();
+    
+    testIter.reset(bowTie);
+    testPath.resetFromIter(&testIter);
+    GrAssert(kConcave_ConvexHint == testPath.getConvexHint());
+
+    GrPath spiral;
+    spiral.moveTo(0, 0);
+    spiral.lineTo(1, 0);
+    spiral.lineTo(1, 1);
+    spiral.lineTo(0, 1);
+    spiral.lineTo(0,.5);
+    spiral.lineTo(.5,.5);
+    spiral.lineTo(.5,.75);
+    spiral.close();
+
+    testIter.reset(spiral);
+    testPath.resetFromIter(&testIter);
+    GrAssert(kConcave_ConvexHint == testPath.getConvexHint());
+
+    GrPath dent;
+    dent.moveTo(0, 0);
+    dent.lineTo(1, 1);
+    dent.lineTo(0, 1);
+    dent.lineTo(-.5,2);
+    dent.lineTo(-2, 1);
+    dent.close();
+
+    testIter.reset(dent);
+    testPath.resetFromIter(&testIter);
+    GrAssert(kConcave_ConvexHint == testPath.getConvexHint());
+}
 ///////////////////////////////////////////////////////////////////////////////
 
-GrPath::Iter::Iter(const GrPath& path) : fPath(path) {
+GrPath::Iter::Iter() : fPath(NULL) {
+}
+
+GrPath::Iter::Iter(const GrPath& path) : fPath(&path) {
     this->rewind();
 }
 
-GrPathIter::Command GrPath::Iter::next(GrPoint points[]) {
-    if (fVerbIndex == fPath.fVerbs.count()) {
-        GrAssert(fPtIndex == fPath.fPts.count());
-        return GrPathIter::kEnd_Command;
+GrPathCmd GrPath::Iter::next(GrPoint points[]) {
+    if (fCmdIndex == fPath->fCmds.count()) {
+        GrAssert(fPtIndex == fPath->fPts.count());
+        return kEnd_PathCmd;
     } else {
-        GrAssert(fVerbIndex < fPath.fVerbs.count());
+        GrAssert(fCmdIndex < fPath->fCmds.count());
     }
 
-    uint8_t cmd = fPath.fVerbs[fVerbIndex++];
-    const GrPoint* srcPts = fPath.fPts.begin() + fPtIndex;
+    GrPathCmd cmd = fPath->fCmds[fCmdIndex++];
+    const GrPoint* srcPts = fPath->fPts.begin() + fPtIndex;
 
     switch (cmd) {
-        case kMove:
+        case kMove_PathCmd:
             if (points) {
                 points[0] = srcPts[0];
             }
             fLastPt = srcPts[0];
-            GrAssert(fPtIndex <= fPath.fPts.count() + 1);
+            GrAssert(fPtIndex <= fPath->fPts.count() + 1);
             fPtIndex += 1;
             break;
-        case kLine:
+        case kLine_PathCmd:
             if (points) {
                 points[0] = fLastPt;
                 points[1] = srcPts[0];
             }
             fLastPt = srcPts[0];
-            GrAssert(fPtIndex <= fPath.fPts.count() + 1);
+            GrAssert(fPtIndex <= fPath->fPts.count() + 1);
             fPtIndex += 1;
             break;
-        case kQuad:
+        case kQuadratic_PathCmd:
             if (points) {
                 points[0] = fLastPt;
                 points[1] = srcPts[0];
                 points[2] = srcPts[1];
             }
-            fLastPt = srcPts[2];
-            GrAssert(fPtIndex <= fPath.fPts.count() + 2);
+            fLastPt = srcPts[1];
+            GrAssert(fPtIndex <= fPath->fPts.count() + 2);
             fPtIndex += 2;
             break;
-        case kCubic:
+        case kCubic_PathCmd:
             if (points) {
                 points[0] = fLastPt;
                 points[1] = srcPts[0];
@@ -168,29 +441,33 @@
                 points[3] = srcPts[2];
             }
             fLastPt = srcPts[2];
-            GrAssert(fPtIndex <= fPath.fPts.count() + 3);
+            GrAssert(fPtIndex <= fPath->fPts.count() + 3);
             fPtIndex += 3;
             break;
-        case kClose:
+        case kClose_PathCmd:
             break;
         default:
-            GrAssert(!"unknown grpath verb");
+            GrAssert(!"unknown grpath cmd");
             break;
     }
-    return (GrPathIter::Command)cmd;
+    return cmd;
 }
 
-GrPathIter::ConvexHint GrPath::Iter::convexHint() const {
-    return fPath.getConvexHint();
+GrConvexHint GrPath::Iter::convexHint() const {
+    return fPath->getConvexHint();
 }
 
-GrPathIter::Command GrPath::Iter::next() {
+GrPathCmd GrPath::Iter::next() {
     return this->next(NULL);
 }
 
 void GrPath::Iter::rewind() {
-    fVerbIndex = fPtIndex = 0;
+    this->reset(*fPath);
 }
 
+void GrPath::Iter::reset(const GrPath& path) {
+    fPath = &path;
+    fCmdIndex = fPtIndex = 0;
+}
 
 
diff --git a/gpu/src/GrPathRenderer.cpp b/gpu/src/GrPathRenderer.cpp
index 3e2b4b3..6d7aabb 100644
--- a/gpu/src/GrPathRenderer.cpp
+++ b/gpu/src/GrPathRenderer.cpp
@@ -240,22 +240,22 @@
 
     bool first = true;
 
-    GrPathIter::Command cmd;
+    GrPathCmd cmd;
 
     GrPoint pts[4];
-    while ((cmd = path->next(pts)) != GrPathIter::kEnd_Command) {
+    while ((cmd = path->next(pts)) != kEnd_PathCmd) {
 
         switch (cmd) {
-            case GrPathIter::kLine_Command:
+            case kLine_PathCmd:
                 pointCount += 1;
                 break;
-            case GrPathIter::kQuadratic_Command:
+            case kQuadratic_PathCmd:
                 pointCount += quadratic_point_count(pts, tol);
                 break;
-            case GrPathIter::kCubic_Command:
+            case kCubic_PathCmd:
                 pointCount += cubic_point_count(pts, tol);
                 break;
-            case GrPathIter::kMove_Command:
+            case kMove_PathCmd:
                 pointCount += 1;
                 if (!first) {
                     ++(*subpaths);
@@ -269,21 +269,21 @@
     return pointCount;
 }
 
-static inline bool single_pass_path(const GrPathIter& path,
-                                    GrPathFill fill,
-                                    const GrDrawTarget& target) {
+static inline bool single_pass_path(const GrDrawTarget& target,
+                                    const GrPathIter& path,
+                                    GrPathFill fill) {
 #if STENCIL_OFF
     return true;
 #else
     if (kEvenOdd_PathFill == fill) {
-        GrPathIter::ConvexHint hint = path.convexHint();
-        return hint == GrPathIter::kConvex_ConvexHint ||
-               hint == GrPathIter::kNonOverlappingConvexPieces_ConvexHint;
+        GrConvexHint hint = path.convexHint();
+        return hint == kConvex_ConvexHint ||
+               hint == kNonOverlappingConvexPieces_ConvexHint;
     } else if (kWinding_PathFill == fill) {
-        GrPathIter::ConvexHint hint = path.convexHint();
-        return hint == GrPathIter::kConvex_ConvexHint ||
-               hint == GrPathIter::kNonOverlappingConvexPieces_ConvexHint ||
-               (hint == GrPathIter::kSameWindingConvexPieces_ConvexHint &&
+        GrConvexHint hint = path.convexHint();
+        return hint == kConvex_ConvexHint ||
+               hint == kNonOverlappingConvexPieces_ConvexHint ||
+               (hint == kSameWindingConvexPieces_ConvexHint &&
                 target.canDisableBlend() && !target.isDitherState());
 
     }
@@ -291,6 +291,12 @@
 #endif
 }
 
+bool GrDefaultPathRenderer::requiresStencilPass(const GrDrawTarget* target,
+                                                GrPathIter* path, 
+                                                GrPathFill fill) const {
+    return single_pass_path(*target, *path, fill);
+}
+
 void GrDefaultPathRenderer::drawPathHelper(GrDrawTarget* target,
                                            GrDrawTarget::StageBitfield stages,
                                            GrPathIter* path,
@@ -358,18 +364,18 @@
         if (stencilOnly) {
             passes[0] = &gDirectToStencil;
         } else {
-            passes[0] = &GrStencilSettings::gDisabled;
+            passes[0] = NULL;
         }
         lastPassIsBounds = false;
         drawFace[0] = GrDrawTarget::kBoth_DrawFace;
     } else {
         type = kTriangleFan_PrimitiveType;
-        if (single_pass_path(*path, fill, *target)) {
+        if (single_pass_path(*target, *path, fill)) {
             passCount = 1;
             if (stencilOnly) {
                 passes[0] = &gDirectToStencil;
             } else {
-                passes[0] = &GrStencilSettings::gDisabled;
+                passes[0] = NULL;
             }
             drawFace[0] = GrDrawTarget::kBoth_DrawFace;
             lastPassIsBounds = false;
@@ -446,9 +452,9 @@
     int subpath = 0;
 
     for (;;) {
-        GrPathIter::Command cmd = path->next(pts);
+        GrPathCmd cmd = path->next(pts);
         switch (cmd) {
-            case GrPathIter::kMove_Command:
+            case kMove_PathCmd:
                 if (!first) {
                     subpathVertCount[subpath] = vert-subpathBase;
                     subpathBase = vert;
@@ -457,25 +463,25 @@
                 *vert = pts[0];
                 vert++;
                 break;
-            case GrPathIter::kLine_Command:
+            case kLine_PathCmd:
                 *vert = pts[1];
                 vert++;
                 break;
-            case GrPathIter::kQuadratic_Command: {
+            case kQuadratic_PathCmd: {
                 generate_quadratic_points(pts[0], pts[1], pts[2],
                                           tolSqd, &vert,
                                           quadratic_point_count(pts, tol));
                 break;
             }
-            case GrPathIter::kCubic_Command: {
+            case kCubic_PathCmd: {
                 generate_cubic_points(pts[0], pts[1], pts[2], pts[3],
                                       tolSqd, &vert,
                                       cubic_point_count(pts, tol));
                 break;
             }
-            case GrPathIter::kClose_Command:
+            case kClose_PathCmd:
                 break;
-            case GrPathIter::kEnd_Command:
+            case kEnd_PathCmd:
                 subpathVertCount[subpath] = vert-subpathBase;
                 ++subpath; // this could be only in debug
                 goto FINISHED;
@@ -519,7 +525,9 @@
 
     for (int p = 0; p < passCount; ++p) {
         target->setDrawFace(drawFace[p]);
-        target->setStencil(*passes[p]);
+        if (NULL != passes[p]) {
+            target->setStencil(*passes[p]);
+        }
 
         if (lastPassIsBounds && (p == passCount-1)) {
             if (!colorWritesWereDisabled) {
diff --git a/gpu/src/GrPathRenderer.h b/gpu/src/GrPathRenderer.h
index 467b0a0..f99b922 100644
--- a/gpu/src/GrPathRenderer.h
+++ b/gpu/src/GrPathRenderer.h
@@ -60,29 +60,41 @@
      * For complex clips Gr uses the stencil buffer. The path renderer must be
      * able to render paths into the stencil buffer. However, the path renderer
      * itself may require the stencil buffer to resolve the path fill rule. This
-     * function queries whether the path render requires its own stencil
+     * function queries whether the path render needs its own stencil
      * pass. If this returns false then drawPath() should not modify the
-     * the target's stencil settings.
+     * the target's stencil settings but use those already set on target.
+     *
+     * @param target target that the path will be rendered to
+     * @param path   the path that will be drawn
+     * @param fill   the fill rule that will be used
      *
      * @return false if this path renderer can generate interior-only fragments
      *         without changing the stencil settings on the target. If it
      *         returns true the drawPathToStencil will be used when rendering
      *         clips.
      */
-    virtual bool requiresStencilPass(GrPathIter*) const { return false; }
+    virtual bool requiresStencilPass(const GrDrawTarget* target,
+                                     GrPathIter* path, 
+                                     GrPathFill fill) const { return false; }
     
-    bool requiresStencilPass(const GrPath& path) const { 
+    bool requiresStencilPass(const GrDrawTarget* target,
+                             const GrPath& path, 
+                             GrPathFill fill) const { 
         GrPath::Iter iter(path);
-        return requiresStencilPass(&iter);
+        return requiresStencilPass(target, &iter, fill);
     }
 
     /**
-     * Draws a path to the stencil buffer. Assume the writable bits are zero
-     * prior and write a nonzero value in interior samples. The default
-     * implementation assumes the path filling algorithm doesn't require a
-     * separate stencil pass and so just calls drawPath.
+     * Draws a path to the stencil buffer. Assume the writable stencil bits 
+     * are already initialized to zero. Fill will always be either
+     * kWinding_PathFill or kEvenOdd_PathFill.
      *
-     * Fill will never be an inverse fill rule.
+     * Only called if requiresStencilPass returns true for the same combo of
+     * target, path, and fill (or inverse of the fill).
+     *
+     * The default implementation assumes the path filling algorithm doesn't
+     * require a separate stencil pass and so crashes.
+     *
      *
      * @param target                the target to draw into.
      * @param path                  the path to draw.
@@ -94,10 +106,7 @@
                                    GrPathIter* path,
                                    GrPathFill fill,
                                    const GrPoint* translate) {
-        GrAssert(kInverseEvenOdd_PathFill != fill);
-        GrAssert(kInverseWinding_PathFill != fill);
-
-        this->drawPath(target, 0, path, fill, translate);
+        GrCrash("Unexpected call to drawPathToStencil.");
     }
 
     void drawPathToStencil(GrDrawTarget* target,
@@ -119,7 +128,9 @@
                           GrPathIter* path,
                           GrPathFill fill,
                           const GrPoint* translate);
-    virtual bool requiresStencilPass(GrPath&) const { return true; }
+    virtual bool requiresStencilPass(const GrDrawTarget* target,
+                                     GrPathIter* path, 
+                                     GrPathFill fill) const;
     virtual void drawPathToStencil(GrDrawTarget* target,
                                    GrPathIter* path,
                                    GrPathFill fill,
diff --git a/gpu/src/GrRedBlackTree.h b/gpu/src/GrRedBlackTree.h
index e6448fb..7ba326f 100644
--- a/gpu/src/GrRedBlackTree.h
+++ b/gpu/src/GrRedBlackTree.h
@@ -355,6 +355,8 @@
     x->fChildren[kRight_Child] = NULL;
     x->fItem = t;
 
+    Node* returnNode = x;
+
     Node* gp = NULL;
     Node* p = NULL;
     Node* n = fRoot;
@@ -371,7 +373,6 @@
         gp = p;
         p = n;
         n = p->fChildren[pc];
-
     }
     if (last) {
         fLast = x;
@@ -385,7 +386,7 @@
         x->fColor = kBlack_Color;
         x->fParent = NULL;
         GrAssert(1 == fCount);
-        return Iter(x, this);
+        return Iter(returnNode, this);
     }
     p->fChildren[pc] = x;
     x->fColor = kRed_Color;
@@ -404,7 +405,7 @@
         // if x's parent is black then we didn't violate any of the
         // red/black properties when we added x as red.
         if (kBlack_Color == p->fColor) {
-            return Iter(x, this);
+            return Iter(returnNode, this);
         }
         // gp must be valid because if p was the root then it is black
         GrAssert(NULL != gp);
@@ -428,7 +429,7 @@
                 GrAssert(fRoot == x);
                 x->fColor = kBlack_Color;
                 validate();
-                return Iter(x, this);
+                return Iter(returnNode, this);
             }
             gp = p->fParent;
             pc = (p->fChildren[kLeft_Child] == x) ? kLeft_Child :
@@ -481,7 +482,7 @@
         rotateLeft(gp);
     }
     validate();
-    return Iter(x, this);
+    return Iter(returnNode, this);
 }
 
 
@@ -656,6 +657,7 @@
         Color xcolor = x->fColor;
         p->fChildren[pc] = NULL;
         delete x;
+        x = NULL;
         // when x is red it can be with an implicit black leaf without
         // violating any of the red-black tree properties.
         if (kRed_Color == xcolor) {
@@ -716,9 +718,9 @@
             }
             // x and s are now both black.
             GrAssert(kBlack_Color == s->fColor);
-            GrAssert(kBlack_Color == x->fColor);
+            GrAssert(NULL == x || kBlack_Color == x->fColor);
             GrAssert(p == s->fParent);
-            GrAssert(p == x->fParent);
+            GrAssert(NULL == x || p == x->fParent);
 
             // when x is deleted its subtree will have reduced black-height.
             slRed = (NULL != sl && kRed_Color == sl->fColor);
diff --git a/gpu/src/GrStencil.cpp b/gpu/src/GrStencil.cpp
index 9d68c65..a1c8c09 100644
--- a/gpu/src/GrStencil.cpp
+++ b/gpu/src/GrStencil.cpp
@@ -243,84 +243,63 @@
     0x00000000,          0x00000000     // set clip bit
 };
 
-static const GrPathFill gNonInvertedFills[] = {
-    kWinding_PathFill, // kWinding_PathFill
-    kEvenOdd_PathFill, // kEvenOdd_PathFill
-    kWinding_PathFill, // kInverseWinding_PathFill
-    kEvenOdd_PathFill, // kInverseEvenOdd_PathFill
-    kWinding_PathFill, // kHairLine_PathFill
-};
-
-static const bool gIsFillInverted[] = {
-    false, // kWinding_PathFill
-    false, // kEvenOdd_PathFill
-    true,  // kInverseWinding_PathFill
-    true,  // kInverseEvenOdd_PathFill
-    false, // kHairLine_PathFill
-};
-GR_STATIC_ASSERT(0 == kWinding_PathFill);
-GR_STATIC_ASSERT(1 == kEvenOdd_PathFill);
-GR_STATIC_ASSERT(2 == kInverseWinding_PathFill);
-GR_STATIC_ASSERT(3 == kInverseEvenOdd_PathFill);
-GR_STATIC_ASSERT(4 == kHairLine_PathFill);
-GR_STATIC_ASSERT(5 == kPathFillCount);
-
 bool GrStencilSettings::GetClipPasses(GrSetOp op, 
                                       bool canBeDirect,
                                       unsigned int stencilClipMask,
-                                      GrPathFill* fill,
+                                      bool invertedFill,
                                       int* numPasses,
                                       GrStencilSettings settings[kMaxStencilClipPasses]) {
     if (canBeDirect) {
-        if (!gIsFillInverted[*fill]) {
-            *numPasses = 0;
-            switch (op) {
-                case kReplace_SetOp:
-                    *numPasses = 1;
-                    settings[0] = gReplaceClip;
-                    break;
-                case kUnion_SetOp:
-                    *numPasses = 1;
-                    settings[0] = gUnionClip;
-                    break;
-                case kXor_SetOp:
-                    *numPasses = 1;
-                    settings[0] = gXorClip;
-                    break;
-                case kDifference_SetOp:
-                    *numPasses = 1;
-                    settings[0] = gDiffClip;
-                    break;
-                default: // suppress warning
-                    break;
-            }
-            if (1 == *numPasses) {
-                settings[0].fFrontFuncRef |= stencilClipMask;
-                settings[0].fFrontWriteMask |= stencilClipMask;
-                settings[0].fBackFuncRef = settings[0].fFrontFuncRef;
-                settings[0].fBackWriteMask = settings[0].fFrontWriteMask;
-                return true;
-            }
+        *numPasses = 0;
+        switch (op) {
+            case kReplace_SetOp:
+                *numPasses = 1;
+                settings[0] = gReplaceClip;
+                break;
+            case kUnion_SetOp:
+                *numPasses = 1;
+                settings[0] = gUnionClip;
+                break;
+            case kXor_SetOp:
+                *numPasses = 1;
+                settings[0] = gXorClip;
+                break;
+            case kDifference_SetOp:
+                *numPasses = 1;
+                settings[0] = gDiffClip;
+                break;
+            default: // suppress warning
+                break;
+        }
+        if (1 == *numPasses) {
+            settings[0].fFrontFuncRef |= stencilClipMask;
+            settings[0].fFrontWriteMask |= stencilClipMask;
+            settings[0].fBackFuncRef = settings[0].fFrontFuncRef;
+            settings[0].fBackWriteMask = settings[0].fFrontWriteMask;
+            return true;
         }
     }
     switch (op) {
+        // if we make the path renderer go to stencil we always give it a
+        // non-inverted fill and we use the stencil rules on the client->clipbit
+        // pass to select either the zeros or nonzeros.
         case kReplace_SetOp:
             *numPasses= 1;
-            settings[0] = gIsFillInverted[*fill] ? gInvUserToClipReplace : gUserToClipReplace;
+            settings[0] = invertedFill ? gInvUserToClipReplace : gUserToClipReplace;
             settings[0].fFrontFuncMask &= ~stencilClipMask;
             settings[0].fFrontFuncRef |= stencilClipMask;
             settings[0].fBackFuncMask = settings[0].fFrontFuncMask;
             settings[0].fBackFuncRef = settings[0].fFrontFuncRef;
-
+            break;
         case kIntersect_SetOp:
             *numPasses = 1;
-            settings[0] = gIsFillInverted[*fill] ? gInvUserToClipIsect : gUserToClipIsect;
+            settings[0] = invertedFill ? gInvUserToClipIsect : gUserToClipIsect;
             settings[0].fFrontFuncRef = stencilClipMask;
             settings[0].fBackFuncRef = settings[0].fFrontFuncRef;
             break;
         case kUnion_SetOp:
             *numPasses = 2;
-            if (gIsFillInverted[*fill]) {
+            if (invertedFill) {
                 settings[0] = gInvUserToClipUnionPass0;
                 settings[0].fFrontFuncRef |= stencilClipMask;
                 settings[0].fBackFuncRef = settings[0].fFrontFuncMask;
@@ -345,7 +324,7 @@
             break;
         case kXor_SetOp:
             *numPasses = 2;
-            if (gIsFillInverted[*fill]) {
+            if (invertedFill) {
                 settings[0] = gInvUserToClipXorPass0;
                 settings[0].fFrontFuncMask &= ~stencilClipMask;
                 settings[0].fBackFuncMask = settings[0].fFrontFuncMask;
@@ -365,12 +344,12 @@
             break;
         case kDifference_SetOp:
             *numPasses = 1;
-            settings[0] = gIsFillInverted[*fill] ? gInvUserToClipDiff : gUserToClipDiff;
+            settings[0] = invertedFill ? gInvUserToClipDiff : gUserToClipDiff;
             settings[0].fFrontFuncRef |= stencilClipMask;
             settings[0].fBackFuncRef = settings[0].fFrontFuncRef;
             break;
         case kReverseDifference_SetOp:
-            if (gIsFillInverted[*fill]) {
+            if (invertedFill) {
                 *numPasses = 1;
                 settings[0] = gInvUserToClipRDiff;
                 settings[0].fFrontWriteMask |= stencilClipMask;
@@ -393,6 +372,5 @@
         default:
             GrCrash("Unknown set op");
     }
-    *fill = gNonInvertedFills[*fill];
     return false;
 }
\ No newline at end of file
diff --git a/gpu/src/gr_unittests.cpp b/gpu/src/gr_unittests.cpp
index 9caaa1f..16fd9dc 100644
--- a/gpu/src/gr_unittests.cpp
+++ b/gpu/src/gr_unittests.cpp
@@ -15,11 +15,12 @@
  */
 
 
-#include "GrClip.h"
+#include "GrDrawTarget.h"
 #include "GrTDArray.h"
 #include "GrTBSearch.h"
 #include "GrMatrix.h"
 #include "GrRedBlackTree.h"
+#include "GrPath.h"
 
 static void dump(const GrTDArray<int>& array) {
 #if 0
@@ -78,6 +79,8 @@
     test_bsearch();
     GrMatrix::UnitTest();
     GrRedBlackTree<int>::UnitTest();
+    GrPath::ConvexUnitTest();
+    GrDrawTarget::VertexLayoutUnitTest();
 }