diff options
-rw-r--r-- | src/core/SkPathMeasure.cpp | 52 | ||||
-rw-r--r-- | tests/PathMeasureTest.cpp | 36 |
2 files changed, 69 insertions, 19 deletions
diff --git a/src/core/SkPathMeasure.cpp b/src/core/SkPathMeasure.cpp index 04d19c2ac5..0d4dcd4121 100644 --- a/src/core/SkPathMeasure.cpp +++ b/src/core/SkPathMeasure.cpp @@ -147,11 +147,16 @@ SkScalar SkPathMeasure::compute_cubic_segs(const SkPoint pts[4], void SkPathMeasure::buildSegments() { SkPoint pts[4]; int ptIndex = fFirstPtIndex; - SkScalar d, distance = 0; + SkScalar distance = 0; bool isClosed = fForceClosed; bool firstMoveTo = ptIndex < 0; Segment* seg; + /* Note: + * as we accumulate distance, we have to check that the result of += + * actually made it larger, since a very small delta might be > 0, but + * still have no effect on distance (if distance >>> delta). + */ fSegments.reset(); bool done = false; do { @@ -166,32 +171,41 @@ void SkPathMeasure::buildSegments() { firstMoveTo = false; break; - case SkPath::kLine_Verb: - d = SkPoint::Distance(pts[0], pts[1]); + case SkPath::kLine_Verb: { + SkScalar d = SkPoint::Distance(pts[0], pts[1]); SkASSERT(d >= 0); + SkScalar prevD = distance; distance += d; - seg = fSegments.append(); - seg->fDistance = distance; - seg->fPtIndex = ptIndex; - seg->fType = kLine_SegType; - seg->fTValue = kMaxTValue; - fPts.append(1, pts + 1); - ptIndex++; - break; + if (distance > prevD) { + seg = fSegments.append(); + seg->fDistance = distance; + seg->fPtIndex = ptIndex; + seg->fType = kLine_SegType; + seg->fTValue = kMaxTValue; + fPts.append(1, pts + 1); + ptIndex++; + } + } break; - case SkPath::kQuad_Verb: + case SkPath::kQuad_Verb: { + SkScalar prevD = distance; distance = this->compute_quad_segs(pts, distance, 0, kMaxTValue, ptIndex); - fPts.append(2, pts + 1); - ptIndex += 2; - break; + if (distance > prevD) { + fPts.append(2, pts + 1); + ptIndex += 2; + } + } break; - case SkPath::kCubic_Verb: + case SkPath::kCubic_Verb: { + SkScalar prevD = distance; distance = this->compute_cubic_segs(pts, distance, 0, kMaxTValue, ptIndex); - fPts.append(3, pts + 1); - ptIndex += 3; - break; + if (distance > prevD) { + fPts.append(3, pts + 1); + ptIndex += 3; + } + } break; case SkPath::kClose_Verb: isClosed = true; diff --git a/tests/PathMeasureTest.cpp b/tests/PathMeasureTest.cpp index 3855a0019d..e636ff38fc 100644 --- a/tests/PathMeasureTest.cpp +++ b/tests/PathMeasureTest.cpp @@ -8,6 +8,40 @@ #include "Test.h" #include "SkPathMeasure.h" +static void test_small_segment(skiatest::Reporter* reporter) { +#ifdef SK_SCALAR_IS_FLOAT + SkPath path; + const SkPoint pts[] = { + { 100000, 100000}, + // big jump between these points, makes a big segment + { 1.0005, 0.9999 }, + // tiny (non-zero) jump between these points + { 1, 1 }, + }; + + path.moveTo(pts[0]); + for (size_t i = 1; i < SK_ARRAY_COUNT(pts); ++i) { + path.lineTo(pts[i]); + } + SkPathMeasure meas(path, false); + + /* this would assert (before a fix) because we added a segment with + the same length as the prev segment, due to the follow (bad) pattern + + d = distance(pts[0], pts[1]); + distance += d; + seg->fDistance = distance; + + SkASSERT(d > 0); // TRUE + SkASSERT(seg->fDistance > prevSeg->fDistance); // FALSE + + This 2nd assert failes because (distance += d) didn't affect distance + because distance >>> d. + */ + meas.getLength(); +#endif +} + static void TestPathMeasure(skiatest::Reporter* reporter) { SkPath path; @@ -131,6 +165,8 @@ static void TestPathMeasure(skiatest::Reporter* reporter) { SkFloatToScalar(0.0001f))); REPORTER_ASSERT(reporter, tangent.fX == -SK_Scalar1); REPORTER_ASSERT(reporter, tangent.fY == 0); + + test_small_segment(reporter); } #include "TestClassDef.h" |