xref: /third_party/skia/gm/convexpaths.cpp (revision cb93a386)
1/*
2 * Copyright 2011 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#include "gm/gm.h"
9#include "include/core/SkCanvas.h"
10#include "include/core/SkColor.h"
11#include "include/core/SkMatrix.h"
12#include "include/core/SkPaint.h"
13#include "include/core/SkPathBuilder.h"
14#include "include/core/SkRect.h"
15#include "include/core/SkScalar.h"
16#include "include/core/SkSize.h"
17#include "include/core/SkString.h"
18#include "include/core/SkTypes.h"
19#include "include/private/SkNoncopyable.h"
20#include "include/private/SkTArray.h"
21#include "include/utils/SkRandom.h"
22
23namespace {
24
25class SkDoOnce : SkNoncopyable {
26public:
27    SkDoOnce() { fDidOnce = false; }
28
29    bool needToDo() const { return !fDidOnce; }
30    bool alreadyDone() const { return fDidOnce; }
31    void accomplished() {
32        SkASSERT(!fDidOnce);
33        fDidOnce = true;
34    }
35
36private:
37    bool fDidOnce;
38};
39
40class ConvexPathsGM : public skiagm::GM {
41    SkDoOnce fOnce;
42
43    void onOnceBeforeDraw() override { this->setBGColor(0xFF000000); }
44
45    SkString onShortName() override { return SkString("convexpaths"); }
46
47
48    SkISize onISize() override { return {1200, 1100}; }
49
50    void makePaths() {
51        if (fOnce.alreadyDone()) {
52            return;
53        }
54        fOnce.accomplished();
55
56        SkPathBuilder b;
57        fPaths.push_back(b.moveTo(0, 0)
58                          .quadTo(50, 100, 0, 100)
59                          .lineTo(0, 0)
60                          .detach());
61
62        fPaths.push_back(b.moveTo(0, 50)
63                          .quadTo(50, 0, 100, 50)
64                          .quadTo(50, 100, 0, 50)
65                          .detach());
66
67        fPaths.push_back(SkPath::Rect({0, 0, 100, 100}, SkPathDirection::kCW));
68        fPaths.push_back(SkPath::Rect({0, 0, 100, 100}, SkPathDirection::kCCW));
69        fPaths.push_back(SkPath::Circle(50, 50, 50, SkPathDirection::kCW));
70        fPaths.push_back(SkPath::Oval(SkRect::MakeXYWH(0, 0, 50, 100), SkPathDirection::kCW));
71        fPaths.push_back(SkPath::Oval(SkRect::MakeXYWH(0, 0, 100, 5), SkPathDirection::kCCW));
72        fPaths.push_back(SkPath::Oval(SkRect::MakeXYWH(0, 0, 1, 100), SkPathDirection::kCCW));
73        fPaths.push_back(SkPath::RRect(SkRRect::MakeRectXY({0, 0, 100, 100}, 40, 20),
74                                       SkPathDirection::kCW));
75
76        // large number of points
77        enum {
78            kLength = 100,
79            kPtsPerSide = (1 << 12),
80        };
81        b.moveTo(0, 0);
82        for (int i = 1; i < kPtsPerSide; ++i) { // skip the first point due to moveTo.
83            b.lineTo(kLength * SkIntToScalar(i) / kPtsPerSide, 0);
84        }
85        for (int i = 0; i < kPtsPerSide; ++i) {
86            b.lineTo(kLength, kLength * SkIntToScalar(i) / kPtsPerSide);
87        }
88        for (int i = kPtsPerSide; i > 0; --i) {
89            b.lineTo(kLength * SkIntToScalar(i) / kPtsPerSide, kLength);
90        }
91        for (int i = kPtsPerSide; i > 0; --i) {
92            b.lineTo(0, kLength * SkIntToScalar(i) / kPtsPerSide);
93        }
94        fPaths.push_back(b.detach());
95
96        // shallow diagonals
97        fPaths.push_back(SkPath::Polygon({{0,0}, {100,1}, {98,100}, {3,96}}, false));
98
99        fPaths.push_back(b.arcTo(SkRect::MakeXYWH(0, 0, 50, 100), 25, 130, false)
100                          .detach());
101
102        // cubics
103        fPaths.push_back(b.cubicTo(  1,  1, 10,  90, 0, 100).detach());
104        fPaths.push_back(b.cubicTo(100, 50, 20, 100, 0,   0).detach());
105
106        // path that has a cubic with a repeated first control point and
107        // a repeated last control point.
108        fPaths.push_back(b.moveTo(10, 10)
109                          .cubicTo(10, 10, 10, 0, 20, 0)
110                          .lineTo(40, 0)
111                          .cubicTo(40, 0, 50, 0, 50, 10)
112                          .detach());
113
114        // path that has two cubics with repeated middle control points.
115        fPaths.push_back(b.moveTo(10, 10)
116                          .cubicTo(10, 0, 10, 0, 20, 0)
117                          .lineTo(40, 0)
118                          .cubicTo(50, 0, 50, 0, 50, 10)
119                          .detach());
120
121        // cubic where last three points are almost a line
122        fPaths.push_back(b.moveTo(0, 228.0f/8)
123                          .cubicTo( 628.0f/ 8,  82.0f/8,
124                                   1255.0f/ 8, 141.0f/8,
125                                   1883.0f/ 8, 202.0f/8)
126                          .detach());
127
128        // flat cubic where the at end point tangents both point outward.
129        fPaths.push_back(b.moveTo(10, 0)
130                          .cubicTo(0, 1, 30, 1, 20, 0)
131                          .detach());
132
133        // flat cubic where initial tangent is in, end tangent out
134        fPaths.push_back(b.moveTo(0, 0)
135                          .cubicTo(10, 1, 30, 1, 20, 0)
136                          .detach());
137
138        // flat cubic where initial tangent is out, end tangent in
139        fPaths.push_back(b.moveTo(10, 0)
140                          .cubicTo(0, 1, 20, 1, 30, 0)
141                          .detach());
142
143        // triangle where one edge is a degenerate quad
144        fPaths.push_back(b.moveTo(8.59375f, 45)
145                          .quadTo(16.9921875f,   45,
146                                  31.25f,        45)
147                          .lineTo(100,          100)
148                          .lineTo(8.59375f,      45)
149                          .detach());
150
151        // triangle where one edge is a quad with a repeated point
152        fPaths.push_back(b.moveTo(0, 25)
153                          .lineTo(50, 0)
154                          .quadTo(50, 50, 50, 50)
155                          .detach());
156
157        // triangle where one edge is a cubic with a 2x repeated point
158        fPaths.push_back(b.moveTo(0, 25)
159                          .lineTo(50, 0)
160                          .cubicTo(50, 0, 50, 50, 50, 50)
161                          .detach());
162
163        // triangle where one edge is a quad with a nearly repeated point
164        fPaths.push_back(b.moveTo(0, 25)
165                          .lineTo(50, 0)
166                          .quadTo(50, 49.95f, 50, 50)
167                          .detach());
168
169        // triangle where one edge is a cubic with a 3x nearly repeated point
170        fPaths.push_back(b.moveTo(0, 25)
171                          .lineTo(50, 0)
172                          .cubicTo(50, 49.95f, 50, 49.97f, 50, 50)
173                          .detach());
174
175        // triangle where there is a point degenerate cubic at one corner
176        fPaths.push_back(b.moveTo(0, 25)
177                          .lineTo(50, 0)
178                          .lineTo(50, 50)
179                          .cubicTo(50, 50, 50, 50, 50, 50)
180                          .detach());
181
182        // point line
183        fPaths.push_back(SkPath::Line({50, 50}, {50, 50}));
184
185        // point quad
186        fPaths.push_back(b.moveTo(50, 50)
187                          .quadTo(50, 50, 50, 50)
188                          .detach());
189
190        // point cubic
191        fPaths.push_back(b.moveTo(50, 50)
192                          .cubicTo(50, 50, 50, 50, 50, 50)
193                          .detach());
194
195        // moveTo only paths
196        fPaths.push_back(b.moveTo(0, 0)
197                          .moveTo(0, 0)
198                          .moveTo(1, 1)
199                          .moveTo(1, 1)
200                          .moveTo(10, 10)
201                          .detach());
202
203        fPaths.push_back(b.moveTo(0, 0)
204                          .moveTo(0, 0)
205                          .detach());
206
207        // line degenerate
208        fPaths.push_back(b.lineTo(100, 100).detach());
209        fPaths.push_back(b.quadTo(100, 100, 0, 0).detach());
210        fPaths.push_back(b.quadTo(100, 100, 50, 50).detach());
211        fPaths.push_back(b.quadTo(50, 50, 100, 100).detach());
212        fPaths.push_back(b.cubicTo(0, 0, 0, 0, 100, 100).detach());
213
214        // skbug.com/8928
215        fPaths.push_back(b.moveTo(16.875f, 192.594f)
216                          .cubicTo(45.625f, 192.594f, 74.375f, 192.594f, 103.125f, 192.594f)
217                          .cubicTo(88.75f, 167.708f, 74.375f, 142.823f, 60, 117.938f)
218                          .cubicTo(45.625f, 142.823f, 31.25f, 167.708f, 16.875f, 192.594f)
219                          .close()
220                          .detach());
221        SkMatrix m;
222        m.setAll(0.1f, 0, -1, 0, 0.115207f, -2.64977f, 0, 0, 1);
223        fPaths.back().transform(m);
224
225        // small circle. This is listed last so that it has device coords far
226        // from the origin (small area relative to x,y values).
227        fPaths.push_back(SkPath::Circle(0, 0, 1.2f));
228    }
229
230    void onDraw(SkCanvas* canvas) override {
231        this->makePaths();
232
233        SkPaint paint;
234        paint.setAntiAlias(true);
235        SkRandom rand;
236        canvas->translate(20, 20);
237
238        // As we've added more paths this has gotten pretty big. Scale the whole thing down.
239        canvas->scale(2.0f/3, 2.0f/3);
240
241        for (int i = 0; i < fPaths.count(); ++i) {
242            canvas->save();
243            // position the path, and make it at off-integer coords.
244            canvas->translate(200.0f * (i % 5) + 1.0f/10,
245                              200.0f * (i / 5) + 9.0f/10);
246            SkColor color = rand.nextU();
247            color |= 0xff000000;
248            paint.setColor(color);
249#if 0       // This hitting on 32bit Linux builds for some paths. Temporarily disabling while it is
250            // debugged.
251            SkASSERT(fPaths[i].isConvex());
252#endif
253            canvas->drawPath(fPaths[i], paint);
254            canvas->restore();
255        }
256    }
257
258    SkTArray<SkPath> fPaths;
259};
260}  // namespace
261
262DEF_GM( return new ConvexPathsGM; )
263