• Home
  • History
  • Annotate
  • 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  
26  #include "gm.h"
27  #include "SkPaint.h"
28  #include "SkPath.h"
29  #include "SkPathMeasure.h"
30  #include "SkPointPriv.h"
31  
32  const SkScalar OVERSTROKE_WIDTH = 500.0f;
33  const SkScalar NORMALSTROKE_WIDTH = 3.0f;
34  
35  //////// path and paint builders
36  
make_normal_paint()37  SkPaint make_normal_paint() {
38      SkPaint p;
39      p.setAntiAlias(true);
40      p.setStyle(SkPaint::kStroke_Style);
41      p.setStrokeWidth(NORMALSTROKE_WIDTH);
42      p.setColor(SK_ColorBLUE);
43  
44      return p;
45  }
46  
make_overstroke_paint()47  SkPaint make_overstroke_paint() {
48      SkPaint p;
49      p.setAntiAlias(true);
50      p.setStyle(SkPaint::kStroke_Style);
51      p.setStrokeWidth(OVERSTROKE_WIDTH);
52  
53      return p;
54  }
55  
quad_path()56  SkPath quad_path() {
57      SkPath path;
58      path.moveTo(0, 0);
59      path.lineTo(100, 0);
60      path.quadTo(50, -40,
61                  0, 0);
62      path.close();
63  
64      return path;
65  }
66  
cubic_path()67  SkPath cubic_path() {
68      SkPath path;
69      path.moveTo(0, 0);
70      path.cubicTo(25, 75,
71                   75, -50,
72                   100, 0);
73  
74      return path;
75  }
76  
oval_path()77  SkPath oval_path() {
78      SkRect oval = SkRect::MakeXYWH(0, -25, 100, 50);
79  
80      SkPath path;
81      path.arcTo(oval, 0, 359, true);
82      path.close();
83  
84      return path;
85  }
86  
ribs_path(SkPath path,SkScalar radius)87  SkPath ribs_path(SkPath path, SkScalar radius) {
88      SkPath ribs;
89  
90      const SkScalar spacing = 5.0f;
91      float accum = 0.0f;
92  
93      SkPathMeasure meas(path, false);
94      SkScalar length = meas.getLength();
95      SkPoint pos;
96      SkVector tan;
97      while (accum < length) {
98          if (meas.getPosTan(accum, &pos, &tan)) {
99              tan.scale(radius);
100              SkPointPriv::RotateCCW(&tan);
101  
102              ribs.moveTo(pos.x() + tan.x(), pos.y() + tan.y());
103              ribs.lineTo(pos.x() - tan.x(), pos.y() - tan.y());
104          }
105          accum += spacing;
106      }
107  
108      return ribs;
109  }
110  
draw_ribs(SkCanvas * canvas,SkPath path)111  void draw_ribs(SkCanvas *canvas, SkPath path) {
112      SkPath ribs = ribs_path(path, OVERSTROKE_WIDTH/2.0f);
113      SkPaint p = make_normal_paint();
114      p.setStrokeWidth(1);
115      p.setColor(SK_ColorBLUE);
116      p.setColor(SK_ColorGREEN);
117  
118      canvas->drawPath(ribs, p);
119  }
120  
121  ///////// quads
122  
draw_small_quad(SkCanvas * canvas)123  void draw_small_quad(SkCanvas *canvas) {
124      // scaled so it's visible
125      // canvas->scale(8, 8);
126  
127      SkPaint p = make_normal_paint();
128      SkPath path = quad_path();
129  
130      draw_ribs(canvas, path);
131      canvas->drawPath(path, p);
132  }
133  
draw_large_quad(SkCanvas * canvas)134  void draw_large_quad(SkCanvas *canvas) {
135      SkPaint p = make_overstroke_paint();
136      SkPath path = quad_path();
137  
138      canvas->drawPath(path, p);
139      draw_ribs(canvas, path);
140  }
141  
draw_quad_fillpath(SkCanvas * canvas)142  void draw_quad_fillpath(SkCanvas *canvas) {
143      SkPath path = quad_path();
144      SkPaint p = make_overstroke_paint();
145  
146      SkPaint fillp = make_normal_paint();
147      fillp.setColor(SK_ColorMAGENTA);
148  
149      SkPath fillpath;
150      p.getFillPath(path, &fillpath);
151  
152      canvas->drawPath(fillpath, fillp);
153  }
154  
draw_stroked_quad(SkCanvas * canvas)155  void draw_stroked_quad(SkCanvas *canvas) {
156      canvas->translate(400, 0);
157      draw_large_quad(canvas);
158      draw_quad_fillpath(canvas);
159  }
160  
161  ////////// cubics
162  
draw_small_cubic(SkCanvas * canvas)163  void draw_small_cubic(SkCanvas *canvas) {
164      SkPaint p = make_normal_paint();
165      SkPath path = cubic_path();
166  
167      draw_ribs(canvas, path);
168      canvas->drawPath(path, p);
169  }
170  
draw_large_cubic(SkCanvas * canvas)171  void draw_large_cubic(SkCanvas *canvas) {
172      SkPaint p = make_overstroke_paint();
173      SkPath path = cubic_path();
174  
175      canvas->drawPath(path, p);
176      draw_ribs(canvas, path);
177  }
178  
draw_cubic_fillpath(SkCanvas * canvas)179  void draw_cubic_fillpath(SkCanvas *canvas) {
180      SkPath path = cubic_path();
181      SkPaint p = make_overstroke_paint();
182  
183      SkPaint fillp = make_normal_paint();
184      fillp.setColor(SK_ColorMAGENTA);
185  
186      SkPath fillpath;
187      p.getFillPath(path, &fillpath);
188  
189      canvas->drawPath(fillpath, fillp);
190  }
191  
draw_stroked_cubic(SkCanvas * canvas)192  void draw_stroked_cubic(SkCanvas *canvas) {
193      canvas->translate(400, 0);
194      draw_large_cubic(canvas);
195      draw_cubic_fillpath(canvas);
196  }
197  
198  ////////// ovals
199  
draw_small_oval(SkCanvas * canvas)200  void draw_small_oval(SkCanvas *canvas) {
201      SkPaint p = make_normal_paint();
202  
203      SkPath path = oval_path();
204  
205      draw_ribs(canvas, path);
206      canvas->drawPath(path, p);
207  }
208  
draw_large_oval(SkCanvas * canvas)209  void draw_large_oval(SkCanvas *canvas) {
210      SkPaint p = make_overstroke_paint();
211      SkPath path = oval_path();
212  
213      canvas->drawPath(path, p);
214      draw_ribs(canvas, path);
215  }
216  
draw_oval_fillpath(SkCanvas * canvas)217  void draw_oval_fillpath(SkCanvas *canvas) {
218      SkPath path = oval_path();
219      SkPaint p = make_overstroke_paint();
220  
221      SkPaint fillp = make_normal_paint();
222      fillp.setColor(SK_ColorMAGENTA);
223  
224      SkPath fillpath;
225      p.getFillPath(path, &fillpath);
226  
227      canvas->drawPath(fillpath, fillp);
228  }
229  
draw_stroked_oval(SkCanvas * canvas)230  void draw_stroked_oval(SkCanvas *canvas) {
231      canvas->translate(400, 0);
232      draw_large_oval(canvas);
233      draw_oval_fillpath(canvas);
234  }
235  
236  ////////// gm
237  
238  void (*examples[])(SkCanvas *canvas) = {
239      draw_small_quad,    draw_stroked_quad, draw_small_cubic,
240      draw_stroked_cubic, draw_small_oval,   draw_stroked_oval,
241  };
242  
243  DEF_SIMPLE_GM(OverStroke, canvas, 500, 500) {
244      const size_t length = sizeof(examples) / sizeof(examples[0]);
245      const size_t width = 2;
246  
247      for (size_t i = 0; i < length; i++) {
248          int x = (int)(i % width);
249          int y = (int)(i / width);
250  
251          canvas->save();
252          canvas->translate(150.0f * x, 150.0f * y);
253          canvas->scale(0.2f, 0.2f);
254          canvas->translate(300.0f, 400.0f);
255  
256          examples[i](canvas);
257  
258          canvas->restore();
259      }
260  }
261