1*c8dee2aaSAndroid Build Coastguard Worker /*
2*c8dee2aaSAndroid Build Coastguard Worker * Copyright 2016 Google Inc.
3*c8dee2aaSAndroid Build Coastguard Worker *
4*c8dee2aaSAndroid Build Coastguard Worker * Use of this source code is governed by a BSD-style license that can be
5*c8dee2aaSAndroid Build Coastguard Worker * found in the LICENSE file.
6*c8dee2aaSAndroid Build Coastguard Worker */
7*c8dee2aaSAndroid Build Coastguard Worker
8*c8dee2aaSAndroid Build Coastguard Worker
9*c8dee2aaSAndroid Build Coastguard Worker /*
10*c8dee2aaSAndroid Build Coastguard Worker * This GM exercises stroking of paths with large stroke lengths, which is
11*c8dee2aaSAndroid Build Coastguard Worker * referred to as "overstroke" for brevity. In Skia as of 8/2016 we offset
12*c8dee2aaSAndroid Build Coastguard Worker * each part of the curve the request amount even if it makes the offsets
13*c8dee2aaSAndroid Build Coastguard Worker * overlap and create holes. There is not a really great algorithm for this
14*c8dee2aaSAndroid Build Coastguard Worker * and several other 2D graphics engines have the same bug.
15*c8dee2aaSAndroid Build Coastguard Worker *
16*c8dee2aaSAndroid Build Coastguard Worker * The old Nvidia Path Renderer used to yield correct results, so a possible
17*c8dee2aaSAndroid Build Coastguard Worker * direction of attack is to use the GPU and a completely different algorithm.
18*c8dee2aaSAndroid Build Coastguard Worker *
19*c8dee2aaSAndroid Build Coastguard Worker * See crbug.com/589769 skbug.com/5405 skbug.com/5406
20*c8dee2aaSAndroid Build Coastguard Worker */
21*c8dee2aaSAndroid Build Coastguard Worker
22*c8dee2aaSAndroid Build Coastguard Worker #include "gm/gm.h"
23*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkCanvas.h"
24*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkColor.h"
25*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkPaint.h"
26*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkPathBuilder.h"
27*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkPathMeasure.h"
28*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkPathUtils.h"
29*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkPoint.h"
30*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkRect.h"
31*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkScalar.h"
32*c8dee2aaSAndroid Build Coastguard Worker #include "src/core/SkPointPriv.h"
33*c8dee2aaSAndroid Build Coastguard Worker
34*c8dee2aaSAndroid Build Coastguard Worker #include <cstddef>
35*c8dee2aaSAndroid Build Coastguard Worker
36*c8dee2aaSAndroid Build Coastguard Worker const SkScalar OVERSTROKE_WIDTH = 500.0f;
37*c8dee2aaSAndroid Build Coastguard Worker const SkScalar NORMALSTROKE_WIDTH = 3.0f;
38*c8dee2aaSAndroid Build Coastguard Worker
39*c8dee2aaSAndroid Build Coastguard Worker //////// path and paint builders
40*c8dee2aaSAndroid Build Coastguard Worker
make_normal_paint()41*c8dee2aaSAndroid Build Coastguard Worker SkPaint make_normal_paint() {
42*c8dee2aaSAndroid Build Coastguard Worker SkPaint p;
43*c8dee2aaSAndroid Build Coastguard Worker p.setAntiAlias(true);
44*c8dee2aaSAndroid Build Coastguard Worker p.setStyle(SkPaint::kStroke_Style);
45*c8dee2aaSAndroid Build Coastguard Worker p.setStrokeWidth(NORMALSTROKE_WIDTH);
46*c8dee2aaSAndroid Build Coastguard Worker p.setColor(SK_ColorBLUE);
47*c8dee2aaSAndroid Build Coastguard Worker
48*c8dee2aaSAndroid Build Coastguard Worker return p;
49*c8dee2aaSAndroid Build Coastguard Worker }
50*c8dee2aaSAndroid Build Coastguard Worker
make_overstroke_paint()51*c8dee2aaSAndroid Build Coastguard Worker SkPaint make_overstroke_paint() {
52*c8dee2aaSAndroid Build Coastguard Worker SkPaint p;
53*c8dee2aaSAndroid Build Coastguard Worker p.setAntiAlias(true);
54*c8dee2aaSAndroid Build Coastguard Worker p.setStyle(SkPaint::kStroke_Style);
55*c8dee2aaSAndroid Build Coastguard Worker p.setStrokeWidth(OVERSTROKE_WIDTH);
56*c8dee2aaSAndroid Build Coastguard Worker
57*c8dee2aaSAndroid Build Coastguard Worker return p;
58*c8dee2aaSAndroid Build Coastguard Worker }
59*c8dee2aaSAndroid Build Coastguard Worker
quad_path()60*c8dee2aaSAndroid Build Coastguard Worker SkPath quad_path() {
61*c8dee2aaSAndroid Build Coastguard Worker return SkPathBuilder().moveTo(0, 0)
62*c8dee2aaSAndroid Build Coastguard Worker .lineTo(100, 0)
63*c8dee2aaSAndroid Build Coastguard Worker .quadTo(50, -40, 0, 0)
64*c8dee2aaSAndroid Build Coastguard Worker .close()
65*c8dee2aaSAndroid Build Coastguard Worker .detach();
66*c8dee2aaSAndroid Build Coastguard Worker }
67*c8dee2aaSAndroid Build Coastguard Worker
cubic_path()68*c8dee2aaSAndroid Build Coastguard Worker SkPath cubic_path() {
69*c8dee2aaSAndroid Build Coastguard Worker SkPath path;
70*c8dee2aaSAndroid Build Coastguard Worker path.moveTo(0, 0);
71*c8dee2aaSAndroid Build Coastguard Worker path.cubicTo(25, 75,
72*c8dee2aaSAndroid Build Coastguard Worker 75, -50,
73*c8dee2aaSAndroid Build Coastguard Worker 100, 0);
74*c8dee2aaSAndroid Build Coastguard Worker
75*c8dee2aaSAndroid Build Coastguard Worker return path;
76*c8dee2aaSAndroid Build Coastguard Worker }
77*c8dee2aaSAndroid Build Coastguard Worker
oval_path()78*c8dee2aaSAndroid Build Coastguard Worker SkPath oval_path() {
79*c8dee2aaSAndroid Build Coastguard Worker SkRect oval = SkRect::MakeXYWH(0, -25, 100, 50);
80*c8dee2aaSAndroid Build Coastguard Worker
81*c8dee2aaSAndroid Build Coastguard Worker return SkPathBuilder().arcTo(oval, 0, 359, true).close().detach();
82*c8dee2aaSAndroid Build Coastguard Worker }
83*c8dee2aaSAndroid Build Coastguard Worker
ribs_path(SkPath path,SkScalar radius)84*c8dee2aaSAndroid Build Coastguard Worker SkPath ribs_path(SkPath path, SkScalar radius) {
85*c8dee2aaSAndroid Build Coastguard Worker SkPath ribs;
86*c8dee2aaSAndroid Build Coastguard Worker
87*c8dee2aaSAndroid Build Coastguard Worker const SkScalar spacing = 5.0f;
88*c8dee2aaSAndroid Build Coastguard Worker float accum = 0.0f;
89*c8dee2aaSAndroid Build Coastguard Worker
90*c8dee2aaSAndroid Build Coastguard Worker SkPathMeasure meas(path, false);
91*c8dee2aaSAndroid Build Coastguard Worker SkScalar length = meas.getLength();
92*c8dee2aaSAndroid Build Coastguard Worker SkPoint pos;
93*c8dee2aaSAndroid Build Coastguard Worker SkVector tan;
94*c8dee2aaSAndroid Build Coastguard Worker while (accum < length) {
95*c8dee2aaSAndroid Build Coastguard Worker if (meas.getPosTan(accum, &pos, &tan)) {
96*c8dee2aaSAndroid Build Coastguard Worker tan.scale(radius);
97*c8dee2aaSAndroid Build Coastguard Worker SkPointPriv::RotateCCW(&tan);
98*c8dee2aaSAndroid Build Coastguard Worker
99*c8dee2aaSAndroid Build Coastguard Worker ribs.moveTo(pos.x() + tan.x(), pos.y() + tan.y());
100*c8dee2aaSAndroid Build Coastguard Worker ribs.lineTo(pos.x() - tan.x(), pos.y() - tan.y());
101*c8dee2aaSAndroid Build Coastguard Worker }
102*c8dee2aaSAndroid Build Coastguard Worker accum += spacing;
103*c8dee2aaSAndroid Build Coastguard Worker }
104*c8dee2aaSAndroid Build Coastguard Worker
105*c8dee2aaSAndroid Build Coastguard Worker return ribs;
106*c8dee2aaSAndroid Build Coastguard Worker }
107*c8dee2aaSAndroid Build Coastguard Worker
draw_ribs(SkCanvas * canvas,SkPath path)108*c8dee2aaSAndroid Build Coastguard Worker void draw_ribs(SkCanvas *canvas, SkPath path) {
109*c8dee2aaSAndroid Build Coastguard Worker SkPath ribs = ribs_path(path, OVERSTROKE_WIDTH/2.0f);
110*c8dee2aaSAndroid Build Coastguard Worker SkPaint p = make_normal_paint();
111*c8dee2aaSAndroid Build Coastguard Worker p.setStrokeWidth(1);
112*c8dee2aaSAndroid Build Coastguard Worker p.setColor(SK_ColorBLUE);
113*c8dee2aaSAndroid Build Coastguard Worker p.setColor(SK_ColorGREEN);
114*c8dee2aaSAndroid Build Coastguard Worker
115*c8dee2aaSAndroid Build Coastguard Worker canvas->drawPath(ribs, p);
116*c8dee2aaSAndroid Build Coastguard Worker }
117*c8dee2aaSAndroid Build Coastguard Worker
118*c8dee2aaSAndroid Build Coastguard Worker ///////// quads
119*c8dee2aaSAndroid Build Coastguard Worker
draw_small_quad(SkCanvas * canvas)120*c8dee2aaSAndroid Build Coastguard Worker void draw_small_quad(SkCanvas *canvas) {
121*c8dee2aaSAndroid Build Coastguard Worker // scaled so it's visible
122*c8dee2aaSAndroid Build Coastguard Worker // canvas->scale(8, 8);
123*c8dee2aaSAndroid Build Coastguard Worker
124*c8dee2aaSAndroid Build Coastguard Worker SkPaint p = make_normal_paint();
125*c8dee2aaSAndroid Build Coastguard Worker SkPath path = quad_path();
126*c8dee2aaSAndroid Build Coastguard Worker
127*c8dee2aaSAndroid Build Coastguard Worker draw_ribs(canvas, path);
128*c8dee2aaSAndroid Build Coastguard Worker canvas->drawPath(path, p);
129*c8dee2aaSAndroid Build Coastguard Worker }
130*c8dee2aaSAndroid Build Coastguard Worker
draw_large_quad(SkCanvas * canvas)131*c8dee2aaSAndroid Build Coastguard Worker void draw_large_quad(SkCanvas *canvas) {
132*c8dee2aaSAndroid Build Coastguard Worker SkPaint p = make_overstroke_paint();
133*c8dee2aaSAndroid Build Coastguard Worker SkPath path = quad_path();
134*c8dee2aaSAndroid Build Coastguard Worker
135*c8dee2aaSAndroid Build Coastguard Worker canvas->drawPath(path, p);
136*c8dee2aaSAndroid Build Coastguard Worker draw_ribs(canvas, path);
137*c8dee2aaSAndroid Build Coastguard Worker }
138*c8dee2aaSAndroid Build Coastguard Worker
draw_quad_fillpath(SkCanvas * canvas)139*c8dee2aaSAndroid Build Coastguard Worker void draw_quad_fillpath(SkCanvas *canvas) {
140*c8dee2aaSAndroid Build Coastguard Worker SkPath path = quad_path();
141*c8dee2aaSAndroid Build Coastguard Worker SkPaint p = make_overstroke_paint();
142*c8dee2aaSAndroid Build Coastguard Worker
143*c8dee2aaSAndroid Build Coastguard Worker SkPaint fillp = make_normal_paint();
144*c8dee2aaSAndroid Build Coastguard Worker fillp.setColor(SK_ColorMAGENTA);
145*c8dee2aaSAndroid Build Coastguard Worker
146*c8dee2aaSAndroid Build Coastguard Worker SkPath fillpath;
147*c8dee2aaSAndroid Build Coastguard Worker skpathutils::FillPathWithPaint(path, p, &fillpath);
148*c8dee2aaSAndroid Build Coastguard Worker
149*c8dee2aaSAndroid Build Coastguard Worker canvas->drawPath(fillpath, fillp);
150*c8dee2aaSAndroid Build Coastguard Worker }
151*c8dee2aaSAndroid Build Coastguard Worker
draw_stroked_quad(SkCanvas * canvas)152*c8dee2aaSAndroid Build Coastguard Worker void draw_stroked_quad(SkCanvas *canvas) {
153*c8dee2aaSAndroid Build Coastguard Worker canvas->translate(400, 0);
154*c8dee2aaSAndroid Build Coastguard Worker draw_large_quad(canvas);
155*c8dee2aaSAndroid Build Coastguard Worker draw_quad_fillpath(canvas);
156*c8dee2aaSAndroid Build Coastguard Worker }
157*c8dee2aaSAndroid Build Coastguard Worker
158*c8dee2aaSAndroid Build Coastguard Worker ////////// cubics
159*c8dee2aaSAndroid Build Coastguard Worker
draw_small_cubic(SkCanvas * canvas)160*c8dee2aaSAndroid Build Coastguard Worker void draw_small_cubic(SkCanvas *canvas) {
161*c8dee2aaSAndroid Build Coastguard Worker SkPaint p = make_normal_paint();
162*c8dee2aaSAndroid Build Coastguard Worker SkPath path = cubic_path();
163*c8dee2aaSAndroid Build Coastguard Worker
164*c8dee2aaSAndroid Build Coastguard Worker draw_ribs(canvas, path);
165*c8dee2aaSAndroid Build Coastguard Worker canvas->drawPath(path, p);
166*c8dee2aaSAndroid Build Coastguard Worker }
167*c8dee2aaSAndroid Build Coastguard Worker
draw_large_cubic(SkCanvas * canvas)168*c8dee2aaSAndroid Build Coastguard Worker void draw_large_cubic(SkCanvas *canvas) {
169*c8dee2aaSAndroid Build Coastguard Worker SkPaint p = make_overstroke_paint();
170*c8dee2aaSAndroid Build Coastguard Worker SkPath path = cubic_path();
171*c8dee2aaSAndroid Build Coastguard Worker
172*c8dee2aaSAndroid Build Coastguard Worker canvas->drawPath(path, p);
173*c8dee2aaSAndroid Build Coastguard Worker draw_ribs(canvas, path);
174*c8dee2aaSAndroid Build Coastguard Worker }
175*c8dee2aaSAndroid Build Coastguard Worker
draw_cubic_fillpath(SkCanvas * canvas)176*c8dee2aaSAndroid Build Coastguard Worker void draw_cubic_fillpath(SkCanvas *canvas) {
177*c8dee2aaSAndroid Build Coastguard Worker SkPath path = cubic_path();
178*c8dee2aaSAndroid Build Coastguard Worker SkPaint p = make_overstroke_paint();
179*c8dee2aaSAndroid Build Coastguard Worker
180*c8dee2aaSAndroid Build Coastguard Worker SkPaint fillp = make_normal_paint();
181*c8dee2aaSAndroid Build Coastguard Worker fillp.setColor(SK_ColorMAGENTA);
182*c8dee2aaSAndroid Build Coastguard Worker
183*c8dee2aaSAndroid Build Coastguard Worker SkPath fillpath;
184*c8dee2aaSAndroid Build Coastguard Worker skpathutils::FillPathWithPaint(path, p, &fillpath);
185*c8dee2aaSAndroid Build Coastguard Worker
186*c8dee2aaSAndroid Build Coastguard Worker canvas->drawPath(fillpath, fillp);
187*c8dee2aaSAndroid Build Coastguard Worker }
188*c8dee2aaSAndroid Build Coastguard Worker
draw_stroked_cubic(SkCanvas * canvas)189*c8dee2aaSAndroid Build Coastguard Worker void draw_stroked_cubic(SkCanvas *canvas) {
190*c8dee2aaSAndroid Build Coastguard Worker canvas->translate(400, 0);
191*c8dee2aaSAndroid Build Coastguard Worker draw_large_cubic(canvas);
192*c8dee2aaSAndroid Build Coastguard Worker draw_cubic_fillpath(canvas);
193*c8dee2aaSAndroid Build Coastguard Worker }
194*c8dee2aaSAndroid Build Coastguard Worker
195*c8dee2aaSAndroid Build Coastguard Worker ////////// ovals
196*c8dee2aaSAndroid Build Coastguard Worker
draw_small_oval(SkCanvas * canvas)197*c8dee2aaSAndroid Build Coastguard Worker void draw_small_oval(SkCanvas *canvas) {
198*c8dee2aaSAndroid Build Coastguard Worker SkPaint p = make_normal_paint();
199*c8dee2aaSAndroid Build Coastguard Worker
200*c8dee2aaSAndroid Build Coastguard Worker SkPath path = oval_path();
201*c8dee2aaSAndroid Build Coastguard Worker
202*c8dee2aaSAndroid Build Coastguard Worker draw_ribs(canvas, path);
203*c8dee2aaSAndroid Build Coastguard Worker canvas->drawPath(path, p);
204*c8dee2aaSAndroid Build Coastguard Worker }
205*c8dee2aaSAndroid Build Coastguard Worker
draw_large_oval(SkCanvas * canvas)206*c8dee2aaSAndroid Build Coastguard Worker void draw_large_oval(SkCanvas *canvas) {
207*c8dee2aaSAndroid Build Coastguard Worker SkPaint p = make_overstroke_paint();
208*c8dee2aaSAndroid Build Coastguard Worker SkPath path = oval_path();
209*c8dee2aaSAndroid Build Coastguard Worker
210*c8dee2aaSAndroid Build Coastguard Worker canvas->drawPath(path, p);
211*c8dee2aaSAndroid Build Coastguard Worker draw_ribs(canvas, path);
212*c8dee2aaSAndroid Build Coastguard Worker }
213*c8dee2aaSAndroid Build Coastguard Worker
draw_oval_fillpath(SkCanvas * canvas)214*c8dee2aaSAndroid Build Coastguard Worker void draw_oval_fillpath(SkCanvas *canvas) {
215*c8dee2aaSAndroid Build Coastguard Worker SkPath path = oval_path();
216*c8dee2aaSAndroid Build Coastguard Worker SkPaint p = make_overstroke_paint();
217*c8dee2aaSAndroid Build Coastguard Worker
218*c8dee2aaSAndroid Build Coastguard Worker SkPaint fillp = make_normal_paint();
219*c8dee2aaSAndroid Build Coastguard Worker fillp.setColor(SK_ColorMAGENTA);
220*c8dee2aaSAndroid Build Coastguard Worker
221*c8dee2aaSAndroid Build Coastguard Worker SkPath fillpath;
222*c8dee2aaSAndroid Build Coastguard Worker skpathutils::FillPathWithPaint(path, p, &fillpath);
223*c8dee2aaSAndroid Build Coastguard Worker
224*c8dee2aaSAndroid Build Coastguard Worker canvas->drawPath(fillpath, fillp);
225*c8dee2aaSAndroid Build Coastguard Worker }
226*c8dee2aaSAndroid Build Coastguard Worker
draw_stroked_oval(SkCanvas * canvas)227*c8dee2aaSAndroid Build Coastguard Worker void draw_stroked_oval(SkCanvas *canvas) {
228*c8dee2aaSAndroid Build Coastguard Worker canvas->translate(400, 0);
229*c8dee2aaSAndroid Build Coastguard Worker draw_large_oval(canvas);
230*c8dee2aaSAndroid Build Coastguard Worker draw_oval_fillpath(canvas);
231*c8dee2aaSAndroid Build Coastguard Worker }
232*c8dee2aaSAndroid Build Coastguard Worker
233*c8dee2aaSAndroid Build Coastguard Worker ////////// gm
234*c8dee2aaSAndroid Build Coastguard Worker
235*c8dee2aaSAndroid Build Coastguard Worker void (*examples[])(SkCanvas *canvas) = {
236*c8dee2aaSAndroid Build Coastguard Worker draw_small_quad, draw_stroked_quad, draw_small_cubic,
237*c8dee2aaSAndroid Build Coastguard Worker draw_stroked_cubic, draw_small_oval, draw_stroked_oval,
238*c8dee2aaSAndroid Build Coastguard Worker };
239*c8dee2aaSAndroid Build Coastguard Worker
240*c8dee2aaSAndroid Build Coastguard Worker DEF_SIMPLE_GM(OverStroke, canvas, 500, 500) {
241*c8dee2aaSAndroid Build Coastguard Worker const size_t length = sizeof(examples) / sizeof(examples[0]);
242*c8dee2aaSAndroid Build Coastguard Worker const size_t width = 2;
243*c8dee2aaSAndroid Build Coastguard Worker
244*c8dee2aaSAndroid Build Coastguard Worker for (size_t i = 0; i < length; i++) {
245*c8dee2aaSAndroid Build Coastguard Worker int x = (int)(i % width);
246*c8dee2aaSAndroid Build Coastguard Worker int y = (int)(i / width);
247*c8dee2aaSAndroid Build Coastguard Worker
248*c8dee2aaSAndroid Build Coastguard Worker canvas->save();
249*c8dee2aaSAndroid Build Coastguard Worker canvas->translate(150.0f * x, 150.0f * y);
250*c8dee2aaSAndroid Build Coastguard Worker canvas->scale(0.2f, 0.2f);
251*c8dee2aaSAndroid Build Coastguard Worker canvas->translate(300.0f, 400.0f);
252*c8dee2aaSAndroid Build Coastguard Worker
253*c8dee2aaSAndroid Build Coastguard Worker examples[i](canvas);
254*c8dee2aaSAndroid Build Coastguard Worker
255*c8dee2aaSAndroid Build Coastguard Worker canvas->restore();
256*c8dee2aaSAndroid Build Coastguard Worker }
257*c8dee2aaSAndroid Build Coastguard Worker }
258