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