blob: 72aa782a488f66799944a464aed25e2f99b570cb [file] [log] [blame]
hstern02aea1c2016-08-02 10:35:57 -07001/*
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 *
Robert Phillips84fd1c22021-03-23 13:18:36 -040016 * 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.
hsternbb9b2242016-08-09 08:53:30 -070018 *
hstern02aea1c2016-08-02 10:35:57 -070019 * See crbug.com/589769 skbug.com/5405 skbug.com/5406
20 */
21
Mike Kleinc0bd9f92019-04-23 12:05:21 -050022#include "gm/gm.h"
Ben Wagner7fde8e12019-05-01 17:28:53 -040023#include "include/core/SkCanvas.h"
24#include "include/core/SkColor.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050025#include "include/core/SkPaint.h"
Mike Reedcfb130c2020-08-03 11:02:20 -040026#include "include/core/SkPathBuilder.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050027#include "include/core/SkPathMeasure.h"
Ben Wagner7fde8e12019-05-01 17:28:53 -040028#include "include/core/SkPoint.h"
29#include "include/core/SkRect.h"
30#include "include/core/SkScalar.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050031#include "src/core/SkPointPriv.h"
hsternbb9b2242016-08-09 08:53:30 -070032
Ben Wagner7fde8e12019-05-01 17:28:53 -040033#include <cstddef>
34
hsternbb9b2242016-08-09 08:53:30 -070035const SkScalar OVERSTROKE_WIDTH = 500.0f;
36const SkScalar NORMALSTROKE_WIDTH = 3.0f;
hstern02aea1c2016-08-02 10:35:57 -070037
38//////// path and paint builders
39
hsternbb9b2242016-08-09 08:53:30 -070040SkPaint 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
hstern02aea1c2016-08-02 10:35:57 -070050SkPaint make_overstroke_paint() {
51 SkPaint p;
52 p.setAntiAlias(true);
53 p.setStyle(SkPaint::kStroke_Style);
hsternbb9b2242016-08-09 08:53:30 -070054 p.setStrokeWidth(OVERSTROKE_WIDTH);
hstern02aea1c2016-08-02 10:35:57 -070055
56 return p;
57}
58
59SkPath quad_path() {
Mike Reed06d7c9d2020-08-26 12:56:51 -040060 return SkPathBuilder().moveTo(0, 0)
61 .lineTo(100, 0)
62 .quadTo(50, -40, 0, 0)
63 .close()
64 .detach();
hstern02aea1c2016-08-02 10:35:57 -070065}
66
67SkPath 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
77SkPath oval_path() {
78 SkRect oval = SkRect::MakeXYWH(0, -25, 100, 50);
79
Mike Reedcfb130c2020-08-03 11:02:20 -040080 return SkPathBuilder().arcTo(oval, 0, 359, true).close().detach();
hstern02aea1c2016-08-02 10:35:57 -070081}
82
hsternbb9b2242016-08-09 08:53:30 -070083SkPath 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);
Cary Clarkdf429f32017-11-08 11:44:31 -050096 SkPointPriv::RotateCCW(&tan);
hsternbb9b2242016-08-09 08:53:30 -070097
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
107void 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
hstern02aea1c2016-08-02 10:35:57 -0700117///////// quads
118
119void draw_small_quad(SkCanvas *canvas) {
120 // scaled so it's visible
hsternbb9b2242016-08-09 08:53:30 -0700121 // canvas->scale(8, 8);
hstern02aea1c2016-08-02 10:35:57 -0700122
hsternbb9b2242016-08-09 08:53:30 -0700123 SkPaint p = make_normal_paint();
hstern02aea1c2016-08-02 10:35:57 -0700124 SkPath path = quad_path();
125
hsternbb9b2242016-08-09 08:53:30 -0700126 draw_ribs(canvas, path);
hstern02aea1c2016-08-02 10:35:57 -0700127 canvas->drawPath(path, p);
128}
129
130void draw_large_quad(SkCanvas *canvas) {
131 SkPaint p = make_overstroke_paint();
132 SkPath path = quad_path();
133
134 canvas->drawPath(path, p);
hsternbb9b2242016-08-09 08:53:30 -0700135 draw_ribs(canvas, path);
hstern02aea1c2016-08-02 10:35:57 -0700136}
137
138void draw_quad_fillpath(SkCanvas *canvas) {
139 SkPath path = quad_path();
140 SkPaint p = make_overstroke_paint();
141
hsternbb9b2242016-08-09 08:53:30 -0700142 SkPaint fillp = make_normal_paint();
hstern02aea1c2016-08-02 10:35:57 -0700143 fillp.setColor(SK_ColorMAGENTA);
144
145 SkPath fillpath;
146 p.getFillPath(path, &fillpath);
147
148 canvas->drawPath(fillpath, fillp);
149}
150
151void draw_stroked_quad(SkCanvas *canvas) {
hsternbb9b2242016-08-09 08:53:30 -0700152 canvas->translate(400, 0);
hstern02aea1c2016-08-02 10:35:57 -0700153 draw_large_quad(canvas);
154 draw_quad_fillpath(canvas);
155}
156
157////////// cubics
158
159void draw_small_cubic(SkCanvas *canvas) {
hsternbb9b2242016-08-09 08:53:30 -0700160 SkPaint p = make_normal_paint();
hstern02aea1c2016-08-02 10:35:57 -0700161 SkPath path = cubic_path();
162
hsternbb9b2242016-08-09 08:53:30 -0700163 draw_ribs(canvas, path);
hstern02aea1c2016-08-02 10:35:57 -0700164 canvas->drawPath(path, p);
165}
166
167void draw_large_cubic(SkCanvas *canvas) {
168 SkPaint p = make_overstroke_paint();
169 SkPath path = cubic_path();
170
171 canvas->drawPath(path, p);
hsternbb9b2242016-08-09 08:53:30 -0700172 draw_ribs(canvas, path);
hstern02aea1c2016-08-02 10:35:57 -0700173}
174
175void draw_cubic_fillpath(SkCanvas *canvas) {
176 SkPath path = cubic_path();
177 SkPaint p = make_overstroke_paint();
178
hsternbb9b2242016-08-09 08:53:30 -0700179 SkPaint fillp = make_normal_paint();
hstern02aea1c2016-08-02 10:35:57 -0700180 fillp.setColor(SK_ColorMAGENTA);
181
182 SkPath fillpath;
183 p.getFillPath(path, &fillpath);
184
185 canvas->drawPath(fillpath, fillp);
186}
187
188void 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
196void draw_small_oval(SkCanvas *canvas) {
hsternbb9b2242016-08-09 08:53:30 -0700197 SkPaint p = make_normal_paint();
hstern02aea1c2016-08-02 10:35:57 -0700198
199 SkPath path = oval_path();
200
hsternbb9b2242016-08-09 08:53:30 -0700201 draw_ribs(canvas, path);
hstern02aea1c2016-08-02 10:35:57 -0700202 canvas->drawPath(path, p);
203}
204
205void draw_large_oval(SkCanvas *canvas) {
206 SkPaint p = make_overstroke_paint();
207 SkPath path = oval_path();
208
209 canvas->drawPath(path, p);
hsternbb9b2242016-08-09 08:53:30 -0700210 draw_ribs(canvas, path);
hstern02aea1c2016-08-02 10:35:57 -0700211}
212
213void draw_oval_fillpath(SkCanvas *canvas) {
214 SkPath path = oval_path();
215 SkPaint p = make_overstroke_paint();
216
hsternbb9b2242016-08-09 08:53:30 -0700217 SkPaint fillp = make_normal_paint();
hstern02aea1c2016-08-02 10:35:57 -0700218 fillp.setColor(SK_ColorMAGENTA);
219
220 SkPath fillpath;
221 p.getFillPath(path, &fillpath);
222
223 canvas->drawPath(fillpath, fillp);
224}
225
226void 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
234void (*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
239DEF_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();
hsternbb9b2242016-08-09 08:53:30 -0700248 canvas->translate(150.0f * x, 150.0f * y);
249 canvas->scale(0.2f, 0.2f);
250 canvas->translate(300.0f, 400.0f);
hstern02aea1c2016-08-02 10:35:57 -0700251
252 examples[i](canvas);
253
254 canvas->restore();
255 }
256}