Track oval in SkPath

Committed on behalf of Guanqun.Lu@gmail.com

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



git-svn-id: http://skia.googlecode.com/svn/trunk@3716 2bbb7eff-a529-9590-31e7-b0007b416f81
diff --git a/src/core/SkPath.cpp b/src/core/SkPath.cpp
index 7f58ae3..de34fe8 100644
--- a/src/core/SkPath.cpp
+++ b/src/core/SkPath.cpp
@@ -32,6 +32,21 @@
     return SkPath::kDone_Verb == iter.next(pts);
 }
 
+class SkAutoDisableOvalCheck {
+public:
+    SkAutoDisableOvalCheck(SkPath* path) : fPath(path) {
+        fSaved = fPath->fIsOval;
+    }
+
+    ~SkAutoDisableOvalCheck() {
+        fPath->fIsOval = fSaved;
+    }
+
+private:
+    SkPath* fPath;
+    bool    fSaved;
+};
+
 /*  This guy's constructor/destructor bracket a path editing operation. It is
     used when we know the bounds of the amount we are going to add to the path
     (usually a new contour, but not required).
@@ -119,6 +134,7 @@
     fConvexity = kUnknown_Convexity;
     fSegmentMask = 0;
     fLastMoveToIndex = INITIAL_LASTMOVETOINDEX_VALUE;
+    fIsOval = false;
 #ifdef SK_BUILD_FOR_ANDROID
     fGenerationID = 0;
     fSourcePath = NULL;
@@ -151,6 +167,7 @@
         fConvexity      = src.fConvexity;
         fSegmentMask    = src.fSegmentMask;
         fLastMoveToIndex = src.fLastMoveToIndex;
+        fIsOval         = src.fIsOval;
         GEN_ID_INC;
     }
     SkDEBUGCODE(this->validate();)
@@ -182,6 +199,7 @@
         SkTSwap<uint8_t>(fConvexity, other.fConvexity);
         SkTSwap<uint8_t>(fSegmentMask, other.fSegmentMask);
         SkTSwap<int>(fLastMoveToIndex, other.fLastMoveToIndex);
+        SkTSwap<SkBool8>(fIsOval, other.fIsOval);
         GEN_ID_INC;
     }
 }
@@ -210,6 +228,7 @@
     fConvexity = kUnknown_Convexity;
     fSegmentMask = 0;
     fLastMoveToIndex = INITIAL_LASTMOVETOINDEX_VALUE;
+    fIsOval = false;
 }
 
 void SkPath::rewind() {
@@ -222,6 +241,7 @@
     fBoundsIsDirty = true;
     fSegmentMask = 0;
     fLastMoveToIndex = INITIAL_LASTMOVETOINDEX_VALUE;
+    fIsOval = false;
 }
 
 bool SkPath::isEmpty() const {
@@ -388,6 +408,7 @@
     if (count == 0) {
         this->moveTo(x, y);
     } else {
+        fIsOval = false;
         fPts[count - 1].set(x, y);
         GEN_ID_INC;
     }
@@ -415,6 +436,7 @@
     do {                                 \
         fBoundsIsDirty = true;           \
         fConvexity = kUnknown_Convexity; \
+        fIsOval = false;                 \
     } while (0)
 
 #define DIRTY_AFTER_EDIT_NO_CONVEXITY_CHANGE    \
@@ -729,7 +751,31 @@
     this->close();
 }
 
+bool SkPath::hasOnlyMoveTos() const {
+    const uint8_t* verbs = fVerbs.begin();
+    const uint8_t* verbStop = fVerbs.end();
+    while (verbs != verbStop) {
+        if (*verbs == kLine_Verb ||
+            *verbs == kQuad_Verb ||
+            *verbs == kCubic_Verb) {
+            return false;
+        }
+        ++verbs;
+    }
+    return true;
+}
+
 void SkPath::addOval(const SkRect& oval, Direction dir) {
+    /* If addOval() is called after previous moveTo(),
+       this path is still marked as an oval. This is used to
+       fit into WebKit's calling sequences.
+       We can't simply check isEmpty() in this case, as additional
+       moveTo() would mark the path non empty.
+     */
+    fIsOval = hasOnlyMoveTos();
+
+    SkAutoDisableOvalCheck adoc(this);
+
     SkAutoPathBoundsUpdate apbu(this, oval);
 
     SkScalar    cx = oval.centerX();
@@ -795,6 +841,14 @@
     this->close();
 }
 
+bool SkPath::isOval(SkRect* rect) const {
+    if (fIsOval && rect) {
+        *rect = getBounds();
+    }
+
+    return fIsOval;
+}
+
 void SkPath::addCircle(SkScalar x, SkScalar y, SkScalar r, Direction dir) {
     if (r > 0) {
         SkRect  rect;
@@ -970,6 +1024,8 @@
 void SkPath::addPath(const SkPath& path, const SkMatrix& matrix) {
     this->incReserve(path.fPts.count());
 
+    fIsOval = false;
+
     RawIter iter(path);
     SkPoint pts[4];
     Verb    verb;
@@ -1023,6 +1079,8 @@
 
     this->incReserve(vcount);
 
+    fIsOval = false;
+
     const uint8_t*  verbs = path.fVerbs.begin();
     const SkPoint*  pts = path.fPts.begin() + 1;    // 1 for the initial moveTo
 
@@ -1055,6 +1113,8 @@
 
     this->incReserve(vcount);
 
+    fIsOval = false;
+
     const uint8_t*  verbs = path.fVerbs.begin();
     const SkPoint*  pts = path.fPts.begin();
 
@@ -1095,6 +1155,8 @@
     const uint8_t* startVerbs = src.fVerbs.begin();
     const uint8_t* verbs = src.fVerbs.end();
 
+    fIsOval = false;
+
     bool needMove = true;
     bool needClose = false;
     while (verbs > startVerbs) {
@@ -1228,8 +1290,16 @@
             dst->fFillType = fFillType;
             dst->fSegmentMask = fSegmentMask;
             dst->fConvexity = fConvexity;
+            dst->fIsOval = fIsOval;
         }
+
         matrix.mapPoints(dst->fPts.begin(), fPts.begin(), fPts.count());
+
+        if (fIsOval) {
+            // It's an oval only if it stays a rect.
+            dst->fIsOval = matrix.rectStaysRect();
+        }
+
         SkDEBUGCODE(dst->validate();)
     }
 }