aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--include/core/SkPath.h16
-rw-r--r--src/core/SkPath.cpp31
-rw-r--r--tests/PathTest.cpp20
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"