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