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