• 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  * 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