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        titlePaint.setLCDRenderText(true);
295        titlePaint.setTextSize(15 * SK_Scalar1);
296        const char title[] = "Random Paths Drawn Into Rectangle Clips With "
297                             "Indicated Style, Fill and Linecaps, "
298                             "with Stroke width 6";
299        canvas->drawText(title, strlen(title),
300                            20 * SK_Scalar1,
301                            20 * SK_Scalar1,
302                            titlePaint);
303
304        SkLCGRandom rand;
305        SkRect rect = SkRect::MakeWH(220*SK_Scalar1, 50*SK_Scalar1);
306        canvas->save();
307        canvas->translate(2*SK_Scalar1, 30 * SK_Scalar1); // The title
308        canvas->save();
309        unsigned numSegments = SK_ARRAY_COUNT(gSegmentFunctions);
310        unsigned numCaps = SK_ARRAY_COUNT(gCaps);
311        unsigned numStyles = SK_ARRAY_COUNT(gStyles);
312        unsigned numFills = SK_ARRAY_COUNT(gFills);
313        for (size_t row = 0; row < 6; ++row) {
314            if (0 < row) {
315                canvas->translate(0, rect.height() + 100*SK_Scalar1);
316            }
317            canvas->save();
318            for (size_t column = 0; column < 4; ++column) {
319                if (0 < column) {
320                    canvas->translate(rect.width() + 4*SK_Scalar1, 0);
321                }
322
323                SkColor color = 0xff007000;
324                StyleAndName style = gStyles[(rand.nextU() >> 16) % numStyles];
325                CapAndName cap = gCaps[(rand.nextU() >> 16) % numCaps];
326                FillAndName fill = gFills[(rand.nextU() >> 16) % numFills];
327                SkPath path;
328                unsigned s1 = (rand.nextU() >> 16) % numSegments;
329                unsigned s2 = (rand.nextU() >> 16) % numSegments;
330                unsigned s3 = (rand.nextU() >> 16) % numSegments;
331                unsigned s4 = (rand.nextU() >> 16) % numSegments;
332                unsigned s5 = (rand.nextU() >> 16) % numSegments;
333                SkPoint pt = SkPoint::Make(10*SK_Scalar1, 0);
334                pt = gSegmentFunctions[s1](path, pt);
335                pt = gSegmentFunctions[s2](path, pt);
336                pt = gSegmentFunctions[s3](path, pt);
337                pt = gSegmentFunctions[s4](path, pt);
338                pt = gSegmentFunctions[s5](path, pt);
339
340                this->drawPath(path, canvas, color, rect,
341                               cap.fCap, cap.fJoin, style.fStyle,
342                               fill.fFill, SK_Scalar1*6);
343
344                SkPaint rectPaint;
345                rectPaint.setColor(SK_ColorBLACK);
346                rectPaint.setStyle(SkPaint::kStroke_Style);
347                rectPaint.setStrokeWidth(-1);
348                rectPaint.setAntiAlias(true);
349                canvas->drawRect(rect, rectPaint);
350
351                SkPaint labelPaint;
352                labelPaint.setColor(color);
353                labelPaint.setAntiAlias(true);
354                labelPaint.setLCDRenderText(true);
355                labelPaint.setTextSize(10 * SK_Scalar1);
356                canvas->drawText(style.fName,
357                                 strlen(style.fName),
358                                 0, rect.height() + 12 * SK_Scalar1,
359                                 labelPaint);
360                canvas->drawText(fill.fName,
361                                 strlen(fill.fName),
362                                 0, rect.height() + 24 * SK_Scalar1,
363                                 labelPaint);
364                canvas->drawText(cap.fName,
365                                 strlen(cap.fName),
366                                 0, rect.height() + 36 * SK_Scalar1,
367                                 labelPaint);
368                canvas->drawText(gSegmentNames[s1],
369                                 strlen(gSegmentNames[s1]),
370                                 0, rect.height() + 48 * SK_Scalar1,
371                                 labelPaint);
372                canvas->drawText(gSegmentNames[s2],
373                                 strlen(gSegmentNames[s2]),
374                                 0, rect.height() + 60 * SK_Scalar1,
375                                 labelPaint);
376                canvas->drawText(gSegmentNames[s3],
377                                 strlen(gSegmentNames[s3]),
378                                 0, rect.height() + 72 * SK_Scalar1,
379                                 labelPaint);
380                canvas->drawText(gSegmentNames[s4],
381                                 strlen(gSegmentNames[s4]),
382                                 0, rect.height() + 84 * SK_Scalar1,
383                                 labelPaint);
384                canvas->drawText(gSegmentNames[s5],
385                                 strlen(gSegmentNames[s5]),
386                                 0, rect.height() + 96 * SK_Scalar1,
387                                 labelPaint);
388            }
389            canvas->restore();
390        }
391        canvas->restore();
392        canvas->restore();
393    }
394
395private:
396    typedef GM INHERITED;
397};
398
399//////////////////////////////////////////////////////////////////////////////
400
401static GM* MyFactory(void*) { return new DegenerateSegmentsGM; }
402static GMRegistry reg(MyFactory);
403
404}
405