SkPath::Direction serves two masters:
- input param to addFoo (e.g. addRect), where only CW or CCW are valid)
- output param from computing functions, that sometimes return kUnknown

This CL's intent is to split these into distinct enums/features:
- Direction (public) loses kUnknown, and is only used for input
- FirstDirection (private) is used for computing the first direction we see when analyzing a contour

BUG=skia:

Review URL: https://codereview.chromium.org/1176953002
diff --git a/gm/convex_all_line_paths.cpp b/gm/convex_all_line_paths.cpp
index 81b98b8..a93a7f6 100644
--- a/gm/convex_all_line_paths.cpp
+++ b/gm/convex_all_line_paths.cpp
@@ -6,6 +6,7 @@
  */
 
 #include "gm.h"
+#include "SkPathPriv.h"
 
 static void create_ngon(int n, SkPoint* pts, SkScalar width, SkScalar height) {
     float angleStep = 360.0f / n, angle = 0.0f, sin, cos;
@@ -237,9 +238,9 @@
         // of the GMs rows.
         SkASSERT(path.isConvex());
         SkASSERT(SkPath::kLine_SegmentMask == path.getSegmentMasks());
-        SkPath::Direction actualDir;
-        SkASSERT(path.cheapComputeDirection(&actualDir));
-        SkASSERT(dir == actualDir);
+        SkPathPriv::FirstDirection actualDir;
+        SkASSERT(SkPathPriv::CheapComputeFirstDirection(path, &actualDir));
+        SkASSERT(SkPathPriv::AsFirstDirection(dir) == actualDir);
         SkRect bounds = path.getBounds();
         SkASSERT(SkScalarNearlyEqual(bounds.centerX(), 0.0f));
         SkASSERT(bounds.height() <= kMaxPathHeight);
diff --git a/gm/strokefill.cpp b/gm/strokefill.cpp
index b6e888b..0498548 100644
--- a/gm/strokefill.cpp
+++ b/gm/strokefill.cpp
@@ -7,7 +7,7 @@
 
 #include "gm.h"
 #include "SkCanvas.h"
-#include "SkPath.h"
+#include "SkPathPriv.h"
 #include "SkTypeface.h"
 
 namespace skiagm {
@@ -69,12 +69,12 @@
         path2.reset();
         path2.addCircle(x + SkIntToScalar(240), y + SkIntToScalar(200), SkIntToScalar(50), SkPath::kCCW_Direction);
         canvas->drawPath(path2, paint);
-        SkASSERT(path2.cheapIsDirection(SkPath::kCCW_Direction));
+        SkASSERT(SkPathPriv::CheapIsFirstDirection(path2, SkPathPriv::kCCW_FirstDirection));
 
         path2.reset();
-        SkASSERT(!path2.cheapComputeDirection(NULL));
+        SkASSERT(!SkPathPriv::CheapComputeFirstDirection(path2, NULL));
         path2.addCircle(x + SkIntToScalar(360), y + SkIntToScalar(200), SkIntToScalar(50), SkPath::kCW_Direction);
-        SkASSERT(path2.cheapIsDirection(SkPath::kCW_Direction));
+        SkASSERT(SkPathPriv::CheapIsFirstDirection(path2, SkPathPriv::kCW_FirstDirection));
         canvas->drawPath(path2, paint);
 
         SkRect r = SkRect::MakeXYWH(x - SkIntToScalar(50), y + SkIntToScalar(280),
@@ -98,18 +98,18 @@
         r = SkRect::MakeXYWH(x + SkIntToScalar(190), y + SkIntToScalar(280), 
                              SkIntToScalar(100), SkIntToScalar(100));
         path4.reset();
-        SkASSERT(!path4.cheapComputeDirection(NULL));
+        SkASSERT(!SkPathPriv::CheapComputeFirstDirection(path4, NULL));
         path4.addRect(r, SkPath::kCCW_Direction);
-        SkASSERT(path4.cheapIsDirection(SkPath::kCCW_Direction));
+        SkASSERT(SkPathPriv::CheapIsFirstDirection(path4, SkPathPriv::kCCW_FirstDirection));
         path4.moveTo(0, 0); // test for crbug.com/247770
         canvas->drawPath(path4, paint);
 
         r = SkRect::MakeXYWH(x + SkIntToScalar(310), y + SkIntToScalar(280), 
                              SkIntToScalar(100), SkIntToScalar(100));
         path4.reset();
-        SkASSERT(!path4.cheapComputeDirection(NULL));
+        SkASSERT(!SkPathPriv::CheapComputeFirstDirection(path4, NULL));
         path4.addRect(r, SkPath::kCW_Direction);
-        SkASSERT(path4.cheapIsDirection(SkPath::kCW_Direction));
+        SkASSERT(SkPathPriv::CheapIsFirstDirection(path4, SkPathPriv::kCW_FirstDirection));
         path4.moveTo(0, 0); // test for crbug.com/247770
         canvas->drawPath(path4, paint);
     }
diff --git a/include/core/SkPath.h b/include/core/SkPath.h
index 54a459b..52d2b2b 100644
--- a/include/core/SkPath.h
+++ b/include/core/SkPath.h
@@ -492,8 +492,6 @@
     void close();
 
     enum Direction {
-        /** Direction either has not been or could not be computed */
-        kUnknown_Direction,
         /** clockwise direction for adding closed contours */
         kCW_Direction,
         /** counter-clockwise direction for adding closed contours */
@@ -501,17 +499,6 @@
     };
 
     /**
-     *  Return the opposite of the specified direction. kUnknown is its own
-     *  opposite.
-     */
-    static Direction OppositeDirection(Direction dir) {
-        static const Direction gOppositeDir[] = {
-            kUnknown_Direction, kCCW_Direction, kCW_Direction
-        };
-        return gOppositeDir[dir];
-    }
-
-    /**
      *  Returns whether or not a fill type is inverted
      *
      *  kWinding_FillType        -> false
@@ -544,27 +531,6 @@
     }
 
     /**
-     *  Tries to quickly compute the direction of the first non-degenerate
-     *  contour. If it can be computed, return true and set dir to that
-     *  direction. If it cannot be (quickly) determined, return false and ignore
-     *  the dir parameter. If the direction was determined, it is cached to make
-     *  subsequent calls return quickly.
-     */
-    bool cheapComputeDirection(Direction* dir) const;
-
-    /**
-     *  Returns true if the path's direction can be computed via
-     *  cheapComputDirection() and if that computed direction matches the
-     *  specified direction. If dir is kUnknown, returns true if the direction
-     *  cannot be computed.
-     */
-    bool cheapIsDirection(Direction dir) const {
-        Direction computedDir = kUnknown_Direction;
-        (void)this->cheapComputeDirection(&computedDir);
-        return computedDir == dir;
-    }
-
-    /**
      *  Returns true if the path specifies a rectangle.
      *
      *  If this returns false, then all output parameters are ignored, and left
@@ -598,8 +564,7 @@
     /**
      *  Add a closed rectangle contour to the path
      *  @param rect The rectangle to add as a closed contour to the path
-     *  @param dir  The direction to wind the rectangle's contour. Cannot be
-     *              kUnknown_Direction.
+     *  @param dir  The direction to wind the rectangle's contour.
      */
     void addRect(const SkRect& rect, Direction dir = kCW_Direction);
 
@@ -614,8 +579,7 @@
      *                  to the path
      *  @param bottom   The bottom of a rectangle to add as a closed contour to
      *                  the path
-     *  @param dir  The direction to wind the rectangle's contour. Cannot be
-     *              kUnknown_Direction.
+     *  @param dir  The direction to wind the rectangle's contour.
      */
     void addRect(SkScalar left, SkScalar top, SkScalar right, SkScalar bottom,
                  Direction dir = kCW_Direction);
@@ -624,8 +588,7 @@
      *  Add a closed oval contour to the path
      *
      *  @param oval The bounding oval to add as a closed contour to the path
-     *  @param dir  The direction to wind the oval's contour. Cannot be
-     *              kUnknown_Direction.
+     *  @param dir  The direction to wind the oval's contour.
      */
     void addOval(const SkRect& oval, Direction dir = kCW_Direction);
 
@@ -638,8 +601,7 @@
      *                  closed contour to the path
      *  @param radius   The radius of a circle to add as a closed contour to the
      *                  path
-     *  @param dir  The direction to wind the circle's contour. Cannot be
-     *              kUnknown_Direction.
+     *  @param dir  The direction to wind the circle's contour.
      */
     void addCircle(SkScalar x, SkScalar y, SkScalar radius,
                    Direction dir = kCW_Direction);
@@ -657,8 +619,7 @@
      *  @param rect The bounds of a round-rectangle to add as a closed contour
      *  @param rx   The x-radius of the rounded corners on the round-rectangle
      *  @param ry   The y-radius of the rounded corners on the round-rectangle
-     *  @param dir  The direction to wind the rectangle's contour. Cannot be
-     *              kUnknown_Direction.
+     *  @param dir  The direction to wind the rectangle's contour.
      */
     void addRoundRect(const SkRect& rect, SkScalar rx, SkScalar ry,
                       Direction dir = kCW_Direction);
@@ -669,8 +630,7 @@
      *  bottom-right, bottom-left.
      *  @param rect The bounds of a round-rectangle to add as a closed contour
      *  @param radii Array of 8 scalars, 4 [X,Y] pairs for each corner
-     *  @param dir  The direction to wind the rectangle's contour. Cannot be
-     *              kUnknown_Direction.
+     *  @param dir  The direction to wind the rectangle's contour.
      * Note: The radii here now go through the same constraint handling as the
      *       SkRRect radii (i.e., either radii at a corner being 0 implies a
      *       sqaure corner and oversized radii are proportionally scaled down).
@@ -681,8 +641,7 @@
     /**
      *  Add an SkRRect contour to the path
      *  @param rrect The rounded rect to add as a closed contour
-     *  @param dir   The winding direction for the new contour. Cannot be
-     *               kUnknown_Direction.
+     *  @param dir   The winding direction for the new contour.
      */
     void addRRect(const SkRRect& rrect, Direction dir = kCW_Direction);
 
@@ -976,7 +935,7 @@
     int                 fLastMoveToIndex;
     uint8_t             fFillType;
     mutable uint8_t     fConvexity;
-    mutable uint8_t     fDirection;
+    mutable uint8_t     fFirstDirection;    // SkPathPriv::FirstDirection
     mutable SkBool8     fIsVolatile;
 
     /** Resets all fields other than fPathRef to their initial 'empty' values.
@@ -992,7 +951,7 @@
     void copyFields(const SkPath& that);
 
     friend class Iter;
-
+    friend class SkPathPriv;
     friend class SkPathStroker;
 
     /*  Append, in reverse order, the first contour of path, ignoring path's
diff --git a/src/core/SkPath.cpp b/src/core/SkPath.cpp
index c9bcb8f..defedd2 100644
--- a/src/core/SkPath.cpp
+++ b/src/core/SkPath.cpp
@@ -9,7 +9,7 @@
 #include "SkErrorInternals.h"
 #include "SkGeometry.h"
 #include "SkMath.h"
-#include "SkPath.h"
+#include "SkPathPriv.h"
 #include "SkPathRef.h"
 #include "SkRRect.h"
 #include "SkThread.h"
@@ -37,16 +37,16 @@
 class SkAutoDisableDirectionCheck {
 public:
     SkAutoDisableDirectionCheck(SkPath* path) : fPath(path) {
-        fSaved = static_cast<SkPath::Direction>(fPath->fDirection);
+        fSaved = static_cast<SkPathPriv::FirstDirection>(fPath->fFirstDirection);
     }
 
     ~SkAutoDisableDirectionCheck() {
-        fPath->fDirection = fSaved;
+        fPath->fFirstDirection = fSaved;
     }
 
 private:
-    SkPath*              fPath;
-    SkPath::Direction    fSaved;
+    SkPath*                     fPath;
+    SkPathPriv::FirstDirection  fSaved;
 };
 #define SkAutoDisableDirectionCheck(...) SK_REQUIRE_LOCAL_VAR(SkAutoDisableDirectionCheck)
 
@@ -135,7 +135,7 @@
     fLastMoveToIndex = INITIAL_LASTMOVETOINDEX_VALUE;
     fFillType = kWinding_FillType;
     fConvexity = kUnknown_Convexity;
-    fDirection = kUnknown_Direction;
+    fFirstDirection = SkPathPriv::kUnknown_FirstDirection;
 
     // We don't touch Android's fSourcePath.  It's used to track texture garbage collection, so we
     // don't want to muck with it if it's been set to something non-NULL.
@@ -167,7 +167,7 @@
     fLastMoveToIndex = that.fLastMoveToIndex;
     fFillType        = that.fFillType;
     fConvexity       = that.fConvexity;
-    fDirection       = that.fDirection;
+    fFirstDirection  = that.fFirstDirection;
     fIsVolatile      = that.fIsVolatile;
 }
 
@@ -184,7 +184,7 @@
         SkTSwap<int>(fLastMoveToIndex, that.fLastMoveToIndex);
         SkTSwap<uint8_t>(fFillType, that.fFillType);
         SkTSwap<uint8_t>(fConvexity, that.fConvexity);
-        SkTSwap<uint8_t>(fDirection, that.fDirection);
+        SkTSwap<uint8_t>(fFirstDirection, that.fFirstDirection);
         SkTSwap<SkBool8>(fIsVolatile, that.fIsVolatile);
     }
 }
@@ -192,10 +192,10 @@
 static inline bool check_edge_against_rect(const SkPoint& p0,
                                            const SkPoint& p1,
                                            const SkRect& rect,
-                                           SkPath::Direction dir) {
+                                           SkPathPriv::FirstDirection dir) {
     const SkPoint* edgeBegin;
     SkVector v;
-    if (SkPath::kCW_Direction == dir) {
+    if (SkPathPriv::kCW_FirstDirection == dir) {
         v = p1 - p0;
         edgeBegin = &p0;
     } else {
@@ -221,8 +221,8 @@
         return false;
     }
 
-    Direction direction;
-    if (!this->cheapComputeDirection(&direction)) {
+    SkPathPriv::FirstDirection direction;
+    if (!SkPathPriv::CheapComputeFirstDirection(*this, &direction)) {
         return false;
     }
 
@@ -662,10 +662,10 @@
 //////////////////////////////////////////////////////////////////////////////
 //  Construction methods
 
-#define DIRTY_AFTER_EDIT                 \
-    do {                                 \
-        fConvexity = kUnknown_Convexity; \
-        fDirection = kUnknown_Direction; \
+#define DIRTY_AFTER_EDIT                                        \
+    do {                                                        \
+        fConvexity = kUnknown_Convexity;                        \
+        fFirstDirection = SkPathPriv::kUnknown_FirstDirection;  \
     } while (0)
 
 void SkPath::incReserve(U16CPU inc) {
@@ -848,7 +848,8 @@
 void SkPath::addRect(SkScalar left, SkScalar top, SkScalar right,
                      SkScalar bottom, Direction dir) {
     assert_known_direction(dir);
-    fDirection = this->hasOnlyMoveTos() ? dir : kUnknown_Direction;
+    fFirstDirection = this->hasOnlyMoveTos() ?
+                        (SkPathPriv::FirstDirection)dir : SkPathPriv::kUnknown_FirstDirection;
     SkAutoDisableDirectionCheck addc(this);
 
     SkAutoPathBoundsUpdate apbu(this, left, top, right, bottom);
@@ -988,7 +989,8 @@
     } else if (rrect.isOval()) {
         this->addOval(bounds, dir);
     } else {
-        fDirection = this->hasOnlyMoveTos() ? dir : kUnknown_Direction;
+        fFirstDirection = this->hasOnlyMoveTos() ?
+                            (SkPathPriv::FirstDirection)dir : SkPathPriv::kUnknown_FirstDirection;
 
         SkAutoPathBoundsUpdate apbu(this, bounds);
         SkAutoDisableDirectionCheck addc(this);
@@ -1077,9 +1079,9 @@
      */
     bool isOval = hasOnlyMoveTos();
     if (isOval) {
-        fDirection = dir;
+        fFirstDirection = (SkPathPriv::FirstDirection)dir;
     } else {
-        fDirection = kUnknown_Direction;
+        fFirstDirection = SkPathPriv::kUnknown_FirstDirection;
     }
 
     SkAutoDisableDirectionCheck addc(this);
@@ -1477,7 +1479,7 @@
         dst->swap(tmp);
         SkPathRef::Editor ed(&dst->fPathRef);
         matrix.mapPoints(ed.points(), ed.pathRef()->countPoints());
-        dst->fDirection = kUnknown_Direction;
+        dst->fFirstDirection = SkPathPriv::kUnknown_FirstDirection;
     } else {
         SkPathRef::CreateTransformedCopy(&dst->fPathRef, *fPathRef.get(), matrix);
 
@@ -1487,19 +1489,19 @@
             dst->fIsVolatile = fIsVolatile;
         }
 
-        if (kUnknown_Direction == fDirection) {
-            dst->fDirection = kUnknown_Direction;
+        if (SkPathPriv::kUnknown_FirstDirection == fFirstDirection) {
+            dst->fFirstDirection = SkPathPriv::kUnknown_FirstDirection;
         } else {
             SkScalar det2x2 =
                 SkScalarMul(matrix.get(SkMatrix::kMScaleX), matrix.get(SkMatrix::kMScaleY)) -
                 SkScalarMul(matrix.get(SkMatrix::kMSkewX), matrix.get(SkMatrix::kMSkewY));
             if (det2x2 < 0) {
-                dst->fDirection = SkPath::OppositeDirection(static_cast<Direction>(fDirection));
+                dst->fFirstDirection = SkPathPriv::OppositeFirstDirection((SkPathPriv::FirstDirection)fFirstDirection);
             } else if (det2x2 > 0) {
-                dst->fDirection = fDirection;
+                dst->fFirstDirection = fFirstDirection;
             } else {
                 dst->fConvexity = kUnknown_Convexity;
-                dst->fDirection = kUnknown_Direction;
+                dst->fFirstDirection = SkPathPriv::kUnknown_FirstDirection;
             }
         }
 
@@ -1858,7 +1860,7 @@
 
     int32_t packed = (fConvexity << kConvexity_SerializationShift) |
                      (fFillType << kFillType_SerializationShift) |
-                     (fDirection << kDirection_SerializationShift) |
+                     (fFirstDirection << kDirection_SerializationShift) |
                      (fIsVolatile << kIsVolatile_SerializationShift);
 
     buffer.write32(packed);
@@ -1879,7 +1881,7 @@
 
     fConvexity = (packed >> kConvexity_SerializationShift) & 0xFF;
     fFillType = (packed >> kFillType_SerializationShift) & 0xFF;
-    fDirection = (packed >> kDirection_SerializationShift) & 0x3;
+    fFirstDirection = (packed >> kDirection_SerializationShift) & 0x3;
     fIsVolatile = (packed >> kIsVolatile_SerializationShift) & 0x1;
     SkPathRef* pathRef = SkPathRef::CreateFromBuffer(&buffer);
 
@@ -2061,7 +2063,7 @@
     Convexicator()
     : fPtCount(0)
     , fConvexity(SkPath::kConvex_Convexity)
-    , fDirection(SkPath::kUnknown_Direction)
+    , fFirstDirection(SkPathPriv::kUnknown_FirstDirection)
     , fIsFinite(true)
     , fIsCurve(false) {
         fExpectedDir = kInvalid_DirChange;
@@ -2078,7 +2080,7 @@
     SkPath::Convexity getConvexity() const { return fConvexity; }
 
     /** The direction returned is only valid if the path is determined convex */
-    SkPath::Direction getDirection() const { return fDirection; }
+    SkPathPriv::FirstDirection getFirstDirection() const { return fFirstDirection; }
 
     void addPt(const SkPoint& pt) {
         if (SkPath::kConcave_Convexity == fConvexity || !fIsFinite) {
@@ -2179,11 +2181,11 @@
             case kRight_DirChange:
                 if (kInvalid_DirChange == fExpectedDir) {
                     fExpectedDir = dir;
-                    fDirection = (kRight_DirChange == dir) ? SkPath::kCW_Direction
-                                                           : SkPath::kCCW_Direction;
+                    fFirstDirection = (kRight_DirChange == dir) ? SkPathPriv::kCW_FirstDirection
+                                                                : SkPathPriv::kCCW_FirstDirection;
                 } else if (dir != fExpectedDir) {
                     fConvexity = SkPath::kConcave_Convexity;
-                    fDirection = SkPath::kUnknown_Direction;
+                    fFirstDirection = SkPathPriv::kUnknown_FirstDirection;
                 }
                 fLastVec = vec;
                 break;
@@ -2192,7 +2194,7 @@
             case kBackwards_DirChange:
                 if (fIsCurve) {
                     fConvexity = SkPath::kConcave_Convexity;
-                    fDirection = SkPath::kUnknown_Direction;
+                    fFirstDirection = SkPathPriv::kUnknown_FirstDirection;
                 }
                 fLastVec = vec;
                 break;
@@ -2211,7 +2213,7 @@
     int                 fPtCount;   // non-degenerate points
     DirChange           fExpectedDir;
     SkPath::Convexity   fConvexity;
-    SkPath::Direction   fDirection;
+    SkPathPriv::FirstDirection   fFirstDirection;
     int                 fDx, fDy, fSx, fSy;
     bool                fIsFinite;
     bool                fIsCurve;
@@ -2277,8 +2279,8 @@
         }
     }
     fConvexity = state.getConvexity();
-    if (kConvex_Convexity == fConvexity && kUnknown_Direction == fDirection) {
-        fDirection = state.getDirection();
+    if (kConvex_Convexity == fConvexity && SkPathPriv::kUnknown_FirstDirection == fFirstDirection) {
+        fFirstDirection = state.getFirstDirection();
     }
     return static_cast<Convexity>(fConvexity);
 }
@@ -2439,8 +2441,8 @@
     return minIndex;
 }
 
-static void crossToDir(SkScalar cross, SkPath::Direction* dir) {
-    *dir = cross > 0 ? SkPath::kCW_Direction : SkPath::kCCW_Direction;
+static void crossToDir(SkScalar cross, SkPathPriv::FirstDirection* dir) {
+    *dir = cross > 0 ? SkPathPriv::kCW_FirstDirection : SkPathPriv::kCCW_FirstDirection;
 }
 
 /*
@@ -2451,24 +2453,24 @@
  *  that is outer most (or at least has the global y-max) before we can consider
  *  its cross product.
  */
-bool SkPath::cheapComputeDirection(Direction* dir) const {
-    if (kUnknown_Direction != fDirection) {
-        *dir = static_cast<Direction>(fDirection);
+bool SkPathPriv::CheapComputeFirstDirection(const SkPath& path, FirstDirection* dir) {
+    if (kUnknown_FirstDirection != path.fFirstDirection) {
+        *dir = static_cast<FirstDirection>(path.fFirstDirection);
         return true;
     }
 
     // don't want to pay the cost for computing this if it
     // is unknown, so we don't call isConvex()
-    if (kConvex_Convexity == this->getConvexityOrUnknown()) {
-        SkASSERT(kUnknown_Direction == fDirection);
-        *dir = static_cast<Direction>(fDirection);
+    if (SkPath::kConvex_Convexity == path.getConvexityOrUnknown()) {
+        SkASSERT(kUnknown_FirstDirection == path.fFirstDirection);
+        *dir = static_cast<FirstDirection>(path.fFirstDirection);
         return false;
     }
 
-    ContourIter iter(*fPathRef.get());
+    ContourIter iter(*path.fPathRef.get());
 
     // initialize with our logical y-min
-    SkScalar ymax = this->getBounds().fTop;
+    SkScalar ymax = path.getBounds().fTop;
     SkScalar ymaxCross = 0;
 
     for (; !iter.done(); iter.next()) {
@@ -2534,7 +2536,7 @@
     }
     if (ymaxCross) {
         crossToDir(ymaxCross, dir);
-        fDirection = *dir;
+        path.fFirstDirection = *dir;
         return true;
     } else {
         return false;
diff --git a/src/core/SkPathPriv.h b/src/core/SkPathPriv.h
new file mode 100644
index 0000000..98e50e3
--- /dev/null
+++ b/src/core/SkPathPriv.h
@@ -0,0 +1,60 @@
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef SkPathPriv_DEFINED
+#define SkPathPriv_DEFINED
+
+#include "SkPath.h"
+
+class SkPathPriv {
+public:
+    enum FirstDirection {
+        kCW_FirstDirection,         // == SkPath::kCW_Direction
+        kCCW_FirstDirection,        // == SkPath::kCCW_Direction
+        kUnknown_FirstDirection,
+    };
+
+    static FirstDirection AsFirstDirection(SkPath::Direction dir) {
+        // since we agree numerically for the values in Direction, we can just cast.
+        return (FirstDirection)dir;
+    }
+
+    /**
+     *  Return the opposite of the specified direction. kUnknown is its own
+     *  opposite.
+     */
+    static FirstDirection OppositeFirstDirection(FirstDirection dir) {
+        static const FirstDirection gOppositeDir[] = {
+            kCCW_FirstDirection, kCW_FirstDirection, kUnknown_FirstDirection, 
+        };
+        return gOppositeDir[dir];
+    }
+
+    /**
+     *  Tries to quickly compute the direction of the first non-degenerate
+     *  contour. If it can be computed, return true and set dir to that
+     *  direction. If it cannot be (quickly) determined, return false and ignore
+     *  the dir parameter. If the direction was determined, it is cached to make
+     *  subsequent calls return quickly.
+     */
+    static bool CheapComputeFirstDirection(const SkPath&, FirstDirection* dir);
+
+    /**
+     *  Returns true if the path's direction can be computed via
+     *  cheapComputDirection() and if that computed direction matches the
+     *  specified direction. If dir is kUnknown, returns true if the direction
+     *  cannot be computed.
+     */
+    static bool CheapIsFirstDirection(const SkPath& path, FirstDirection dir) {
+        FirstDirection computedDir = kUnknown_FirstDirection;
+        (void)CheapComputeFirstDirection(path, &computedDir);
+        return computedDir == dir;
+    }
+
+};
+
+#endif
diff --git a/src/core/SkRecords.h b/src/core/SkRecords.h
index 6e25dd2..183c647 100644
--- a/src/core/SkRecords.h
+++ b/src/core/SkRecords.h
@@ -10,6 +10,7 @@
 
 #include "SkCanvas.h"
 #include "SkDrawable.h"
+#include "SkPathPriv.h"
 #include "SkPicture.h"
 #include "SkTextBlob.h"
 
@@ -215,8 +216,8 @@
     PreCachedPath() {}
     explicit PreCachedPath(const SkPath& path) : SkPath(path) {
         this->updateBoundsCache();
-        SkPath::Direction junk;
-        (void)this->cheapComputeDirection(&junk);
+        SkPathPriv::FirstDirection junk;
+        (void)SkPathPriv::CheapComputeFirstDirection(*this, &junk);
     }
 };
 
diff --git a/src/core/SkStroke.cpp b/src/core/SkStroke.cpp
index 2296315..86c0571 100644
--- a/src/core/SkStroke.cpp
+++ b/src/core/SkStroke.cpp
@@ -7,7 +7,7 @@
 
 #include "SkStrokerPriv.h"
 #include "SkGeometry.h"
-#include "SkPath.h"
+#include "SkPathPriv.h"
 
 enum {
     kTangent_RecursiveLimit,
@@ -1344,7 +1344,7 @@
     stroker.done(dst, lastSegment == SkPath::kLine_Verb);
 
     if (fDoFill) {
-        if (src.cheapIsDirection(SkPath::kCCW_Direction)) {
+        if (SkPathPriv::CheapIsFirstDirection(src, SkPathPriv::kCCW_FirstDirection)) {
             dst->reverseAddPath(src);
         } else {
             dst->addPath(src);
@@ -1379,8 +1379,8 @@
 }
 
 static SkPath::Direction reverse_direction(SkPath::Direction dir) {
-    SkASSERT(SkPath::kUnknown_Direction != dir);
-    return SkPath::kCW_Direction == dir ? SkPath::kCCW_Direction : SkPath::kCW_Direction;
+    static const SkPath::Direction gOpposite[] = { SkPath::kCCW_Direction, SkPath::kCW_Direction };
+    return gOpposite[dir];
 }
 
 static void addBevel(SkPath* path, const SkRect& r, const SkRect& outer, SkPath::Direction dir) {
diff --git a/src/gpu/GrAAConvexPathRenderer.cpp b/src/gpu/GrAAConvexPathRenderer.cpp
index 5d08aa5..45a4ca3 100644
--- a/src/gpu/GrAAConvexPathRenderer.cpp
+++ b/src/gpu/GrAAConvexPathRenderer.cpp
@@ -22,6 +22,7 @@
 #include "GrPipelineBuilder.h"
 #include "GrStrokeInfo.h"
 #include "SkGeometry.h"
+#include "SkPathPriv.h"
 #include "SkString.h"
 #include "SkTraceEvent.h"
 #include "gl/GrGLProcessor.h"
@@ -116,7 +117,7 @@
 
 static void compute_vectors(SegmentArray* segments,
                             SkPoint* fanPt,
-                            SkPath::Direction dir,
+                            SkPathPriv::FirstDirection dir,
                             int* vCount,
                             int* iCount) {
     center_of_mass(*segments, fanPt);
@@ -124,7 +125,7 @@
 
     // Make the normals point towards the outside
     SkPoint::Side normSide;
-    if (dir == SkPath::kCCW_Direction) {
+    if (dir == SkPathPriv::kCCW_FirstDirection) {
         normSide = SkPoint::kRight_Side;
     } else {
         normSide = SkPoint::kLeft_Side;
@@ -212,8 +213,9 @@
     }
 }
 
-static inline bool get_direction(const SkPath& path, const SkMatrix& m, SkPath::Direction* dir) {
-    if (!path.cheapComputeDirection(dir)) {
+static inline bool get_direction(const SkPath& path, const SkMatrix& m,
+                                 SkPathPriv::FirstDirection* dir) {
+    if (!SkPathPriv::CheapComputeFirstDirection(path, dir)) {
         return false;
     }
     // check whether m reverses the orientation
@@ -221,7 +223,7 @@
     SkScalar det2x2 = SkScalarMul(m.get(SkMatrix::kMScaleX), m.get(SkMatrix::kMScaleY)) -
                       SkScalarMul(m.get(SkMatrix::kMSkewX), m.get(SkMatrix::kMSkewY));
     if (det2x2 < 0) {
-        *dir = SkPath::OppositeDirection(*dir);
+        *dir = SkPathPriv::OppositeFirstDirection(*dir);
     }
     return true;
 }
@@ -248,7 +250,7 @@
 }
 
 static inline void add_cubic_segments(const SkPoint pts[4],
-                                      SkPath::Direction dir,
+                                      SkPathPriv::FirstDirection dir,
                                       SegmentArray* segments) {
     SkSTArray<15, SkPoint, true> quads;
     GrPathUtils::convertCubicToQuads(pts, SK_Scalar1, true, dir, &quads);
@@ -273,7 +275,7 @@
     // line paths. We detect paths that are very close to a line (zero area) and
     // draw nothing.
     DegenerateTestData degenerateData;
-    SkPath::Direction dir;
+    SkPathPriv::FirstDirection dir;
     // get_direction can fail for some degenerate paths.
     if (!get_direction(path, m, &dir)) {
         return false;
diff --git a/src/gpu/GrAAHairLinePathRenderer.cpp b/src/gpu/GrAAHairLinePathRenderer.cpp
index b7cb237..da60a6c 100644
--- a/src/gpu/GrAAHairLinePathRenderer.cpp
+++ b/src/gpu/GrAAHairLinePathRenderer.cpp
@@ -345,7 +345,7 @@
                 if (SkIRect::Intersects(devClipBounds, ibounds)) {
                     PREALLOC_PTARRAY(32) q;
                     // we don't need a direction if we aren't constraining the subdivision
-                    static const SkPath::Direction kDummyDir = SkPath::kCCW_Direction;
+                    const SkPathPriv::FirstDirection kDummyDir = SkPathPriv::kCCW_FirstDirection;
                     // We convert cubics to quadratics (for now).
                     // In perspective have to do conversion in src space.
                     if (persp) {
diff --git a/src/gpu/GrPathUtils.cpp b/src/gpu/GrPathUtils.cpp
index 3e2c3bf..21d1155 100644
--- a/src/gpu/GrPathUtils.cpp
+++ b/src/gpu/GrPathUtils.cpp
@@ -365,16 +365,16 @@
                                     const SkVector& ab,
                                     const SkVector& dc,
                                     const SkPoint& d,
-                                    SkPath::Direction dir,
+                                    SkPathPriv::FirstDirection dir,
                                     const SkPoint p) {
     SkVector ap = p - a;
     SkScalar apXab = ap.cross(ab);
-    if (SkPath::kCW_Direction == dir) {
+    if (SkPathPriv::kCW_FirstDirection == dir) {
         if (apXab > 0) {
             return false;
         }
     } else {
-        SkASSERT(SkPath::kCCW_Direction == dir);
+        SkASSERT(SkPathPriv::kCCW_FirstDirection == dir);
         if (apXab < 0) {
             return false;
         }
@@ -382,12 +382,12 @@
 
     SkVector dp = p - d;
     SkScalar dpXdc = dp.cross(dc);
-    if (SkPath::kCW_Direction == dir) {
+    if (SkPathPriv::kCW_FirstDirection == dir) {
         if (dpXdc < 0) {
             return false;
         }
     } else {
-        SkASSERT(SkPath::kCCW_Direction == dir);
+        SkASSERT(SkPathPriv::kCCW_FirstDirection == dir);
         if (dpXdc > 0) {
             return false;
         }
@@ -398,7 +398,7 @@
 void convert_noninflect_cubic_to_quads(const SkPoint p[4],
                                        SkScalar toleranceSqd,
                                        bool constrainWithinTangents,
-                                       SkPath::Direction dir,
+                                       SkPathPriv::FirstDirection dir,
                                        SkTArray<SkPoint, true>* quads,
                                        int sublevel = 0) {
 
@@ -546,7 +546,7 @@
 void GrPathUtils::convertCubicToQuads(const SkPoint p[4],
                                       SkScalar tolScale,
                                       bool constrainWithinTangents,
-                                      SkPath::Direction dir,
+                                      SkPathPriv::FirstDirection dir,
                                       SkTArray<SkPoint, true>* quads) {
     SkPoint chopped[10];
     int count = SkChopCubicAtInflections(p, chopped);
diff --git a/src/gpu/GrPathUtils.h b/src/gpu/GrPathUtils.h
index 250b0a2..8e52e33 100644
--- a/src/gpu/GrPathUtils.h
+++ b/src/gpu/GrPathUtils.h
@@ -9,7 +9,7 @@
 #define GrPathUtils_DEFINED
 
 #include "SkRect.h"
-#include "SkPath.h"
+#include "SkPathPriv.h"
 #include "SkTArray.h"
 
 class SkMatrix;
@@ -121,7 +121,7 @@
     void convertCubicToQuads(const SkPoint p[4],
                              SkScalar tolScale,
                              bool constrainWithinTangents,
-                             SkPath::Direction dir,
+                             SkPathPriv::FirstDirection dir,
                              SkTArray<SkPoint, true>* quads);
 
     // Chops the cubic bezier passed in by src, at the double point (intersection point)
diff --git a/src/gpu/effects/GrConvexPolyEffect.cpp b/src/gpu/effects/GrConvexPolyEffect.cpp
index 0cef3fa..0048279 100644
--- a/src/gpu/effects/GrConvexPolyEffect.cpp
+++ b/src/gpu/effects/GrConvexPolyEffect.cpp
@@ -7,7 +7,7 @@
 
 #include "GrConvexPolyEffect.h"
 #include "GrInvariantOutput.h"
-#include "SkPath.h"
+#include "SkPathPriv.h"
 #include "gl/GrGLProcessor.h"
 #include "gl/GrGLSL.h"
 #include "gl/builders/GrGLProgramBuilder.h"
@@ -279,8 +279,8 @@
     SkPoint pts[kMaxEdges];
     SkScalar edges[3 * kMaxEdges];
 
-    SkPath::Direction dir;
-    SkAssertResult(path.cheapComputeDirection(&dir));
+    SkPathPriv::FirstDirection dir;
+    SkAssertResult(SkPathPriv::CheapComputeFirstDirection(path, &dir));
 
     SkVector t;
     if (NULL == offset) {
@@ -295,7 +295,7 @@
         if (pts[lastPt] != pts[i]) {
             SkVector v = pts[i] - pts[lastPt];
             v.normalize();
-            if (SkPath::kCCW_Direction == dir) {
+            if (SkPathPriv::kCCW_FirstDirection == dir) {
                 edges[3 * n] = v.fY;
                 edges[3 * n + 1] = -v.fX;
             } else {
diff --git a/src/pathops/SkOpBuilder.cpp b/src/pathops/SkOpBuilder.cpp
index 5cc3fbd..a434fbc 100644
--- a/src/pathops/SkOpBuilder.cpp
+++ b/src/pathops/SkOpBuilder.cpp
@@ -7,7 +7,7 @@
 
 #include "SkMatrix.h"
 #include "SkOpEdgeBuilder.h"
-#include "SkPath.h"
+#include "SkPathPriv.h"
 #include "SkPathOps.h"
 #include "SkPathOpsCommon.h"
 
@@ -32,9 +32,9 @@
     } else if (fillType == SkPath::kEvenOdd_FillType) {
         fillType = SkPath::kWinding_FillType;
     }
-    SkPath::Direction dir;
-    if (one_contour(*path) && path->cheapComputeDirection(&dir)) {
-        if (dir != SkPath::kCCW_Direction) {
+    SkPathPriv::FirstDirection dir;
+    if (one_contour(*path) && SkPathPriv::CheapComputeFirstDirection(*path, &dir)) {
+        if (dir != SkPathPriv::kCCW_FirstDirection) {
             SkPath temp;
             temp.reverseAddPath(*path);
             *path = temp;
@@ -106,7 +106,7 @@
     SkPath original = *result;
     int count = fOps.count();
     bool allUnion = true;
-    SkPath::Direction firstDir;
+    SkPathPriv::FirstDirection firstDir;
     for (int index = 0; index < count; ++index) {
         SkPath* test = &fPathRefs[index];
         if (kUnion_SkPathOp != fOps[index] || test->isInverseFillType()) {
@@ -115,8 +115,8 @@
         }
         // If all paths are convex, track direction, reversing as needed.
         if (test->isConvex()) {
-            SkPath::Direction dir;
-            if (!test->cheapComputeDirection(&dir)) {
+            SkPathPriv::FirstDirection dir;
+            if (!SkPathPriv::CheapComputeFirstDirection(*test, &dir)) {
                 allUnion = false;
                 break;
             }
diff --git a/tests/PathTest.cpp b/tests/PathTest.cpp
index d25d349..d7660a1 100644
--- a/tests/PathTest.cpp
+++ b/tests/PathTest.cpp
@@ -9,7 +9,7 @@
 #include "SkPaint.h"
 #include "SkParse.h"
 #include "SkParsePath.h"
-#include "SkPath.h"
+#include "SkPathPriv.h"
 #include "SkPathEffect.h"
 #include "SkRRect.h"
 #include "SkRandom.h"
@@ -807,30 +807,30 @@
 
 // Set this for paths that don't have a consistent direction such as a bowtie.
 // (cheapComputeDirection is not expected to catch these.)
-static const SkPath::Direction kDontCheckDir = static_cast<SkPath::Direction>(-1);
+const SkPathPriv::FirstDirection kDontCheckDir = static_cast<SkPathPriv::FirstDirection>(-1);
 
 static void check_direction(skiatest::Reporter* reporter, const SkPath& path,
-                            SkPath::Direction expected) {
+                            SkPathPriv::FirstDirection expected) {
     if (expected == kDontCheckDir) {
         return;
     }
     SkPath copy(path); // we make a copy so that we don't cache the result on the passed in path.
 
-    SkPath::Direction dir;
-    if (copy.cheapComputeDirection(&dir)) {
+    SkPathPriv::FirstDirection dir;
+    if (SkPathPriv::CheapComputeFirstDirection(copy, &dir)) {
         REPORTER_ASSERT(reporter, dir == expected);
     } else {
-        REPORTER_ASSERT(reporter, SkPath::kUnknown_Direction == expected);
+        REPORTER_ASSERT(reporter, SkPathPriv::kUnknown_FirstDirection == expected);
     }
 }
 
 static void test_direction(skiatest::Reporter* reporter) {
     size_t i;
     SkPath path;
-    REPORTER_ASSERT(reporter, !path.cheapComputeDirection(NULL));
-    REPORTER_ASSERT(reporter, !path.cheapIsDirection(SkPath::kCW_Direction));
-    REPORTER_ASSERT(reporter, !path.cheapIsDirection(SkPath::kCCW_Direction));
-    REPORTER_ASSERT(reporter, path.cheapIsDirection(SkPath::kUnknown_Direction));
+    REPORTER_ASSERT(reporter, !SkPathPriv::CheapComputeFirstDirection(path, NULL));
+    REPORTER_ASSERT(reporter, !SkPathPriv::CheapIsFirstDirection(path, SkPathPriv::kCW_FirstDirection));
+    REPORTER_ASSERT(reporter, !SkPathPriv::CheapIsFirstDirection(path, SkPathPriv::kCCW_FirstDirection));
+    REPORTER_ASSERT(reporter, SkPathPriv::CheapIsFirstDirection(path, SkPathPriv::kUnknown_FirstDirection));
 
     static const char* gDegen[] = {
         "M 10 10",
@@ -844,7 +844,7 @@
         path.reset();
         bool valid = SkParsePath::FromSVGString(gDegen[i], &path);
         REPORTER_ASSERT(reporter, valid);
-        REPORTER_ASSERT(reporter, !path.cheapComputeDirection(NULL));
+        REPORTER_ASSERT(reporter, !SkPathPriv::CheapComputeFirstDirection(path, NULL));
     }
 
     static const char* gCW[] = {
@@ -860,7 +860,7 @@
         path.reset();
         bool valid = SkParsePath::FromSVGString(gCW[i], &path);
         REPORTER_ASSERT(reporter, valid);
-        check_direction(reporter, path, SkPath::kCW_Direction);
+        check_direction(reporter, path, SkPathPriv::kCW_FirstDirection);
     }
 
     static const char* gCCW[] = {
@@ -876,7 +876,7 @@
         path.reset();
         bool valid = SkParsePath::FromSVGString(gCCW[i], &path);
         REPORTER_ASSERT(reporter, valid);
-        check_direction(reporter, path, SkPath::kCCW_Direction);
+        check_direction(reporter, path, SkPathPriv::kCCW_FirstDirection);
     }
 
     // Test two donuts, each wound a different direction. Only the outer contour
@@ -884,12 +884,12 @@
     path.reset();
     path.addCircle(0, 0, SkIntToScalar(2), SkPath::kCW_Direction);
     path.addCircle(0, 0, SkIntToScalar(1), SkPath::kCCW_Direction);
-    check_direction(reporter, path, SkPath::kCW_Direction);
+    check_direction(reporter, path, SkPathPriv::kCW_FirstDirection);
 
     path.reset();
     path.addCircle(0, 0, SkIntToScalar(1), SkPath::kCW_Direction);
     path.addCircle(0, 0, SkIntToScalar(2), SkPath::kCCW_Direction);
-    check_direction(reporter, path, SkPath::kCCW_Direction);
+    check_direction(reporter, path, SkPathPriv::kCCW_FirstDirection);
 
     // triangle with one point really far from the origin.
     path.reset();
@@ -897,19 +897,19 @@
     path.moveTo(SkBits2Float(0x501c7652), SkBits2Float(0x501c7652));
     path.lineTo(110 * SK_Scalar1, -10 * SK_Scalar1);
     path.lineTo(-10 * SK_Scalar1, 60 * SK_Scalar1);
-    check_direction(reporter, path, SkPath::kCCW_Direction);
+    check_direction(reporter, path, SkPathPriv::kCCW_FirstDirection);
 
     path.reset();
     path.conicTo(20, 0, 20, 20, 0.5f);
     path.close();
-    check_direction(reporter, path, SkPath::kCW_Direction);
+    check_direction(reporter, path, SkPathPriv::kCW_FirstDirection);
 
     path.reset();
     path.lineTo(1, 1e7f);
     path.lineTo(1e7f, 2e7f);
     path.close();
     REPORTER_ASSERT(reporter, SkPath::kConvex_Convexity == path.getConvexity());
-    check_direction(reporter, path, SkPath::kCCW_Direction);
+    check_direction(reporter, path, SkPathPriv::kCCW_FirstDirection);
 }
 
 static void add_rect(SkPath* path, const SkRect& r) {
@@ -1085,7 +1085,7 @@
     tinyConvexPolygon.close();
     tinyConvexPolygon.getConvexity();
     check_convexity(reporter, tinyConvexPolygon, SkPath::kConvex_Convexity);
-    check_direction(reporter, tinyConvexPolygon, SkPath::kCW_Direction);
+    check_direction(reporter, tinyConvexPolygon, SkPathPriv::kCW_FirstDirection);
 
     SkPath  platTriangle;
     platTriangle.moveTo(0, 0);
@@ -1093,7 +1093,7 @@
     platTriangle.lineTo(100, 0.04f);
     platTriangle.close();
     platTriangle.getConvexity();
-    check_direction(reporter, platTriangle, SkPath::kCW_Direction);
+    check_direction(reporter, platTriangle, SkPathPriv::kCW_FirstDirection);
 
     platTriangle.reset();
     platTriangle.moveTo(0, 0);
@@ -1101,7 +1101,7 @@
     platTriangle.lineTo(100, 0.03f);
     platTriangle.close();
     platTriangle.getConvexity();
-    check_direction(reporter, platTriangle, SkPath::kCW_Direction);
+    check_direction(reporter, platTriangle, SkPathPriv::kCW_FirstDirection);
 }
 
 static void test_convexity2(skiatest::Reporter* reporter) {
@@ -1109,14 +1109,14 @@
     pt.moveTo(0, 0);
     pt.close();
     check_convexity(reporter, pt, SkPath::kConvex_Convexity);
-    check_direction(reporter, pt, SkPath::kUnknown_Direction);
+    check_direction(reporter, pt, SkPathPriv::kUnknown_FirstDirection);
 
     SkPath line;
     line.moveTo(12*SK_Scalar1, 20*SK_Scalar1);
     line.lineTo(-12*SK_Scalar1, -20*SK_Scalar1);
     line.close();
     check_convexity(reporter, line, SkPath::kConvex_Convexity);
-    check_direction(reporter, line, SkPath::kUnknown_Direction);
+    check_direction(reporter, line, SkPathPriv::kUnknown_FirstDirection);
 
     SkPath triLeft;
     triLeft.moveTo(0, 0);
@@ -1124,7 +1124,7 @@
     triLeft.lineTo(SK_Scalar1, SK_Scalar1);
     triLeft.close();
     check_convexity(reporter, triLeft, SkPath::kConvex_Convexity);
-    check_direction(reporter, triLeft, SkPath::kCW_Direction);
+    check_direction(reporter, triLeft, SkPathPriv::kCW_FirstDirection);
 
     SkPath triRight;
     triRight.moveTo(0, 0);
@@ -1132,7 +1132,7 @@
     triRight.lineTo(SK_Scalar1, SK_Scalar1);
     triRight.close();
     check_convexity(reporter, triRight, SkPath::kConvex_Convexity);
-    check_direction(reporter, triRight, SkPath::kCCW_Direction);
+    check_direction(reporter, triRight, SkPathPriv::kCCW_FirstDirection);
 
     SkPath square;
     square.moveTo(0, 0);
@@ -1141,7 +1141,7 @@
     square.lineTo(0, SK_Scalar1);
     square.close();
     check_convexity(reporter, square, SkPath::kConvex_Convexity);
-    check_direction(reporter, square, SkPath::kCW_Direction);
+    check_direction(reporter, square, SkPathPriv::kCW_FirstDirection);
 
     SkPath redundantSquare;
     redundantSquare.moveTo(0, 0);
@@ -1158,7 +1158,7 @@
     redundantSquare.lineTo(0, SK_Scalar1);
     redundantSquare.close();
     check_convexity(reporter, redundantSquare, SkPath::kConvex_Convexity);
-    check_direction(reporter, redundantSquare, SkPath::kCW_Direction);
+    check_direction(reporter, redundantSquare, SkPathPriv::kCW_FirstDirection);
 
     SkPath bowTie;
     bowTie.moveTo(0, 0);
@@ -1197,7 +1197,7 @@
     dent.lineTo(-200*SK_Scalar1, 100*SK_Scalar1);
     dent.close();
     check_convexity(reporter, dent, SkPath::kConcave_Convexity);
-    check_direction(reporter, dent, SkPath::kCW_Direction);
+    check_direction(reporter, dent, SkPathPriv::kCW_FirstDirection);
 
     // http://skbug.com/2235
     SkPath strokedSin;
@@ -1225,7 +1225,7 @@
     degenerateConcave.lineTo(-55.971577f, 460.0f);
     degenerateConcave.lineTo(41.446522f, 376.25f);
     check_convexity(reporter, degenerateConcave, SkPath::kConcave_Convexity);
-    check_direction(reporter, degenerateConcave, SkPath::kUnknown_Direction);
+    check_direction(reporter, degenerateConcave, SkPathPriv::kUnknown_FirstDirection);
 
     // http://crbug.com/433683
     SkPath badFirstVector;
@@ -1288,26 +1288,26 @@
     path.reset();
     path.addRect(0, 0, SkIntToScalar(10), SkIntToScalar(10), SkPath::kCCW_Direction);
     check_convexity(reporter, path, SkPath::kConvex_Convexity);
-    REPORTER_ASSERT(reporter, path.cheapIsDirection(SkPath::kCCW_Direction));
+    REPORTER_ASSERT(reporter, SkPathPriv::CheapIsFirstDirection(path, SkPathPriv::kCCW_FirstDirection));
 
     path.reset();
     path.addRect(0, 0, SkIntToScalar(10), SkIntToScalar(10), SkPath::kCW_Direction);
     check_convexity(reporter, path, SkPath::kConvex_Convexity);
-    REPORTER_ASSERT(reporter, path.cheapIsDirection(SkPath::kCW_Direction));
+    REPORTER_ASSERT(reporter, SkPathPriv::CheapIsFirstDirection(path, SkPathPriv::kCW_FirstDirection));
 
     static const struct {
-        const char*         fPathStr;
-        SkPath::Convexity   fExpectedConvexity;
-        SkPath::Direction   fExpectedDirection;
+        const char*                 fPathStr;
+        SkPath::Convexity           fExpectedConvexity;
+        SkPathPriv::FirstDirection  fExpectedDirection;
     } gRec[] = {
-        { "", SkPath::kConvex_Convexity, SkPath::kUnknown_Direction },
-        { "0 0", SkPath::kConvex_Convexity, SkPath::kUnknown_Direction },
-        { "0 0 10 10", SkPath::kConvex_Convexity, SkPath::kUnknown_Direction },
-        { "0 0 10 10 20 20 0 0 10 10", SkPath::kConcave_Convexity, SkPath::kUnknown_Direction },
-        { "0 0 10 10 10 20", SkPath::kConvex_Convexity, SkPath::kCW_Direction },
-        { "0 0 10 10 10 0", SkPath::kConvex_Convexity, SkPath::kCCW_Direction },
+        { "", SkPath::kConvex_Convexity, SkPathPriv::kUnknown_FirstDirection },
+        { "0 0", SkPath::kConvex_Convexity, SkPathPriv::kUnknown_FirstDirection },
+        { "0 0 10 10", SkPath::kConvex_Convexity, SkPathPriv::kUnknown_FirstDirection },
+        { "0 0 10 10 20 20 0 0 10 10", SkPath::kConcave_Convexity, SkPathPriv::kUnknown_FirstDirection },
+        { "0 0 10 10 10 20", SkPath::kConvex_Convexity, SkPathPriv::kCW_FirstDirection },
+        { "0 0 10 10 10 0", SkPath::kConvex_Convexity, SkPathPriv::kCCW_FirstDirection },
         { "0 0 10 10 10 0 0 10", SkPath::kConcave_Convexity, kDontCheckDir },
-        { "0 0 10 0 0 10 -10 -10", SkPath::kConcave_Convexity, SkPath::kCW_Direction },
+        { "0 0 10 0 0 10 -10 -10", SkPath::kConcave_Convexity, SkPathPriv::kCW_FirstDirection },
     };
 
     for (size_t i = 0; i < SK_ARRAY_COUNT(gRec); ++i) {
@@ -1318,9 +1318,9 @@
         // check after setting the initial convex and direction
         if (kDontCheckDir != gRec[i].fExpectedDirection) {
             SkPath copy(path);
-            SkPath::Direction dir;
-            bool foundDir = copy.cheapComputeDirection(&dir);
-            REPORTER_ASSERT(reporter, (gRec[i].fExpectedDirection == SkPath::kUnknown_Direction)
+            SkPathPriv::FirstDirection dir;
+            bool foundDir = SkPathPriv::CheapComputeFirstDirection(copy, &dir);
+            REPORTER_ASSERT(reporter, (gRec[i].fExpectedDirection == SkPathPriv::kUnknown_FirstDirection)
                     ^ foundDir);
             REPORTER_ASSERT(reporter, !foundDir || gRec[i].fExpectedDirection == dir);
             check_convexity(reporter, copy, gRec[i].fExpectedConvexity);
@@ -1781,13 +1781,14 @@
         if (tests[testIndex].fIsRect) {
             SkRect computed, expected;
             bool isClosed;
-            SkPath::Direction direction, cheapDirection;
+            SkPath::Direction direction;
+            SkPathPriv::FirstDirection cheapDirection;
             expected.set(tests[testIndex].fPoints, tests[testIndex].fPointCount);
-            REPORTER_ASSERT(reporter, path.cheapComputeDirection(&cheapDirection));
+            REPORTER_ASSERT(reporter, SkPathPriv::CheapComputeFirstDirection(path, &cheapDirection));
             REPORTER_ASSERT(reporter, path.isRect(&computed, &isClosed, &direction));
             REPORTER_ASSERT(reporter, expected == computed);
             REPORTER_ASSERT(reporter, isClosed == tests[testIndex].fClose);
-            REPORTER_ASSERT(reporter, direction == cheapDirection);
+            REPORTER_ASSERT(reporter, SkPathPriv::AsFirstDirection(direction) == cheapDirection);
         } else {
             SkRect computed;
             computed.set(123, 456, 789, 1011);
@@ -1891,36 +1892,36 @@
     struct IsNestedRectTest {
         SkPoint *fPoints;
         int fPointCount;
-        SkPath::Direction fDirection;
+        SkPathPriv::FirstDirection fDirection;
         bool fClose;
         bool fIsNestedRect; // nests with path.addRect(-1, -1, 2, 2);
     } tests[] = {
-        { r1, SK_ARRAY_COUNT(r1), SkPath::kCW_Direction , true, true },
-        { r2, SK_ARRAY_COUNT(r2), SkPath::kCW_Direction , true, true },
-        { r3, SK_ARRAY_COUNT(r3), SkPath::kCW_Direction , true, true },
-        { r4, SK_ARRAY_COUNT(r4), SkPath::kCW_Direction , true, true },
-        { r5, SK_ARRAY_COUNT(r5), SkPath::kCCW_Direction, true, true },
-        { r6, SK_ARRAY_COUNT(r6), SkPath::kCCW_Direction, true, true },
-        { r7, SK_ARRAY_COUNT(r7), SkPath::kCCW_Direction, true, true },
-        { r8, SK_ARRAY_COUNT(r8), SkPath::kCCW_Direction, true, true },
-        { r9, SK_ARRAY_COUNT(r9), SkPath::kCCW_Direction, true, true },
-        { ra, SK_ARRAY_COUNT(ra), SkPath::kCCW_Direction, true, true },
-        { rb, SK_ARRAY_COUNT(rb), SkPath::kCW_Direction,  true, true },
-        { rc, SK_ARRAY_COUNT(rc), SkPath::kCW_Direction,  true, true },
-        { rd, SK_ARRAY_COUNT(rd), SkPath::kCCW_Direction, true, true },
-        { re, SK_ARRAY_COUNT(re), SkPath::kCW_Direction,  true, true },
+        { r1, SK_ARRAY_COUNT(r1), SkPathPriv::kCW_FirstDirection , true, true },
+        { r2, SK_ARRAY_COUNT(r2), SkPathPriv::kCW_FirstDirection , true, true },
+        { r3, SK_ARRAY_COUNT(r3), SkPathPriv::kCW_FirstDirection , true, true },
+        { r4, SK_ARRAY_COUNT(r4), SkPathPriv::kCW_FirstDirection , true, true },
+        { r5, SK_ARRAY_COUNT(r5), SkPathPriv::kCCW_FirstDirection, true, true },
+        { r6, SK_ARRAY_COUNT(r6), SkPathPriv::kCCW_FirstDirection, true, true },
+        { r7, SK_ARRAY_COUNT(r7), SkPathPriv::kCCW_FirstDirection, true, true },
+        { r8, SK_ARRAY_COUNT(r8), SkPathPriv::kCCW_FirstDirection, true, true },
+        { r9, SK_ARRAY_COUNT(r9), SkPathPriv::kCCW_FirstDirection, true, true },
+        { ra, SK_ARRAY_COUNT(ra), SkPathPriv::kCCW_FirstDirection, true, true },
+        { rb, SK_ARRAY_COUNT(rb), SkPathPriv::kCW_FirstDirection,  true, true },
+        { rc, SK_ARRAY_COUNT(rc), SkPathPriv::kCW_FirstDirection,  true, true },
+        { rd, SK_ARRAY_COUNT(rd), SkPathPriv::kCCW_FirstDirection, true, true },
+        { re, SK_ARRAY_COUNT(re), SkPathPriv::kCW_FirstDirection,  true, true },
 
-        { f1, SK_ARRAY_COUNT(f1), SkPath::kUnknown_Direction, true, false },
-        { f2, SK_ARRAY_COUNT(f2), SkPath::kUnknown_Direction, true, false },
-        { f3, SK_ARRAY_COUNT(f3), SkPath::kUnknown_Direction, true, false },
-        { f4, SK_ARRAY_COUNT(f4), SkPath::kUnknown_Direction, true, false },
-        { f5, SK_ARRAY_COUNT(f5), SkPath::kUnknown_Direction, true, false },
-        { f6, SK_ARRAY_COUNT(f6), SkPath::kUnknown_Direction, true, false },
-        { f7, SK_ARRAY_COUNT(f7), SkPath::kUnknown_Direction, true, false },
-        { f8, SK_ARRAY_COUNT(f8), SkPath::kUnknown_Direction, true, false },
+        { f1, SK_ARRAY_COUNT(f1), SkPathPriv::kUnknown_FirstDirection, true, false },
+        { f2, SK_ARRAY_COUNT(f2), SkPathPriv::kUnknown_FirstDirection, true, false },
+        { f3, SK_ARRAY_COUNT(f3), SkPathPriv::kUnknown_FirstDirection, true, false },
+        { f4, SK_ARRAY_COUNT(f4), SkPathPriv::kUnknown_FirstDirection, true, false },
+        { f5, SK_ARRAY_COUNT(f5), SkPathPriv::kUnknown_FirstDirection, true, false },
+        { f6, SK_ARRAY_COUNT(f6), SkPathPriv::kUnknown_FirstDirection, true, false },
+        { f7, SK_ARRAY_COUNT(f7), SkPathPriv::kUnknown_FirstDirection, true, false },
+        { f8, SK_ARRAY_COUNT(f8), SkPathPriv::kUnknown_FirstDirection, true, false },
 
-        { c1, SK_ARRAY_COUNT(c1), SkPath::kCW_Direction, false, true },
-        { c2, SK_ARRAY_COUNT(c2), SkPath::kCW_Direction, false, true },
+        { c1, SK_ARRAY_COUNT(c1), SkPathPriv::kCW_FirstDirection, false, true },
+        { c2, SK_ARRAY_COUNT(c2), SkPathPriv::kCW_FirstDirection, false, true },
     };
 
     const size_t testCount = SK_ARRAY_COUNT(tests);
@@ -1945,22 +1946,23 @@
                     tests[testIndex].fIsNestedRect == path.isNestedFillRects(NULL));
             if (tests[testIndex].fIsNestedRect) {
                 SkRect expected[2], computed[2];
-                SkPath::Direction expectedDirs[2], computedDirs[2];
+                SkPathPriv::FirstDirection expectedDirs[2];
+                SkPath::Direction computedDirs[2];
                 SkRect testBounds;
                 testBounds.set(tests[testIndex].fPoints, tests[testIndex].fPointCount);
                 expected[0] = SkRect::MakeLTRB(-1, -1, 2, 2);
                 expected[1] = testBounds;
                 if (rectFirst) {
-                    expectedDirs[0] = SkPath::kCW_Direction;
+                    expectedDirs[0] = SkPathPriv::kCW_FirstDirection;
                 } else {
-                    expectedDirs[0] = SkPath::kCCW_Direction;
+                    expectedDirs[0] = SkPathPriv::kCCW_FirstDirection;
                 }
                 expectedDirs[1] = tests[testIndex].fDirection;
                 REPORTER_ASSERT(reporter, path.isNestedFillRects(computed, computedDirs));
                 REPORTER_ASSERT(reporter, expected[0] == computed[0]);
                 REPORTER_ASSERT(reporter, expected[1] == computed[1]);
-                REPORTER_ASSERT(reporter, expectedDirs[0] == computedDirs[0]);
-                REPORTER_ASSERT(reporter, expectedDirs[1] == computedDirs[1]);
+                REPORTER_ASSERT(reporter, expectedDirs[0] == SkPathPriv::AsFirstDirection(computedDirs[0]));
+                REPORTER_ASSERT(reporter, expectedDirs[1] == SkPathPriv::AsFirstDirection(computedDirs[1]));
             }
         }
 
@@ -2233,7 +2235,7 @@
         p1.moveTo(SkPoint::Make(0, 0));
 
         p.transform(matrix, &p1);
-        REPORTER_ASSERT(reporter, p1.cheapIsDirection(SkPath::kCW_Direction));
+        REPORTER_ASSERT(reporter, SkPathPriv::CheapIsFirstDirection(p1, SkPathPriv::kCW_FirstDirection));
     }
 
 
@@ -2245,7 +2247,7 @@
         p1.moveTo(SkPoint::Make(0, 0)); // Make p1 unique (i.e., not empty path)
 
         p.transform(matrix, &p1);
-        REPORTER_ASSERT(reporter, p1.cheapIsDirection(SkPath::kCCW_Direction));
+        REPORTER_ASSERT(reporter, SkPathPriv::CheapIsFirstDirection(p1, SkPathPriv::kCCW_FirstDirection));
     }
 
     {
@@ -2255,7 +2257,7 @@
         p1.moveTo(SkPoint::Make(0, 0)); // Make p1 unique (i.e., not empty path)
 
         p.transform(matrix, &p1);
-        REPORTER_ASSERT(reporter, p1.cheapIsDirection(SkPath::kUnknown_Direction));
+        REPORTER_ASSERT(reporter, SkPathPriv::CheapIsFirstDirection(p1, SkPathPriv::kUnknown_FirstDirection));
     }
 }
 
@@ -2769,10 +2771,10 @@
 static void check_for_circle(skiatest::Reporter* reporter,
                              const SkPath& path,
                              bool expectedCircle,
-                             SkPath::Direction expectedDir) {
+                             SkPathPriv::FirstDirection expectedDir) {
     SkRect rect = SkRect::MakeEmpty();
     REPORTER_ASSERT(reporter, path.isOval(&rect) == expectedCircle);
-    REPORTER_ASSERT(reporter, path.cheapIsDirection(expectedDir));
+    REPORTER_ASSERT(reporter, SkPathPriv::CheapIsFirstDirection(path, expectedDir));
 
     if (expectedCircle) {
         REPORTER_ASSERT(reporter, rect.height() == rect.width());
@@ -2781,25 +2783,25 @@
 
 static void test_circle_skew(skiatest::Reporter* reporter,
                              const SkPath& path,
-                             SkPath::Direction dir) {
+                             SkPathPriv::FirstDirection dir) {
     SkPath tmp;
 
     SkMatrix m;
     m.setSkew(SkIntToScalar(3), SkIntToScalar(5));
     path.transform(m, &tmp);
     // this matrix reverses the direction.
-    if (SkPath::kCCW_Direction == dir) {
-        dir = SkPath::kCW_Direction;
+    if (SkPathPriv::kCCW_FirstDirection == dir) {
+        dir = SkPathPriv::kCW_FirstDirection;
     } else {
-        REPORTER_ASSERT(reporter, SkPath::kCW_Direction == dir);
-        dir = SkPath::kCCW_Direction;
+        REPORTER_ASSERT(reporter, SkPathPriv::kCW_FirstDirection == dir);
+        dir = SkPathPriv::kCCW_FirstDirection;
     }
     check_for_circle(reporter, tmp, false, dir);
 }
 
 static void test_circle_translate(skiatest::Reporter* reporter,
                                   const SkPath& path,
-                                  SkPath::Direction dir) {
+                                  SkPathPriv::FirstDirection dir) {
     SkPath tmp;
 
     // translate at small offset
@@ -2819,7 +2821,7 @@
 
 static void test_circle_rotate(skiatest::Reporter* reporter,
                                const SkPath& path,
-                               SkPath::Direction dir) {
+                               SkPathPriv::FirstDirection dir) {
     for (int angle = 0; angle < 360; ++angle) {
         SkPath tmp;
         SkMatrix m;
@@ -2839,18 +2841,18 @@
 
 static void test_circle_mirror_x(skiatest::Reporter* reporter,
                                  const SkPath& path,
-                                 SkPath::Direction dir) {
+                                 SkPathPriv::FirstDirection dir) {
     SkPath tmp;
     SkMatrix m;
     m.reset();
     m.setScaleX(-SK_Scalar1);
     path.transform(m, &tmp);
 
-    if (SkPath::kCW_Direction == dir) {
-        dir = SkPath::kCCW_Direction;
+    if (SkPathPriv::kCW_FirstDirection == dir) {
+        dir = SkPathPriv::kCCW_FirstDirection;
     } else {
-        REPORTER_ASSERT(reporter, SkPath::kCCW_Direction == dir);
-        dir = SkPath::kCW_Direction;
+        REPORTER_ASSERT(reporter, SkPathPriv::kCCW_FirstDirection == dir);
+        dir = SkPathPriv::kCW_FirstDirection;
     }
 
     check_for_circle(reporter, tmp, true, dir);
@@ -2858,18 +2860,18 @@
 
 static void test_circle_mirror_y(skiatest::Reporter* reporter,
                                  const SkPath& path,
-                                 SkPath::Direction dir) {
+                                 SkPathPriv::FirstDirection dir) {
     SkPath tmp;
     SkMatrix m;
     m.reset();
     m.setScaleY(-SK_Scalar1);
     path.transform(m, &tmp);
 
-    if (SkPath::kCW_Direction == dir) {
-        dir = SkPath::kCCW_Direction;
+    if (SkPathPriv::kCW_FirstDirection == dir) {
+        dir = SkPathPriv::kCCW_FirstDirection;
     } else {
-        REPORTER_ASSERT(reporter, SkPath::kCCW_Direction == dir);
-        dir = SkPath::kCW_Direction;
+        REPORTER_ASSERT(reporter, SkPathPriv::kCCW_FirstDirection == dir);
+        dir = SkPathPriv::kCW_FirstDirection;
     }
 
     check_for_circle(reporter, tmp, true, dir);
@@ -2877,7 +2879,7 @@
 
 static void test_circle_mirror_xy(skiatest::Reporter* reporter,
                                  const SkPath& path,
-                                 SkPath::Direction dir) {
+                                 SkPathPriv::FirstDirection dir) {
     SkPath tmp;
     SkMatrix m;
     m.reset();
@@ -2889,11 +2891,13 @@
 }
 
 static void test_circle_with_direction(skiatest::Reporter* reporter,
-                                       SkPath::Direction dir) {
+                                       SkPath::Direction inDir) {
+    const SkPathPriv::FirstDirection dir = SkPathPriv::AsFirstDirection(inDir);
     SkPath path;
 
     // circle at origin
-    path.addCircle(0, 0, SkIntToScalar(20), dir);
+    path.addCircle(0, 0, SkIntToScalar(20), inDir);
+
     check_for_circle(reporter, path, true, dir);
     test_circle_rotate(reporter, path, dir);
     test_circle_translate(reporter, path, dir);
@@ -2902,7 +2906,8 @@
     // circle at an offset at (10, 10)
     path.reset();
     path.addCircle(SkIntToScalar(10), SkIntToScalar(10),
-                   SkIntToScalar(20), dir);
+                   SkIntToScalar(20), inDir);
+
     check_for_circle(reporter, path, true, dir);
     test_circle_rotate(reporter, path, dir);
     test_circle_translate(reporter, path, dir);
@@ -2918,8 +2923,8 @@
     SkPath rect;
     SkPath empty;
 
-    static const SkPath::Direction kCircleDir = SkPath::kCW_Direction;
-    static const SkPath::Direction kCircleDirOpposite = SkPath::kCCW_Direction;
+    const SkPath::Direction kCircleDir = SkPath::kCW_Direction;
+    const SkPath::Direction kCircleDirOpposite = SkPath::kCCW_Direction;
 
     circle.addCircle(0, 0, SkIntToScalar(10), kCircleDir);
     rect.addRect(SkIntToScalar(5), SkIntToScalar(5),
@@ -2934,17 +2939,17 @@
     // empty + circle (translate)
     path = empty;
     path.addPath(circle, translate);
-    check_for_circle(reporter, path, false, kCircleDir);
+    check_for_circle(reporter, path, false, SkPathPriv::AsFirstDirection(kCircleDir));
 
     // circle + empty (translate)
     path = circle;
     path.addPath(empty, translate);
-    check_for_circle(reporter, path, true, kCircleDir);
+    check_for_circle(reporter, path, true, SkPathPriv::AsFirstDirection(kCircleDir));
 
     // test reverseAddPath
     path = circle;
     path.reverseAddPath(rect);
-    check_for_circle(reporter, path, false, kCircleDirOpposite);
+    check_for_circle(reporter, path, false, SkPathPriv::AsFirstDirection(kCircleDirOpposite));
 }
 
 static void test_circle(skiatest::Reporter* reporter) {
@@ -2955,19 +2960,19 @@
     SkPath path;
     path.addCircle(0, 0, SkIntToScalar(10), SkPath::kCW_Direction);
     path.addCircle(0, 0, SkIntToScalar(20), SkPath::kCW_Direction);
-    check_for_circle(reporter, path, false, SkPath::kCW_Direction);
+    check_for_circle(reporter, path, false, SkPathPriv::kCW_FirstDirection);
 
     // some extra lineTo() would make isOval() fail
     path.reset();
     path.addCircle(0, 0, SkIntToScalar(10), SkPath::kCW_Direction);
     path.lineTo(0, 0);
-    check_for_circle(reporter, path, false, SkPath::kCW_Direction);
+    check_for_circle(reporter, path, false, SkPathPriv::kCW_FirstDirection);
 
     // not back to the original point
     path.reset();
     path.addCircle(0, 0, SkIntToScalar(10), SkPath::kCW_Direction);
     path.setLastPt(SkIntToScalar(5), SkIntToScalar(5));
-    check_for_circle(reporter, path, false, SkPath::kCW_Direction);
+    check_for_circle(reporter, path, false, SkPathPriv::kCW_FirstDirection);
 
     test_circle_with_add_paths(reporter);
 
@@ -3049,7 +3054,7 @@
 static void test_rrect_is_convex(skiatest::Reporter* reporter, SkPath* path,
                                  SkPath::Direction dir) {
     REPORTER_ASSERT(reporter, path->isConvex());
-    REPORTER_ASSERT(reporter, path->cheapIsDirection(dir));
+    REPORTER_ASSERT(reporter, SkPathPriv::CheapIsFirstDirection(*path, SkPathPriv::AsFirstDirection(dir)));
     path->setConvexity(SkPath::kUnknown_Convexity);
     REPORTER_ASSERT(reporter, path->isConvex());
     path->reset();
@@ -3058,7 +3063,7 @@
 static void test_rrect_convexity_is_unknown(skiatest::Reporter* reporter, SkPath* path,
                                  SkPath::Direction dir) {
     REPORTER_ASSERT(reporter, path->isConvex());
-    REPORTER_ASSERT(reporter, path->cheapIsDirection(dir));
+    REPORTER_ASSERT(reporter, SkPathPriv::CheapIsFirstDirection(*path, SkPathPriv::AsFirstDirection(dir)));
     path->setConvexity(SkPath::kUnknown_Convexity);
     REPORTER_ASSERT(reporter, path->getConvexity() == SkPath::kUnknown_Convexity);
     path->reset();
@@ -3154,7 +3159,7 @@
     p.reset();
     p.addArc(oval, 1, 180);
     REPORTER_ASSERT(reporter, p.isConvex());
-    REPORTER_ASSERT(reporter, p.cheapIsDirection(SkPath::kCW_Direction));
+    REPORTER_ASSERT(reporter, SkPathPriv::CheapIsFirstDirection(p, SkPathPriv::kCW_FirstDirection));
     p.setConvexity(SkPath::kUnknown_Convexity);
     REPORTER_ASSERT(reporter, p.isConvex());
 }