xref: /aosp_15_r20/external/skia/gm/overstroke.cpp (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
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