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
12// skbug.com/1316 shows that this cubic, when slightly clipped, creates big
13// (incorrect) changes to its control points.
14class ClippedCubicGM : public skiagm::GM {
15public:
16    ClippedCubicGM() {}
17
18protected:
19    virtual uint32_t onGetFlags() const SK_OVERRIDE {
20        return kSkipTiled_Flag;
21    }
22
23    SkString onShortName() {
24        return SkString("clippedcubic");
25    }
26
27    SkISize onISize() { return SkISize::Make(1240, 390); }
28
29    virtual void onDraw(SkCanvas* canvas) {
30        SkPath path;
31        path.moveTo(0, 0);
32        path.cubicTo(140, 150, 40, 10, 170, 150);
33
34        SkPaint paint;
35        SkRect bounds = path.getBounds();
36
37        for (SkScalar dy = -1; dy <= 1; dy += 1) {
38            canvas->save();
39            for (SkScalar dx = -1; dx <= 1; dx += 1) {
40                canvas->save();
41                canvas->clipRect(bounds);
42                canvas->translate(dx, dy);
43                canvas->drawPath(path, paint);
44                canvas->restore();
45
46                canvas->translate(bounds.width(), 0);
47            }
48            canvas->restore();
49            canvas->translate(0, bounds.height());
50        }
51    }
52
53private:
54    typedef skiagm::GM INHERITED;
55};
56
57class CubicPathGM : public skiagm::GM {
58public:
59    CubicPathGM() {}
60
61protected:
62    virtual uint32_t onGetFlags() const SK_OVERRIDE {
63        return kSkipTiled_Flag;
64    }
65
66    SkString onShortName() {
67        return SkString("cubicpath");
68    }
69
70    SkISize onISize() { return SkISize::Make(1240, 390); }
71
72    void drawPath(SkPath& path,SkCanvas* canvas,SkColor color,
73                  const SkRect& clip,SkPaint::Cap cap, SkPaint::Join join,
74                  SkPaint::Style style, SkPath::FillType fill,
75                  SkScalar strokeWidth) {
76        path.setFillType(fill);
77        SkPaint paint;
78        paint.setStrokeCap(cap);
79        paint.setStrokeWidth(strokeWidth);
80        paint.setStrokeJoin(join);
81        paint.setColor(color);
82        paint.setStyle(style);
83        canvas->save();
84        canvas->clipRect(clip);
85        canvas->drawPath(path, paint);
86        canvas->restore();
87    }
88
89    virtual void onDraw(SkCanvas* canvas) {
90        struct FillAndName {
91            SkPath::FillType fFill;
92            const char*      fName;
93        };
94        static const FillAndName gFills[] = {
95            {SkPath::kWinding_FillType, "Winding"},
96            {SkPath::kEvenOdd_FillType, "Even / Odd"},
97            {SkPath::kInverseWinding_FillType, "Inverse Winding"},
98            {SkPath::kInverseEvenOdd_FillType, "Inverse Even / Odd"},
99        };
100        struct StyleAndName {
101            SkPaint::Style fStyle;
102            const char*    fName;
103        };
104        static const StyleAndName gStyles[] = {
105            {SkPaint::kFill_Style, "Fill"},
106            {SkPaint::kStroke_Style, "Stroke"},
107            {SkPaint::kStrokeAndFill_Style, "Stroke And Fill"},
108        };
109        struct CapAndName {
110            SkPaint::Cap  fCap;
111            SkPaint::Join fJoin;
112            const char*   fName;
113        };
114        static const CapAndName gCaps[] = {
115            {SkPaint::kButt_Cap, SkPaint::kBevel_Join, "Butt"},
116            {SkPaint::kRound_Cap, SkPaint::kRound_Join, "Round"},
117            {SkPaint::kSquare_Cap, SkPaint::kBevel_Join, "Square"}
118        };
119        struct PathAndName {
120            SkPath      fPath;
121            const char* fName;
122        };
123        PathAndName path;
124        path.fPath.moveTo(25*SK_Scalar1, 10*SK_Scalar1);
125        path.fPath.cubicTo(40*SK_Scalar1, 20*SK_Scalar1,
126                           60*SK_Scalar1, 20*SK_Scalar1,
127                           75*SK_Scalar1, 10*SK_Scalar1);
128        path.fName = "moveTo-cubic";
129
130        SkPaint titlePaint;
131        titlePaint.setColor(SK_ColorBLACK);
132        titlePaint.setAntiAlias(true);
133        titlePaint.setLCDRenderText(true);
134        titlePaint.setTextSize(15 * SK_Scalar1);
135        const char title[] = "Cubic Drawn Into Rectangle Clips With "
136                             "Indicated Style, Fill and Linecaps, with stroke width 10";
137        canvas->drawText(title, strlen(title),
138                            20 * SK_Scalar1,
139                            20 * SK_Scalar1,
140                            titlePaint);
141
142        SkLCGRandom rand;
143        SkRect rect = SkRect::MakeWH(100*SK_Scalar1, 30*SK_Scalar1);
144        canvas->save();
145        canvas->translate(10 * SK_Scalar1, 30 * SK_Scalar1);
146        canvas->save();
147        for (size_t cap = 0; cap < SK_ARRAY_COUNT(gCaps); ++cap) {
148            if (0 < cap) {
149                canvas->translate((rect.width() + 40 * SK_Scalar1) * SK_ARRAY_COUNT(gStyles), 0);
150            }
151            canvas->save();
152            for (size_t fill = 0; fill < SK_ARRAY_COUNT(gFills); ++fill) {
153                if (0 < fill) {
154                    canvas->translate(0, rect.height() + 40 * SK_Scalar1);
155                }
156                canvas->save();
157                for (size_t style = 0; style < SK_ARRAY_COUNT(gStyles); ++style) {
158                    if (0 < style) {
159                        canvas->translate(rect.width() + 40 * SK_Scalar1, 0);
160                    }
161
162                    SkColor color = 0xff007000;
163                    this->drawPath(path.fPath, canvas, color, rect,
164                                    gCaps[cap].fCap, gCaps[cap].fJoin, gStyles[style].fStyle,
165                                    gFills[fill].fFill, SK_Scalar1*10);
166
167                    SkPaint rectPaint;
168                    rectPaint.setColor(SK_ColorBLACK);
169                    rectPaint.setStyle(SkPaint::kStroke_Style);
170                    rectPaint.setStrokeWidth(-1);
171                    rectPaint.setAntiAlias(true);
172                    canvas->drawRect(rect, rectPaint);
173
174                    SkPaint labelPaint;
175                    labelPaint.setColor(color);
176                    labelPaint.setAntiAlias(true);
177                    labelPaint.setLCDRenderText(true);
178                    labelPaint.setTextSize(10 * SK_Scalar1);
179                    canvas->drawText(gStyles[style].fName,
180                                        strlen(gStyles[style].fName),
181                                        0, rect.height() + 12 * SK_Scalar1,
182                                        labelPaint);
183                    canvas->drawText(gFills[fill].fName,
184                                        strlen(gFills[fill].fName),
185                                        0, rect.height() + 24 * SK_Scalar1,
186                                        labelPaint);
187                    canvas->drawText(gCaps[cap].fName,
188                                        strlen(gCaps[cap].fName),
189                                        0, rect.height() + 36 * SK_Scalar1,
190                                        labelPaint);
191                }
192                canvas->restore();
193            }
194            canvas->restore();
195        }
196        canvas->restore();
197        canvas->restore();
198    }
199
200private:
201    typedef skiagm::GM INHERITED;
202};
203
204class CubicClosePathGM : public skiagm::GM {
205public:
206    CubicClosePathGM() {}
207
208protected:
209    virtual uint32_t onGetFlags() const SK_OVERRIDE {
210        return kSkipTiled_Flag;
211    }
212
213    SkString onShortName() {
214        return SkString("cubicclosepath");
215    }
216
217    SkISize onISize() { return SkISize::Make(1240, 390); }
218
219    void drawPath(SkPath& path,SkCanvas* canvas,SkColor color,
220                  const SkRect& clip,SkPaint::Cap cap, SkPaint::Join join,
221                  SkPaint::Style style, SkPath::FillType fill,
222                  SkScalar strokeWidth) {
223        path.setFillType(fill);
224        SkPaint paint;
225        paint.setStrokeCap(cap);
226        paint.setStrokeWidth(strokeWidth);
227        paint.setStrokeJoin(join);
228        paint.setColor(color);
229        paint.setStyle(style);
230        canvas->save();
231        canvas->clipRect(clip);
232        canvas->drawPath(path, paint);
233        canvas->restore();
234    }
235
236    virtual void onDraw(SkCanvas* canvas) {
237        struct FillAndName {
238            SkPath::FillType fFill;
239            const char*      fName;
240        };
241        static const FillAndName gFills[] = {
242            {SkPath::kWinding_FillType, "Winding"},
243            {SkPath::kEvenOdd_FillType, "Even / Odd"},
244            {SkPath::kInverseWinding_FillType, "Inverse Winding"},
245            {SkPath::kInverseEvenOdd_FillType, "Inverse Even / Odd"},
246        };
247        struct StyleAndName {
248            SkPaint::Style fStyle;
249            const char*    fName;
250        };
251        static const StyleAndName gStyles[] = {
252            {SkPaint::kFill_Style, "Fill"},
253            {SkPaint::kStroke_Style, "Stroke"},
254            {SkPaint::kStrokeAndFill_Style, "Stroke And Fill"},
255        };
256        struct CapAndName {
257            SkPaint::Cap  fCap;
258            SkPaint::Join fJoin;
259            const char*   fName;
260        };
261        static const CapAndName gCaps[] = {
262            {SkPaint::kButt_Cap, SkPaint::kBevel_Join, "Butt"},
263            {SkPaint::kRound_Cap, SkPaint::kRound_Join, "Round"},
264            {SkPaint::kSquare_Cap, SkPaint::kBevel_Join, "Square"}
265        };
266        struct PathAndName {
267            SkPath      fPath;
268            const char* fName;
269        };
270        PathAndName path;
271        path.fPath.moveTo(25*SK_Scalar1, 10*SK_Scalar1);
272        path.fPath.cubicTo(40*SK_Scalar1, 20*SK_Scalar1,
273                           60*SK_Scalar1, 20*SK_Scalar1,
274                           75*SK_Scalar1, 10*SK_Scalar1);
275        path.fPath.close();
276        path.fName = "moveTo-cubic-close";
277
278        SkPaint titlePaint;
279        titlePaint.setColor(SK_ColorBLACK);
280        titlePaint.setAntiAlias(true);
281        titlePaint.setLCDRenderText(true);
282        titlePaint.setTextSize(15 * SK_Scalar1);
283        const char title[] = "Cubic Closed Drawn Into Rectangle Clips With "
284                             "Indicated Style, Fill and Linecaps, with stroke width 10";
285        canvas->drawText(title, strlen(title),
286                            20 * SK_Scalar1,
287                            20 * SK_Scalar1,
288                            titlePaint);
289
290        SkLCGRandom rand;
291        SkRect rect = SkRect::MakeWH(100*SK_Scalar1, 30*SK_Scalar1);
292        canvas->save();
293        canvas->translate(10 * SK_Scalar1, 30 * SK_Scalar1);
294        canvas->save();
295        for (size_t cap = 0; cap < SK_ARRAY_COUNT(gCaps); ++cap) {
296            if (0 < cap) {
297                canvas->translate((rect.width() + 40 * SK_Scalar1) * SK_ARRAY_COUNT(gStyles), 0);
298            }
299            canvas->save();
300            for (size_t fill = 0; fill < SK_ARRAY_COUNT(gFills); ++fill) {
301                if (0 < fill) {
302                    canvas->translate(0, rect.height() + 40 * SK_Scalar1);
303                }
304                canvas->save();
305                for (size_t style = 0; style < SK_ARRAY_COUNT(gStyles); ++style) {
306                    if (0 < style) {
307                        canvas->translate(rect.width() + 40 * SK_Scalar1, 0);
308                    }
309
310                    SkColor color = 0xff007000;
311                    this->drawPath(path.fPath, canvas, color, rect,
312                                    gCaps[cap].fCap, gCaps[cap].fJoin, gStyles[style].fStyle,
313                                    gFills[fill].fFill, SK_Scalar1*10);
314
315                    SkPaint rectPaint;
316                    rectPaint.setColor(SK_ColorBLACK);
317                    rectPaint.setStyle(SkPaint::kStroke_Style);
318                    rectPaint.setStrokeWidth(-1);
319                    rectPaint.setAntiAlias(true);
320                    canvas->drawRect(rect, rectPaint);
321
322                    SkPaint labelPaint;
323                    labelPaint.setColor(color);
324                    labelPaint.setAntiAlias(true);
325                    labelPaint.setLCDRenderText(true);
326                    labelPaint.setTextSize(10 * SK_Scalar1);
327                    canvas->drawText(gStyles[style].fName,
328                                        strlen(gStyles[style].fName),
329                                        0, rect.height() + 12 * SK_Scalar1,
330                                        labelPaint);
331                    canvas->drawText(gFills[fill].fName,
332                                        strlen(gFills[fill].fName),
333                                        0, rect.height() + 24 * SK_Scalar1,
334                                        labelPaint);
335                    canvas->drawText(gCaps[cap].fName,
336                                        strlen(gCaps[cap].fName),
337                                        0, rect.height() + 36 * SK_Scalar1,
338                                        labelPaint);
339                }
340                canvas->restore();
341            }
342            canvas->restore();
343        }
344        canvas->restore();
345        canvas->restore();
346    }
347
348private:
349    typedef skiagm::GM INHERITED;
350};
351
352//////////////////////////////////////////////////////////////////////////////
353
354DEF_GM( return new CubicPathGM; )
355DEF_GM( return new CubicClosePathGM; )
356DEF_GM( return new ClippedCubicGM; )
357