diff --git a/gm/strokes.cpp b/gm/strokes.cpp index aa2db9ccb2..303d4a538b 100644 --- a/gm/strokes.cpp +++ b/gm/strokes.cpp @@ -210,6 +210,32 @@ DEF_SIMPLE_GM(CubicStroke, canvas, 384, 384) { canvas->drawPath(path, p); } +DEF_SIMPLE_GM(zerolinestroke, canvas, 90, 120) { + SkPaint paint; + paint.setStyle(SkPaint::kStroke_Style); + paint.setStrokeWidth(20); + paint.setAntiAlias(true); + paint.setStrokeCap(SkPaint::kRound_Cap); + + SkPath path; + path.moveTo(30, 90); + path.lineTo(30, 90); + path.lineTo(60, 90); + path.lineTo(60, 90); + canvas->drawPath(path, paint); + + path.reset(); + path.moveTo(30, 30); + path.lineTo(60, 30); + canvas->drawPath(path, paint); + + path.reset(); + path.moveTo(30, 60); + path.lineTo(30, 60); + path.lineTo(60, 60); + canvas->drawPath(path, paint); +} + class Strokes2GM : public skiagm::GM { SkPath fPath; protected: diff --git a/src/core/SkStroke.cpp b/src/core/SkStroke.cpp index 65267d11fd..5a43075284 100644 --- a/src/core/SkStroke.cpp +++ b/src/core/SkStroke.cpp @@ -126,7 +126,7 @@ public: SkPoint moveToPt() const { return fFirstPt; } void moveTo(const SkPoint&); - void lineTo(const SkPoint&); + void lineTo(const SkPoint&, const SkPath::Iter* iter = nullptr); void quadTo(const SkPoint&, const SkPoint&); void conicTo(const SkPoint&, const SkPoint&, SkScalar weight); void cubicTo(const SkPoint&, const SkPoint&, const SkPoint&); @@ -187,6 +187,7 @@ private: int fRecursionDepth; // track stack depth to abort if numerics run amok bool fFoundTangents; // do less work until tangents meet (cubic) + bool fJoinCompleted; // previous join was not degenerate void addDegenerateLine(const SkQuadConstruct* ); static ReductionType CheckConicLinear(const SkConic& , SkPoint* reduction); @@ -273,6 +274,7 @@ bool SkPathStroker::preJoinTo(const SkPoint& currPt, SkVector* normal, void SkPathStroker::postJoinTo(const SkPoint& currPt, const SkVector& normal, const SkVector& unitNormal) { + fJoinCompleted = true; fPrevPt = currPt; fPrevUnitNormal = unitNormal; fPrevNormal = normal; @@ -359,6 +361,7 @@ void SkPathStroker::moveTo(const SkPoint& pt) { } fSegmentCount = 0; fFirstPt = fPrevPt = pt; + fJoinCompleted = false; } void SkPathStroker::line_to(const SkPoint& currPt, const SkVector& normal) { @@ -366,11 +369,46 @@ void SkPathStroker::line_to(const SkPoint& currPt, const SkVector& normal) { fInner.lineTo(currPt.fX - normal.fX, currPt.fY - normal.fY); } -void SkPathStroker::lineTo(const SkPoint& currPt) { +static bool has_valid_tangent(const SkPath::Iter* iter) { + SkPath::Iter copy = *iter; + SkPath::Verb verb; + SkPoint pts[4]; + while ((verb = copy.next(pts))) { + switch (verb) { + case SkPath::kMove_Verb: + return false; + case SkPath::kLine_Verb: + if (pts[0] == pts[1]) { + continue; + } + return true; + case SkPath::kQuad_Verb: + case SkPath::kConic_Verb: + if (pts[0] == pts[1] && pts[0] == pts[2]) { + continue; + } + return true; + case SkPath::kCubic_Verb: + if (pts[0] == pts[1] && pts[0] == pts[2] && pts[0] == pts[3]) { + continue; + } + return true; + case SkPath::kClose_Verb: + case SkPath::kDone_Verb: + return false; + } + } + return false; +} + +void SkPathStroker::lineTo(const SkPoint& currPt, const SkPath::Iter* iter) { if (SkStrokerPriv::CapFactory(SkPaint::kButt_Cap) == fCapper && fPrevPt.equalsWithinTolerance(currPt, SK_ScalarNearlyZero * fInvResScale)) { return; } + if (fPrevPt == currPt && (fJoinCompleted || (iter && has_valid_tangent(iter)))) { + return; + } SkVector normal, unitNormal; if (!this->preJoinTo(currPt, &normal, &unitNormal, true)) { @@ -1339,7 +1377,7 @@ void SkStroke::strokePath(const SkPath& src, SkPath* dst) const { stroker.moveTo(pts[0]); break; case SkPath::kLine_Verb: - stroker.lineTo(pts[1]); + stroker.lineTo(pts[1], &iter); lastSegment = SkPath::kLine_Verb; break; case SkPath::kQuad_Verb: