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