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