blob: c8be68e69a017a9ef4a4b5fce989996d0ea7fc76 [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"
Kevin Lubick177a7122022-12-12 10:25:15 -050028#include "include/core/SkPathUtils.h"
Ben Wagner7fde8e12019-05-01 17:28:53 -040029#include "include/core/SkPoint.h"
30#include "include/core/SkRect.h"
31#include "include/core/SkScalar.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050032#include "src/core/SkPointPriv.h"
hsternbb9b2242016-08-09 08:53:30 -070033
Ben Wagner7fde8e12019-05-01 17:28:53 -040034#include <cstddef>
35
hsternbb9b2242016-08-09 08:53:30 -070036const SkScalar OVERSTROKE_WIDTH = 500.0f;
37const SkScalar NORMALSTROKE_WIDTH = 3.0f;
hstern02aea1c2016-08-02 10:35:57 -070038
39//////// path and paint builders
40
hsternbb9b2242016-08-09 08:53:30 -070041SkPaint 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
hstern02aea1c2016-08-02 10:35:57 -070051SkPaint make_overstroke_paint() {
52 SkPaint p;
53 p.setAntiAlias(true);
54 p.setStyle(SkPaint::kStroke_Style);
hsternbb9b2242016-08-09 08:53:30 -070055 p.setStrokeWidth(OVERSTROKE_WIDTH);
hstern02aea1c2016-08-02 10:35:57 -070056
57 return p;
58}
59
60SkPath quad_path() {
Mike Reed06d7c9d2020-08-26 12:56:51 -040061 return SkPathBuilder().moveTo(0, 0)
62 .lineTo(100, 0)
63 .quadTo(50, -40, 0, 0)
64 .close()
65 .detach();
hstern02aea1c2016-08-02 10:35:57 -070066}
67
68SkPath 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
78SkPath oval_path() {
79 SkRect oval = SkRect::MakeXYWH(0, -25, 100, 50);
80
Mike Reedcfb130c2020-08-03 11:02:20 -040081 return SkPathBuilder().arcTo(oval, 0, 359, true).close().detach();
hstern02aea1c2016-08-02 10:35:57 -070082}
83
hsternbb9b2242016-08-09 08:53:30 -070084SkPath 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);
Cary Clarkdf429f32017-11-08 11:44:31 -050097 SkPointPriv::RotateCCW(&tan);
hsternbb9b2242016-08-09 08:53:30 -070098
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
108void 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
hstern02aea1c2016-08-02 10:35:57 -0700118///////// quads
119
120void draw_small_quad(SkCanvas *canvas) {
121 // scaled so it's visible
hsternbb9b2242016-08-09 08:53:30 -0700122 // canvas->scale(8, 8);
hstern02aea1c2016-08-02 10:35:57 -0700123
hsternbb9b2242016-08-09 08:53:30 -0700124 SkPaint p = make_normal_paint();
hstern02aea1c2016-08-02 10:35:57 -0700125 SkPath path = quad_path();
126
hsternbb9b2242016-08-09 08:53:30 -0700127 draw_ribs(canvas, path);
hstern02aea1c2016-08-02 10:35:57 -0700128 canvas->drawPath(path, p);
129}
130
131void draw_large_quad(SkCanvas *canvas) {
132 SkPaint p = make_overstroke_paint();
133 SkPath path = quad_path();
134
135 canvas->drawPath(path, p);
hsternbb9b2242016-08-09 08:53:30 -0700136 draw_ribs(canvas, path);
hstern02aea1c2016-08-02 10:35:57 -0700137}
138
139void draw_quad_fillpath(SkCanvas *canvas) {
140 SkPath path = quad_path();
141 SkPaint p = make_overstroke_paint();
142
hsternbb9b2242016-08-09 08:53:30 -0700143 SkPaint fillp = make_normal_paint();
hstern02aea1c2016-08-02 10:35:57 -0700144 fillp.setColor(SK_ColorMAGENTA);
145
146 SkPath fillpath;
Kevin Lubickf5491282022-12-15 08:37:49 -0500147 skpathutils::FillPathWithPaint(path, p, &fillpath);
hstern02aea1c2016-08-02 10:35:57 -0700148
149 canvas->drawPath(fillpath, fillp);
150}
151
152void draw_stroked_quad(SkCanvas *canvas) {
hsternbb9b2242016-08-09 08:53:30 -0700153 canvas->translate(400, 0);
hstern02aea1c2016-08-02 10:35:57 -0700154 draw_large_quad(canvas);
155 draw_quad_fillpath(canvas);
156}
157
158////////// cubics
159
160void draw_small_cubic(SkCanvas *canvas) {
hsternbb9b2242016-08-09 08:53:30 -0700161 SkPaint p = make_normal_paint();
hstern02aea1c2016-08-02 10:35:57 -0700162 SkPath path = cubic_path();
163
hsternbb9b2242016-08-09 08:53:30 -0700164 draw_ribs(canvas, path);
hstern02aea1c2016-08-02 10:35:57 -0700165 canvas->drawPath(path, p);
166}
167
168void draw_large_cubic(SkCanvas *canvas) {
169 SkPaint p = make_overstroke_paint();
170 SkPath path = cubic_path();
171
172 canvas->drawPath(path, p);
hsternbb9b2242016-08-09 08:53:30 -0700173 draw_ribs(canvas, path);
hstern02aea1c2016-08-02 10:35:57 -0700174}
175
176void draw_cubic_fillpath(SkCanvas *canvas) {
177 SkPath path = cubic_path();
178 SkPaint p = make_overstroke_paint();
179
hsternbb9b2242016-08-09 08:53:30 -0700180 SkPaint fillp = make_normal_paint();
hstern02aea1c2016-08-02 10:35:57 -0700181 fillp.setColor(SK_ColorMAGENTA);
182
183 SkPath fillpath;
Kevin Lubickf5491282022-12-15 08:37:49 -0500184 skpathutils::FillPathWithPaint(path, p, &fillpath);
hstern02aea1c2016-08-02 10:35:57 -0700185
186 canvas->drawPath(fillpath, fillp);
187}
188
189void 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
197void draw_small_oval(SkCanvas *canvas) {
hsternbb9b2242016-08-09 08:53:30 -0700198 SkPaint p = make_normal_paint();
hstern02aea1c2016-08-02 10:35:57 -0700199
200 SkPath path = oval_path();
201
hsternbb9b2242016-08-09 08:53:30 -0700202 draw_ribs(canvas, path);
hstern02aea1c2016-08-02 10:35:57 -0700203 canvas->drawPath(path, p);
204}
205
206void draw_large_oval(SkCanvas *canvas) {
207 SkPaint p = make_overstroke_paint();
208 SkPath path = oval_path();
209
210 canvas->drawPath(path, p);
hsternbb9b2242016-08-09 08:53:30 -0700211 draw_ribs(canvas, path);
hstern02aea1c2016-08-02 10:35:57 -0700212}
213
214void draw_oval_fillpath(SkCanvas *canvas) {
215 SkPath path = oval_path();
216 SkPaint p = make_overstroke_paint();
217
hsternbb9b2242016-08-09 08:53:30 -0700218 SkPaint fillp = make_normal_paint();
hstern02aea1c2016-08-02 10:35:57 -0700219 fillp.setColor(SK_ColorMAGENTA);
220
221 SkPath fillpath;
Kevin Lubickf5491282022-12-15 08:37:49 -0500222 skpathutils::FillPathWithPaint(path, p, &fillpath);
hstern02aea1c2016-08-02 10:35:57 -0700223
224 canvas->drawPath(fillpath, fillp);
225}
226
227void 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
235void (*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
240DEF_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();
hsternbb9b2242016-08-09 08:53:30 -0700249 canvas->translate(150.0f * x, 150.0f * y);
250 canvas->scale(0.2f, 0.2f);
251 canvas->translate(300.0f, 400.0f);
hstern02aea1c2016-08-02 10:35:57 -0700252
253 examples[i](canvas);
254
255 canvas->restore();
256 }
257}