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