add segment types query to SkPath (i.e. does it have any quads)
authorreed@google.com <reed@google.com@2bbb7eff-a529-9590-31e7-b0007b416f81>
Wed, 21 Sep 2011 12:29:05 +0000 (12:29 +0000)
committerreed@google.com <reed@google.com@2bbb7eff-a529-9590-31e7-b0007b416f81>
Wed, 21 Sep 2011 12:29:05 +0000 (12:29 +0000)
git-svn-id: http://skia.googlecode.com/svn/trunk@2292 2bbb7eff-a529-9590-31e7-b0007b416f81

include/core/SkPath.h
src/core/SkPath.cpp
tests/PathTest.cpp

index e8aeb4b..c3bfd8e 100644 (file)
@@ -569,6 +569,19 @@ public:
         this->setLastPt(p.fX, p.fY);
     }
 
+    enum SegmentMask {
+        kLine_SegmentMask   = 1 << 0,
+        kQuad_SegmentMask   = 1 << 1,
+        kCubic_SegmentMask  = 1 << 2
+    };
+
+    /**
+     *  Returns a mask, where each bit corresponding to a SegmentMask is
+     *  set if the path contains 1 or more segments of that type.
+     *  Returns 0 for an empty path (no segments).
+     */
+    uint32_t getSegmentMasks() const { return fSegmentMask; }
+
     enum Verb {
         kMove_Verb,     //!< iter.next returns 1 point
         kLine_Verb,     //!< iter.next returns 2 points
@@ -642,8 +655,9 @@ private:
     SkTDArray<SkPoint>  fPts;
     SkTDArray<uint8_t>  fVerbs;
     mutable SkRect      fBounds;
-    mutable uint8_t     fBoundsIsDirty;
     uint8_t             fFillType;
+    uint8_t             fSegmentMask;
+    mutable uint8_t     fBoundsIsDirty;
     mutable uint8_t     fConvexity;
 #ifdef ANDROID
     uint32_t            fGenerationID;
index e3eda6b..2c363ce 100644 (file)
@@ -90,6 +90,7 @@ static void compute_pt_bounds(SkRect* bounds, const SkTDArray<SkPoint>& pts) {
 
 SkPath::SkPath() : fBoundsIsDirty(true), fFillType(kWinding_FillType) {
     fConvexity = kUnknown_Convexity;
+    fSegmentMask = 0;
 #ifdef ANDROID
     fGenerationID = 0;
 #endif
@@ -118,6 +119,7 @@ SkPath& SkPath::operator=(const SkPath& src) {
         fFillType       = src.fFillType;
         fBoundsIsDirty  = src.fBoundsIsDirty;
         fConvexity      = src.fConvexity;
+        fSegmentMask    = src.fSegmentMask;
         GEN_ID_INC;
     }
     SkDEBUGCODE(this->validate();)
@@ -127,8 +129,14 @@ SkPath& SkPath::operator=(const SkPath& src) {
 bool operator==(const SkPath& a, const SkPath& b) {
     // note: don't need to look at isConvex or bounds, since just comparing the
     // raw data is sufficient.
+
+    // We explicitly check fSegmentMask as a quick-reject. We could skip it,
+    // since it is only a cache of info in the fVerbs, but its a fast way to
+    // notice a difference
+
     return &a == &b ||
-        (a.fFillType == b.fFillType && a.fVerbs == b.fVerbs && a.fPts == b.fPts);
+        (a.fFillType == b.fFillType && a.fSegmentMask == b.fSegmentMask &&
+         a.fVerbs == b.fVerbs && a.fPts == b.fPts);
 }
 
 void SkPath::swap(SkPath& other) {
@@ -141,6 +149,7 @@ void SkPath::swap(SkPath& other) {
         SkTSwap<uint8_t>(fFillType, other.fFillType);
         SkTSwap<uint8_t>(fBoundsIsDirty, other.fBoundsIsDirty);
         SkTSwap<uint8_t>(fConvexity, other.fConvexity);
+        SkTSwap<uint8_t>(fSegmentMask, other.fSegmentMask);
         GEN_ID_INC;
     }
 }
@@ -159,6 +168,7 @@ void SkPath::reset() {
     GEN_ID_INC;
     fBoundsIsDirty = true;
     fConvexity = kUnknown_Convexity;
+    fSegmentMask = 0;
 }
 
 void SkPath::rewind() {
@@ -169,6 +179,7 @@ void SkPath::rewind() {
     GEN_ID_INC;
     fBoundsIsDirty = true;
     fConvexity = kUnknown_Convexity;
+    fSegmentMask = 0;
 }
 
 bool SkPath::isEmpty() const {
@@ -405,6 +416,7 @@ void SkPath::lineTo(SkScalar x, SkScalar y) {
     }
     fPts.append()->set(x, y);
     *fVerbs.append() = kLine_Verb;
+    fSegmentMask |= kLine_SegmentMask;
 
     GEN_ID_INC;
     DIRTY_AFTER_EDIT;
@@ -428,6 +440,7 @@ void SkPath::quadTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2) {
     pts[0].set(x1, y1);
     pts[1].set(x2, y2);
     *fVerbs.append() = kQuad_Verb;
+    fSegmentMask |= kQuad_SegmentMask;
 
     GEN_ID_INC;
     DIRTY_AFTER_EDIT;
@@ -452,6 +465,7 @@ void SkPath::cubicTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2,
     pts[1].set(x2, y2);
     pts[2].set(x3, y3);
     *fVerbs.append() = kCubic_Verb;
+    fSegmentMask |= kCubic_SegmentMask;
 
     GEN_ID_INC;
     DIRTY_AFTER_EDIT;
@@ -1414,6 +1428,21 @@ void SkPath::validate() const {
             fBounds.contains(bounds);
         }
     }
+
+    uint32_t mask = 0;
+    for (int i = 0; i < fVerbs.count(); i++) {
+        switch (fVerbs[i]) {
+            case kLine_Verb:
+                mask |= kLine_SegmentMask;
+                break;
+            case kQuad_Verb:
+                mask |= kQuad_SegmentMask;
+                break;
+            case kCubic_Verb:
+                mask |= kCubic_SegmentMask;
+        }
+    }
+    SkASSERT(mask == fSegmentMask);
 }
 #endif
 
index 4c0113e..45e7d15 100644 (file)
@@ -438,6 +438,8 @@ static void test_isRect(skiatest::Reporter* reporter) {
     REPORTER_ASSERT(reporter, fail ^ path1.isRect(0));
 }
 
+#define kCurveSegmentMask   (SkPath::kQuad_SegmentMask | SkPath::kCubic_SegmentMask)
+
 void TestPath(skiatest::Reporter* reporter);
 void TestPath(skiatest::Reporter* reporter) {
     {
@@ -454,6 +456,7 @@ void TestPath(skiatest::Reporter* reporter) {
     SkRect  bounds, bounds2;
 
     REPORTER_ASSERT(reporter, p.isEmpty());
+    REPORTER_ASSERT(reporter, 0 == p.getSegmentMasks());
     REPORTER_ASSERT(reporter, p.isConvex());
     REPORTER_ASSERT(reporter, p.getFillType() == SkPath::kWinding_FillType);
     REPORTER_ASSERT(reporter, !p.isInverseFillType());
@@ -466,14 +469,20 @@ void TestPath(skiatest::Reporter* reporter) {
 
     p.addRoundRect(bounds, SK_Scalar1, SK_Scalar1);
     check_convex_bounds(reporter, p, bounds);
+    // we have quads or cubics
+    REPORTER_ASSERT(reporter, p.getSegmentMasks() & kCurveSegmentMask);
 
     p.reset();
+    REPORTER_ASSERT(reporter, 0 == p.getSegmentMasks());
+
     p.addOval(bounds);
     check_convex_bounds(reporter, p, bounds);
 
     p.reset();
     p.addRect(bounds);
     check_convex_bounds(reporter, p, bounds);
+    // we have only lines
+    REPORTER_ASSERT(reporter, SkPath::kLine_SegmentMask == p.getSegmentMasks());
 
     REPORTER_ASSERT(reporter, p != p2);
     REPORTER_ASSERT(reporter, !(p == p2));
@@ -510,6 +519,17 @@ void TestPath(skiatest::Reporter* reporter) {
     test_convexity(reporter);
     test_convexity2(reporter);
     test_close(reporter);
+
+    p.reset();
+    p.moveTo(0, 0);
+    p.quadTo(100, 100, 200, 200);
+    REPORTER_ASSERT(reporter, SkPath::kQuad_SegmentMask == p.getSegmentMasks());
+    p.cubicTo(100, 100, 200, 200, 300, 300);
+    REPORTER_ASSERT(reporter, kCurveSegmentMask == p.getSegmentMasks());
+    p.reset();
+    p.moveTo(0, 0);
+    p.cubicTo(100, 100, 200, 200, 300, 300);
+    REPORTER_ASSERT(reporter, SkPath::kCubic_SegmentMask == p.getSegmentMasks());
 }
 
 #include "TestClassDef.h"