• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1  /*
2   * Copyright 2016 Google Inc.
3   *
4   * Use of this source code is governed by a BSD-style license that can be
5   * found in the LICENSE file.
6   */
7  
8  
9  /*
10   * This GM exercises stroking of paths with large stroke lengths, which is
11   * referred to as "overstroke" for brevity. In Skia as of 8/2016 we offset
12   * each part of the curve the request amount even if it makes the offsets
13   * overlap and create holes. There is not a really great algorithm for this
14   * and several other 2D graphics engines have the same bug.
15   *
16   * If we run this using Nvidia Path Renderer with:
17   * `path/to/dm --match OverStroke -w gm_out --gpu --config nvpr16`
18   * then we get correct results, so that is a possible direction of attack -
19   * use the GPU and a completely different algorithm to get correctness in
20   * Skia.
21   *
22   * See crbug.com/589769 skbug.com/5405 skbug.com/5406
23   */
24  
25  #include "gm/gm.h"
26  #include "include/core/SkCanvas.h"
27  #include "include/core/SkColor.h"
28  #include "include/core/SkPaint.h"
29  #include "include/core/SkPath.h"
30  #include "include/core/SkPathMeasure.h"
31  #include "include/core/SkPoint.h"
32  #include "include/core/SkRect.h"
33  #include "include/core/SkScalar.h"
34  #include "src/core/SkPointPriv.h"
35  
36  #include <cstddef>
37  
38  const SkScalar OVERSTROKE_WIDTH = 500.0f;
39  const SkScalar NORMALSTROKE_WIDTH = 3.0f;
40  
41  //////// path and paint builders
42  
make_normal_paint()43  SkPaint make_normal_paint() {
44      SkPaint p;
45      p.setAntiAlias(true);
46      p.setStyle(SkPaint::kStroke_Style);
47      p.setStrokeWidth(NORMALSTROKE_WIDTH);
48      p.setColor(SK_ColorBLUE);
49  
50      return p;
51  }
52  
make_overstroke_paint()53  SkPaint make_overstroke_paint() {
54      SkPaint p;
55      p.setAntiAlias(true);
56      p.setStyle(SkPaint::kStroke_Style);
57      p.setStrokeWidth(OVERSTROKE_WIDTH);
58  
59      return p;
60  }
61  
quad_path()62  SkPath quad_path() {
63      SkPath path;
64      path.moveTo(0, 0);
65      path.lineTo(100, 0);
66      path.quadTo(50, -40,
67                  0, 0);
68      path.close();
69  
70      return path;
71  }
72  
cubic_path()73  SkPath cubic_path() {
74      SkPath path;
75      path.moveTo(0, 0);
76      path.cubicTo(25, 75,
77                   75, -50,
78                   100, 0);
79  
80      return path;
81  }
82  
oval_path()83  SkPath oval_path() {
84      SkRect oval = SkRect::MakeXYWH(0, -25, 100, 50);
85  
86      SkPath path;
87      path.arcTo(oval, 0, 359, true);
88      path.close();
89  
90      return path;
91  }
92  
ribs_path(SkPath path,SkScalar radius)93  SkPath ribs_path(SkPath path, SkScalar radius) {
94      SkPath ribs;
95  
96      const SkScalar spacing = 5.0f;
97      float accum = 0.0f;
98  
99      SkPathMeasure meas(path, false);
100      SkScalar length = meas.getLength();
101      SkPoint pos;
102      SkVector tan;
103      while (accum < length) {
104          if (meas.getPosTan(accum, &pos, &tan)) {
105              tan.scale(radius);
106              SkPointPriv::RotateCCW(&tan);
107  
108              ribs.moveTo(pos.x() + tan.x(), pos.y() + tan.y());
109              ribs.lineTo(pos.x() - tan.x(), pos.y() - tan.y());
110          }
111          accum += spacing;
112      }
113  
114      return ribs;
115  }
116  
draw_ribs(SkCanvas * canvas,SkPath path)117  void draw_ribs(SkCanvas *canvas, SkPath path) {
118      SkPath ribs = ribs_path(path, OVERSTROKE_WIDTH/2.0f);
119      SkPaint p = make_normal_paint();
120      p.setStrokeWidth(1);
121      p.setColor(SK_ColorBLUE);
122      p.setColor(SK_ColorGREEN);
123  
124      canvas->drawPath(ribs, p);
125  }
126  
127  ///////// quads
128  
draw_small_quad(SkCanvas * canvas)129  void draw_small_quad(SkCanvas *canvas) {
130      // scaled so it's visible
131      // canvas->scale(8, 8);
132  
133      SkPaint p = make_normal_paint();
134      SkPath path = quad_path();
135  
136      draw_ribs(canvas, path);
137      canvas->drawPath(path, p);
138  }
139  
draw_large_quad(SkCanvas * canvas)140  void draw_large_quad(SkCanvas *canvas) {
141      SkPaint p = make_overstroke_paint();
142      SkPath path = quad_path();
143  
144      canvas->drawPath(path, p);
145      draw_ribs(canvas, path);
146  }
147  
draw_quad_fillpath(SkCanvas * canvas)148  void draw_quad_fillpath(SkCanvas *canvas) {
149      SkPath path = quad_path();
150      SkPaint p = make_overstroke_paint();
151  
152      SkPaint fillp = make_normal_paint();
153      fillp.setColor(SK_ColorMAGENTA);
154  
155      SkPath fillpath;
156      p.getFillPath(path, &fillpath);
157  
158      canvas->drawPath(fillpath, fillp);
159  }
160  
draw_stroked_quad(SkCanvas * canvas)161  void draw_stroked_quad(SkCanvas *canvas) {
162      canvas->translate(400, 0);
163      draw_large_quad(canvas);
164      draw_quad_fillpath(canvas);
165  }
166  
167  ////////// cubics
168  
draw_small_cubic(SkCanvas * canvas)169  void draw_small_cubic(SkCanvas *canvas) {
170      SkPaint p = make_normal_paint();
171      SkPath path = cubic_path();
172  
173      draw_ribs(canvas, path);
174      canvas->drawPath(path, p);
175  }
176  
draw_large_cubic(SkCanvas * canvas)177  void draw_large_cubic(SkCanvas *canvas) {
178      SkPaint p = make_overstroke_paint();
179      SkPath path = cubic_path();
180  
181      canvas->drawPath(path, p);
182      draw_ribs(canvas, path);
183  }
184  
draw_cubic_fillpath(SkCanvas * canvas)185  void draw_cubic_fillpath(SkCanvas *canvas) {
186      SkPath path = cubic_path();
187      SkPaint p = make_overstroke_paint();
188  
189      SkPaint fillp = make_normal_paint();
190      fillp.setColor(SK_ColorMAGENTA);
191  
192      SkPath fillpath;
193      p.getFillPath(path, &fillpath);
194  
195      canvas->drawPath(fillpath, fillp);
196  }
197  
draw_stroked_cubic(SkCanvas * canvas)198  void draw_stroked_cubic(SkCanvas *canvas) {
199      canvas->translate(400, 0);
200      draw_large_cubic(canvas);
201      draw_cubic_fillpath(canvas);
202  }
203  
204  ////////// ovals
205  
draw_small_oval(SkCanvas * canvas)206  void draw_small_oval(SkCanvas *canvas) {
207      SkPaint p = make_normal_paint();
208  
209      SkPath path = oval_path();
210  
211      draw_ribs(canvas, path);
212      canvas->drawPath(path, p);
213  }
214  
draw_large_oval(SkCanvas * canvas)215  void draw_large_oval(SkCanvas *canvas) {
216      SkPaint p = make_overstroke_paint();
217      SkPath path = oval_path();
218  
219      canvas->drawPath(path, p);
220      draw_ribs(canvas, path);
221  }
222  
draw_oval_fillpath(SkCanvas * canvas)223  void draw_oval_fillpath(SkCanvas *canvas) {
224      SkPath path = oval_path();
225      SkPaint p = make_overstroke_paint();
226  
227      SkPaint fillp = make_normal_paint();
228      fillp.setColor(SK_ColorMAGENTA);
229  
230      SkPath fillpath;
231      p.getFillPath(path, &fillpath);
232  
233      canvas->drawPath(fillpath, fillp);
234  }
235  
draw_stroked_oval(SkCanvas * canvas)236  void draw_stroked_oval(SkCanvas *canvas) {
237      canvas->translate(400, 0);
238      draw_large_oval(canvas);
239      draw_oval_fillpath(canvas);
240  }
241  
242  ////////// gm
243  
244  void (*examples[])(SkCanvas *canvas) = {
245      draw_small_quad,    draw_stroked_quad, draw_small_cubic,
246      draw_stroked_cubic, draw_small_oval,   draw_stroked_oval,
247  };
248  
249  DEF_SIMPLE_GM(OverStroke, canvas, 500, 500) {
250      const size_t length = sizeof(examples) / sizeof(examples[0]);
251      const size_t width = 2;
252  
253      for (size_t i = 0; i < length; i++) {
254          int x = (int)(i % width);
255          int y = (int)(i / width);
256  
257          canvas->save();
258          canvas->translate(150.0f * x, 150.0f * y);
259          canvas->scale(0.2f, 0.2f);
260          canvas->translate(300.0f, 400.0f);
261  
262          examples[i](canvas);
263  
264          canvas->restore();
265      }
266  }
267