[PDF] Ensure that filling single line paths (with no area) does not draw anything.

Add a test to a gm to confirm the new behavior.

Fixes http://crbug.com/123072

Review URL: https://codereview.appspot.com/6137060

git-svn-id: http://skia.googlecode.com/svn/trunk@3884 2bbb7eff-a529-9590-31e7-b0007b416f81
This commit is contained in:
vandebo@chromium.org 2012-05-09 17:17:51 +00:00
parent b1af07aa43
commit 683001ce0d
5 changed files with 66 additions and 11 deletions

View File

@ -92,6 +92,18 @@ static SkScalar make_star(SkPath* path, int n) {
static SkScalar make_star_5(SkPath* path) { return make_star(path, 5); } static SkScalar make_star_5(SkPath* path) { return make_star(path, 5); }
static SkScalar make_star_13(SkPath* path) { return make_star(path, 13); } static SkScalar make_star_13(SkPath* path) { return make_star(path, 13); }
// We don't expect any output from this path.
static SkScalar make_line(SkPath* path) {
path->moveTo(SkIntToScalar(30), SkIntToScalar(30));
path->lineTo(SkIntToScalar(120), SkIntToScalar(40));
path->close();
path->moveTo(SkIntToScalar(150), SkIntToScalar(30));
path->lineTo(SkIntToScalar(150), SkIntToScalar(30));
path->lineTo(SkIntToScalar(300), SkIntToScalar(40));
path->close();
return SkIntToScalar(40);
}
static const MakePathProc gProcs[] = { static const MakePathProc gProcs[] = {
make_frame, make_frame,
make_triangle, make_triangle,
@ -99,7 +111,8 @@ static const MakePathProc gProcs[] = {
make_oval, make_oval,
make_sawtooth, make_sawtooth,
make_star_5, make_star_5,
make_star_13 make_star_13,
make_line,
}; };
#define N SK_ARRAY_COUNT(gProcs) #define N SK_ARRAY_COUNT(gProcs)

View File

@ -262,7 +262,7 @@ static void emit_clip(SkPath* clipPath, SkRect* clipRect,
SkPath::FillType clipFill; SkPath::FillType clipFill;
if (clipPath) { if (clipPath) {
SkPDFUtils::EmitPath(*clipPath, contentStream); SkPDFUtils::EmitPath(*clipPath, SkPaint::kFill_Style, contentStream);
clipFill = clipPath->getFillType(); clipFill = clipPath->getFillType();
} else { } else {
SkPDFUtils::AppendRectangle(*clipRect, contentStream); SkPDFUtils::AppendRectangle(*clipRect, contentStream);
@ -765,7 +765,8 @@ void SkPDFDevice::drawPath(const SkDraw& d, const SkPath& origPath,
if (!content.entry()) { if (!content.entry()) {
return; return;
} }
SkPDFUtils::EmitPath(*pathPtr, &content.entry()->fContent); SkPDFUtils::EmitPath(*pathPtr, paint.getStyle(),
&content.entry()->fContent);
SkPDFUtils::PaintPath(paint.getStyle(), pathPtr->getFillType(), SkPDFUtils::PaintPath(paint.getStyle(), pathPtr->getFillType(),
&content.entry()->fContent); &content.entry()->fContent);
} }

View File

@ -1378,7 +1378,7 @@ bool SkPDFType3Font::populate(int16_t glyphID) {
&content); &content);
const SkPath* path = cache->findPath(glyph); const SkPath* path = cache->findPath(glyph);
if (path) { if (path) {
SkPDFUtils::EmitPath(*path, &content); SkPDFUtils::EmitPath(*path, paint.getStyle(), &content);
SkPDFUtils::PaintPath(paint.getStyle(), path->getFillType(), SkPDFUtils::PaintPath(paint.getStyle(), path->getFillType(),
&content); &content);
} }

View File

@ -7,6 +7,7 @@
*/ */
#include "SkData.h"
#include "SkGeometry.h" #include "SkGeometry.h"
#include "SkPaint.h" #include "SkPaint.h"
#include "SkPath.h" #include "SkPath.h"
@ -98,7 +99,23 @@ void SkPDFUtils::AppendRectangle(const SkRect& rect, SkWStream* content) {
} }
// static // static
void SkPDFUtils::EmitPath(const SkPath& path, SkWStream* content) { void SkPDFUtils::EmitPath(const SkPath& path, SkPaint::Style paintStyle,
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,
};
SkipFillState fillState = kEmpty_SkipFillState;
if (paintStyle != SkPaint::kFill_Style) {
fillState = kNonSingleLine_SkipFillState;
}
SkPoint lastMovePt;
SkDynamicMemoryWStream currentSegment;
SkPoint args[4]; SkPoint args[4];
SkPath::Iter iter(path, false); SkPath::Iter iter(path, false);
for (SkPath::Verb verb = iter.next(args); for (SkPath::Verb verb = iter.next(args);
@ -107,30 +124,52 @@ void SkPDFUtils::EmitPath(const SkPath& path, SkWStream* content) {
// args gets all the points, even the implicit first point. // args gets all the points, even the implicit first point.
switch (verb) { switch (verb) {
case SkPath::kMove_Verb: case SkPath::kMove_Verb:
MoveTo(args[0].fX, args[0].fY, content); MoveTo(args[0].fX, args[0].fY, &currentSegment);
lastMovePt = args[0];
fillState = kEmpty_SkipFillState;
break; break;
case SkPath::kLine_Verb: case SkPath::kLine_Verb:
AppendLine(args[1].fX, args[1].fY, content); AppendLine(args[1].fX, args[1].fY, &currentSegment);
if (fillState == kEmpty_SkipFillState) {
if (args[0] != lastMovePt) {
fillState = kSingleLine_SkipFillState;
}
} else if (fillState == kSingleLine_SkipFillState) {
fillState = kNonSingleLine_SkipFillState;
}
break; break;
case SkPath::kQuad_Verb: { case SkPath::kQuad_Verb: {
SkPoint cubic[4]; SkPoint cubic[4];
SkConvertQuadToCubic(args, cubic); SkConvertQuadToCubic(args, cubic);
AppendCubic(cubic[1].fX, cubic[1].fY, cubic[2].fX, cubic[2].fY, AppendCubic(cubic[1].fX, cubic[1].fY, cubic[2].fX, cubic[2].fY,
cubic[3].fX, cubic[3].fY, content); cubic[3].fX, cubic[3].fY, &currentSegment);
fillState = kNonSingleLine_SkipFillState;
break; break;
} }
case SkPath::kCubic_Verb: case SkPath::kCubic_Verb:
AppendCubic(args[1].fX, args[1].fY, args[2].fX, args[2].fY, AppendCubic(args[1].fX, args[1].fY, args[2].fX, args[2].fY,
args[3].fX, args[3].fY, content); args[3].fX, args[3].fY, &currentSegment);
fillState = kNonSingleLine_SkipFillState;
break; break;
case SkPath::kClose_Verb: case SkPath::kClose_Verb:
ClosePath(content); if (fillState != kSingleLine_SkipFillState) {
ClosePath(&currentSegment);
SkData* data = currentSegment.copyToData();
content->write(data->data(), data->size());
data->unref();
}
currentSegment.reset();
break; break;
default: default:
SkASSERT(false); SkASSERT(false);
break; break;
} }
} }
if (currentSegment.bytesWritten() > 0) {
SkData* data = currentSegment.copyToData();
content->write(data->data(), data->size());
data->unref();
}
} }
// static // static

View File

@ -10,6 +10,7 @@
#ifndef SkPDFUtils_DEFINED #ifndef SkPDFUtils_DEFINED
#define SkPDFUtils_DEFINED #define SkPDFUtils_DEFINED
#include "SkPaint.h"
#include "SkPath.h" #include "SkPath.h"
class SkMatrix; class SkMatrix;
@ -42,7 +43,8 @@ public:
SkScalar ctl2X, SkScalar ctl2Y, SkScalar ctl2X, SkScalar ctl2Y,
SkScalar dstX, SkScalar dstY, SkWStream* content); SkScalar dstX, SkScalar dstY, SkWStream* content);
static void AppendRectangle(const SkRect& rect, SkWStream* content); static void AppendRectangle(const SkRect& rect, SkWStream* content);
static void EmitPath(const SkPath& path, SkWStream* content); static void EmitPath(const SkPath& path, SkPaint::Style paintStyle,
SkWStream* content);
static void ClosePath(SkWStream* content); static void ClosePath(SkWStream* content);
static void PaintPath(SkPaint::Style style, SkPath::FillType fill, static void PaintPath(SkPaint::Style style, SkPath::FillType fill,
SkWStream* content); SkWStream* content);