SkPDF: when drawing stroked path, draw using SVG rules for zero-length segments
The "zeroPath" and emptystroke GMs capture this issue. This CL changes the following PDF GMs: emptystroke dashing4 lineclosepath dashing3 zeroPath linepath complexclip3_complex complexclip3_simple roundrects degeneratesegments filltypes strokerect pathfill inverse_paths desk_chalkboard.skp After this change, all PDF GMs look better (closer to 8888). The dashing4, emptystroke, and zeroPath GMs still need a lot of work to make them look right. BUG=538726 Review URL: https://codereview.chromium.org/1374383004
This commit is contained in:
parent
4a33952961
commit
8b2bc252fa
54
gm/path_stroke_with_zero_length.cpp
Normal file
54
gm/path_stroke_with_zero_length.cpp
Normal file
@ -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);
|
||||
}
|
@ -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);
|
||||
|
@ -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:
|
||||
|
@ -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);
|
||||
|
Loading…
Reference in New Issue
Block a user