diff options
-rw-r--r-- | include/core/SkPath.h | 16 | ||||
-rw-r--r-- | src/core/SkPath.cpp | 31 | ||||
-rw-r--r-- | tests/PathTest.cpp | 20 |
3 files changed, 65 insertions, 2 deletions
diff --git a/include/core/SkPath.h b/include/core/SkPath.h index e8aeb4bd37..c3bfd8e0cf 100644 --- a/include/core/SkPath.h +++ b/include/core/SkPath.h @@ -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; diff --git a/src/core/SkPath.cpp b/src/core/SkPath.cpp index e3eda6b7f4..2c363ce101 100644 --- a/src/core/SkPath.cpp +++ b/src/core/SkPath.cpp @@ -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 diff --git a/tests/PathTest.cpp b/tests/PathTest.cpp index 4c0113e978..45e7d15457 100644 --- a/tests/PathTest.cpp +++ b/tests/PathTest.cpp @@ -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" |