• 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