Add the ability to iterate through a path without modification. This change is
required by WebKit SVG in order to correctly draw markers and endcaps.
BUG=415
TEST=TestPath in the unit tests
Review URL: http://codereview.appspot.com/5505097
git-svn-id: http://skia.googlecode.com/svn/trunk@2962 2bbb7eff-a529-9590-31e7-b0007b416f81
diff --git a/gm/degeneratesegments.cpp b/gm/degeneratesegments.cpp
index 99dd4ad..63d9dba 100644
--- a/gm/degeneratesegments.cpp
+++ b/gm/degeneratesegments.cpp
@@ -31,8 +31,8 @@
typedef SkPoint (*AddSegmentFunc)(SkPath&, SkPoint&);
// We need to use explicit commands here, instead of addPath, because we
- // do not want the SkPath::Iter used in addPath to remove the degenerate
- // segments before we send th epath off for drawing.
+ // do not want the moveTo that is added at the beginning of a path to
+ // appear in the appended path.
static SkPoint AddMove(SkPath& path, SkPoint& startPt) {
SkPoint moveToPt = startPt + SkPoint::Make(0, 10*SK_Scalar1);
path.moveTo(moveToPt);
@@ -207,52 +207,52 @@
}
virtual void onDraw(SkCanvas* canvas) {
- static const AddSegmentFunc gSegmentFunctions[] = {
- AddMove,
- AddMoveClose,
- AddDegenLine,
- AddMoveDegenLine,
- AddMoveDegenLineClose,
- AddDegenQuad,
- AddMoveDegenQuad,
- AddMoveDegenQuadClose,
- AddDegenCubic,
- AddMoveDegenCubic,
- AddMoveDegenCubicClose,
- AddClose,
- AddLine,
- AddMoveLine,
- AddMoveLineClose,
- AddQuad,
- AddMoveQuad,
- AddMoveQuadClose,
- AddCubic,
- AddMoveCubic,
- AddMoveCubicClose
- };
- static const char* gSegmentNames[] = {
- "Move",
- "MoveClose",
- "DegenLine",
- "MoveDegenLine",
- "MoveDegenLineClose",
- "DegenQuad",
- "MoveDegenQuad",
- "MoveDegenQuadClose",
- "DegenCubic",
- "MoveDegenCubic",
- "MoveDegenCubicClose",
- "Close",
- "Line",
- "MoveLine",
- "MoveLineClose",
- "Quad",
- "MoveQuad",
- "MoveQuadClose",
- "Cubic",
- "MoveCubic",
- "MoveCubicClose"
- };
+ static const AddSegmentFunc gSegmentFunctions[] = {
+ AddMove,
+ AddMoveClose,
+ AddDegenLine,
+ AddMoveDegenLine,
+ AddMoveDegenLineClose,
+ AddDegenQuad,
+ AddMoveDegenQuad,
+ AddMoveDegenQuadClose,
+ AddDegenCubic,
+ AddMoveDegenCubic,
+ AddMoveDegenCubicClose,
+ AddClose,
+ AddLine,
+ AddMoveLine,
+ AddMoveLineClose,
+ AddQuad,
+ AddMoveQuad,
+ AddMoveQuadClose,
+ AddCubic,
+ AddMoveCubic,
+ AddMoveCubicClose
+ };
+ static const char* gSegmentNames[] = {
+ "Move",
+ "MoveClose",
+ "DegenLine",
+ "MoveDegenLine",
+ "MoveDegenLineClose",
+ "DegenQuad",
+ "MoveDegenQuad",
+ "MoveDegenQuadClose",
+ "DegenCubic",
+ "MoveDegenCubic",
+ "MoveDegenCubicClose",
+ "Close",
+ "Line",
+ "MoveLine",
+ "MoveLineClose",
+ "Quad",
+ "MoveQuad",
+ "MoveQuadClose",
+ "Cubic",
+ "MoveCubic",
+ "MoveCubicClose"
+ };
struct FillAndName {
SkPath::FillType fFill;
diff --git a/include/core/SkPath.h b/include/core/SkPath.h
index 98c79a6..cbbccd5 100644
--- a/include/core/SkPath.h
+++ b/include/core/SkPath.h
@@ -676,6 +676,31 @@
void consumeDegenerateSegments();
};
+ /** Iterate through the verbs in the path, providing the associated points.
+ */
+ class SK_API RawIter {
+ public:
+ RawIter();
+ RawIter(const SkPath&);
+
+ void setPath(const SkPath&);
+
+ /** Return the next verb in this iteration of the path. When all
+ segments have been visited, return kDone_Verb.
+
+ @param pts The points representing the current verb and/or segment
+ @return The verb for the current segment
+ */
+ Verb next(SkPoint pts[4]);
+
+ private:
+ const SkPoint* fPts;
+ const uint8_t* fVerbs;
+ const uint8_t* fVerbStop;
+ SkPoint fMoveTo;
+ SkPoint fLastPt;
+ };
+
void dump(bool forceClose, const char title[] = NULL) const;
void dump() const;
diff --git a/src/core/SkPath.cpp b/src/core/SkPath.cpp
index 22860bf..75a1e40 100644
--- a/src/core/SkPath.cpp
+++ b/src/core/SkPath.cpp
@@ -935,7 +935,7 @@
void SkPath::addPath(const SkPath& path, const SkMatrix& matrix) {
this->incReserve(path.fPts.count());
- Iter iter(path, false);
+ RawIter iter(path);
SkPoint pts[4];
Verb verb;
@@ -1447,6 +1447,82 @@
///////////////////////////////////////////////////////////////////////////////
+SkPath::RawIter::RawIter() {
+#ifdef SK_DEBUG
+ fPts = NULL;
+ fMoveTo.fX = fMoveTo.fY = fLastPt.fX = fLastPt.fY = 0;
+#endif
+ // need to init enough to make next() harmlessly return kDone_Verb
+ fVerbs = NULL;
+ fVerbStop = NULL;
+}
+
+SkPath::RawIter::RawIter(const SkPath& path) {
+ this->setPath(path);
+}
+
+void SkPath::RawIter::setPath(const SkPath& path) {
+ fPts = path.fPts.begin();
+ fVerbs = path.fVerbs.begin();
+ fVerbStop = path.fVerbs.end();
+ fMoveTo.fX = fMoveTo.fY = 0;
+ fLastPt.fX = fLastPt.fY = 0;
+}
+
+SkPath::Verb SkPath::RawIter::next(SkPoint pts[4]) {
+ if (fVerbs == fVerbStop) {
+ return kDone_Verb;
+ }
+
+ unsigned verb = *fVerbs++;
+ const SkPoint* srcPts = fPts;
+
+ switch (verb) {
+ case kMove_Verb:
+ if (pts) {
+ pts[0] = *srcPts;
+ }
+ fMoveTo = srcPts[0];
+ fLastPt = fMoveTo;
+ srcPts += 1;
+ break;
+ case kLine_Verb:
+ if (pts) {
+ pts[0] = fLastPt;
+ pts[1] = srcPts[0];
+ }
+ fLastPt = srcPts[0];
+ srcPts += 1;
+ break;
+ case kQuad_Verb:
+ if (pts) {
+ pts[0] = fLastPt;
+ memcpy(&pts[1], srcPts, 2 * sizeof(SkPoint));
+ }
+ fLastPt = srcPts[1];
+ srcPts += 2;
+ break;
+ case kCubic_Verb:
+ if (pts) {
+ pts[0] = fLastPt;
+ memcpy(&pts[1], srcPts, 3 * sizeof(SkPoint));
+ }
+ fLastPt = srcPts[2];
+ srcPts += 3;
+ break;
+ case kClose_Verb:
+ fLastPt = fMoveTo;
+ if (pts) {
+ pts[0] = fMoveTo;
+ }
+ break;
+ }
+ fPts = srcPts;
+ return (Verb)verb;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
/*
Format in flattened buffer: [ptCount, verbCount, pts[], verbs[]]
*/
@@ -1477,7 +1553,6 @@
}
///////////////////////////////////////////////////////////////////////////////
-///////////////////////////////////////////////////////////////////////////////
void SkPath::dump(bool forceClose, const char title[]) const {
Iter iter(*this, forceClose);
diff --git a/tests/PathTest.cpp b/tests/PathTest.cpp
index 407fcf4..270fa68 100644
--- a/tests/PathTest.cpp
+++ b/tests/PathTest.cpp
@@ -9,6 +9,7 @@
#include "SkPaint.h"
#include "SkPath.h"
#include "SkParse.h"
+#include "SkRandom.h"
#include "SkReader32.h"
#include "SkSize.h"
#include "SkWriter32.h"
@@ -523,21 +524,28 @@
}
static void test_zero_length_paths(skiatest::Reporter* reporter) {
- SkPath p;
- SkRect bounds;
+ SkPath p;
+ SkPoint pt;
+ SkRect bounds;
// Lone moveTo case
p.moveTo(SK_Scalar1, SK_Scalar1);
- bounds.set(0, 0, 0, 0);
REPORTER_ASSERT(reporter, !p.isEmpty());
REPORTER_ASSERT(reporter, 1 == p.countPoints());
+ p.getLastPt(&pt);
+ REPORTER_ASSERT(reporter, pt.fX == SK_Scalar1);
+ REPORTER_ASSERT(reporter, pt.fY == SK_Scalar1);
+ bounds.set(0, 0, 0, 0);
REPORTER_ASSERT(reporter, bounds == p.getBounds());
// MoveTo-MoveTo case
p.moveTo(SK_Scalar1*2, SK_Scalar1);
- bounds.set(SK_Scalar1, SK_Scalar1, 2*SK_Scalar1, SK_Scalar1);
REPORTER_ASSERT(reporter, !p.isEmpty());
REPORTER_ASSERT(reporter, 2 == p.countPoints());
+ p.getLastPt(&pt);
+ REPORTER_ASSERT(reporter, pt.fX == SK_Scalar1*2);
+ REPORTER_ASSERT(reporter, pt.fY == SK_Scalar1);
+ bounds.set(SK_Scalar1, SK_Scalar1, 2*SK_Scalar1, SK_Scalar1);
REPORTER_ASSERT(reporter, bounds == p.getBounds());
// moveTo-close case
@@ -659,6 +667,302 @@
#define kCurveSegmentMask (SkPath::kQuad_SegmentMask | SkPath::kCubic_SegmentMask)
+static void test_segment_masks(skiatest::Reporter* reporter) {
+ SkPath p;
+ p.moveTo(0, 0);
+ p.quadTo(100, 100, 200, 200);
+ REPORTER_ASSERT(reporter, SkPath::kQuad_SegmentMask == p.getSegmentMasks());
+ REPORTER_ASSERT(reporter, !p.isEmpty());
+ p.cubicTo(100, 100, 200, 200, 300, 300);
+ REPORTER_ASSERT(reporter, kCurveSegmentMask == p.getSegmentMasks());
+ REPORTER_ASSERT(reporter, !p.isEmpty());
+ p.reset();
+ p.moveTo(0, 0);
+ p.cubicTo(100, 100, 200, 200, 300, 300);
+ REPORTER_ASSERT(reporter, SkPath::kCubic_SegmentMask == p.getSegmentMasks());
+ REPORTER_ASSERT(reporter, !p.isEmpty());
+}
+
+static void test_iter(skiatest::Reporter* reporter) {
+ SkPath p;
+ SkPoint pts[4];
+
+ // Test an iterator with no path
+ SkPath::Iter noPathIter;
+ REPORTER_ASSERT(reporter, noPathIter.next(pts) == SkPath::kDone_Verb);
+ // Test that setting an empty path works
+ noPathIter.setPath(p, false);
+ REPORTER_ASSERT(reporter, noPathIter.next(pts) == SkPath::kDone_Verb);
+ // Test that close path makes no difference for an empty path
+ noPathIter.setPath(p, true);
+ REPORTER_ASSERT(reporter, noPathIter.next(pts) == SkPath::kDone_Verb);
+
+ // Test an iterator with an initial empty path
+ SkPath::Iter iter(p, false);
+ REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb);
+
+ // Test that close path makes no difference
+ SkPath::Iter forceCloseIter(p, true);
+ REPORTER_ASSERT(reporter, forceCloseIter.next(pts) == SkPath::kDone_Verb);
+
+ // Test that a move-only path produces nothing when iterated.
+ p.moveTo(SK_Scalar1, 0);
+ iter.setPath(p, false);
+ REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb);
+
+ // No matter how many moves we add, we should still get nothing back.
+ p.moveTo(SK_Scalar1*2, 0);
+ p.moveTo(SK_Scalar1*3, 0);
+ p.moveTo(SK_Scalar1*4, 0);
+ p.moveTo(SK_Scalar1*5, 0);
+ iter.setPath(p, false);
+ REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb);
+
+ // Nor should force closing
+ forceCloseIter.setPath(p, true);
+ REPORTER_ASSERT(reporter, forceCloseIter.next(pts) == SkPath::kDone_Verb);
+
+ // Initial closes should be ignored
+ p.reset();
+ p.close();
+ iter.setPath(p, false);
+ REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb);
+ // Even if force closed
+ forceCloseIter.setPath(p, true);
+ REPORTER_ASSERT(reporter, forceCloseIter.next(pts) == SkPath::kDone_Verb);
+
+ // Move/close sequences should also be ignored
+ p.reset();
+ p.close();
+ p.moveTo(SK_Scalar1, 0);
+ p.close();
+ p.close();
+ p.moveTo(SK_Scalar1*2, 0);
+ p.close();
+ p.moveTo(SK_Scalar1*3, 0);
+ p.moveTo(SK_Scalar1*4, 0);
+ p.close();
+ iter.setPath(p, false);
+ REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb);
+ // Even if force closed
+ forceCloseIter.setPath(p, true);
+ REPORTER_ASSERT(reporter, forceCloseIter.next(pts) == SkPath::kDone_Verb);
+
+ // The GM degeneratesegments.cpp test is more extensive
+}
+
+static void test_raw_iter(skiatest::Reporter* reporter) {
+ SkPath p;
+ SkPoint pts[4];
+
+ // Test an iterator with no path
+ SkPath::RawIter noPathIter;
+ REPORTER_ASSERT(reporter, noPathIter.next(pts) == SkPath::kDone_Verb);
+ // Test that setting an empty path works
+ noPathIter.setPath(p);
+ REPORTER_ASSERT(reporter, noPathIter.next(pts) == SkPath::kDone_Verb);
+
+ // Test an iterator with an initial empty path
+ SkPath::RawIter iter(p);
+ REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb);
+
+ // Test that a move-only path returns the move.
+ p.moveTo(SK_Scalar1, 0);
+ iter.setPath(p);
+ REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb);
+ REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1);
+ REPORTER_ASSERT(reporter, pts[0].fY == 0);
+ REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb);
+
+ // No matter how many moves we add, we should get them all back
+ p.moveTo(SK_Scalar1*2, SK_Scalar1);
+ p.moveTo(SK_Scalar1*3, SK_Scalar1*2);
+ iter.setPath(p);
+ REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb);
+ REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1);
+ REPORTER_ASSERT(reporter, pts[0].fY == 0);
+ REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb);
+ REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1*2);
+ REPORTER_ASSERT(reporter, pts[0].fY == SK_Scalar1);
+ REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb);
+ REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1*3);
+ REPORTER_ASSERT(reporter, pts[0].fY == SK_Scalar1*2);
+ REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb);
+
+ // Initial close is never ever stored
+ p.reset();
+ p.close();
+ iter.setPath(p);
+ REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb);
+
+ // Move/close sequences
+ p.reset();
+ p.close(); // Not stored, no purpose
+ p.moveTo(SK_Scalar1, 0);
+ p.close();
+ p.close(); // Not stored, no purpose
+ p.moveTo(SK_Scalar1*2, SK_Scalar1);
+ p.close();
+ p.moveTo(SK_Scalar1*3, SK_Scalar1*2);
+ p.moveTo(SK_Scalar1*4, SK_Scalar1*3);
+ p.close();
+ iter.setPath(p);
+ REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb);
+ REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1);
+ REPORTER_ASSERT(reporter, pts[0].fY == 0);
+ REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kClose_Verb);
+ REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1);
+ REPORTER_ASSERT(reporter, pts[0].fY == 0);
+ REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb);
+ REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1*2);
+ REPORTER_ASSERT(reporter, pts[0].fY == SK_Scalar1);
+ REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kClose_Verb);
+ REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1*2);
+ REPORTER_ASSERT(reporter, pts[0].fY == SK_Scalar1);
+ REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb);
+ REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1*3);
+ REPORTER_ASSERT(reporter, pts[0].fY == SK_Scalar1*2);
+ REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb);
+ REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1*4);
+ REPORTER_ASSERT(reporter, pts[0].fY == SK_Scalar1*3);
+ REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kClose_Verb);
+ REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1*4);
+ REPORTER_ASSERT(reporter, pts[0].fY == SK_Scalar1*3);
+ REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb);
+
+ // Generate random paths and verify
+ SkPoint randomPts[25];
+ for (int i = 0; i < 5; ++i) {
+ for (int j = 0; j < 5; ++j) {
+ randomPts[i*5+j].set(SK_Scalar1*i, SK_Scalar1*j);
+ }
+ }
+
+ // Max of 10 segments, max 3 points per segment
+ SkRandom rand(9876543);
+ SkPoint expectedPts[31]; // May have leading moveTo
+ SkPath::Verb expectedVerbs[11]; // May have leading moveTo
+ SkPath::Verb nextVerb;
+ for (int i = 0; i < 500; ++i) {
+ p.reset();
+ bool lastWasClose = true;
+ bool haveMoveTo = false;
+ int numPoints = 0;
+ int numVerbs = (rand.nextU() >> 16) % 10;
+ int numIterVerbs = 0;
+ for (int j = 0; j < numVerbs; ++j) {
+ do {
+ nextVerb = static_cast<SkPath::Verb>((rand.nextU() >> 16) % SkPath::kDone_Verb);
+ } while (lastWasClose && nextVerb == SkPath::kClose_Verb);
+ int numRequiredPts;
+ switch (nextVerb) {
+ case SkPath::kMove_Verb:
+ expectedPts[numPoints] = randomPts[(rand.nextU() >> 16) % 25];
+ p.moveTo(expectedPts[numPoints]);
+ numPoints += 1;
+ lastWasClose = false;
+ haveMoveTo = true;
+ break;
+ case SkPath::kLine_Verb:
+ if (!haveMoveTo) {
+ expectedPts[numPoints++].set(0, 0);
+ expectedVerbs[numIterVerbs++] = SkPath::kMove_Verb;
+ haveMoveTo = true;
+ }
+ expectedPts[numPoints] = randomPts[(rand.nextU() >> 16) % 25];
+ p.lineTo(expectedPts[numPoints]);
+ numPoints += 1;
+ lastWasClose = false;
+ break;
+ case SkPath::kQuad_Verb:
+ if (!haveMoveTo) {
+ expectedPts[numPoints++].set(0, 0);
+ expectedVerbs[numIterVerbs++] = SkPath::kMove_Verb;
+ haveMoveTo = true;
+ }
+ expectedPts[numPoints] = randomPts[(rand.nextU() >> 16) % 25];
+ expectedPts[numPoints + 1] = randomPts[(rand.nextU() >> 16) % 25];
+ p.quadTo(expectedPts[numPoints], expectedPts[numPoints + 1]);
+ numPoints += 2;
+ lastWasClose = false;
+ break;
+ case SkPath::kCubic_Verb:
+ if (!haveMoveTo) {
+ expectedPts[numPoints++].set(0, 0);
+ expectedVerbs[numIterVerbs++] = SkPath::kMove_Verb;
+ haveMoveTo = true;
+ }
+ expectedPts[numPoints] = randomPts[(rand.nextU() >> 16) % 25];
+ expectedPts[numPoints + 1] = randomPts[(rand.nextU() >> 16) % 25];
+ expectedPts[numPoints + 2] = randomPts[(rand.nextU() >> 16) % 25];
+ p.cubicTo(expectedPts[numPoints], expectedPts[numPoints + 1],
+ expectedPts[numPoints + 2]);
+ numPoints += 3;
+ lastWasClose = false;
+ break;
+ case SkPath::kClose_Verb:
+ p.close();
+ lastWasClose = true;
+ break;
+ default:;
+ }
+ expectedVerbs[numIterVerbs++] = nextVerb;
+ }
+
+ iter.setPath(p);
+ numVerbs = numIterVerbs;
+ numIterVerbs = 0;
+ int numIterPts = 0;
+ SkPoint lastMoveTo;
+ SkPoint lastPt;
+ lastMoveTo.set(0, 0);
+ lastPt.set(0, 0);
+ while ((nextVerb = iter.next(pts)) != SkPath::kDone_Verb) {
+ REPORTER_ASSERT(reporter, nextVerb == expectedVerbs[numIterVerbs]);
+ numIterVerbs++;
+ switch (nextVerb) {
+ case SkPath::kMove_Verb:
+ REPORTER_ASSERT(reporter, numIterPts < numPoints);
+ REPORTER_ASSERT(reporter, pts[0] == expectedPts[numIterPts]);
+ lastPt = lastMoveTo = pts[0];
+ numIterPts += 1;
+ break;
+ case SkPath::kLine_Verb:
+ REPORTER_ASSERT(reporter, numIterPts < numPoints + 1);
+ REPORTER_ASSERT(reporter, pts[0] == lastPt);
+ REPORTER_ASSERT(reporter, pts[1] == expectedPts[numIterPts]);
+ lastPt = pts[1];
+ numIterPts += 1;
+ break;
+ case SkPath::kQuad_Verb:
+ REPORTER_ASSERT(reporter, numIterPts < numPoints + 2);
+ REPORTER_ASSERT(reporter, pts[0] == lastPt);
+ REPORTER_ASSERT(reporter, pts[1] == expectedPts[numIterPts]);
+ REPORTER_ASSERT(reporter, pts[2] == expectedPts[numIterPts + 1]);
+ lastPt = pts[2];
+ numIterPts += 2;
+ break;
+ case SkPath::kCubic_Verb:
+ REPORTER_ASSERT(reporter, numIterPts < numPoints + 3);
+ REPORTER_ASSERT(reporter, pts[0] == lastPt);
+ REPORTER_ASSERT(reporter, pts[1] == expectedPts[numIterPts]);
+ REPORTER_ASSERT(reporter, pts[2] == expectedPts[numIterPts + 1]);
+ REPORTER_ASSERT(reporter, pts[3] == expectedPts[numIterPts + 2]);
+ lastPt = pts[3];
+ numIterPts += 3;
+ break;
+ case SkPath::kClose_Verb:
+ REPORTER_ASSERT(reporter, pts[0] == lastMoveTo);
+ lastPt = lastMoveTo;
+ break;
+ default:;
+ }
+ }
+ REPORTER_ASSERT(reporter, numIterPts == numPoints);
+ REPORTER_ASSERT(reporter, numIterVerbs == numVerbs);
+ }
+}
+
void TestPath(skiatest::Reporter* reporter);
void TestPath(skiatest::Reporter* reporter) {
{
@@ -734,35 +1038,16 @@
REPORTER_ASSERT(reporter, !p.isRect(NULL));
test_isRect(reporter);
- SkPoint pt;
-
- p.moveTo(SK_Scalar1, 0);
- p.getLastPt(&pt);
- REPORTER_ASSERT(reporter, pt.fX == SK_Scalar1);
- REPORTER_ASSERT(reporter, !p.isEmpty());
-
test_zero_length_paths(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());
- REPORTER_ASSERT(reporter, !p.isEmpty());
- p.cubicTo(100, 100, 200, 200, 300, 300);
- REPORTER_ASSERT(reporter, kCurveSegmentMask == p.getSegmentMasks());
- REPORTER_ASSERT(reporter, !p.isEmpty());
- p.reset();
- p.moveTo(0, 0);
- p.cubicTo(100, 100, 200, 200, 300, 300);
- REPORTER_ASSERT(reporter, SkPath::kCubic_SegmentMask == p.getSegmentMasks());
- REPORTER_ASSERT(reporter, !p.isEmpty());
-
+ test_segment_masks(reporter);
test_flattening(reporter);
test_transform(reporter);
test_bounds(reporter);
+ test_iter(reporter);
+ test_raw_iter(reporter);
}
#include "TestClassDef.h"