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