diff options
author | 2015-08-21 13:27:37 -0700 | |
---|---|---|
committer | 2015-08-21 13:27:37 -0700 | |
commit | dd3c165828fffb369d0f4b13b48381169a0249a9 (patch) | |
tree | 91dd40e798e4a6a982b43e414102d57a7a4809b1 | |
parent | 1cbdcde9116e9efb514236faf8cfa42649a041d1 (diff) |
experiment with zero-length round capped line segments
If the endcap is not butt, draw the endcaps even when the line
has zero length.
If the dash length is zero, generate a zero length line segment.
Treat a move followed by a close as a move followed by a zero-length
line.
R=reed@google.com,schenney@google.com
BUG=422974
Review URL: https://codereview.chromium.org/1309753002
-rw-r--r-- | gm/strokes.cpp | 74 | ||||
-rw-r--r-- | include/core/SkPaint.h | 9 | ||||
-rw-r--r-- | src/core/SkPathMeasure.cpp | 9 | ||||
-rw-r--r-- | src/core/SkStroke.cpp | 23 | ||||
-rw-r--r-- | src/utils/SkDashPath.cpp | 2 |
5 files changed, 112 insertions, 5 deletions
diff --git a/gm/strokes.cpp b/gm/strokes.cpp index 73823907a3..e914d0a0b7 100644 --- a/gm/strokes.cpp +++ b/gm/strokes.cpp @@ -8,6 +8,8 @@ #include "gm.h" #include "SkPath.h" #include "SkRandom.h" +#include "SkDashPathEffect.h" +#include "SkParsePath.h" #define W 400 #define H 400 @@ -76,6 +78,76 @@ private: typedef skiagm::GM INHERITED; }; +/* See + https://code.google.com/p/chromium/issues/detail?id=422974 and + http://jsfiddle.net/1xnku3sg/2/ + */ +class ZeroLenStrokesGM : public skiagm::GM { + SkPath fMoveHfPath, fMoveZfPath, fDashedfPath, fRefPath[4]; +protected: + void onOnceBeforeDraw() override { + + SkAssertResult(SkParsePath::FromSVGString("M0,0h0M10,0h0M20,0h0", &fMoveHfPath)); + SkAssertResult(SkParsePath::FromSVGString("M0,0zM10,0zM20,0z", &fMoveZfPath)); + SkAssertResult(SkParsePath::FromSVGString("M0,0h25", &fDashedfPath)); + + for (int i = 0; i < 3; ++i) { + fRefPath[0].addCircle(i * 10.f, 0, 5); + fRefPath[1].addCircle(i * 10.f, 0, 10); + fRefPath[2].addRect(i * 10.f - 4, -2, i * 10.f + 4, 6); + fRefPath[3].addRect(i * 10.f - 10, -10, i * 10.f + 10, 10); + } + } + + SkString onShortName() override { + return SkString("zeroPath"); + } + + SkISize onISize() override { + return SkISize::Make(W, H*2); + } + + void onDraw(SkCanvas* canvas) override { + SkPaint fillPaint, strokePaint, dashPaint; + fillPaint.setAntiAlias(true); + strokePaint = fillPaint; + strokePaint.setStyle(SkPaint::kStroke_Style); + for (int i = 0; i < 2; ++i) { + fillPaint.setAlpha(255); + strokePaint.setAlpha(255); + strokePaint.setStrokeWidth(i ? 8.f : 10.f); + strokePaint.setStrokeCap(i ? SkPaint::kSquare_Cap : SkPaint::kRound_Cap); + canvas->save(); + canvas->translate(10 + i * 100.f, 10); + canvas->drawPath(fMoveHfPath, strokePaint); + canvas->translate(0, 20); + canvas->drawPath(fMoveZfPath, strokePaint); + dashPaint = strokePaint; + const SkScalar intervals[] = { 0, 10 }; + dashPaint.setPathEffect(SkDashPathEffect::Create(intervals, 2, 0))->unref(); + SkPath fillPath; + dashPaint.getFillPath(fDashedfPath, &fillPath); + canvas->translate(0, 20); + canvas->drawPath(fDashedfPath, dashPaint); + canvas->translate(0, 20); + canvas->drawPath(fRefPath[i * 2], fillPaint); + strokePaint.setStrokeWidth(20); + strokePaint.setAlpha(127); + canvas->translate(0, 50); + canvas->drawPath(fMoveHfPath, strokePaint); + canvas->translate(0, 30); + canvas->drawPath(fMoveZfPath, strokePaint); + canvas->translate(0, 30); + fillPaint.setAlpha(127); + canvas->drawPath(fRefPath[1 + i * 2], fillPaint); + canvas->restore(); + } + } + +private: + typedef skiagm::GM INHERITED; +}; + class Strokes2GM : public skiagm::GM { SkPath fPath; protected: @@ -278,3 +350,5 @@ static skiagm::GMRegistry R0(F0); static skiagm::GMRegistry R1(F1); static skiagm::GMRegistry R2(F2); static skiagm::GMRegistry R3(F3); + +DEF_GM(return SkNEW(ZeroLenStrokesGM);) diff --git a/include/core/SkPaint.h b/include/core/SkPaint.h index 1b993fc5ea..0c071ad361 100644 --- a/include/core/SkPaint.h +++ b/include/core/SkPaint.h @@ -398,6 +398,15 @@ public: /** Cap enum specifies the settings for the paint's strokecap. This is the treatment that is applied to the beginning and end of each non-closed contour (e.g. lines). + + If the cap is round or square, the caps are drawn when the contour has + a zero length. Zero length contours can be created by following moveTo + with a lineTo at the same point, or a moveTo followed by a close. + + A dash with an on interval of zero also creates a zero length contour. + + The zero length contour draws the square cap without rotation, since + the no direction can be inferred. */ enum Cap { kButt_Cap, //!< begin/end contours with no extension diff --git a/src/core/SkPathMeasure.cpp b/src/core/SkPathMeasure.cpp index a5dd84048c..17ae95e801 100644 --- a/src/core/SkPathMeasure.cpp +++ b/src/core/SkPathMeasure.cpp @@ -314,7 +314,12 @@ static void seg_to(const SkPoint pts[], int segType, SkASSERT(startT <= stopT); if (startT == stopT) { - return; // should we report this, to undo a moveTo? + /* if the dash as a zero-length on segment, add a corresponding zero-length line. + The stroke code will add end caps to zero length lines as appropriate */ + SkPoint lastPt; + SkAssertResult(dst->getLastPt(&lastPt)); + dst->lineTo(lastPt); + return; } SkPoint tmp0[7], tmp1[7]; @@ -568,7 +573,7 @@ bool SkPathMeasure::getSegment(SkScalar startD, SkScalar stopD, SkPath* dst, if (stopD > length) { stopD = length; } - if (startD >= stopD) { + if (startD > stopD) { return false; } diff --git a/src/core/SkStroke.cpp b/src/core/SkStroke.cpp index ede3d21055..2db5bba524 100644 --- a/src/core/SkStroke.cpp +++ b/src/core/SkStroke.cpp @@ -121,6 +121,9 @@ public: SkScalar radius, SkScalar miterLimit, SkPaint::Cap, SkPaint::Join, SkScalar resScale); + bool hasOnlyMoveTo() const { return 0 == fSegmentCount; } + SkPoint moveToPt() const { return fFirstPt; } + void moveTo(const SkPoint&); void lineTo(const SkPoint&); void quadTo(const SkPoint&, const SkPoint&); @@ -242,7 +245,14 @@ bool SkPathStroker::preJoinTo(const SkPoint& currPt, SkVector* normal, SkScalar prevY = fPrevPt.fY; if (!set_normal_unitnormal(fPrevPt, currPt, fRadius, normal, unitNormal)) { - return false; + if (SkStrokerPriv::CapFactory(SkPaint::kButt_Cap) == fCapper) { + return false; + } + /* Square caps and round caps draw even if the segment length is zero. + Since the zero length segment has no direction, set the orientation + to upright as the default orientation */ + normal->set(fRadius, 0); + unitNormal->set(1, 0); } if (fSegmentCount == 0) { @@ -356,7 +366,8 @@ void SkPathStroker::line_to(const SkPoint& currPt, const SkVector& normal) { } void SkPathStroker::lineTo(const SkPoint& currPt) { - if (SkPath::IsLineDegenerate(fPrevPt, currPt, false)) { + if (SkStrokerPriv::CapFactory(SkPaint::kButt_Cap) == fCapper + && SkPath::IsLineDegenerate(fPrevPt, currPt, false)) { return; } SkVector normal, unitNormal; @@ -1334,6 +1345,14 @@ void SkStroke::strokePath(const SkPath& src, SkPath* dst) const { lastSegment = SkPath::kCubic_Verb; break; case SkPath::kClose_Verb: + if (stroker.hasOnlyMoveTo() && SkPaint::kButt_Cap != this->getCap()) { + /* If the stroke consists of a moveTo followed by a close, treat it + as if it were followed by a zero-length line. Lines without length + can have square and round end caps. */ + stroker.lineTo(stroker.moveToPt()); + lastSegment = SkPath::kLine_Verb; + break; + } stroker.close(lastSegment == SkPath::kLine_Verb); break; case SkPath::kDone_Verb: diff --git a/src/utils/SkDashPath.cpp b/src/utils/SkDashPath.cpp index 4b2b33d2c4..de249f6fff 100644 --- a/src/utils/SkDashPath.cpp +++ b/src/utils/SkDashPath.cpp @@ -272,7 +272,7 @@ bool SkDashPath::FilterDashPath(SkPath* dst, const SkPath& src, SkStrokeRec* rec while (distance < length) { SkASSERT(dlen >= 0); addedSegment = false; - if (is_even(index) && dlen > 0 && !skipFirstSegment) { + if (is_even(index) && !skipFirstSegment) { addedSegment = true; ++segCount; |