blob: 68278905cdf827be5764db858d49adee9e4e674b [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 *
hsternbb9b2242016-08-09 08:53:30 -070016 * If we run this using Nvidia Path Renderer with:
17 * `path/to/dm --match OverStroke -w gm_out --gpu --config nvpr16`
18 * then we get correct results, so that is a possible direction of attack -
19 * use the GPU and a completely different algorithm to get correctness in
20 * Skia.
21 *
hstern02aea1c2016-08-02 10:35:57 -070022 * See crbug.com/589769 skbug.com/5405 skbug.com/5406
23 */
24
25
26#include "gm.h"
27#include "SkPaint.h"
28#include "SkPath.h"
hsternbb9b2242016-08-09 08:53:30 -070029#include "SkPathMeasure.h"
Cary Clarkdf429f32017-11-08 11:44:31 -050030#include "SkPointPriv.h"
hsternbb9b2242016-08-09 08:53:30 -070031
32const SkScalar OVERSTROKE_WIDTH = 500.0f;
33const SkScalar NORMALSTROKE_WIDTH = 3.0f;
hstern02aea1c2016-08-02 10:35:57 -070034
35//////// path and paint builders
36
hsternbb9b2242016-08-09 08:53:30 -070037SkPaint make_normal_paint() {
38 SkPaint p;
39 p.setAntiAlias(true);
40 p.setStyle(SkPaint::kStroke_Style);
41 p.setStrokeWidth(NORMALSTROKE_WIDTH);
42 p.setColor(SK_ColorBLUE);
43
44 return p;
45}
46
hstern02aea1c2016-08-02 10:35:57 -070047SkPaint make_overstroke_paint() {
48 SkPaint p;
49 p.setAntiAlias(true);
50 p.setStyle(SkPaint::kStroke_Style);
hsternbb9b2242016-08-09 08:53:30 -070051 p.setStrokeWidth(OVERSTROKE_WIDTH);
hstern02aea1c2016-08-02 10:35:57 -070052
53 return p;
54}
55
56SkPath quad_path() {
57 SkPath path;
58 path.moveTo(0, 0);
59 path.lineTo(100, 0);
60 path.quadTo(50, -40,
61 0, 0);
hsternbb9b2242016-08-09 08:53:30 -070062 path.close();
hstern02aea1c2016-08-02 10:35:57 -070063
64 return path;
65}
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
80 SkPath path;
81 path.arcTo(oval, 0, 359, true);
82 path.close();
83
84 return path;
85}
86
hsternbb9b2242016-08-09 08:53:30 -070087SkPath ribs_path(SkPath path, SkScalar radius) {
88 SkPath ribs;
89
90 const SkScalar spacing = 5.0f;
91 float accum = 0.0f;
92
93 SkPathMeasure meas(path, false);
94 SkScalar length = meas.getLength();
95 SkPoint pos;
96 SkVector tan;
97 while (accum < length) {
98 if (meas.getPosTan(accum, &pos, &tan)) {
99 tan.scale(radius);
Cary Clarkdf429f32017-11-08 11:44:31 -0500100 SkPointPriv::RotateCCW(&tan);
hsternbb9b2242016-08-09 08:53:30 -0700101
102 ribs.moveTo(pos.x() + tan.x(), pos.y() + tan.y());
103 ribs.lineTo(pos.x() - tan.x(), pos.y() - tan.y());
104 }
105 accum += spacing;
106 }
107
108 return ribs;
109}
110
111void draw_ribs(SkCanvas *canvas, SkPath path) {
112 SkPath ribs = ribs_path(path, OVERSTROKE_WIDTH/2.0f);
113 SkPaint p = make_normal_paint();
114 p.setStrokeWidth(1);
115 p.setColor(SK_ColorBLUE);
116 p.setColor(SK_ColorGREEN);
117
118 canvas->drawPath(ribs, p);
119}
120
hstern02aea1c2016-08-02 10:35:57 -0700121///////// quads
122
123void draw_small_quad(SkCanvas *canvas) {
124 // scaled so it's visible
hsternbb9b2242016-08-09 08:53:30 -0700125 // canvas->scale(8, 8);
hstern02aea1c2016-08-02 10:35:57 -0700126
hsternbb9b2242016-08-09 08:53:30 -0700127 SkPaint p = make_normal_paint();
hstern02aea1c2016-08-02 10:35:57 -0700128 SkPath path = quad_path();
129
hsternbb9b2242016-08-09 08:53:30 -0700130 draw_ribs(canvas, path);
hstern02aea1c2016-08-02 10:35:57 -0700131 canvas->drawPath(path, p);
132}
133
134void draw_large_quad(SkCanvas *canvas) {
135 SkPaint p = make_overstroke_paint();
136 SkPath path = quad_path();
137
138 canvas->drawPath(path, p);
hsternbb9b2242016-08-09 08:53:30 -0700139 draw_ribs(canvas, path);
hstern02aea1c2016-08-02 10:35:57 -0700140}
141
142void draw_quad_fillpath(SkCanvas *canvas) {
143 SkPath path = quad_path();
144 SkPaint p = make_overstroke_paint();
145
hsternbb9b2242016-08-09 08:53:30 -0700146 SkPaint fillp = make_normal_paint();
hstern02aea1c2016-08-02 10:35:57 -0700147 fillp.setColor(SK_ColorMAGENTA);
148
149 SkPath fillpath;
150 p.getFillPath(path, &fillpath);
151
152 canvas->drawPath(fillpath, fillp);
153}
154
155void draw_stroked_quad(SkCanvas *canvas) {
hsternbb9b2242016-08-09 08:53:30 -0700156 canvas->translate(400, 0);
hstern02aea1c2016-08-02 10:35:57 -0700157 draw_large_quad(canvas);
158 draw_quad_fillpath(canvas);
159}
160
161////////// cubics
162
163void draw_small_cubic(SkCanvas *canvas) {
hsternbb9b2242016-08-09 08:53:30 -0700164 SkPaint p = make_normal_paint();
hstern02aea1c2016-08-02 10:35:57 -0700165 SkPath path = cubic_path();
166
hsternbb9b2242016-08-09 08:53:30 -0700167 draw_ribs(canvas, path);
hstern02aea1c2016-08-02 10:35:57 -0700168 canvas->drawPath(path, p);
169}
170
171void draw_large_cubic(SkCanvas *canvas) {
172 SkPaint p = make_overstroke_paint();
173 SkPath path = cubic_path();
174
175 canvas->drawPath(path, p);
hsternbb9b2242016-08-09 08:53:30 -0700176 draw_ribs(canvas, path);
hstern02aea1c2016-08-02 10:35:57 -0700177}
178
179void draw_cubic_fillpath(SkCanvas *canvas) {
180 SkPath path = cubic_path();
181 SkPaint p = make_overstroke_paint();
182
hsternbb9b2242016-08-09 08:53:30 -0700183 SkPaint fillp = make_normal_paint();
hstern02aea1c2016-08-02 10:35:57 -0700184 fillp.setColor(SK_ColorMAGENTA);
185
186 SkPath fillpath;
187 p.getFillPath(path, &fillpath);
188
189 canvas->drawPath(fillpath, fillp);
190}
191
192void draw_stroked_cubic(SkCanvas *canvas) {
193 canvas->translate(400, 0);
194 draw_large_cubic(canvas);
195 draw_cubic_fillpath(canvas);
196}
197
198////////// ovals
199
200void draw_small_oval(SkCanvas *canvas) {
hsternbb9b2242016-08-09 08:53:30 -0700201 SkPaint p = make_normal_paint();
hstern02aea1c2016-08-02 10:35:57 -0700202
203 SkPath path = oval_path();
204
hsternbb9b2242016-08-09 08:53:30 -0700205 draw_ribs(canvas, path);
hstern02aea1c2016-08-02 10:35:57 -0700206 canvas->drawPath(path, p);
207}
208
209void draw_large_oval(SkCanvas *canvas) {
210 SkPaint p = make_overstroke_paint();
211 SkPath path = oval_path();
212
213 canvas->drawPath(path, p);
hsternbb9b2242016-08-09 08:53:30 -0700214 draw_ribs(canvas, path);
hstern02aea1c2016-08-02 10:35:57 -0700215}
216
217void draw_oval_fillpath(SkCanvas *canvas) {
218 SkPath path = oval_path();
219 SkPaint p = make_overstroke_paint();
220
hsternbb9b2242016-08-09 08:53:30 -0700221 SkPaint fillp = make_normal_paint();
hstern02aea1c2016-08-02 10:35:57 -0700222 fillp.setColor(SK_ColorMAGENTA);
223
224 SkPath fillpath;
225 p.getFillPath(path, &fillpath);
226
227 canvas->drawPath(fillpath, fillp);
228}
229
230void draw_stroked_oval(SkCanvas *canvas) {
231 canvas->translate(400, 0);
232 draw_large_oval(canvas);
233 draw_oval_fillpath(canvas);
234}
235
236////////// gm
237
238void (*examples[])(SkCanvas *canvas) = {
239 draw_small_quad, draw_stroked_quad, draw_small_cubic,
240 draw_stroked_cubic, draw_small_oval, draw_stroked_oval,
241};
242
243DEF_SIMPLE_GM(OverStroke, canvas, 500, 500) {
244 const size_t length = sizeof(examples) / sizeof(examples[0]);
245 const size_t width = 2;
246
247 for (size_t i = 0; i < length; i++) {
248 int x = (int)(i % width);
249 int y = (int)(i / width);
250
251 canvas->save();
hsternbb9b2242016-08-09 08:53:30 -0700252 canvas->translate(150.0f * x, 150.0f * y);
253 canvas->scale(0.2f, 0.2f);
254 canvas->translate(300.0f, 400.0f);
hstern02aea1c2016-08-02 10:35:57 -0700255
256 examples[i](canvas);
257
258 canvas->restore();
259 }
260}