blob: 6352a94560fa9a510b29210e1fff16c31e4f2d68 [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
Mike Kleinc0bd9f92019-04-23 12:05:21 -050025#include "gm/gm.h"
Ben Wagner7fde8e12019-05-01 17:28:53 -040026#include "include/core/SkCanvas.h"
27#include "include/core/SkColor.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050028#include "include/core/SkPaint.h"
29#include "include/core/SkPath.h"
30#include "include/core/SkPathMeasure.h"
Ben Wagner7fde8e12019-05-01 17:28:53 -040031#include "include/core/SkPoint.h"
32#include "include/core/SkRect.h"
33#include "include/core/SkScalar.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050034#include "src/core/SkPointPriv.h"
hsternbb9b2242016-08-09 08:53:30 -070035
Ben Wagner7fde8e12019-05-01 17:28:53 -040036#include <cstddef>
37
hsternbb9b2242016-08-09 08:53:30 -070038const SkScalar OVERSTROKE_WIDTH = 500.0f;
39const SkScalar NORMALSTROKE_WIDTH = 3.0f;
hstern02aea1c2016-08-02 10:35:57 -070040
41//////// path and paint builders
42
hsternbb9b2242016-08-09 08:53:30 -070043SkPaint make_normal_paint() {
44 SkPaint p;
45 p.setAntiAlias(true);
46 p.setStyle(SkPaint::kStroke_Style);
47 p.setStrokeWidth(NORMALSTROKE_WIDTH);
48 p.setColor(SK_ColorBLUE);
49
50 return p;
51}
52
hstern02aea1c2016-08-02 10:35:57 -070053SkPaint make_overstroke_paint() {
54 SkPaint p;
55 p.setAntiAlias(true);
56 p.setStyle(SkPaint::kStroke_Style);
hsternbb9b2242016-08-09 08:53:30 -070057 p.setStrokeWidth(OVERSTROKE_WIDTH);
hstern02aea1c2016-08-02 10:35:57 -070058
59 return p;
60}
61
62SkPath quad_path() {
63 SkPath path;
64 path.moveTo(0, 0);
65 path.lineTo(100, 0);
66 path.quadTo(50, -40,
67 0, 0);
hsternbb9b2242016-08-09 08:53:30 -070068 path.close();
hstern02aea1c2016-08-02 10:35:57 -070069
70 return path;
71}
72
73SkPath cubic_path() {
74 SkPath path;
75 path.moveTo(0, 0);
76 path.cubicTo(25, 75,
77 75, -50,
78 100, 0);
79
80 return path;
81}
82
83SkPath oval_path() {
84 SkRect oval = SkRect::MakeXYWH(0, -25, 100, 50);
85
86 SkPath path;
87 path.arcTo(oval, 0, 359, true);
88 path.close();
89
90 return path;
91}
92
hsternbb9b2242016-08-09 08:53:30 -070093SkPath ribs_path(SkPath path, SkScalar radius) {
94 SkPath ribs;
95
96 const SkScalar spacing = 5.0f;
97 float accum = 0.0f;
98
99 SkPathMeasure meas(path, false);
100 SkScalar length = meas.getLength();
101 SkPoint pos;
102 SkVector tan;
103 while (accum < length) {
104 if (meas.getPosTan(accum, &pos, &tan)) {
105 tan.scale(radius);
Cary Clarkdf429f32017-11-08 11:44:31 -0500106 SkPointPriv::RotateCCW(&tan);
hsternbb9b2242016-08-09 08:53:30 -0700107
108 ribs.moveTo(pos.x() + tan.x(), pos.y() + tan.y());
109 ribs.lineTo(pos.x() - tan.x(), pos.y() - tan.y());
110 }
111 accum += spacing;
112 }
113
114 return ribs;
115}
116
117void draw_ribs(SkCanvas *canvas, SkPath path) {
118 SkPath ribs = ribs_path(path, OVERSTROKE_WIDTH/2.0f);
119 SkPaint p = make_normal_paint();
120 p.setStrokeWidth(1);
121 p.setColor(SK_ColorBLUE);
122 p.setColor(SK_ColorGREEN);
123
124 canvas->drawPath(ribs, p);
125}
126
hstern02aea1c2016-08-02 10:35:57 -0700127///////// quads
128
129void draw_small_quad(SkCanvas *canvas) {
130 // scaled so it's visible
hsternbb9b2242016-08-09 08:53:30 -0700131 // canvas->scale(8, 8);
hstern02aea1c2016-08-02 10:35:57 -0700132
hsternbb9b2242016-08-09 08:53:30 -0700133 SkPaint p = make_normal_paint();
hstern02aea1c2016-08-02 10:35:57 -0700134 SkPath path = quad_path();
135
hsternbb9b2242016-08-09 08:53:30 -0700136 draw_ribs(canvas, path);
hstern02aea1c2016-08-02 10:35:57 -0700137 canvas->drawPath(path, p);
138}
139
140void draw_large_quad(SkCanvas *canvas) {
141 SkPaint p = make_overstroke_paint();
142 SkPath path = quad_path();
143
144 canvas->drawPath(path, p);
hsternbb9b2242016-08-09 08:53:30 -0700145 draw_ribs(canvas, path);
hstern02aea1c2016-08-02 10:35:57 -0700146}
147
148void draw_quad_fillpath(SkCanvas *canvas) {
149 SkPath path = quad_path();
150 SkPaint p = make_overstroke_paint();
151
hsternbb9b2242016-08-09 08:53:30 -0700152 SkPaint fillp = make_normal_paint();
hstern02aea1c2016-08-02 10:35:57 -0700153 fillp.setColor(SK_ColorMAGENTA);
154
155 SkPath fillpath;
156 p.getFillPath(path, &fillpath);
157
158 canvas->drawPath(fillpath, fillp);
159}
160
161void draw_stroked_quad(SkCanvas *canvas) {
hsternbb9b2242016-08-09 08:53:30 -0700162 canvas->translate(400, 0);
hstern02aea1c2016-08-02 10:35:57 -0700163 draw_large_quad(canvas);
164 draw_quad_fillpath(canvas);
165}
166
167////////// cubics
168
169void draw_small_cubic(SkCanvas *canvas) {
hsternbb9b2242016-08-09 08:53:30 -0700170 SkPaint p = make_normal_paint();
hstern02aea1c2016-08-02 10:35:57 -0700171 SkPath path = cubic_path();
172
hsternbb9b2242016-08-09 08:53:30 -0700173 draw_ribs(canvas, path);
hstern02aea1c2016-08-02 10:35:57 -0700174 canvas->drawPath(path, p);
175}
176
177void draw_large_cubic(SkCanvas *canvas) {
178 SkPaint p = make_overstroke_paint();
179 SkPath path = cubic_path();
180
181 canvas->drawPath(path, p);
hsternbb9b2242016-08-09 08:53:30 -0700182 draw_ribs(canvas, path);
hstern02aea1c2016-08-02 10:35:57 -0700183}
184
185void draw_cubic_fillpath(SkCanvas *canvas) {
186 SkPath path = cubic_path();
187 SkPaint p = make_overstroke_paint();
188
hsternbb9b2242016-08-09 08:53:30 -0700189 SkPaint fillp = make_normal_paint();
hstern02aea1c2016-08-02 10:35:57 -0700190 fillp.setColor(SK_ColorMAGENTA);
191
192 SkPath fillpath;
193 p.getFillPath(path, &fillpath);
194
195 canvas->drawPath(fillpath, fillp);
196}
197
198void draw_stroked_cubic(SkCanvas *canvas) {
199 canvas->translate(400, 0);
200 draw_large_cubic(canvas);
201 draw_cubic_fillpath(canvas);
202}
203
204////////// ovals
205
206void draw_small_oval(SkCanvas *canvas) {
hsternbb9b2242016-08-09 08:53:30 -0700207 SkPaint p = make_normal_paint();
hstern02aea1c2016-08-02 10:35:57 -0700208
209 SkPath path = oval_path();
210
hsternbb9b2242016-08-09 08:53:30 -0700211 draw_ribs(canvas, path);
hstern02aea1c2016-08-02 10:35:57 -0700212 canvas->drawPath(path, p);
213}
214
215void draw_large_oval(SkCanvas *canvas) {
216 SkPaint p = make_overstroke_paint();
217 SkPath path = oval_path();
218
219 canvas->drawPath(path, p);
hsternbb9b2242016-08-09 08:53:30 -0700220 draw_ribs(canvas, path);
hstern02aea1c2016-08-02 10:35:57 -0700221}
222
223void draw_oval_fillpath(SkCanvas *canvas) {
224 SkPath path = oval_path();
225 SkPaint p = make_overstroke_paint();
226
hsternbb9b2242016-08-09 08:53:30 -0700227 SkPaint fillp = make_normal_paint();
hstern02aea1c2016-08-02 10:35:57 -0700228 fillp.setColor(SK_ColorMAGENTA);
229
230 SkPath fillpath;
231 p.getFillPath(path, &fillpath);
232
233 canvas->drawPath(fillpath, fillp);
234}
235
236void draw_stroked_oval(SkCanvas *canvas) {
237 canvas->translate(400, 0);
238 draw_large_oval(canvas);
239 draw_oval_fillpath(canvas);
240}
241
242////////// gm
243
244void (*examples[])(SkCanvas *canvas) = {
245 draw_small_quad, draw_stroked_quad, draw_small_cubic,
246 draw_stroked_cubic, draw_small_oval, draw_stroked_oval,
247};
248
249DEF_SIMPLE_GM(OverStroke, canvas, 500, 500) {
250 const size_t length = sizeof(examples) / sizeof(examples[0]);
251 const size_t width = 2;
252
253 for (size_t i = 0; i < length; i++) {
254 int x = (int)(i % width);
255 int y = (int)(i / width);
256
257 canvas->save();
hsternbb9b2242016-08-09 08:53:30 -0700258 canvas->translate(150.0f * x, 150.0f * y);
259 canvas->scale(0.2f, 0.2f);
260 canvas->translate(300.0f, 400.0f);
hstern02aea1c2016-08-02 10:35:57 -0700261
262 examples[i](canvas);
263
264 canvas->restore();
265 }
266}