Index: gm/OverStroke.cpp |
diff --git a/gm/OverStroke.cpp b/gm/OverStroke.cpp |
new file mode 100644 |
index 0000000000000000000000000000000000000000..0c5679786cfc742b912a7cccee91fe66ba0c4435 |
--- /dev/null |
+++ b/gm/OverStroke.cpp |
@@ -0,0 +1,221 @@ |
+/* |
+ * Copyright 2016 Google Inc. |
+ * |
+ * Use of this source code is governed by a BSD-style license that can be |
+ * found in the LICENSE file. |
+ */ |
+ |
+ |
+/* |
+ * This GM exercises stroking of paths with large stroke lengths, which is |
+ * referred to as "overstroke" for brevity. In Skia as of 8/2016 we offset |
+ * each part of the curve the request amount even if it makes the offsets |
+ * overlap and create holes. There is not a really great algorithm for this |
+ * and several other 2D graphics engines have the same bug. |
+ * |
+ * See crbug.com/589769 skbug.com/5405 skbug.com/5406 |
+ */ |
+ |
+ |
+#include "gm.h" |
+#include "SkPaint.h" |
+#include "SkPath.h" |
+ |
+//////// path and paint builders |
+ |
+SkPaint make_overstroke_paint() { |
+ SkPaint p; |
+ p.setAntiAlias(true); |
+ p.setStyle(SkPaint::kStroke_Style); |
+ p.setStrokeWidth(500); |
+ |
+ return p; |
+} |
+ |
+SkPath quad_path() { |
+ SkPath path; |
+ path.moveTo(0, 0); |
+ path.lineTo(100, 0); |
+ path.quadTo(50, -40, |
+ 0, 0); |
+ |
+ return path; |
+} |
+ |
+SkPath cubic_path() { |
+ SkPath path; |
+ path.moveTo(0, 0); |
+ path.cubicTo(25, 75, |
+ 75, -50, |
+ 100, 0); |
+ |
+ return path; |
+} |
+ |
+SkPath oval_path() { |
+ SkRect oval = SkRect::MakeXYWH(0, -25, 100, 50); |
+ |
+ SkPath path; |
+ path.arcTo(oval, 0, 359, true); |
+ path.close(); |
+ |
+ return path; |
+} |
+ |
+///////// quads |
+ |
+void draw_small_quad(SkCanvas *canvas) { |
+ // scaled so it's visible |
+ canvas->scale(8, 8); |
+ |
+ SkPaint p; |
+ p.setAntiAlias(true); |
+ p.setStyle(SkPaint::kStroke_Style); |
+ p.setStrokeWidth(3); |
+ |
+ SkPath path = quad_path(); |
+ |
+ canvas->drawPath(path, p); |
+} |
+ |
+void draw_large_quad(SkCanvas *canvas) { |
+ SkPaint p = make_overstroke_paint(); |
+ SkPath path = quad_path(); |
+ |
+ canvas->drawPath(path, p); |
+} |
+ |
+void draw_quad_fillpath(SkCanvas *canvas) { |
+ SkPath path = quad_path(); |
+ SkPaint p = make_overstroke_paint(); |
+ |
+ SkPaint fillp; |
+ fillp.setAntiAlias(true); |
+ fillp.setStyle(SkPaint::kStroke_Style); |
+ fillp.setColor(SK_ColorMAGENTA); |
+ |
+ SkPath fillpath; |
+ p.getFillPath(path, &fillpath); |
+ |
+ canvas->drawPath(fillpath, fillp); |
+} |
+ |
+void draw_stroked_quad(SkCanvas *canvas) { |
+ canvas->translate(200, 0); |
+ draw_large_quad(canvas); |
+ draw_quad_fillpath(canvas); |
+} |
+ |
+////////// cubics |
+ |
+void draw_small_cubic(SkCanvas *canvas) { |
+ // scaled so it's visible |
+ canvas->scale(8, 8); |
+ |
+ SkPaint p; |
+ p.setAntiAlias(true); |
+ p.setStyle(SkPaint::kStroke_Style); |
+ p.setStrokeWidth(3); |
+ |
+ SkPath path = cubic_path(); |
+ |
+ canvas->drawPath(path, p); |
+} |
+ |
+void draw_large_cubic(SkCanvas *canvas) { |
+ SkPaint p = make_overstroke_paint(); |
+ SkPath path = cubic_path(); |
+ |
+ canvas->drawPath(path, p); |
+} |
+ |
+void draw_cubic_fillpath(SkCanvas *canvas) { |
+ SkPath path = cubic_path(); |
+ SkPaint p = make_overstroke_paint(); |
+ |
+ SkPaint fillp; |
+ fillp.setAntiAlias(true); |
+ fillp.setStyle(SkPaint::kStroke_Style); |
+ fillp.setColor(SK_ColorMAGENTA); |
+ |
+ SkPath fillpath; |
+ p.getFillPath(path, &fillpath); |
+ |
+ canvas->drawPath(fillpath, fillp); |
+} |
+ |
+void draw_stroked_cubic(SkCanvas *canvas) { |
+ canvas->translate(400, 0); |
+ draw_large_cubic(canvas); |
+ draw_cubic_fillpath(canvas); |
+} |
+ |
+////////// ovals |
+ |
+void draw_small_oval(SkCanvas *canvas) { |
+ // scaled so it's visible |
+ canvas->scale(8, 8); |
+ |
+ SkPaint p; |
+ p.setAntiAlias(true); |
+ p.setStyle(SkPaint::kStroke_Style); |
+ p.setStrokeWidth(3); |
+ |
+ SkPath path = oval_path(); |
+ |
+ canvas->drawPath(path, p); |
+} |
+ |
+void draw_large_oval(SkCanvas *canvas) { |
+ SkPaint p = make_overstroke_paint(); |
+ SkPath path = oval_path(); |
+ |
+ canvas->drawPath(path, p); |
+} |
+ |
+void draw_oval_fillpath(SkCanvas *canvas) { |
+ SkPath path = oval_path(); |
+ SkPaint p = make_overstroke_paint(); |
+ |
+ SkPaint fillp; |
+ fillp.setAntiAlias(true); |
+ fillp.setStyle(SkPaint::kStroke_Style); |
+ fillp.setColor(SK_ColorMAGENTA); |
+ |
+ SkPath fillpath; |
+ p.getFillPath(path, &fillpath); |
+ |
+ canvas->drawPath(fillpath, fillp); |
+} |
+ |
+void draw_stroked_oval(SkCanvas *canvas) { |
+ canvas->translate(400, 0); |
+ draw_large_oval(canvas); |
+ draw_oval_fillpath(canvas); |
+} |
+ |
+////////// gm |
+ |
+void (*examples[])(SkCanvas *canvas) = { |
+ draw_small_quad, draw_stroked_quad, draw_small_cubic, |
+ draw_stroked_cubic, draw_small_oval, draw_stroked_oval, |
+}; |
+ |
+DEF_SIMPLE_GM(OverStroke, canvas, 500, 500) { |
+ const size_t length = sizeof(examples) / sizeof(examples[0]); |
+ const size_t width = 2; |
+ |
+ for (size_t i = 0; i < length; i++) { |
+ int x = (int)(i % width); |
+ int y = (int)(i / width); |
+ |
+ canvas->save(); |
+ canvas->translate(200.0f * x, 150.0f * y); |
+ canvas->scale(0.25f, 0.25f); |
+ canvas->translate(100.0f, 400.0f); |
+ |
+ examples[i](canvas); |
+ |
+ canvas->restore(); |
+ } |
+} |