aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--src/core/SkPathMeasure.cpp52
-rw-r--r--tests/PathMeasureTest.cpp36
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"