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 "SkCanvas.h"
9#include "SkPaint.h"
10#include "SkRandom.h"
11
12namespace skiagm {
13
14class DegenerateSegmentsGM : public GM {
15public:
16    DegenerateSegmentsGM() {}
17
18protected:
19    struct PathAndName {
20        SkPath      fPath;
21        const char* fName1;
22        const char* fName2;
23    };
24
25    SkString onShortName() {
26        return SkString("degeneratesegments");
27    }
28
29    SkISize onISize() { return make_isize(896, 930); }
30
31    typedef SkPoint (*AddSegmentFunc)(SkPath&, SkPoint&);
32
33    // We need to use explicit commands here, instead of addPath, because we
34    // do not want the moveTo that is added at the beginning of a path to
35    // appear in the appended path.
36    static SkPoint AddMove(SkPath& path, SkPoint& startPt) {
37        SkPoint moveToPt = startPt + SkPoint::Make(0, 10*SK_Scalar1);
38        path.moveTo(moveToPt);
39        return moveToPt;
40    }
41
42    static SkPoint AddMoveClose(SkPath& path, SkPoint& startPt) {
43        SkPoint moveToPt = startPt + SkPoint::Make(0, 10*SK_Scalar1);
44        path.moveTo(moveToPt);
45        path.close();
46        return moveToPt;
47    }
48
49    static SkPoint AddDegenLine(SkPath& path, SkPoint& startPt) {
50        path.lineTo(startPt);
51        return startPt;
52    }
53
54    static SkPoint AddMoveDegenLine(SkPath& path, SkPoint& startPt) {
55        SkPoint moveToPt = startPt + SkPoint::Make(0, 10*SK_Scalar1);
56        path.moveTo(moveToPt);
57        path.lineTo(moveToPt);
58        return moveToPt;
59    }
60
61    static SkPoint AddMoveDegenLineClose(SkPath& path, SkPoint& startPt) {
62        SkPoint moveToPt = startPt + SkPoint::Make(0, 10*SK_Scalar1);
63        path.moveTo(moveToPt);
64        path.lineTo(moveToPt);
65        path.close();
66        return moveToPt;
67    }
68
69    static SkPoint AddDegenQuad(SkPath& path, SkPoint& startPt) {
70        path.quadTo(startPt, startPt);
71        return startPt;
72    }
73
74    static SkPoint AddMoveDegenQuad(SkPath& path, SkPoint& startPt) {
75        SkPoint moveToPt = startPt + SkPoint::Make(0, 10*SK_Scalar1);
76        path.moveTo(moveToPt);
77        path.quadTo(moveToPt, moveToPt);
78        return moveToPt;
79    }
80
81    static SkPoint AddMoveDegenQuadClose(SkPath& path, SkPoint& startPt) {
82        SkPoint moveToPt = startPt + SkPoint::Make(0, 10*SK_Scalar1);
83        path.moveTo(moveToPt);
84        path.quadTo(moveToPt, moveToPt);
85        path.close();
86        return moveToPt;
87    }
88
89    static SkPoint AddDegenCubic(SkPath& path, SkPoint& startPt) {
90        path.cubicTo(startPt, startPt, startPt);
91        return startPt;
92    }
93
94    static SkPoint AddMoveDegenCubic(SkPath& path, SkPoint& startPt) {
95        SkPoint moveToPt = startPt + SkPoint::Make(0, 10*SK_Scalar1);
96        path.moveTo(moveToPt);
97        path.cubicTo(moveToPt, moveToPt, moveToPt);
98        return moveToPt;
99    }
100
101    static SkPoint AddMoveDegenCubicClose(SkPath& path, SkPoint& startPt) {
102        SkPoint moveToPt = startPt + SkPoint::Make(0, 10*SK_Scalar1);
103        path.moveTo(moveToPt);
104        path.cubicTo(moveToPt, moveToPt, moveToPt);
105        path.close();
106        return moveToPt;
107    }
108
109    static SkPoint AddClose(SkPath& path, SkPoint& startPt) {
110        path.close();
111        return startPt;
112    }
113
114    static SkPoint AddLine(SkPath& path, SkPoint& startPt) {
115        SkPoint endPt = startPt + SkPoint::Make(40*SK_Scalar1, 0);
116        path.lineTo(endPt);
117        return endPt;
118    }
119
120    static SkPoint AddMoveLine(SkPath& path, SkPoint& startPt) {
121        SkPoint moveToPt = startPt + SkPoint::Make(0, 10*SK_Scalar1);
122        SkPoint endPt = moveToPt + SkPoint::Make(40*SK_Scalar1, 0);
123        path.moveTo(moveToPt);
124        path.lineTo(endPt);
125        return endPt;
126    }
127
128    static SkPoint AddMoveLineClose(SkPath& path, SkPoint& startPt) {
129        SkPoint moveToPt = startPt + SkPoint::Make(0, 10*SK_Scalar1);
130        SkPoint endPt = moveToPt + SkPoint::Make(40*SK_Scalar1, 0);
131        path.moveTo(moveToPt);
132        path.lineTo(endPt);
133        path.close();
134        return endPt;
135    }
136
137    static SkPoint AddQuad(SkPath& path, SkPoint& startPt) {
138        SkPoint midPt = startPt + SkPoint::Make(20*SK_Scalar1, 5*SK_Scalar1);
139        SkPoint endPt = startPt + SkPoint::Make(40*SK_Scalar1, 0);
140        path.quadTo(midPt, endPt);
141        return endPt;
142    }
143
144    static SkPoint AddMoveQuad(SkPath& path, SkPoint& startPt) {
145        SkPoint moveToPt = startPt + SkPoint::Make(0, 10*SK_Scalar1);
146        SkPoint midPt = moveToPt + SkPoint::Make(20*SK_Scalar1, 5*SK_Scalar1);
147        SkPoint endPt = moveToPt + SkPoint::Make(40*SK_Scalar1, 0);
148        path.moveTo(moveToPt);
149        path.quadTo(midPt, endPt);
150        return endPt;
151    }
152
153    static SkPoint AddMoveQuadClose(SkPath& path, SkPoint& startPt) {
154        SkPoint moveToPt = startPt + SkPoint::Make(0, 10*SK_Scalar1);
155        SkPoint midPt = moveToPt + SkPoint::Make(20*SK_Scalar1, 5*SK_Scalar1);
156        SkPoint endPt = moveToPt + SkPoint::Make(40*SK_Scalar1, 0);
157        path.moveTo(moveToPt);
158        path.quadTo(midPt, endPt);
159        path.close();
160        return endPt;
161    }
162
163    static SkPoint AddCubic(SkPath& path, SkPoint& startPt) {
164        SkPoint t1Pt = startPt + SkPoint::Make(15*SK_Scalar1, 5*SK_Scalar1);
165        SkPoint t2Pt = startPt + SkPoint::Make(25*SK_Scalar1, 5*SK_Scalar1);
166        SkPoint endPt = startPt + SkPoint::Make(40*SK_Scalar1, 0);
167        path.cubicTo(t1Pt, t2Pt, endPt);
168        return endPt;
169    }
170
171    static SkPoint AddMoveCubic(SkPath& path, SkPoint& startPt) {
172        SkPoint moveToPt = startPt + SkPoint::Make(0, 10*SK_Scalar1);
173        SkPoint t1Pt = moveToPt + SkPoint::Make(15*SK_Scalar1, 5*SK_Scalar1);
174        SkPoint t2Pt = moveToPt + SkPoint::Make(25*SK_Scalar1, 5*SK_Scalar1);
175        SkPoint endPt = moveToPt + SkPoint::Make(40*SK_Scalar1, 0);
176        path.moveTo(moveToPt);
177        path.cubicTo(t1Pt, t2Pt, endPt);
178        return endPt;
179    }
180
181    static SkPoint AddMoveCubicClose(SkPath& path, SkPoint& startPt) {
182        SkPoint moveToPt = startPt + SkPoint::Make(0, 10*SK_Scalar1);
183        SkPoint t1Pt = moveToPt + SkPoint::Make(15*SK_Scalar1, 5*SK_Scalar1);
184        SkPoint t2Pt = moveToPt + SkPoint::Make(25*SK_Scalar1, 5*SK_Scalar1);
185        SkPoint endPt = moveToPt + SkPoint::Make(40*SK_Scalar1, 0);
186        path.moveTo(moveToPt);
187        path.cubicTo(t1Pt, t2Pt, endPt);
188        path.close();
189        return endPt;
190    }
191
192    void drawPath(SkPath& path, SkCanvas* canvas, SkColor color,
193                  const SkRect& clip, SkPaint::Cap cap, SkPaint::Join join,
194                  SkPaint::Style style, SkPath::FillType fill,
195                  SkScalar strokeWidth) {
196        path.setFillType(fill);
197        SkPaint paint;
198        paint.setStrokeCap(cap);
199        paint.setStrokeWidth(strokeWidth);
200        paint.setStrokeJoin(join);
201        paint.setColor(color);
202        paint.setStyle(style);
203        canvas->save();
204        canvas->clipRect(clip);
205        canvas->drawPath(path, paint);
206        canvas->restore();
207    }
208
209    virtual void onDraw(SkCanvas* canvas) {
210    static const AddSegmentFunc gSegmentFunctions[] = {
211        AddMove,
212        AddMoveClose,
213        AddDegenLine,
214        AddMoveDegenLine,
215        AddMoveDegenLineClose,
216        AddDegenQuad,
217        AddMoveDegenQuad,
218        AddMoveDegenQuadClose,
219        AddDegenCubic,
220        AddMoveDegenCubic,
221        AddMoveDegenCubicClose,
222        AddClose,
223        AddLine,
224        AddMoveLine,
225        AddMoveLineClose,
226        AddQuad,
227        AddMoveQuad,
228        AddMoveQuadClose,
229        AddCubic,
230        AddMoveCubic,
231        AddMoveCubicClose
232    };
233    static const char* gSegmentNames[] = {
234        "Move",
235        "MoveClose",
236        "DegenLine",
237        "MoveDegenLine",
238        "MoveDegenLineClose",
239        "DegenQuad",
240        "MoveDegenQuad",
241        "MoveDegenQuadClose",
242        "DegenCubic",
243        "MoveDegenCubic",
244        "MoveDegenCubicClose",
245        "Close",
246        "Line",
247        "MoveLine",
248        "MoveLineClose",
249        "Quad",
250        "MoveQuad",
251        "MoveQuadClose",
252        "Cubic",
253        "MoveCubic",
254        "MoveCubicClose"
255    };
256
257        struct FillAndName {
258            SkPath::FillType fFill;
259            const char*      fName;
260        };
261        static const FillAndName gFills[] = {
262            {SkPath::kWinding_FillType, "Winding"},
263            {SkPath::kEvenOdd_FillType, "Even / Odd"},
264            {SkPath::kInverseWinding_FillType, "Inverse Winding"},
265            {SkPath::kInverseEvenOdd_FillType, "Inverse Even / Odd"}
266        };
267        struct StyleAndName {
268            SkPaint::Style fStyle;
269            const char*    fName;
270        };
271        static const StyleAndName gStyles[] = {
272            {SkPaint::kFill_Style, "Fill"},
273            {SkPaint::kStroke_Style, "Stroke 10"},
274            {SkPaint::kStrokeAndFill_Style, "Stroke 10 And Fill"}
275        };
276        struct CapAndName {
277            SkPaint::Cap  fCap;
278            SkPaint::Join fJoin;
279            const char*   fName;
280        };
281        static const CapAndName gCaps[] = {
282            {SkPaint::kButt_Cap, SkPaint::kBevel_Join, "Butt"},
283            {SkPaint::kRound_Cap, SkPaint::kRound_Join, "Round"},
284            {SkPaint::kSquare_Cap, SkPaint::kBevel_Join, "Square"}
285        };
286
287        SkPaint titlePaint;
288        titlePaint.setColor(SK_ColorBLACK);
289        titlePaint.setAntiAlias(true);
290        titlePaint.setLCDRenderText(true);
291        titlePaint.setTextSize(15 * SK_Scalar1);
292        const char title[] = "Random Paths Drawn Into Rectangle Clips With "
293                             "Indicated Style, Fill and Linecaps, "
294                             "with Stroke width 6";
295        canvas->drawText(title, strlen(title),
296                            20 * SK_Scalar1,
297                            20 * SK_Scalar1,
298                            titlePaint);
299
300        SkRandom rand;
301        SkRect rect = SkRect::MakeWH(220*SK_Scalar1, 50*SK_Scalar1);
302        canvas->save();
303        canvas->translate(2*SK_Scalar1, 30 * SK_Scalar1); // The title
304        canvas->save();
305        unsigned numSegments = SK_ARRAY_COUNT(gSegmentFunctions);
306        unsigned numCaps = SK_ARRAY_COUNT(gCaps);
307        unsigned numStyles = SK_ARRAY_COUNT(gStyles);
308        unsigned numFills = SK_ARRAY_COUNT(gFills);
309        for (size_t row = 0; row < 6; ++row) {
310            if (0 < row) {
311                canvas->translate(0, rect.height() + 100*SK_Scalar1);
312            }
313            canvas->save();
314            for (size_t column = 0; column < 4; ++column) {
315                if (0 < column) {
316                    canvas->translate(rect.width() + 4*SK_Scalar1, 0);
317                }
318
319                SkColor color = 0xff007000;
320                StyleAndName style = gStyles[(rand.nextU() >> 16) % numStyles];
321                CapAndName cap = gCaps[(rand.nextU() >> 16) % numCaps];
322                FillAndName fill = gFills[(rand.nextU() >> 16) % numFills];
323                SkPath path;
324                unsigned s1 = (rand.nextU() >> 16) % numSegments;
325                unsigned s2 = (rand.nextU() >> 16) % numSegments;
326                unsigned s3 = (rand.nextU() >> 16) % numSegments;
327                unsigned s4 = (rand.nextU() >> 16) % numSegments;
328                unsigned s5 = (rand.nextU() >> 16) % numSegments;
329                SkPoint pt = SkPoint::Make(10*SK_Scalar1, 0);
330                pt = gSegmentFunctions[s1](path, pt);
331                pt = gSegmentFunctions[s2](path, pt);
332                pt = gSegmentFunctions[s3](path, pt);
333                pt = gSegmentFunctions[s4](path, pt);
334                pt = gSegmentFunctions[s5](path, pt);
335
336                this->drawPath(path, canvas, color, rect,
337                               cap.fCap, cap.fJoin, style.fStyle,
338                               fill.fFill, SK_Scalar1*6);
339
340                SkPaint rectPaint;
341                rectPaint.setColor(SK_ColorBLACK);
342                rectPaint.setStyle(SkPaint::kStroke_Style);
343                rectPaint.setStrokeWidth(-1);
344                rectPaint.setAntiAlias(true);
345                canvas->drawRect(rect, rectPaint);
346
347                SkPaint labelPaint;
348                labelPaint.setColor(color);
349                labelPaint.setAntiAlias(true);
350                labelPaint.setLCDRenderText(true);
351                labelPaint.setTextSize(10 * SK_Scalar1);
352                canvas->drawText(style.fName,
353                                 strlen(style.fName),
354                                 0, rect.height() + 12 * SK_Scalar1,
355                                 labelPaint);
356                canvas->drawText(fill.fName,
357                                 strlen(fill.fName),
358                                 0, rect.height() + 24 * SK_Scalar1,
359                                 labelPaint);
360                canvas->drawText(cap.fName,
361                                 strlen(cap.fName),
362                                 0, rect.height() + 36 * SK_Scalar1,
363                                 labelPaint);
364                canvas->drawText(gSegmentNames[s1],
365                                 strlen(gSegmentNames[s1]),
366                                 0, rect.height() + 48 * SK_Scalar1,
367                                 labelPaint);
368                canvas->drawText(gSegmentNames[s2],
369                                 strlen(gSegmentNames[s2]),
370                                 0, rect.height() + 60 * SK_Scalar1,
371                                 labelPaint);
372                canvas->drawText(gSegmentNames[s3],
373                                 strlen(gSegmentNames[s3]),
374                                 0, rect.height() + 72 * SK_Scalar1,
375                                 labelPaint);
376                canvas->drawText(gSegmentNames[s4],
377                                 strlen(gSegmentNames[s4]),
378                                 0, rect.height() + 84 * SK_Scalar1,
379                                 labelPaint);
380                canvas->drawText(gSegmentNames[s5],
381                                 strlen(gSegmentNames[s5]),
382                                 0, rect.height() + 96 * SK_Scalar1,
383                                 labelPaint);
384            }
385            canvas->restore();
386        }
387        canvas->restore();
388        canvas->restore();
389    }
390
391private:
392    typedef GM INHERITED;
393};
394
395//////////////////////////////////////////////////////////////////////////////
396
397static GM* MyFactory(void*) { return new DegenerateSegmentsGM; }
398static GMRegistry reg(MyFactory);
399
400}
401