diff --git a/gm/path_stroke_with_zero_length.cpp b/gm/path_stroke_with_zero_length.cpp new file mode 100644 index 0000000000..dc52947da3 --- /dev/null +++ b/gm/path_stroke_with_zero_length.cpp @@ -0,0 +1,54 @@ +/* + * Copyright 2015 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "SkPath.h" +#include "SkStream.h" +#include "gm.h" + +// Test how short paths are stroked with various caps +DEF_SIMPLE_GM(path_stroke_with_zero_length, canvas, 240, 120) { + SkPath paths[5]; + paths[0].moveTo(30.0f, 0); // single line segment + paths[0].rLineTo(30.0f, 0); + + paths[1].moveTo(90.0f, 0); // single line segment with close + paths[1].rLineTo(30.0f, 0); + paths[1].close(); + + paths[2].moveTo(150.0f, 0); // zero-length line + paths[2].rLineTo(0, 0); + + paths[3].moveTo(180.0f, 0); // zero-length line with close + paths[3].rLineTo(0, 0); + paths[3].close(); + + paths[4].moveTo(210.0f, 0); // close only, no line + paths[4].close(); + + auto drawPaths = [&](const SkPaint& paint) { + canvas->translate(0, 30.0f); + for (const SkPath& path : paths) { + canvas->drawPath(path, paint); + } + }; + + SkAutoCanvasRestore autoCanvasRestore(canvas, true); + + SkPaint butt; + butt.setStyle(SkPaint::kStroke_Style); + butt.setStrokeWidth(20.0f); + butt.setStrokeCap(SkPaint::kButt_Cap); + drawPaths(butt); + + SkPaint round(butt); + round.setStrokeCap(SkPaint::kRound_Cap); + drawPaths(round); + + SkPaint square(butt); + square.setStrokeCap(SkPaint::kSquare_Cap); + drawPaths(square); +} diff --git a/src/pdf/SkPDFDevice.cpp b/src/pdf/SkPDFDevice.cpp index b6500c1ed4..9134d802ff 100644 --- a/src/pdf/SkPDFDevice.cpp +++ b/src/pdf/SkPDFDevice.cpp @@ -1035,7 +1035,12 @@ void SkPDFDevice::drawPath(const SkDraw& d, if (!content.entry()) { return; } + bool consumeDegeratePathSegments = + paint.getStyle() == SkPaint::kFill_Style || + (paint.getStrokeCap() != SkPaint::kRound_Cap && + paint.getStrokeCap() != SkPaint::kSquare_Cap); SkPDFUtils::EmitPath(*pathPtr, paint.getStyle(), + consumeDegeratePathSegments, &content.entry()->fContent); SkPDFUtils::PaintPath(paint.getStyle(), pathPtr->getFillType(), &content.entry()->fContent); diff --git a/src/pdf/SkPDFUtils.cpp b/src/pdf/SkPDFUtils.cpp index fc1bdbeb06..108f2c10ab 100644 --- a/src/pdf/SkPDFUtils.cpp +++ b/src/pdf/SkPDFUtils.cpp @@ -118,25 +118,27 @@ void SkPDFUtils::AppendRectangle(const SkRect& rect, SkWStream* content) { // static void SkPDFUtils::EmitPath(const SkPath& path, SkPaint::Style paintStyle, - SkWStream* content) { + bool doConsumeDegerates, SkWStream* content) { // Filling a path with no area results in a drawing in PDF renderers but // Chrome expects to be able to draw some such entities with no visible // result, so we detect those cases and discard the drawing for them. // Specifically: moveTo(X), lineTo(Y) and moveTo(X), lineTo(X), lineTo(Y). enum SkipFillState { - kEmpty_SkipFillState = 0, - kSingleLine_SkipFillState = 1, - kNonSingleLine_SkipFillState = 2, + kEmpty_SkipFillState, + kSingleLine_SkipFillState, + kNonSingleLine_SkipFillState, }; SkipFillState fillState = kEmpty_SkipFillState; - if (paintStyle != SkPaint::kFill_Style) { - fillState = kNonSingleLine_SkipFillState; - } + //if (paintStyle != SkPaint::kFill_Style) { + // fillState = kNonSingleLine_SkipFillState; + //} SkPoint lastMovePt = SkPoint::Make(0,0); SkDynamicMemoryWStream currentSegment; SkPoint args[4]; SkPath::Iter iter(path, false); - for (SkPath::Verb verb = iter.next(args); verb != SkPath::kDone_Verb; verb = iter.next(args)) { + for (SkPath::Verb verb = iter.next(args, doConsumeDegerates); + verb != SkPath::kDone_Verb; + verb = iter.next(args, doConsumeDegerates)) { // args gets all the points, even the implicit first point. switch (verb) { case SkPath::kMove_Verb: @@ -146,13 +148,11 @@ void SkPDFUtils::EmitPath(const SkPath& path, SkPaint::Style paintStyle, break; case SkPath::kLine_Verb: AppendLine(args[1].fX, args[1].fY, ¤tSegment); - if (fillState == kEmpty_SkipFillState) { - if (args[0] != lastMovePt) { - fillState = kSingleLine_SkipFillState; - } - } else if (fillState == kSingleLine_SkipFillState) { - fillState = kNonSingleLine_SkipFillState; + if ((fillState == kEmpty_SkipFillState) && (args[0] != lastMovePt)) { + fillState = kSingleLine_SkipFillState; + break; } + fillState = kNonSingleLine_SkipFillState; break; case SkPath::kQuad_Verb: append_quad(args, ¤tSegment); @@ -165,6 +165,7 @@ void SkPDFUtils::EmitPath(const SkPath& path, SkPaint::Style paintStyle, for (int i = 0; i < converter.countQuads(); ++i) { append_quad(&quads[i * 2], ¤tSegment); } + fillState = kNonSingleLine_SkipFillState; } break; case SkPath::kCubic_Verb: AppendCubic(args[1].fX, args[1].fY, args[2].fX, args[2].fY, @@ -172,10 +173,10 @@ void SkPDFUtils::EmitPath(const SkPath& path, SkPaint::Style paintStyle, fillState = kNonSingleLine_SkipFillState; break; case SkPath::kClose_Verb: - if (fillState != kSingleLine_SkipFillState) { + ClosePath(¤tSegment); - currentSegment.writeToStream(content); - } + + currentSegment.writeToStream(content); currentSegment.reset(); break; default: diff --git a/src/pdf/SkPDFUtils.h b/src/pdf/SkPDFUtils.h index 38f300aacf..0aa05a088a 100644 --- a/src/pdf/SkPDFUtils.h +++ b/src/pdf/SkPDFUtils.h @@ -46,7 +46,11 @@ public: SkScalar dstX, SkScalar dstY, SkWStream* content); static void AppendRectangle(const SkRect& rect, SkWStream* content); static void EmitPath(const SkPath& path, SkPaint::Style paintStyle, - SkWStream* content); + bool doConsumeDegerates, SkWStream* content); + static void EmitPath(const SkPath& path, SkPaint::Style paintStyle, + SkWStream* content) { + SkPDFUtils::EmitPath(path, paintStyle, true, content); + } static void ClosePath(SkWStream* content); static void PaintPath(SkPaint::Style style, SkPath::FillType fill, SkWStream* content);