bump picture version since SkPath has changed (conics)

enable conics in SkPath

git-svn-id: http://skia.googlecode.com/svn/trunk@9370 2bbb7eff-a529-9590-31e7-b0007b416f81
diff --git a/bench/PathBench.cpp b/bench/PathBench.cpp
index 5fe0025..057a2b0 100644
--- a/bench/PathBench.cpp
+++ b/bench/PathBench.cpp
@@ -275,6 +275,12 @@
                                  fPoints[(fCurrPoint + 1) & (kNumPoints - 1)]);
                     fCurrPoint += 2;
                     break;
+                case SkPath::kConic_Verb:
+                    path->conicTo(fPoints[(fCurrPoint + 0) & (kNumPoints - 1)],
+                                  fPoints[(fCurrPoint + 1) & (kNumPoints - 1)],
+                                  SK_ScalarHalf);
+                    fCurrPoint += 2;
+                    break;
                 case SkPath::kCubic_Verb:
                     path->cubicTo(fPoints[(fCurrPoint + 0) & (kNumPoints - 1)],
                                   fPoints[(fCurrPoint + 1) & (kNumPoints - 1)],
diff --git a/include/core/SkGeometry.h b/include/core/SkGeometry.h
index 6218ed1..2c37fff 100644
--- a/include/core/SkGeometry.h
+++ b/include/core/SkGeometry.h
@@ -230,7 +230,16 @@
     void computeAsQuadError(SkVector* err) const;
     bool asQuadTol(SkScalar tol) const;
 
+    /**
+     *  return the power-of-2 number of quads needed to approximate this conic
+     *  with a sequence of quads. Will be >= 0.
+     */
     int computeQuadPOW2(SkScalar tol) const;
+
+    /**
+     *  Chop this conic into N quads, stored continguously in pts[], where
+     *  N = 1 << pow2. The amount of storage needed is (1 + 2 * N)
+     */
     int chopIntoQuadsPOW2(SkPoint pts[], int pow2) const;
 
     bool findXExtrema(SkScalar* t) const;
diff --git a/include/core/SkPath.h b/include/core/SkPath.h
index 65612cb..9300863 100644
--- a/include/core/SkPath.h
+++ b/include/core/SkPath.h
@@ -413,6 +413,14 @@
     */
     void rQuadTo(SkScalar dx1, SkScalar dy1, SkScalar dx2, SkScalar dy2);
 
+    void conicTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2,
+                 SkScalar w);
+    void conicTo(const SkPoint& p1, const SkPoint& p2, SkScalar w) {
+        this->conicTo(p1.fX, p1.fY, p2.fX, p2.fY, w);
+    }
+    void rConicTo(SkScalar dx1, SkScalar dy1, SkScalar dx2, SkScalar dy2,
+                  SkScalar w);
+    
     /** Add a cubic bezier from the last point, approaching control points
         (x1,y1) and (x2,y2), and ending at (x3,y3). If no moveTo() call has been
         made for this contour, the first point is automatically set to (0,0).
@@ -779,7 +787,8 @@
     enum SegmentMask {
         kLine_SegmentMask   = 1 << 0,
         kQuad_SegmentMask   = 1 << 1,
-        kCubic_SegmentMask  = 1 << 2
+        kConic_SegmentMask  = 1 << 2,
+        kCubic_SegmentMask  = 1 << 3,
     };
 
     /**
@@ -793,9 +802,10 @@
         kMove_Verb,     //!< iter.next returns 1 point
         kLine_Verb,     //!< iter.next returns 2 points
         kQuad_Verb,     //!< iter.next returns 3 points
+        kConic_Verb,    //!< iter.next returns 3 points + iter.conicWeight()
         kCubic_Verb,    //!< iter.next returns 4 points
         kClose_Verb,    //!< iter.next returns 1 point (contour's moveTo pt)
-        kDone_Verb      //!< iter.next returns 0 points
+        kDone_Verb,     //!< iter.next returns 0 points
     };
 
     /** Iterate through all of the segments (lines, quadratics, cubics) of
@@ -829,6 +839,12 @@
             return this->doNext(pts);
         }
 
+        /**
+         *  Return the weight for the current conic. Only valid if the current
+         *  segment return by next() was a conic.
+         */
+        SkScalar conicWeight() const { return *fConicWeights; }
+
         /** If next() returns kLine_Verb, then this query returns true if the
             line was the result of a close() command (i.e. the end point is the
             initial moveto for this contour). If next() returned a different
@@ -848,6 +864,7 @@
         const SkPoint*  fPts;
         const uint8_t*  fVerbs;
         const uint8_t*  fVerbStop;
+        const SkScalar* fConicWeights;
         SkPoint         fMoveTo;
         SkPoint         fLastPt;
         SkBool8         fForceClose;
@@ -879,10 +896,13 @@
         */
         Verb next(SkPoint pts[4]);
 
+        SkScalar conicWeight() const { return *fConicWeights; }
+
     private:
         const SkPoint*  fPts;
         const uint8_t*  fVerbs;
         const uint8_t*  fVerbStop;
+        const SkScalar* fConicWeights;
         SkPoint         fMoveTo;
         SkPoint         fLastPt;
     };
@@ -922,7 +942,7 @@
         kIsOval_SerializationShift = 24,    // requires 1 bit
         kConvexity_SerializationShift = 16, // requires 2 bits
         kFillType_SerializationShift = 8,   // requires 2 bits
-        kSegmentMask_SerializationShift = 0 // requires 3 bits
+        kSegmentMask_SerializationShift = 0 // requires 4 bits
     };
 
 #if SK_DEBUG_PATH_REF
diff --git a/include/core/SkPicture.h b/include/core/SkPicture.h
index aea8fc7..d01cadc 100644
--- a/include/core/SkPicture.h
+++ b/include/core/SkPicture.h
@@ -210,7 +210,8 @@
     //      SK_SUPPORT_HINTING_SCALE_FACTOR
     // V10: add drawRRect, drawOval, clipRRect
     // V11: modify how readBitmap and writeBitmap store their info.
-    static const uint32_t PICTURE_VERSION = 11;
+    // V12: add conics to SkPath, use new SkPathRef flattening
+    static const uint32_t PICTURE_VERSION = 12;
 
     // fPlayback, fRecord, fWidth & fHeight are protected to allow derived classes to
     // install their own SkPicturePlayback-derived players,SkPictureRecord-derived
diff --git a/src/core/SkEdgeBuilder.cpp b/src/core/SkEdgeBuilder.cpp
index 608a83c..9ac4e97 100644
--- a/src/core/SkEdgeBuilder.cpp
+++ b/src/core/SkEdgeBuilder.cpp
@@ -153,12 +153,22 @@
     return edgePtr - fEdgeList;
 }
 
+static void handle_quad(SkEdgeBuilder* builder, const SkPoint pts[3]) {
+    SkPoint monoX[5];
+    int n = SkChopQuadAtYExtrema(pts, monoX);
+    for (int i = 0; i <= n; i++) {
+        builder->addQuad(&monoX[i * 2]);
+    }
+}
+
 int SkEdgeBuilder::build(const SkPath& path, const SkIRect* iclip,
                          int shiftUp) {
     fAlloc.reset();
     fList.reset();
     fShiftUp = shiftUp;
 
+    SkScalar conicTol = SK_ScalarHalf * (1 << shiftUp);
+    
     if (SkPath::kLine_SegmentMask == path.getSegmentMasks()) {
         return this->buildPoly(path, iclip, shiftUp);
     }
@@ -192,6 +202,24 @@
                         this->addClipper(&clipper);
                     }
                     break;
+                case SkPath::kConic_Verb: {
+                    const int MAX_POW2 = 4;
+                    const int MAX_QUADS = 1 << MAX_POW2;
+                    const int MAX_QUAD_PTS = 1 + 2 * MAX_QUADS;
+                    SkPoint storage[MAX_QUAD_PTS];
+
+                    SkConic conic;
+                    conic.set(pts, iter.conicWeight());
+                    int pow2 = conic.computeQuadPOW2(conicTol);
+                    pow2 = SkMin32(pow2, MAX_POW2);
+                    int quadCount = conic.chopIntoQuadsPOW2(storage, pow2);
+                    SkASSERT(quadCount <= MAX_QUADS);
+                    for (int i = 0; i < quadCount; ++i) {
+                        if (clipper.clipQuad(&storage[i * 2], clip)) {
+                            this->addClipper(&clipper);
+                        }
+                    }
+                } break;
                 case SkPath::kCubic_Verb:
                     if (clipper.clipCubic(pts, clip)) {
                         this->addClipper(&clipper);
@@ -214,13 +242,26 @@
                     this->addLine(pts);
                     break;
                 case SkPath::kQuad_Verb: {
-                    SkPoint monoX[5];
-                    int n = SkChopQuadAtYExtrema(pts, monoX);
-                    for (int i = 0; i <= n; i++) {
-                        this->addQuad(&monoX[i * 2]);
-                    }
+                    handle_quad(this, pts);
                     break;
                 }
+                case SkPath::kConic_Verb: {
+                    const int MAX_POW2 = 4;
+                    const int MAX_QUADS = 1 << MAX_POW2;
+                    const int MAX_QUAD_PTS = 1 + 2 * MAX_QUADS;
+                    SkPoint storage[MAX_QUAD_PTS];
+                    
+                    SkConic conic;
+                    conic.set(pts, iter.conicWeight());
+                    int pow2 = conic.computeQuadPOW2(conicTol);
+                    pow2 = SkMin32(pow2, MAX_POW2);
+                    int quadCount = conic.chopIntoQuadsPOW2(storage, pow2);
+                    SkASSERT(quadCount <= MAX_QUADS);
+                    SkDebugf("--- quadCount = %d\n", quadCount);
+                    for (int i = 0; i < quadCount; ++i) {
+                        handle_quad(this, &storage[i * 2]);
+                    }
+                } break;
                 case SkPath::kCubic_Verb: {
                     SkPoint monoY[10];
                     int n = SkChopCubicAtYExtrema(pts, monoY);
diff --git a/src/core/SkEdgeBuilder.h b/src/core/SkEdgeBuilder.h
index b296f77..f9e5976 100644
--- a/src/core/SkEdgeBuilder.h
+++ b/src/core/SkEdgeBuilder.h
@@ -40,6 +40,7 @@
 
     int                 fShiftUp;
 
+public:
     void addLine(const SkPoint pts[]);
     void addQuad(const SkPoint pts[]);
     void addCubic(const SkPoint pts[]);
diff --git a/src/core/SkPath.cpp b/src/core/SkPath.cpp
index 95cd9fe..7485c0b 100644
--- a/src/core/SkPath.cpp
+++ b/src/core/SkPath.cpp
@@ -369,6 +369,7 @@
                 SkASSERT(moveCnt);
                 break;
             case kQuad_Verb:
+            case kConic_Verb:
                 SkASSERT(moveCnt);
                 nextPt = 2;
                 break;
@@ -558,12 +559,16 @@
                 break;
             }
             case kQuad_Verb:
+            case kConic_Verb:
             case kCubic_Verb:
                 return false; // quadratic, cubic not allowed
             case kMove_Verb:
                 last = *pts++;
                 closedOrMoved = true;
                 break;
+            default:
+                SkASSERT(!"unexpected verb");
+                break;
         }
         *currVerb += 1;
         lastDirection = nextDirection;
@@ -805,15 +810,15 @@
 
 void SkPath::quadTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2) {
     SkDEBUGCODE(this->validate();)
-
+    
     this->injectMoveToIfNeeded();
-
+    
     SkPathRef::Editor ed(&fPathRef);
     SkPoint* pts = ed.growForVerb(kQuad_Verb);
     pts[0].set(x1, y1);
     pts[1].set(x2, y2);
     fSegmentMask |= kQuad_SegmentMask;
-
+    
     GEN_ID_INC;
     DIRTY_AFTER_EDIT;
 }
@@ -824,6 +829,39 @@
     this->quadTo(pt.fX + x1, pt.fY + y1, pt.fX + x2, pt.fY + y2);
 }
 
+void SkPath::conicTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2,
+                     SkScalar w) {
+    // check for <= 0 or NaN with this test
+    if (!(w > 0)) {
+        this->lineTo(x2, y2);
+    } else if (!SkScalarIsFinite(w)) {
+        this->lineTo(x1, y1);
+        this->lineTo(x2, y2);
+    } else if (SK_Scalar1 == w) {
+        this->quadTo(x1, y1, x2, y2);
+    } else {
+        SkDEBUGCODE(this->validate();)
+        
+        this->injectMoveToIfNeeded();
+        
+        SkPathRef::Editor ed(&fPathRef);
+        SkPoint* pts = ed.growForConic(w);
+        pts[0].set(x1, y1);
+        pts[1].set(x2, y2);
+        fSegmentMask |= kConic_SegmentMask;
+        
+        GEN_ID_INC;
+        DIRTY_AFTER_EDIT;
+    }
+}
+
+void SkPath::rConicTo(SkScalar dx1, SkScalar dy1, SkScalar dx2, SkScalar dy2,
+                      SkScalar w) {
+    SkPoint pt;
+    this->getLastPt(&pt);
+    this->conicTo(pt.fX + dx1, pt.fY + dy1, pt.fX + dx2, pt.fY + dy2, w);
+}
+
 void SkPath::cubicTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2,
                      SkScalar x3, SkScalar y3) {
     SkDEBUGCODE(this->validate();)
@@ -857,6 +895,7 @@
         switch (fPathRef->atVerb(count - 1)) {
             case kLine_Verb:
             case kQuad_Verb:
+            case kConic_Verb:
             case kCubic_Verb:
             case kMove_Verb: {
                 SkPathRef::Editor ed(&fPathRef);
@@ -864,9 +903,12 @@
                 GEN_ID_INC;
                 break;
             }
-            default:
+            case kClose_Verb:
                 // don't add a close if it's the first verb or a repeat
                 break;
+            default:
+                SkASSERT(!"unexpected verb");
+                break;
         }
     }
 
@@ -1430,6 +1472,10 @@
                 proc(matrix, &pts[1], &pts[1], 2);
                 this->quadTo(pts[1], pts[2]);
                 break;
+            case kConic_Verb:
+                proc(matrix, &pts[1], &pts[1], 2);
+                this->conicTo(pts[1], pts[2], iter.conicWeight());
+                break;
             case kCubic_Verb:
                 proc(matrix, &pts[1], &pts[1], 3);
                 this->cubicTo(pts[1], pts[2], pts[3]);
@@ -1445,14 +1491,20 @@
 
 ///////////////////////////////////////////////////////////////////////////////
 
-static const uint8_t gPtsInVerb[] = {
-    1,  // kMove
-    1,  // kLine
-    2,  // kQuad
-    3,  // kCubic
-    0,  // kClose
-    0   // kDone
-};
+static int pts_in_verb(unsigned verb) {
+    static const uint8_t gPtsInVerb[] = {
+        1,  // kMove
+        1,  // kLine
+        2,  // kQuad
+        2,  // kConic
+        3,  // kCubic
+        0,  // kClose
+        0   // kDone
+    };
+
+    SkASSERT(verb < SK_ARRAY_COUNT(gPtsInVerb));
+    return gPtsInVerb[verb];
+}
 
 // ignore the initial moveto, and stop when the 1st contour ends
 void SkPath::pathTo(const SkPath& path) {
@@ -1469,6 +1521,7 @@
     const uint8_t* verbs = path.fPathRef->verbs();
     // skip the initial moveTo
     const SkPoint*  pts = path.fPathRef->points() + 1;
+    const SkScalar* conicWeight = path.fPathRef->conicWeights();
 
     SkASSERT(verbs[~0] == kMove_Verb);
     for (i = 1; i < vcount; i++) {
@@ -1479,13 +1532,16 @@
             case kQuad_Verb:
                 this->quadTo(pts[0].fX, pts[0].fY, pts[1].fX, pts[1].fY);
                 break;
+            case kConic_Verb:
+                this->conicTo(pts[0], pts[1], *conicWeight++);
+                break;
             case kCubic_Verb:
                 this->cubicTo(pts[0].fX, pts[0].fY, pts[1].fX, pts[1].fY, pts[2].fX, pts[2].fY);
                 break;
             case kClose_Verb:
                 return;
         }
-        pts += gPtsInVerb[verbs[~i]];
+        pts += pts_in_verb(verbs[~i]);
     }
 }
 
@@ -1503,14 +1559,17 @@
 
     const uint8_t*  verbs = path.fPathRef->verbs();
     const SkPoint*  pts = path.fPathRef->points();
+    const SkScalar* conicWeights = path.fPathRef->conicWeights();
 
     SkASSERT(verbs[~0] == kMove_Verb);
     for (i = 1; i < vcount; ++i) {
-        int n = gPtsInVerb[verbs[~i]];
+        unsigned v = verbs[~i];
+        int n = pts_in_verb(v);
         if (n == 0) {
             break;
         }
         pts += n;
+        conicWeights += (SkPath::kConic_Verb == v);
     }
 
     while (--i > 0) {
@@ -1521,6 +1580,9 @@
             case kQuad_Verb:
                 this->quadTo(pts[-1].fX, pts[-1].fY, pts[-2].fX, pts[-2].fY);
                 break;
+            case kConic_Verb:
+                this->conicTo(pts[-1], pts[-2], *--conicWeights);
+                break;
             case kCubic_Verb:
                 this->cubicTo(pts[-1].fX, pts[-1].fY, pts[-2].fX, pts[-2].fY,
                               pts[-3].fX, pts[-3].fY);
@@ -1529,7 +1591,7 @@
                 SkDEBUGFAIL("bad verb");
                 break;
         }
-        pts -= gPtsInVerb[verbs[~i]];
+        pts -= pts_in_verb(verbs[~i]);
     }
 }
 
@@ -1540,6 +1602,7 @@
     // we will iterator through src's verbs backwards
     const uint8_t* verbs = src.fPathRef->verbsMemBegin(); // points at the last verb
     const uint8_t* verbsEnd = src.fPathRef->verbs(); // points just past the first verb
+    const SkScalar* conicWeights = src.fPathRef->conicWeightsEnd();
 
     fIsOval = false;
 
@@ -1547,7 +1610,7 @@
     bool needClose = false;
     while (verbs < verbsEnd) {
         uint8_t v = *(verbs++);
-        int n = gPtsInVerb[v];
+        int n = pts_in_verb(v);
 
         if (needMove) {
             --pts;
@@ -1570,6 +1633,9 @@
             case kQuad_Verb:
                 this->quadTo(pts[1], pts[0]);
                 break;
+            case kConic_Verb:
+                this->conicTo(pts[1], pts[0], *--conicWeights);
+                break;
             case kCubic_Verb:
                 this->cubicTo(pts[2], pts[1], pts[0]);
                 break;
@@ -1644,6 +1710,10 @@
                 case kQuad_Verb:
                     subdivide_quad_to(&tmp, pts);
                     break;
+                case kConic_Verb:
+                    SkASSERT(!"TODO: compute new weight");
+                    tmp.conicTo(pts[1], pts[2], iter.conicWeight());
+                    break;
                 case kCubic_Verb:
                     subdivide_cubic_to(&tmp, pts);
                     break;
@@ -1741,6 +1811,7 @@
 SkPath::Iter::Iter() {
 #ifdef SK_DEBUG
     fPts = NULL;
+    fConicWeights = NULL;
     fMoveTo.fX = fMoveTo.fY = fLastPt.fX = fLastPt.fY = 0;
     fForceClose = fCloseLine = false;
     fSegmentState = kEmptyContour_SegmentState;
@@ -1759,6 +1830,7 @@
     fPts = path.fPathRef->points();
     fVerbs = path.fPathRef->verbs();
     fVerbStop = path.fPathRef->verbsMemBegin();
+    fConicWeights = path.fPathRef->conicWeights() - 1; // begin one behind
     fLastPt.fX = fLastPt.fY = 0;
     fMoveTo.fX = fMoveTo.fY = 0;
     fForceClose = SkToU8(forceClose);
@@ -1870,6 +1942,7 @@
                 fPts++;
                 break;
 
+            case kConic_Verb:
             case kQuad_Verb:
                 if (!IsQuadDegenerate(lastPt, fPts[0], fPts[1])) {
                     if (lastMoveVerb) {
@@ -1882,6 +1955,7 @@
                 // Ignore this line and continue
                 fVerbs--;
                 fPts += 2;
+                fConicWeights += (kConic_Verb == verb);
                 break;
 
             case kCubic_Verb:
@@ -1951,6 +2025,9 @@
             fCloseLine = false;
             srcPts += 1;
             break;
+        case kConic_Verb:
+            fConicWeights += 1;
+            // fall-through
         case kQuad_Verb:
             pts[0] = this->cons_moveTo();
             memcpy(&pts[1], srcPts, 2 * sizeof(SkPoint));
@@ -1983,6 +2060,7 @@
 SkPath::RawIter::RawIter() {
 #ifdef SK_DEBUG
     fPts = NULL;
+    fConicWeights = NULL;
     fMoveTo.fX = fMoveTo.fY = fLastPt.fX = fLastPt.fY = 0;
 #endif
     // need to init enough to make next() harmlessly return kDone_Verb
@@ -1998,6 +2076,7 @@
     fPts = path.fPathRef->points();
     fVerbs = path.fPathRef->verbs();
     fVerbStop = path.fPathRef->verbsMemBegin();
+    fConicWeights = path.fPathRef->conicWeights() - 1; // begin one behind
     fMoveTo.fX = fMoveTo.fY = 0;
     fLastPt.fX = fLastPt.fY = 0;
 }
@@ -2025,6 +2104,9 @@
             fLastPt = srcPts[0];
             srcPts += 1;
             break;
+        case kConic_Verb:
+            fConicWeights += 1;
+            // fall-through
         case kQuad_Verb:
             pts[0] = fLastPt;
             memcpy(&pts[1], srcPts, 2 * sizeof(SkPoint));
@@ -2057,22 +2139,12 @@
 
     if (NULL == storage) {
         const int byteCount = sizeof(int32_t)
-#if NEW_PICTURE_FORMAT
                       + fPathRef->writeSize()
-#else
-                      + 2 * sizeof(int32_t)
-                      + sizeof(SkPoint) * fPathRef->countPoints()
-                      + sizeof(uint8_t) * fPathRef->countVerbs()
-#endif
                       + sizeof(SkRect);
         return SkAlign4(byteCount);
     }
 
     SkWBuffer   buffer(storage);
-#if !NEW_PICTURE_FORMAT
-    buffer.write32(fPathRef->countPoints());
-    buffer.write32(fPathRef->countVerbs());
-#endif
 
     // Call getBounds() to ensure (as a side-effect) that fBounds
     // and fIsFinite are computed.
@@ -2098,24 +2170,16 @@
 
 uint32_t SkPath::readFromMemory(const void* storage) {
     SkRBuffer   buffer(storage);
-#if !NEW_PICTURE_FORMAT
-    int32_t pcount = buffer.readS32();
-    int32_t vcount = buffer.readS32();
-#endif
 
     uint32_t packed = buffer.readS32();
     fIsFinite = (packed >> kIsFinite_SerializationShift) & 1;
     fIsOval = (packed >> kIsOval_SerializationShift) & 1;
     fConvexity = (packed >> kConvexity_SerializationShift) & 0xFF;
     fFillType = (packed >> kFillType_SerializationShift) & 0xFF;
-    fSegmentMask = (packed >> kSegmentMask_SerializationShift) & 0x7;
+    fSegmentMask = (packed >> kSegmentMask_SerializationShift) & 0xF;
     fDirection = (packed >> kDirection_SerializationShift) & 0x3;
 
-#if NEW_PICTURE_FORMAT
     fPathRef.reset(SkPathRef::CreateFromBuffer(&buffer));
-#else
-    fPathRef.reset(SkPathRef::CreateFromBuffer(vcount, pcount, &buffer));
-#endif
 
     buffer.read(&fBounds, sizeof(fBounds));
     fBoundsIsDirty = false;
@@ -2142,7 +2206,7 @@
 }
 
 static void append_params(SkString* str, const char label[], const SkPoint pts[],
-                          int count) {
+                          int count, SkScalar conicWeight = -1) {
     str->append(label);
     str->append("(");
 
@@ -2155,6 +2219,10 @@
             str->append(", ");
         }
     }
+    if (conicWeight >= 0) {
+        str->append(", ");
+        append_scalar(str, conicWeight);
+    }
     str->append(");\n");
 }
 
@@ -2180,6 +2248,9 @@
             case kQuad_Verb:
                 append_params(&builder, "path.quadTo", &pts[1], 2);
                 break;
+            case kConic_Verb:
+                append_params(&builder, "path.conicTo", &pts[1], 2, iter.conicWeight());
+                break;
             case kCubic_Verb:
                 append_params(&builder, "path.cubicTo", &pts[1], 3);
                 break;
@@ -2239,6 +2310,9 @@
             case kQuad_Verb:
                 mask |= kQuad_SegmentMask;
                 break;
+            case kConic_Verb:
+                mask |= kConic_SegmentMask;
+                break;
             case kCubic_Verb:
                 mask |= kCubic_SegmentMask;
             case kMove_Verb:  // these verbs aren't included in the segment mask.
@@ -2379,6 +2453,7 @@
                 break;
             case kLine_Verb: count = 1; break;
             case kQuad_Verb: count = 2; break;
+            case kConic_Verb: count = 2; break;
             case kCubic_Verb: count = 3; break;
             case kClose_Verb:
                 state.close();
@@ -2423,6 +2498,7 @@
     const SkPoint* fCurrPt;
     const uint8_t* fCurrVerb;
     const uint8_t* fStopVerbs;
+    const SkScalar* fCurrConicWeight;
     bool fDone;
     SkDEBUGCODE(int fContourCounter;)
 };
@@ -2432,6 +2508,7 @@
     fDone = false;
     fCurrPt = pathRef.points();
     fCurrVerb = pathRef.verbs();
+    fCurrConicWeight = pathRef.conicWeights();
     fCurrPtCount = 0;
     SkDEBUGCODE(fContourCounter = 0;)
     this->next();
@@ -2459,13 +2536,19 @@
             case SkPath::kLine_Verb:
                 ptCount += 1;
                 break;
+            case SkPath::kConic_Verb:
+                fCurrConicWeight += 1;
+                // fall-through
             case SkPath::kQuad_Verb:
                 ptCount += 2;
                 break;
             case SkPath::kCubic_Verb:
                 ptCount += 3;
                 break;
-            default:    // kClose_Verb, just keep going
+            case SkPath::kClose_Verb:
+                break;
+            default:
+                SkASSERT(!"unexpected verb");
                 break;
         }
     }
@@ -2957,13 +3040,16 @@
             case SkPath::kQuad_Verb:
                 w += winding_quad(pts, x, y);
                 break;
+            case SkPath::kConic_Verb:
+                SkASSERT(0);
+                break;
             case SkPath::kCubic_Verb:
                 w += winding_cubic(pts, x, y);
                 break;
             case SkPath::kDone_Verb:
                 done = true;
                 break;
-        }
+       }
     } while (!done);
 
     switch (this->getFillType()) {
diff --git a/src/core/SkPathMeasure.cpp b/src/core/SkPathMeasure.cpp
index af2579f..c519f93 100644
--- a/src/core/SkPathMeasure.cpp
+++ b/src/core/SkPathMeasure.cpp
@@ -152,6 +152,9 @@
     bool done = false;
     do {
         switch (fIter.next(pts)) {
+            case SkPath::kConic_Verb:
+                SkASSERT(0);
+                break;
             case SkPath::kMove_Verb:
                 ptIndex += 1;
                 fPts.append(1, pts);
diff --git a/src/core/SkPathRef.h b/src/core/SkPathRef.h
index 5f85b7b..b670039 100644
--- a/src/core/SkPathRef.h
+++ b/src/core/SkPathRef.h
@@ -12,12 +12,6 @@
 #include "SkRefCnt.h"
 #include <stddef.h> // ptrdiff_t
 
-// When we're ready to break the picture format. Changes:
-// * Write genID.
-// * SkPathRef read/write counts (which will change the field order)
-// * SkPathRef reads/writes verbs backwards.
-#define NEW_PICTURE_FORMAT 0
-
 /**
  * Holds the path verbs and points. It is versioned by a generation ID. None of its public methods
  * modify the contents. To modify or append to the verbs/points wrap the SkPathRef in an
@@ -109,6 +103,13 @@
             return fPathRef->growForVerb(verb);
         }
 
+        SkPoint* growForConic(SkScalar w) {
+            fPathRef->validate();
+            SkPoint* pts = fPathRef->growForVerb(SkPath::kConic_Verb);
+            *fPathRef->fConicWeights.append() = w;
+            return pts;
+        }
+
         /**
          * Allocates space for additional verbs and points and returns pointers to the new verbs and
          * points. verbs will point one beyond the first new verb (index it using [~<i>]). pts points
@@ -131,8 +132,8 @@
          * Resets the path ref to a new verb and point count. The new verbs and points are
          * uninitialized.
          */
-        void resetToSize(int newVerbCnt, int newPointCnt) {
-            fPathRef->resetToSize(newVerbCnt, newPointCnt);
+        void resetToSize(int newVerbCnt, int newPointCnt, int newConicCount) {
+            fPathRef->resetToSize(newVerbCnt, newPointCnt, newConicCount);
         }
         /**
          * Gets the path ref that is wrapped in the Editor.
@@ -202,40 +203,29 @@
         } else if (rcnt > 1) {
             dst->reset(SkNEW(SkPathRef));
         }
-        (*dst)->resetToSize(src.fVerbCnt, src.fPointCnt);
+        (*dst)->resetToSize(src.fVerbCnt, src.fPointCnt, src.fConicWeights.count());
         memcpy((*dst)->verbsMemWritable(), src.verbsMemBegin(), src.fVerbCnt * sizeof(uint8_t));
         matrix.mapPoints((*dst)->fPoints, src.points(), src.fPointCnt);
+        (*dst)->fConicWeights = src.fConicWeights;
         (*dst)->validate();
     }
 
-#if NEW_PICTURE_FORMAT
     static SkPathRef* CreateFromBuffer(SkRBuffer* buffer) {
         SkPathRef* ref = SkNEW(SkPathRef);
         ref->fGenerationID = buffer->readU32();
         int32_t verbCount = buffer->readS32();
         int32_t pointCount = buffer->readS32();
-        ref->resetToSize(verbCount, pointCount);
+        int32_t conicCount = buffer->readS32();
+        ref->resetToSize(verbCount, pointCount, conicCount);
 
         SkASSERT(verbCount == ref->countVerbs());
         SkASSERT(pointCount == ref->countPoints());
+        SkASSERT(conicCount == ref->fConicWeights.count());
         buffer->read(ref->verbsMemWritable(), verbCount * sizeof(uint8_t));
         buffer->read(ref->fPoints, pointCount * sizeof(SkPoint));
+        buffer->read(ref->fConicWeights.begin(), conicCount * sizeof(SkScalar));
         return ref;
     }
-#else
-    static SkPathRef* CreateFromBuffer(int verbCount, int pointCount, SkRBuffer* buffer) {
-        SkPathRef* ref = SkNEW(SkPathRef);
-
-        ref->resetToSize(verbCount, pointCount);
-        SkASSERT(verbCount == ref->countVerbs());
-        SkASSERT(pointCount == ref->countPoints());
-        buffer->read(ref->fPoints, pointCount * sizeof(SkPoint));
-        for (int i = 0; i < verbCount; ++i) {
-            ref->fVerbs[~i] = buffer->readU8();
-        }
-        return ref;
-    }
-#endif
 
     /**
      * Rollsback a path ref to zero verbs and points with the assumption that the path ref will be
@@ -249,12 +239,13 @@
             (*pathRef)->fPointCnt = 0;
             (*pathRef)->fFreeSpace = (*pathRef)->currSize();
             (*pathRef)->fGenerationID = 0;
+            (*pathRef)->fConicWeights.rewind();
             (*pathRef)->validate();
         } else {
             int oldVCnt = (*pathRef)->countVerbs();
             int oldPCnt = (*pathRef)->countPoints();
             pathRef->reset(SkNEW(SkPathRef));
-            (*pathRef)->resetToSize(0, 0, oldVCnt, oldPCnt);
+            (*pathRef)->resetToSize(0, 0, 0, oldVCnt, oldPCnt);
         }
     }
 
@@ -299,6 +290,9 @@
      */
     const SkPoint* pointsEnd() const { return this->points() + this->countPoints(); }
 
+    const SkScalar* conicWeights() const { this->validate(); return fConicWeights.begin(); }
+    const SkScalar* conicWeightsEnd() const { this->validate(); return fConicWeights.end(); }
+
     /**
      * Convenience methods for getting to a verb or point by index.
      */
@@ -337,6 +331,10 @@
             SkASSERT(!genIDMatch);
             return false;
         }
+        if (fConicWeights != ref.fConicWeights) {
+            SkASSERT(!genIDMatch);
+            return false;
+        }
         // We've done the work to determine that these are equal. If either has a zero genID, copy
         // the other's. If both are 0 then genID() will compute the next ID.
         if (0 == fGenerationID) {
@@ -350,7 +348,6 @@
     /**
      * Writes the path points and verbs to a buffer.
      */
-#if NEW_PICTURE_FORMAT
     void writeToBuffer(SkWBuffer* buffer) {
         this->validate();
         SkDEBUGCODE_X(size_t beforePos = buffer->pos();)
@@ -358,10 +355,12 @@
         // TODO: write gen ID here. Problem: We don't know if we're cross process or not from
         // SkWBuffer. Until this is fixed we write 0.
         buffer->write32(0);
-        buffer->write32(this->fVerbCnt);
-        buffer->write32(this->fPointCnt);
-        buffer->write(this->verbsMemBegin(), fVerbCnt * sizeof(uint8_t));
+        buffer->write32(fVerbCnt);
+        buffer->write32(fPointCnt);
+        buffer->write32(fConicWeights.count());
+        buffer->write(verbsMemBegin(), fVerbCnt * sizeof(uint8_t));
         buffer->write(fPoints, fPointCnt * sizeof(SkPoint));
+        buffer->write(fConicWeights.begin(), fConicWeights.bytes());
 
         SkASSERT(buffer->pos() - beforePos == (size_t) this->writeSize());
     }
@@ -370,17 +369,11 @@
      * Gets the number of bytes that would be written in writeBuffer()
      */
     uint32_t writeSize() {
-        return 3 * sizeof(uint32_t) + fVerbCnt * sizeof(uint8_t) + fPointCnt * sizeof(SkPoint);
+        return 4 * sizeof(uint32_t) +
+               fVerbCnt * sizeof(uint8_t) +
+               fPointCnt * sizeof(SkPoint) +
+               fConicWeights.bytes();
     }
-#else
-    void writeToBuffer(SkWBuffer* buffer) {
-        this->validate();
-        buffer->write(fPoints, fPointCnt * sizeof(SkPoint));
-        for (int i = 0; i < fVerbCnt; ++i) {
-            buffer->write8(fVerbs[~i]);
-        }
-    }
-#endif
 
 private:
     SkPathRef() {
@@ -396,10 +389,11 @@
 
     void copy(const SkPathRef& ref, int additionalReserveVerbs, int additionalReservePoints) {
         this->validate();
-        this->resetToSize(ref.fVerbCnt, ref.fPointCnt,
+        this->resetToSize(ref.fVerbCnt, ref.fPointCnt, ref.fConicWeights.count(),
                           additionalReserveVerbs, additionalReservePoints);
         memcpy(this->verbsMemWritable(), ref.verbsMemBegin(), ref.fVerbCnt * sizeof(uint8_t));
         memcpy(this->fPoints, ref.fPoints, ref.fPointCnt * sizeof(SkPoint));
+        fConicWeights = ref.fConicWeights;
         // We could call genID() here to force a real ID (instead of 0). However, if we're making
         // a copy then presumably we intend to make a modification immediately afterwards.
         fGenerationID = ref.fGenerationID;
@@ -416,7 +410,8 @@
 
     /** Resets the path ref with verbCount verbs and pointCount points, all unitialized. Also
      *  allocates space for reserveVerb additional verbs and reservePoints additional points.*/
-    void resetToSize(int verbCount, int pointCount, int reserveVerbs = 0, int reservePoints = 0) {
+    void resetToSize(int verbCount, int pointCount, int conicCount,
+                     int reserveVerbs = 0, int reservePoints = 0) {
         this->validate();
         fGenerationID = 0;
 
@@ -442,6 +437,7 @@
             fVerbCnt = verbCount;
             fFreeSpace = this->currSize() - minSize;
         }
+        fConicWeights.setCount(conicCount);
         this->validate();
     }
 
@@ -474,13 +470,17 @@
             case SkPath::kLine_Verb:
                 pCnt = 1;
                 break;
+            case SkPath::kConic_Verb:
             case SkPath::kQuad_Verb:
                 pCnt = 2;
                 break;
             case SkPath::kCubic_Verb:
                 pCnt = 3;
                 break;
-            default:
+            case SkPath::kDone_Verb:
+                SkASSERT(!"growForVerb called for kDone");
+                // fall through
+            case SkPath::kClose_Verb:
                 pCnt = 0;
         }
         size_t space = sizeof(uint8_t) + pCnt * sizeof (SkPoint);
@@ -588,6 +588,8 @@
     int                 fVerbCnt;
     int                 fPointCnt;
     size_t              fFreeSpace; // redundant but saves computation
+    SkTDArray<SkScalar> fConicWeights;
+
     enum {
         kEmptyGenID = 1, // GenID reserved for path ref with zero points and zero verbs.
     };
diff --git a/src/core/SkRegion_path.cpp b/src/core/SkRegion_path.cpp
index 80149a3..0a5a401 100644
--- a/src/core/SkRegion_path.cpp
+++ b/src/core/SkRegion_path.cpp
@@ -216,25 +216,36 @@
     *runs = SkRegion::kRunTypeSentinel;
 }
 
-static int count_path_runtype_values(const SkPath& path, int* itop, int* ibot) {
+static unsigned verb_to_initial_last_index(unsigned verb) {
     static const uint8_t gPathVerbToInitialLastIndex[] = {
         0,  //  kMove_Verb
         1,  //  kLine_Verb
         2,  //  kQuad_Verb
+        2,  //  kConic_Verb
         3,  //  kCubic_Verb
         0,  //  kClose_Verb
         0   //  kDone_Verb
     };
+    SkASSERT((unsigned)verb < SK_ARRAY_COUNT(gPathVerbToInitialLastIndex));
+    return gPathVerbToInitialLastIndex[verb];
+}
 
+static unsigned verb_to_max_edges(unsigned verb) {
     static const uint8_t gPathVerbToMaxEdges[] = {
         0,  //  kMove_Verb
         1,  //  kLine_Verb
         2,  //  kQuad_VerbB
+        2,  //  kConic_VerbB
         3,  //  kCubic_Verb
         0,  //  kClose_Verb
         0   //  kDone_Verb
     };
+    SkASSERT((unsigned)verb < SK_ARRAY_COUNT(gPathVerbToMaxEdges));
+    return gPathVerbToMaxEdges[verb];
+}
 
+
+static int count_path_runtype_values(const SkPath& path, int* itop, int* ibot) {
     SkPath::Iter    iter(path, true);
     SkPoint         pts[4];
     SkPath::Verb    verb;
@@ -244,9 +255,9 @@
     SkScalar    bot = SkIntToScalar(SK_MinS16);
 
     while ((verb = iter.next(pts, false)) != SkPath::kDone_Verb) {
-        maxEdges += gPathVerbToMaxEdges[verb];
+        maxEdges += verb_to_max_edges(verb);
 
-        int lastIndex = gPathVerbToInitialLastIndex[verb];
+        int lastIndex = verb_to_initial_last_index(verb);
         if (lastIndex > 0) {
             for (int i = 1; i <= lastIndex; i++) {
                 if (top > pts[i].fY) {
diff --git a/src/effects/SkCornerPathEffect.cpp b/src/effects/SkCornerPathEffect.cpp
index 1cf797a..f4c3147 100644
--- a/src/effects/SkCornerPathEffect.cpp
+++ b/src/effects/SkCornerPathEffect.cpp
@@ -111,6 +111,9 @@
                     }
                 dst->close();
                 break;
+            case SkPath::kConic_Verb:
+                SkASSERT(0);
+                break;
             case SkPath::kDone_Verb:
                 goto DONE;
         }
diff --git a/src/gpu/GrAAHairLinePathRenderer.cpp b/src/gpu/GrAAHairLinePathRenderer.cpp
index 1a15521..bed931c 100644
--- a/src/gpu/GrAAHairLinePathRenderer.cpp
+++ b/src/gpu/GrAAHairLinePathRenderer.cpp
@@ -221,7 +221,10 @@
         GrPoint devPts[4];
         SkPath::Verb verb = iter.next(pathPts);
         switch (verb) {
-            case SkPath::kMove_Verb:
+             case SkPath::kConic_Verb:
+                SkASSERT(0);
+                break;
+           case SkPath::kMove_Verb:
                 break;
             case SkPath::kLine_Verb:
                 m.mapPoints(devPts, pathPts, 2);
diff --git a/src/gpu/GrDefaultPathRenderer.cpp b/src/gpu/GrDefaultPathRenderer.cpp
index 13bfe92..19fd2be 100644
--- a/src/gpu/GrDefaultPathRenderer.cpp
+++ b/src/gpu/GrDefaultPathRenderer.cpp
@@ -255,6 +255,9 @@
     for (;;) {
         SkPath::Verb verb = iter.next(pts);
         switch (verb) {
+            case SkPath::kConic_Verb:
+                SkASSERT(0);
+                break;
             case SkPath::kMove_Verb:
                 if (!first) {
                     uint16_t currIdx = (uint16_t) (vert - base);
diff --git a/src/gpu/gl/GrGLPath.cpp b/src/gpu/gl/GrGLPath.cpp
index 8c6e11e..63c2fa1 100644
--- a/src/gpu/gl/GrGLPath.cpp
+++ b/src/gpu/gl/GrGLPath.cpp
@@ -20,14 +20,15 @@
         GR_GL_MOVE_TO,
         GR_GL_LINE_TO,
         GR_GL_QUADRATIC_CURVE_TO,
+        0xFF, // conic
         GR_GL_CUBIC_CURVE_TO,
         GR_GL_CLOSE_PATH,
     };
     GR_STATIC_ASSERT(0 == SkPath::kMove_Verb);
     GR_STATIC_ASSERT(1 == SkPath::kLine_Verb);
     GR_STATIC_ASSERT(2 == SkPath::kQuad_Verb);
-    GR_STATIC_ASSERT(3 == SkPath::kCubic_Verb);
-    GR_STATIC_ASSERT(4 == SkPath::kClose_Verb);
+    GR_STATIC_ASSERT(4 == SkPath::kCubic_Verb);
+    GR_STATIC_ASSERT(5 == SkPath::kClose_Verb);
 
     GrAssert(verb >= 0 && (size_t)verb < GR_ARRAY_COUNT(gTable));
     return gTable[verb];
@@ -39,14 +40,15 @@
         1, // move
         1, // line
         2, // quad
+        2, // conic
         3, // cubic
         0, // close
     };
     GR_STATIC_ASSERT(0 == SkPath::kMove_Verb);
     GR_STATIC_ASSERT(1 == SkPath::kLine_Verb);
     GR_STATIC_ASSERT(2 == SkPath::kQuad_Verb);
-    GR_STATIC_ASSERT(3 == SkPath::kCubic_Verb);
-    GR_STATIC_ASSERT(4 == SkPath::kClose_Verb);
+    GR_STATIC_ASSERT(4 == SkPath::kCubic_Verb);
+    GR_STATIC_ASSERT(5 == SkPath::kClose_Verb);
 
     GrAssert(verb >= 0 && (size_t)verb < GR_ARRAY_COUNT(gTable));
     return gTable[verb];
diff --git a/src/pathops/SkOpAngle.cpp b/src/pathops/SkOpAngle.cpp
index aa72394..dee2906 100644
--- a/src/pathops/SkOpAngle.cpp
+++ b/src/pathops/SkOpAngle.cpp
@@ -113,11 +113,11 @@
         SkPath::Verb partVerb = useThis ? fVerb : rh.fVerb;
         ray[0] = partVerb == SkPath::kCubic_Verb && part[0].approximatelyEqual(part[1]) ?
             part[2] : part[1];
-        ray[1].fX = (part[0].fX + part[partVerb].fX) / 2;
-        ray[1].fY = (part[0].fY + part[partVerb].fY) / 2;
+        ray[1].fX = (part[0].fX + part[SkPathOpsVerbToPoints(partVerb)].fX) / 2;
+        ray[1].fY = (part[0].fY + part[SkPathOpsVerbToPoints(partVerb)].fY) / 2;
         SkASSERT(ray[0] != ray[1]);
-        roots = (i.*CurveRay[fVerb])(fPts, ray);
-        rroots = (ri.*CurveRay[rh.fVerb])(rh.fPts, ray);
+        roots = (i.*CurveRay[SkPathOpsVerbToPoints(fVerb)])(fPts, ray);
+        rroots = (ri.*CurveRay[SkPathOpsVerbToPoints(rh.fVerb)])(rh.fPts, ray);
     } while ((roots == 0 || rroots == 0) && (flip ^= true));
     if (roots == 0 || rroots == 0) {
         // FIXME: we don't have a solution in this case. The interim solution
@@ -314,8 +314,8 @@
         fUnsortable = step > 0 ? thisSpan.fUnsortableStart : nextSpan.fUnsortableEnd;
 #if DEBUG_UNSORTABLE
         if (fUnsortable) {
-            SkPoint iPt = (*CurvePointAtT[fVerb])(fPts, thisSpan.fT);
-            SkPoint ePt = (*CurvePointAtT[fVerb])(fPts, nextSpan.fT);
+            SkPoint iPt = (*CurvePointAtT[SkPathOpsVerbToPoints(fVerb)])(fPts, thisSpan.fT);
+            SkPoint ePt = (*CurvePointAtT[SkPathOpsVerbToPoints(fVerb)])(fPts, nextSpan.fT);
             SkDebugf("%s unsortable [%d] (%1.9g,%1.9g) [%d] (%1.9g,%1.9g)\n", __FUNCTION__,
                     index, iPt.fX, iPt.fY, fEnd, ePt.fX, ePt.fY);
         }
@@ -330,8 +330,8 @@
     }
 #if 1
 #if DEBUG_UNSORTABLE
-    SkPoint iPt = (*CurvePointAtT[fVerb])(fPts, startT);
-    SkPoint ePt = (*CurvePointAtT[fVerb])(fPts, endT);
+    SkPoint iPt = (*CurvePointAtT[SkPathOpsVerbToPoints(fVerb)])(fPts, startT);
+    SkPoint ePt = (*CurvePointAtT[SkPathOpsVerbToPoints(fVerb)])(fPts, endT);
     SkDebugf("%s all tiny unsortable [%d] (%1.9g,%1.9g) [%d] (%1.9g,%1.9g)\n", __FUNCTION__,
         fStart, iPt.fX, iPt.fY, fEnd, ePt.fX, ePt.fY);
 #endif
diff --git a/src/pathops/SkOpContour.h b/src/pathops/SkOpContour.h
index c90c218..c57fbac 100644
--- a/src/pathops/SkOpContour.h
+++ b/src/pathops/SkOpContour.h
@@ -114,7 +114,7 @@
 
     const SkPoint& end() const {
         const SkOpSegment& segment = fSegments.back();
-        return segment.pts()[segment.verb()];
+        return segment.pts()[SkPathOpsVerbToPoints(segment.verb())];
     }
 
     void findTooCloseToCall() {
@@ -195,7 +195,7 @@
     int updateSegment(int index, const SkPoint* pts) {
         SkOpSegment& segment = fSegments[index];
         segment.updatePts(pts);
-        return segment.verb() + 1;
+        return SkPathOpsVerbToPoints(segment.verb()) + 1;
     }
 
 #if DEBUG_TEST
diff --git a/src/pathops/SkOpEdgeBuilder.cpp b/src/pathops/SkOpEdgeBuilder.cpp
index 57e3b41..5803afa 100644
--- a/src/pathops/SkOpEdgeBuilder.cpp
+++ b/src/pathops/SkOpEdgeBuilder.cpp
@@ -76,7 +76,7 @@
         if (verb == SkPath::kMove_Verb) {
             *fPathPts.append() = pts[0];
         } else if (verb >= SkPath::kLine_Verb && verb <= SkPath::kCubic_Verb) {
-            fPathPts.append(verb, &pts[1]);
+            fPathPts.append(SkPathOpsVerbToPoints(verb), &pts[1]);
         }
     } while (verb != SkPath::kDone_Verb);
     return fPathVerbs.count() - 1;
@@ -137,7 +137,7 @@
                 if (reducedVerb == 0) {
                     break;  // skip degenerate points
                 }
-                if (reducedVerb == 1) {
+                if (reducedVerb == SkPath::kLine_Verb) {
                     const SkPoint* lineStart = fReducePts.end() - 2;
                    *fExtra.append() = fCurrentContour->addLine(lineStart);
                     break;
@@ -150,12 +150,12 @@
                 if (reducedVerb == 0) {
                     break;  // skip degenerate points
                 }
-                if (reducedVerb == 1) {
+                if (reducedVerb == SkPath::kLine_Verb) {
                     const SkPoint* lineStart = fReducePts.end() - 2;
                     *fExtra.append() = fCurrentContour->addLine(lineStart);
                     break;
                 }
-                if (reducedVerb == 2) {
+                if (reducedVerb == SkPath::kQuad_Verb) {
                     const SkPoint* quadStart = fReducePts.end() - 3;
                     *fExtra.append() = fCurrentContour->addQuad(quadStart);
                     break;
@@ -172,8 +172,8 @@
                 SkDEBUGFAIL("bad verb");
                 return false;
         }
-        fFinalCurveStart = &pointsPtr[verb - 1];
-        pointsPtr += verb;
+        fFinalCurveStart = &pointsPtr[SkPathOpsVerbToPoints(verb) - 1];
+        pointsPtr += SkPathOpsVerbToPoints(verb);
         SkASSERT(fCurrentContour);
     }
    if (fCurrentContour && !fAllowOpenContours && !close()) {
diff --git a/src/pathops/SkOpSegment.cpp b/src/pathops/SkOpSegment.cpp
index bcefd71..817732e 100644
--- a/src/pathops/SkOpSegment.cpp
+++ b/src/pathops/SkOpSegment.cpp
@@ -133,7 +133,7 @@
                 }
             }
             if (fVerb != SkPath::kLine_Verb && !lastDone) {
-                SkPoint curveTop = (*CurveTop[fVerb])(fPts, lastT, span.fT);
+                SkPoint curveTop = (*CurveTop[SkPathOpsVerbToPoints(fVerb)])(fPts, lastT, span.fT);
                 if (topPt.fY > curveTop.fY || (topPt.fY == curveTop.fY
                         && topPt.fX > curveTop.fX)) {
                     topPt = curveTop;
@@ -210,9 +210,9 @@
 #if DEBUG_ANGLE
     SkTDArray<SkOpAngle>& angles = *anglesPtr;
     if (angles.count() > 1 && !fTs[start].fTiny) {
-        SkPoint angle0Pt = (*CurvePointAtT[angles[0].verb()])(angles[0].pts(),
+        SkPoint angle0Pt = (*CurvePointAtT[SkPathOpsVerbToPoints(angles[0].verb())])(angles[0].pts(),
                 (*angles[0].spans())[angles[0].start()].fT);
-        SkPoint newPt = (*CurvePointAtT[fVerb])(fPts, fTs[start].fT);
+        SkPoint newPt = (*CurvePointAtT[SkPathOpsVerbToPoints(fVerb)])(fPts, fTs[start].fT);
         bool match = AlmostEqualUlps(angle0Pt.fX, newPt.fX);
         match &= AlmostEqualUlps(angle0Pt.fY, newPt.fY);
         if (!match) {
@@ -354,7 +354,7 @@
     if (active) {
         bool reverse = ePtr == fPts && start != 0;
         if (reverse) {
-            path->deferredMoveLine(ePtr[fVerb]);
+            path->deferredMoveLine(ePtr[SkPathOpsVerbToPoints(fVerb)]);
             switch (fVerb) {
                 case SkPath::kLine_Verb:
                     path->deferredLine(ePtr[0]);
@@ -386,7 +386,7 @@
             }
         }
     }
-  //  return ePtr[fVerb];
+  //  return ePtr[SkPathOpsVerbToPoints(fVerb)];
 }
 
 void SkOpSegment::addLine(const SkPoint pts[2], bool operand, bool evenOdd) {
@@ -979,7 +979,7 @@
     SkIntersections intersections;
     // OPTIMIZE: use specialty function that intersects ray with curve,
     // returning t values only for curve (we don't care about t on ray)
-    int pts = (intersections.*CurveVertical[fVerb])(fPts, top, bottom, basePt.fX, false);
+    int pts = (intersections.*CurveVertical[SkPathOpsVerbToPoints(fVerb)])(fPts, top, bottom, basePt.fX, false);
     if (pts == 0 || (current && pts == 1)) {
         return bestTIndex;
     }
@@ -1003,7 +1003,7 @@
                     || approximately_greater_than_one(foundT)) {
             continue;
         }
-        SkScalar testY = (*CurvePointAtT[fVerb])(fPts, foundT).fY;
+        SkScalar testY = (*CurvePointAtT[SkPathOpsVerbToPoints(fVerb)])(fPts, foundT).fY;
         if (approximately_negative(testY - *bestY)
                 || approximately_negative(basePt.fY - testY)) {
             continue;
@@ -1012,7 +1012,7 @@
             return SK_MinS32;  // if the intersection is edge on, wait for another one
         }
         if (fVerb > SkPath::kLine_Verb) {
-            SkScalar dx = (*CurveSlopeAtT[fVerb])(fPts, foundT).fX;
+            SkScalar dx = (*CurveSlopeAtT[SkPathOpsVerbToPoints(fVerb)])(fPts, foundT).fX;
             if (approximately_zero(dx)) {
                 return SK_MinS32;  // hit vertical, wait for another one
             }
@@ -1738,7 +1738,7 @@
 void SkOpSegment::initWinding(int start, int end, double tHit, int winding, SkScalar hitDx,
                               int oppWind, SkScalar hitOppDx) {
     SkASSERT(hitDx || !winding);
-    SkScalar dx = (*CurveSlopeAtT[fVerb])(fPts, tHit).fX;
+    SkScalar dx = (*CurveSlopeAtT[SkPathOpsVerbToPoints(fVerb)])(fPts, tHit).fX;
     SkASSERT(dx);
     int windVal = windValue(SkMin32(start, end));
 #if DEBUG_WINDING_AT_T
@@ -2081,7 +2081,7 @@
     SkASSERT(fVerb != SkPath::kLine_Verb);
     SkPoint edge[4];
     subDivide(tStart, tEnd, edge);
-    double sum = (edge[0].fX - edge[fVerb].fX) * (edge[0].fY + edge[fVerb].fY);
+    double sum = (edge[0].fX - edge[SkPathOpsVerbToPoints(fVerb)].fX) * (edge[0].fY + edge[SkPathOpsVerbToPoints(fVerb)].fY);
     if (fVerb == SkPath::kCubic_Verb) {
         SkScalar lesser = SkTMin<SkScalar>(edge[0].fY, edge[3].fY);
         if (edge[1].fY < lesser && edge[2].fY < lesser) {
@@ -2095,7 +2095,7 @@
             }
         }
     }
-    for (int idx = 0; idx < fVerb; ++idx){
+    for (int idx = 0; idx < SkPathOpsVerbToPoints(fVerb); ++idx){
         sum += (edge[idx + 1].fX - edge[idx].fX) * (edge[idx + 1].fY + edge[idx].fY);
     }
     return sum <= 0;
@@ -2365,9 +2365,9 @@
 
 void SkOpSegment::subDivide(int start, int end, SkPoint edge[4]) const {
     edge[0] = fTs[start].fPt;
-    edge[fVerb] = fTs[end].fPt;
+    edge[SkPathOpsVerbToPoints(fVerb)] = fTs[end].fPt;
     if (fVerb == SkPath::kQuad_Verb || fVerb == SkPath::kCubic_Verb) {
-        SkDPoint sub[2] = {{ edge[0].fX, edge[0].fY}, {edge[fVerb].fX, edge[fVerb].fY }};
+        SkDPoint sub[2] = {{ edge[0].fX, edge[0].fY}, {edge[SkPathOpsVerbToPoints(fVerb)].fX, edge[SkPathOpsVerbToPoints(fVerb)].fY }};
         if (fVerb == SkPath::kQuad_Verb) {
             edge[1] = SkDQuad::SubDivide(fPts, sub[0], sub[1], fTs[start].fT,
                     fTs[end].fT).asSkPoint();
@@ -2382,7 +2382,7 @@
 void SkOpSegment::subDivideBounds(int start, int end, SkPathOpsBounds* bounds) const {
     SkPoint edge[4];
     subDivide(start, end, edge);
-    (bounds->*SetCurveBounds[fVerb])(edge);
+    (bounds->*SetCurveBounds[SkPathOpsVerbToPoints(fVerb)])(edge);
 }
 
 bool SkOpSegment::tiny(const SkOpAngle* angle) const {
@@ -2473,7 +2473,7 @@
     SkDebugf("%s oldWinding=%d windValue=%d", __FUNCTION__, winding, windVal);
 #endif
     // see if a + change in T results in a +/- change in X (compute x'(T))
-    *dx = (*CurveSlopeAtT[fVerb])(fPts, tHit).fX;
+    *dx = (*CurveSlopeAtT[SkPathOpsVerbToPoints(fVerb)])(fPts, tHit).fX;
     if (fVerb > SkPath::kLine_Verb && approximately_zero(*dx)) {
         *dx = fPts[2].fX - fPts[1].fX - *dx;
     }
@@ -2611,7 +2611,7 @@
 #endif
         SkDebugf("%s id=%d", __FUNCTION__, fID);
         SkDebugf(" (%1.9g,%1.9g", fPts[0].fX, fPts[0].fY);
-        for (int vIndex = 1; vIndex <= fVerb; ++vIndex) {
+        for (int vIndex = 1; vIndex <= SkPathOpsVerbToPoints(fVerb); ++vIndex) {
             SkDebugf(" %1.9g,%1.9g", fPts[vIndex].fX, fPts[vIndex].fY);
         }
         const SkOpSpan* span = &fTs[i];
@@ -2640,7 +2640,7 @@
     const SkPoint& pt = xyAtT(&span);
     SkDebugf("%s id=%d", fun, fID);
     SkDebugf(" (%1.9g,%1.9g", fPts[0].fX, fPts[0].fY);
-    for (int vIndex = 1; vIndex <= fVerb; ++vIndex) {
+    for (int vIndex = 1; vIndex <= SkPathOpsVerbToPoints(fVerb); ++vIndex) {
         SkDebugf(" %1.9g,%1.9g", fPts[vIndex].fX, fPts[vIndex].fY);
     }
     SkASSERT(&span == &span.fOther->fTs[span.fOtherIndex].fOther->
@@ -2661,7 +2661,7 @@
     const SkPoint& pt = xyAtT(&span);
     SkDebugf("%s id=%d", fun, fID);
     SkDebugf(" (%1.9g,%1.9g", fPts[0].fX, fPts[0].fY);
-    for (int vIndex = 1; vIndex <= fVerb; ++vIndex) {
+    for (int vIndex = 1; vIndex <= SkPathOpsVerbToPoints(fVerb); ++vIndex) {
         SkDebugf(" %1.9g,%1.9g", fPts[vIndex].fX, fPts[vIndex].fY);
     }
     SkASSERT(&span == &span.fOther->fTs[span.fOtherIndex].fOther->
@@ -2737,7 +2737,7 @@
                 angle.unsortable() ? "*** UNSORTABLE *** " : "");
     #if COMPACT_DEBUG_SORT
         SkDebugf("id=%d %s start=%d (%1.9g,%,1.9g) end=%d (%1.9g,%,1.9g)",
-                segment.fID, kLVerbStr[segment.fVerb],
+                segment.fID, kLVerbStr[SkPathOpsVerbToPoints(segment.fVerb)],
                 start, segment.xAtT(&sSpan), segment.yAtT(&sSpan), end,
                 segment.xAtT(&eSpan), segment.yAtT(&eSpan));
     #else
diff --git a/src/pathops/SkOpSegment.h b/src/pathops/SkOpSegment.h
index d2322c8..e489acf 100644
--- a/src/pathops/SkOpSegment.h
+++ b/src/pathops/SkOpSegment.h
@@ -54,7 +54,7 @@
     }
 
     SkVector dxdy(int index) const {
-        return (*CurveSlopeAtT[fVerb])(fPts, fTs[index].fT);
+        return (*CurveSlopeAtT[SkPathOpsVerbToPoints(fVerb)])(fPts, fTs[index].fT);
     }
 
     SkScalar dy(int index) const {
@@ -82,7 +82,7 @@
     }
 
     bool isVertical(int start, int end) const {
-        return (*CurveIsVertical[fVerb])(fPts, start, end);
+        return (*CurveIsVertical[SkPathOpsVerbToPoints(fVerb)])(fPts, start, end);
     }
 
     bool operand() const {
@@ -206,7 +206,7 @@
 
     // used only by right angle winding finding
     SkPoint xyAtT(double mid) const {
-        return (*CurvePointAtT[fVerb])(fPts, mid);
+        return (*CurvePointAtT[SkPathOpsVerbToPoints(fVerb)])(fPts, mid);
     }
 
     const SkPoint& xyAtT(int index) const {
diff --git a/src/pathops/SkPathOpsTypes.h b/src/pathops/SkPathOpsTypes.h
index 5a5394a..0f689d8 100644
--- a/src/pathops/SkPathOpsTypes.h
+++ b/src/pathops/SkPathOpsTypes.h
@@ -7,10 +7,13 @@
 #ifndef SkPathOpsTypes_DEFINED
 #define SkPathOpsTypes_DEFINED
 
+#define SK_CONIC_SUPPORT_ENABLED 1
+
 #include <float.h>  // for FLT_EPSILON
 #include <math.h>   // for fabs, sqrt
 
 #include "SkFloatingPoint.h"
+#include "SkPath.h"
 #include "SkPathOps.h"
 #include "SkPathOpsDebug.h"
 #include "SkScalar.h"
@@ -189,6 +192,47 @@
 struct SkDCubic;
 struct SkDRect;
 
+#if SK_CONIC_SUPPORT_ENABLED
+
+inline SkPath::Verb SkPathOpsPointsToVerb(int points) {
+    int verb = (1 << points) >> 1;
+#ifdef SK_DEBUG
+    switch (points) {
+        case 0: SkASSERT(SkPath::kMove_Verb == verb); break;
+        case 1: SkASSERT(SkPath::kLine_Verb == verb); break;
+        case 2: SkASSERT(SkPath::kQuad_Verb == verb); break;
+        case 3: SkASSERT(SkPath::kCubic_Verb == verb); break;
+        default: SkASSERT(!"should not be here");
+    }
+#endif
+    return (SkPath::Verb)verb;
+}
+
+inline int SkPathOpsVerbToPoints(SkPath::Verb verb) {
+    int points = (int) verb - ((int) verb >> 2);
+#ifdef SK_DEBUG
+    switch (verb) {
+        case SkPath::kLine_Verb: SkASSERT(1 == points); break;
+        case SkPath::kQuad_Verb: SkASSERT(2 == points); break;
+        case SkPath::kCubic_Verb: SkASSERT(3 == points); break;
+        default: SkASSERT(!"should not get here");
+    }
+#endif
+    return points;
+}
+
+#else
+
+inline SkPath::Verb SkOpPointsToVerb(int points) {
+    return (SkPath::Verb) (points);
+}
+
+inline SkPath::Verb SkOpVerbToPoints(SkPath::Verb verb) {
+    return (int) verb ;
+}
+
+#endif
+
 inline double SkDInterp(double A, double B, double t) {
     return A + (B - A) * t;
 }
diff --git a/src/pathops/SkReduceOrder.cpp b/src/pathops/SkReduceOrder.cpp
index 5410d35..6d2339c 100644
--- a/src/pathops/SkReduceOrder.cpp
+++ b/src/pathops/SkReduceOrder.cpp
@@ -437,7 +437,7 @@
             pt->fY = SkDoubleToScalar(reducer.fLine[index].fY);
         }
     }
-    return (SkPath::Verb) (order - 1);
+    return SkPathOpsPointsToVerb(order - 1);
 }
 
 SkPath::Verb SkReduceOrder::Cubic(const SkPoint a[4], SkTDArray<SkPoint>* reducePts) {
@@ -452,5 +452,5 @@
             pt->fY = SkDoubleToScalar(reducer.fQuad[index].fY);
         }
     }
-    return (SkPath::Verb) (order - 1);
+    return SkPathOpsPointsToVerb(order - 1);
 }
diff --git a/src/utils/SkDumpCanvas.cpp b/src/utils/SkDumpCanvas.cpp
index 87ddd31..593ad0c 100644
--- a/src/utils/SkDumpCanvas.cpp
+++ b/src/utils/SkDumpCanvas.cpp
@@ -84,6 +84,9 @@
                 break;
             case SkPath::kDone_Verb:
                 return;
+            case SkPath::kConic_Verb:
+                SkASSERT(0);
+                break;
         }
     }
 }
diff --git a/src/utils/SkParsePath.cpp b/src/utils/SkParsePath.cpp
index 8c7a4d0..4c9923f 100644
--- a/src/utils/SkParsePath.cpp
+++ b/src/utils/SkParsePath.cpp
@@ -221,7 +221,10 @@
 
     for (;;) {
         switch (iter.next(pts, false)) {
-            case SkPath::kMove_Verb:
+             case SkPath::kConic_Verb:
+                SkASSERT(0);
+                break;
+           case SkPath::kMove_Verb:
                 append_scalars(&stream, 'M', &pts[0].fX, 2);
                 break;
             case SkPath::kLine_Verb:
diff --git a/tests/PathOpsAngleTest.cpp b/tests/PathOpsAngleTest.cpp
index e0331ec..003b8c8 100644
--- a/tests/PathOpsAngleTest.cpp
+++ b/tests/PathOpsAngleTest.cpp
@@ -8,16 +8,38 @@
 #include "Test.h"
 
 static const SkPoint cubics[][4] = {
-    {{0, 1}, {2, 6}, {4, 2}, {5, 3}},
-    {{10, 234}, {10, 229.581726f}, {13.5817204f, 226}, {18, 226}},
+/* 0 */    {{0, 1}, {2, 6}, {4, 2}, {5, 3}},
+/* 1 */    {{10, 234}, {10, 229.581726f}, {13.5817204f, 226}, {18, 226}},
+/* 2 */    {{132, 11419}, {130.89543151855469f, 11419}, {130, 11418.1044921875f}, {130, 11417}},
+/* 3 */    {{130.04275512695312f, 11417.4130859375f}, {130.23307800292969f, 11418.3193359375f},
+                    {131.03709411621094f, 11419}, {132, 11419}},
+/* 4 */    {{0,1}, {0,5}, {4,1}, {6,4}},
+/* 5 */    {{1,5}, {4,6}, {1,0}, {4,0}},
+/* 6 */    {{0,1}, {0,4}, {5,1}, {6,4}},
+/* 7 */    {{0,1}, {1,2}, {1,0}, {6,1}},
+/* 8 */    {{0,3}, {0,1}, {2,0}, {1,0}},
+/* 9 */    {{189,7}, {189,5.3431458473205566f}, {190.3431396484375f,4}, {192,4}},
+/* 10 */   {{0,1}, {1,3}, {1,0}, {6,4}},
+/* 11 */   {{0,1}, {2,3}, {2,1}, {4,3}},
+/* 12 */   {{1,2}, {3,4}, {1,0}, {3,2}},
 };
 
 static const SkPoint quads[][3] = {
-    {{12.3423996f, 228.342407f}, {10, 230.686295f}, {10, 234}},
+/* 0 */    {{12.3423996f, 228.342407f}, {10, 230.686295f}, {10, 234}},
+/* 1 */    {{304.24319458007812f,591.75677490234375f}, {306,593.51470947265625f}, {306,596}},
 };
 
 static const SkPoint lines[][2] = {
-    {{6, 2}, {2, 4}},
+/* 0 */    {{6, 2}, {2, 4}},
+/* 1 */    {{306,617}, {306,590}},
+/* 2 */    {{306,596}, {306,617}},
+/* 3 */    {{6,4}, {0,1}},
+/* 4 */    {{6,1}, {0,1}},
+/* 5 */    {{1,0}, {0,3}},
+/* 6 */    {{246,4}, {189,4}},
+/* 7 */    {{192,4}, {243,4}},
+/* 8 */    {{4,3}, {0,1}},
+/* 9 */    {{3,2}, {1,2}},
 };
 
 struct SortSet {
@@ -46,14 +68,86 @@
     {quads[0], 3, 1, 0},
 };
 
+static const SortSet set4[] = {
+    {cubics[2], 4, 0.812114222, 1},
+    {cubics[3], 4, 0.0684734759, 0},
+};
+
+static const SortSet set5[] = {
+    {lines[1], 2, 0.777777778, 1},
+    {quads[1], 3, 1, 4.34137342e-06},
+    {lines[2], 2, 0, 1},
+};
+
+static const SortSet set6[] = {
+    {lines[3], 2, 0.407407407, 0.554627832},
+    {cubics[4], 4, 0.666666667, 0.548022446},
+    {lines[3], 2, 0.407407407, 0},
+    {cubics[4], 4, 0.666666667, 1},
+};
+
+static const SortSet set7[] = {
+    {cubics[5], 4, 0.545233342, 0.545454545},
+    {cubics[6], 4, 0.484938134, 0.484805744},
+    {cubics[5], 4, 0.545233342, 0},
+    {cubics[6], 4, 0.484938134, 0.545454545},
+};
+
+static const SortSet set8[] = {
+    {cubics[7], 4, 0.5, 0.522986744 },
+    {lines[4], 2, 0.75, 1},
+    {cubics[7], 4, 0.5, 0},
+    {lines[4], 2, 0.75, 0.737654321},
+};
+
+static const SortSet set9[] = {
+    {cubics[8], 4, 0.4, 1},
+    {lines[5], 2, 0.36, 0},
+    {cubics[8], 4, 0.4, 0.394675838},
+    {lines[5], 2, 0.36, 0.363999782},
+};
+
+static const SortSet set10[] = {
+    {lines[6], 2, 0.947368421, 1},
+    {cubics[9], 4, 1, 0.500000357},
+    {lines[7], 2, 0, 1},
+};
+
+static const SortSet set11[] = {
+    {lines[3], 2, 0.75, 1},
+    {cubics[10], 4, 0.5, 0.228744269},
+    {lines[3], 2, 0.75, 0.627112191},
+    {cubics[10], 4, 0.5, 0.6339746},
+};
+
+static const SortSet set12[] = {
+    {cubics[12], 4, 0.5, 1},
+    {lines[8], 2, 0.5, 1},
+    {cubics[11], 4, 0.5, 0},
+    {lines[9], 2, 0.5, 1},
+    {cubics[12], 4, 0.5, 0},
+    {lines[8], 2, 0.5, 0},
+    {cubics[11], 4, 0.5, 1},
+    {lines[9], 2, 0.5, 0},
+};
+
 struct SortSetTests {
     const SortSet* set;
     size_t count;
 };
 
 static const SortSetTests tests[] = {
-    { set3, SK_ARRAY_COUNT(set3) },
+    { set12, SK_ARRAY_COUNT(set12) },
+    { set11, SK_ARRAY_COUNT(set11) },
+    { set10, SK_ARRAY_COUNT(set10) },
+    { set9, SK_ARRAY_COUNT(set9) },
+    { set8, SK_ARRAY_COUNT(set8) },
+    { set7, SK_ARRAY_COUNT(set7) },
+    { set6, SK_ARRAY_COUNT(set6) },
     { set2, SK_ARRAY_COUNT(set2) },
+    { set5, SK_ARRAY_COUNT(set5) },
+    { set4, SK_ARRAY_COUNT(set4) },
+    { set3, SK_ARRAY_COUNT(set3) },
     { set1, SK_ARRAY_COUNT(set1) },
 };
 
@@ -122,16 +216,18 @@
             size_t idxG = idxL + 1;
             setup(set, idxG, &greaterData, &greater, greaterTs);
             SkOpAngle first, second;
-            first.set(lesserData, (SkPath::Verb) (set[idxL].ptCount - 1), &lesser,
+            first.set(lesserData, SkPathOpsPointsToVerb(set[idxL].ptCount - 1), &lesser,
                     lesserTs[0], lesserTs[1], lesser.spans());
-            second.set(greaterData, (SkPath::Verb) (set[idxG].ptCount - 1), &greater,
+            second.set(greaterData, SkPathOpsPointsToVerb(set[idxG].ptCount - 1), &greater,
                     greaterTs[0], greaterTs[1], greater.spans());
             bool compare = first < second;
             if (!compare) {
                 SkDebugf("%s test[%d]:  lesser[%d] > greater[%d]\n", __FUNCTION__,
                         index, idxL,  idxG);
+                compare = first < second;
             }
             REPORTER_ASSERT(reporter, compare);
+            reporter->bumpTestCount();
         }
     }
 }
diff --git a/tests/PathOpsOpTest.cpp b/tests/PathOpsOpTest.cpp
index 08dff90..48ed866 100644
--- a/tests/PathOpsOpTest.cpp
+++ b/tests/PathOpsOpTest.cpp
@@ -1144,6 +1144,7 @@
     testPathOp(reporter, path, pathB, kDifference_PathOp);
 }
 
+
 SkPathOp ops[] = {
     kUnion_PathOp,
     kXOR_PathOp,
@@ -1343,9 +1344,175 @@
     testPathOp(reporter, path, pathB, kDifference_PathOp);
 }
 
-static void (*firstTest)(skiatest::Reporter* ) = 0;
+static void cubicOp71d(skiatest::Reporter* reporter) {
+    SkPath path, pathB;
+    path.setFillType(SkPath::kWinding_FillType);
+    path.moveTo(0,1);
+    path.cubicTo(0,5, 4,1, 6,4);
+    path.close();
+    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.moveTo(1,4);
+    pathB.cubicTo(4,6, 1,0, 5,0);
+    pathB.close();
+    testPathOp(reporter, path, pathB, kDifference_PathOp);
+}
+
+static void cubicOp72i(skiatest::Reporter* reporter) {
+    SkPath path, pathB;
+    path.setFillType(SkPath::kWinding_FillType);
+    path.moveTo(0,1);
+    path.cubicTo(0,5, 5,2, 5,4);
+    path.close();
+    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.moveTo(2,5);
+    pathB.cubicTo(4,5, 1,0, 5,0);
+    pathB.close();
+    testPathOp(reporter, path, pathB, kIntersect_PathOp);
+}
+
+static void cubicOp73d(skiatest::Reporter* reporter) {
+    SkPath path, pathB;
+    path.setFillType(SkPath::kWinding_FillType);
+    path.moveTo(0,1);
+    path.cubicTo(3,4, 4,0, 6,4);
+    path.lineTo(0,1);
+    path.close();
+    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.moveTo(0,4);
+    pathB.cubicTo(4,6, 1,0, 4,3);
+    pathB.lineTo(0,4);
+    pathB.close();
+    testPathOp(reporter, path, pathB, kDifference_PathOp);
+}
+
+static void cubicOp75d(skiatest::Reporter* reporter) {
+    SkPath path, pathB;
+    path.setFillType(SkPath::kWinding_FillType);
+    path.moveTo(0,1);
+    path.cubicTo(0,4, 5,1, 6,4);
+    path.lineTo(0,1);
+    path.close();
+    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.moveTo(1,5);
+    pathB.cubicTo(4,6, 1,0, 4,0);
+    pathB.lineTo(1,5);
+    pathB.close();
+    testPathOp(reporter, path, pathB, kDifference_PathOp);
+}
+
+static void cubicOp77i(skiatest::Reporter* reporter) {
+    SkPath path, pathB;
+    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.moveTo(0,1);
+    path.cubicTo(1,3, 2,0, 3,2);
+    path.lineTo(0,1);
+    path.close();
+    pathB.setFillType(SkPath::kEvenOdd_FillType);
+    pathB.moveTo(0,2);
+    pathB.cubicTo(2,3, 1,0, 3,1);
+    pathB.lineTo(0,2);
+    pathB.close();
+    testPathOp(reporter, path, pathB, kIntersect_PathOp);
+}
+
+static void cubicOp78u(skiatest::Reporter* reporter) {
+    SkPath path, pathB;
+    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.moveTo(1,6);
+    path.cubicTo(1,6, 5,0, 6,1);
+    path.lineTo(1,6);
+    path.close();
+    pathB.setFillType(SkPath::kEvenOdd_FillType);
+    pathB.moveTo(0,5);
+    pathB.cubicTo(1,6, 6,1, 6,1);
+    pathB.lineTo(0,5);
+    pathB.close();
+    testPathOp(reporter, path, pathB, kUnion_PathOp);
+}
+
+static void cubicOp79d(skiatest::Reporter* reporter) {
+    SkPath path, pathB;
+    path.setFillType(SkPath::kWinding_FillType);
+    path.moveTo(0,2);
+    path.cubicTo(0,1, 3,-0.1f, 1,0);
+    path.close();
+    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.moveTo(0,3);
+    pathB.cubicTo(0,1, 2,0, 1,0);
+    pathB.close();
+    testPathOp(reporter, path, pathB, kDifference_PathOp);
+}
+
+static void cubicOp79u(skiatest::Reporter* reporter) {
+    SkPath path, pathB;
+    path.setFillType(SkPath::kWinding_FillType);
+    path.moveTo(0,1);
+    path.cubicTo(1,3, 1,0, 6,4);
+    path.close();
+    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.moveTo(0,1);
+    pathB.cubicTo(4,6, 1,0, 3,1);
+    pathB.close();
+    testPathOp(reporter, path, pathB, kIntersect_PathOp);
+}
+
+static void cubicOp80i(skiatest::Reporter* reporter) {
+    SkPath path, pathB;
+    path.setFillType(SkPath::kWinding_FillType);
+    path.moveTo(0,1);
+    path.cubicTo(2,3, 2,1, 4,3);
+    path.lineTo(0,1);
+    path.close();
+    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.moveTo(1,2);
+    pathB.cubicTo(3,4, 1,0, 3,2);
+    pathB.lineTo(1,2);
+    pathB.close();
+    testPathOp(reporter, path, pathB, kIntersect_PathOp);
+}
+
+static void cubicOp82i(skiatest::Reporter* reporter) {
+    SkPath path, pathB;
+    path.setFillType(SkPath::kEvenOdd_FillType);
+    path.moveTo(0,1);
+    path.cubicTo(2,3, 5,2, 3,0);
+    path.lineTo(0,1);
+    path.close();
+    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.moveTo(2,5);
+    pathB.cubicTo(0,3, 1,0, 3,2);
+    pathB.lineTo(2,5);
+    pathB.close();
+    testPathOp(reporter, path, pathB, kIntersect_PathOp);
+}
+
+static void cubicOp81d(skiatest::Reporter* reporter) {
+    SkPath path, pathB;
+    path.setFillType(SkPath::kWinding_FillType);
+    path.moveTo(0,1);
+    path.cubicTo(4,6, 4,3, 5,4);
+    path.close();
+    pathB.setFillType(SkPath::kWinding_FillType);
+    pathB.moveTo(3,4);
+    pathB.cubicTo(4,5, 1,0, 6,4);
+    pathB.close();
+    testPathOp(reporter, path, pathB, kDifference_PathOp);
+}
+
+static void (*firstTest)(skiatest::Reporter* ) = cubicOp81d;
 
 static struct TestDesc tests[] = {
+    TEST(cubicOp82i),
+    TEST(cubicOp81d),
+    TEST(cubicOp80i),
+    TEST(cubicOp79u),
+    TEST(cubicOp79d),
+    TEST(cubicOp78u),
+    TEST(cubicOp77i),
+    TEST(cubicOp75d),
+    TEST(cubicOp73d),
+    TEST(cubicOp72i),
+    TEST(cubicOp71d),
     TEST(skp5),
     TEST(skp4),
     TEST(skp3),
diff --git a/tests/PathOpsSimplifyTest.cpp b/tests/PathOpsSimplifyTest.cpp
index ce2c89b..f4a332e 100644
--- a/tests/PathOpsSimplifyTest.cpp
+++ b/tests/PathOpsSimplifyTest.cpp
@@ -3772,7 +3772,7 @@
     testSimplify(reporter, path);
 }
 
-static void (*firstTest)(skiatest::Reporter* ) = 0;
+static void (*firstTest)(skiatest::Reporter* ) = testQuadratic85;
 
 static TestDesc tests[] = {
     TEST(testQuad7),
diff --git a/tests/PathTest.cpp b/tests/PathTest.cpp
index fa95a35..0f59aeb 100644
--- a/tests/PathTest.cpp
+++ b/tests/PathTest.cpp
@@ -470,6 +470,9 @@
             case SkPath::kQuad_Verb:
                 REPORTER_ASSERT(reporter, !"unexpected quad verb");
                 break;
+            case SkPath::kConic_Verb:
+                REPORTER_ASSERT(reporter, !"unexpected conic verb");
+                break;
             case SkPath::kCubic_Verb:
                 REPORTER_ASSERT(reporter, !"unexpected cubic verb");
                 break;
@@ -1969,6 +1972,19 @@
                     numPoints += 2;
                     lastWasClose = false;
                     break;
+                case SkPath::kConic_Verb:
+                    if (!haveMoveTo) {
+                        expectedPts[numPoints++] = lastMoveToPt;
+                        expectedVerbs[numIterVerbs++] = SkPath::kMove_Verb;
+                        haveMoveTo = true;
+                    }
+                    expectedPts[numPoints] = randomPts[(rand.nextU() >> 16) % 25];
+                    expectedPts[numPoints + 1] = randomPts[(rand.nextU() >> 16) % 25];
+                    p.conicTo(expectedPts[numPoints], expectedPts[numPoints + 1],
+                              rand.nextUScalar1() * 4);
+                    numPoints += 2;
+                    lastWasClose = false;
+                    break;
                 case SkPath::kCubic_Verb:
                     if (!haveMoveTo) {
                         expectedPts[numPoints++] = lastMoveToPt;
@@ -1988,7 +2004,8 @@
                     haveMoveTo = false;
                     lastWasClose = true;
                     break;
-                default:;
+                default:
+                    SkASSERT(!"unexpected verb");
             }
             expectedVerbs[numIterVerbs++] = nextVerb;
         }
@@ -2019,6 +2036,7 @@
                     numIterPts += 1;
                     break;
                 case SkPath::kQuad_Verb:
+                case SkPath::kConic_Verb:
                     REPORTER_ASSERT(reporter, numIterPts < numPoints + 2);
                     REPORTER_ASSERT(reporter, pts[0] == lastPt);
                     REPORTER_ASSERT(reporter, pts[1] == expectedPts[numIterPts]);
@@ -2039,7 +2057,8 @@
                     REPORTER_ASSERT(reporter, pts[0] == lastMoveTo);
                     lastPt = lastMoveTo;
                     break;
-                default:;
+                default:
+                    SkASSERT(!"unexpected verb");
             }
         }
         REPORTER_ASSERT(reporter, numIterPts == numPoints);