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