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
diff --git a/gm/path_stroke_with_zero_length.cpp b/gm/path_stroke_with_zero_length.cpp
new file mode 100644
index 0000000..dc52947
--- /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 b6500c1..9134d80 100644
--- a/src/pdf/SkPDFDevice.cpp
+++ b/src/pdf/SkPDFDevice.cpp
@@ -1035,7 +1035,12 @@
     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 fc1bdbe..108f2c1 100644
--- a/src/pdf/SkPDFUtils.cpp
+++ b/src/pdf/SkPDFUtils.cpp
@@ -118,25 +118,27 @@
 
 // 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 @@
                 break;
             case SkPath::kLine_Verb:
                 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;
+                if ((fillState == kEmpty_SkipFillState) && (args[0] != lastMovePt)) {
+                    fillState = kSingleLine_SkipFillState;
+                    break;
                 }
+                fillState = kNonSingleLine_SkipFillState;
                 break;
             case SkPath::kQuad_Verb:
                 append_quad(args, &currentSegment);
@@ -165,6 +165,7 @@
                 for (int i = 0; i < converter.countQuads(); ++i) {
                     append_quad(&quads[i * 2], &currentSegment);
                 }
+                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 @@
                 fillState = kNonSingleLine_SkipFillState;
                 break;
             case SkPath::kClose_Verb:
-                if (fillState != kSingleLine_SkipFillState) {
+
                     ClosePath(&currentSegment);
-                    currentSegment.writeToStream(content);
-                }
+
+                currentSegment.writeToStream(content);
                 currentSegment.reset();
                 break;
             default:
diff --git a/src/pdf/SkPDFUtils.h b/src/pdf/SkPDFUtils.h
index 38f300a..0aa05a0 100644
--- a/src/pdf/SkPDFUtils.h
+++ b/src/pdf/SkPDFUtils.h
@@ -46,7 +46,11 @@
                             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);