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
8#include "gm.h"
9#include "sk_tool_utils.h"
10#include "SkCanvas.h"
11#include "SkPaint.h"
12#include "SkPath.h"
13#include "SkRandom.h"
14
15// https://bug.skia.org/1316 shows that this cubic, when slightly clipped, creates big
16// (incorrect) changes to its control points.
17class ClippedCubicGM : public skiagm::GM {
18public:
19    ClippedCubicGM() {}
20
21protected:
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
57
58class ClippedCubic2GM : public skiagm::GM {
59public:
60    ClippedCubic2GM() {}
61
62protected:
63
64    SkString onShortName() override {
65        return SkString("clippedcubic2");
66    }
67
68    SkISize onISize() override { return SkISize::Make(1240, 390); }
69
70    void onDraw(SkCanvas* canvas) override {
71        canvas->save();
72        canvas->translate(-2, 120);
73        drawOne(canvas, fPath, SkRect::MakeLTRB(0, 0, 80, 150));
74        canvas->translate(0, 170);
75        drawOne(canvas, fPath, SkRect::MakeLTRB(0, 0, 80, 100));
76        canvas->translate(0, 170);
77        drawOne(canvas, fPath, SkRect::MakeLTRB(0, 0, 30, 150));
78        canvas->translate(0, 170);
79        drawOne(canvas, fPath, SkRect::MakeLTRB(0, 0, 10, 150));
80        canvas->restore();
81        canvas->save();
82        canvas->translate(20, -2);
83        drawOne(canvas, fFlipped, SkRect::MakeLTRB(0, 0, 150, 80));
84        canvas->translate(170, 0);
85        drawOne(canvas, fFlipped, SkRect::MakeLTRB(0, 0, 100, 80));
86        canvas->translate(170, 0);
87        drawOne(canvas, fFlipped, SkRect::MakeLTRB(0, 0, 150, 30));
88        canvas->translate(170, 0);
89        drawOne(canvas, fFlipped, SkRect::MakeLTRB(0, 0, 150, 10));
90        canvas->restore();
91    }
92
93    void drawOne(SkCanvas* canvas, const SkPath& path, const SkRect& clip) {
94        SkPaint framePaint, fillPaint;
95        framePaint.setStyle(SkPaint::kStroke_Style);
96        canvas->drawRect(clip, framePaint);
97        canvas->drawPath(path, framePaint);
98        canvas->save();
99        canvas->clipRect(clip);
100        canvas->drawPath(path, fillPaint);
101        canvas->restore();
102    }
103
104    void onOnceBeforeDraw() override {
105        fPath.moveTo(69.7030518991886f, 0);
106        fPath.cubicTo( 69.7030518991886f, 21.831149999999997f,
107                58.08369508178456f, 43.66448333333333f, 34.8449814469765f, 65.5f);
108        fPath.cubicTo( 11.608591683531916f, 87.33115f, -0.010765133872116195f, 109.16448333333332f,
109                -0.013089005235602302f, 131);
110        fPath.close();
111        fFlipped = fPath;
112        SkMatrix matrix;
113        matrix.reset();
114        matrix.setScaleX(0);
115        matrix.setScaleY(0);
116        matrix.setSkewX(1);
117        matrix.setSkewY(1);
118        fFlipped.transform(matrix);
119    }
120
121    SkPath fPath;
122    SkPath fFlipped;
123private:
124    typedef skiagm::GM INHERITED;
125};
126
127
128class CubicPathGM : public skiagm::GM {
129public:
130    CubicPathGM() {}
131
132protected:
133
134    SkString onShortName() {
135        return SkString("cubicpath");
136    }
137
138    SkISize onISize() { return SkISize::Make(1240, 390); }
139
140    void drawPath(SkPath& path,SkCanvas* canvas,SkColor color,
141                  const SkRect& clip,SkPaint::Cap cap, SkPaint::Join join,
142                  SkPaint::Style style, SkPath::FillType fill,
143                  SkScalar strokeWidth) {
144        path.setFillType(fill);
145        SkPaint paint;
146        paint.setStrokeCap(cap);
147        paint.setStrokeWidth(strokeWidth);
148        paint.setStrokeJoin(join);
149        paint.setColor(color);
150        paint.setStyle(style);
151        canvas->save();
152        canvas->clipRect(clip);
153        canvas->drawPath(path, paint);
154        canvas->restore();
155    }
156
157    virtual void onDraw(SkCanvas* canvas) {
158        struct FillAndName {
159            SkPath::FillType fFill;
160            const char*      fName;
161        };
162        constexpr FillAndName gFills[] = {
163            {SkPath::kWinding_FillType, "Winding"},
164            {SkPath::kEvenOdd_FillType, "Even / Odd"},
165            {SkPath::kInverseWinding_FillType, "Inverse Winding"},
166            {SkPath::kInverseEvenOdd_FillType, "Inverse Even / Odd"},
167        };
168        struct StyleAndName {
169            SkPaint::Style fStyle;
170            const char*    fName;
171        };
172        constexpr StyleAndName gStyles[] = {
173            {SkPaint::kFill_Style, "Fill"},
174            {SkPaint::kStroke_Style, "Stroke"},
175            {SkPaint::kStrokeAndFill_Style, "Stroke And Fill"},
176        };
177        struct CapAndName {
178            SkPaint::Cap  fCap;
179            SkPaint::Join fJoin;
180            const char*   fName;
181        };
182        constexpr CapAndName gCaps[] = {
183            {SkPaint::kButt_Cap, SkPaint::kBevel_Join, "Butt"},
184            {SkPaint::kRound_Cap, SkPaint::kRound_Join, "Round"},
185            {SkPaint::kSquare_Cap, SkPaint::kBevel_Join, "Square"}
186        };
187        struct PathAndName {
188            SkPath      fPath;
189            const char* fName;
190        };
191        PathAndName path;
192        path.fPath.moveTo(25*SK_Scalar1, 10*SK_Scalar1);
193        path.fPath.cubicTo(40*SK_Scalar1, 20*SK_Scalar1,
194                           60*SK_Scalar1, 20*SK_Scalar1,
195                           75*SK_Scalar1, 10*SK_Scalar1);
196        path.fName = "moveTo-cubic";
197
198        SkPaint titlePaint;
199        titlePaint.setColor(SK_ColorBLACK);
200        titlePaint.setAntiAlias(true);
201        sk_tool_utils::set_portable_typeface(&titlePaint);
202        titlePaint.setTextSize(15 * SK_Scalar1);
203        const char title[] = "Cubic Drawn Into Rectangle Clips With "
204                             "Indicated Style, Fill and Linecaps, with stroke width 10";
205        canvas->drawString(title,
206                           20 * SK_Scalar1,
207                           20 * SK_Scalar1,
208                           titlePaint);
209
210        SkRandom rand;
211        SkRect rect = SkRect::MakeWH(100*SK_Scalar1, 30*SK_Scalar1);
212        canvas->save();
213        canvas->translate(10 * SK_Scalar1, 30 * SK_Scalar1);
214        canvas->save();
215        for (size_t cap = 0; cap < SK_ARRAY_COUNT(gCaps); ++cap) {
216            if (0 < cap) {
217                canvas->translate((rect.width() + 40 * SK_Scalar1) * SK_ARRAY_COUNT(gStyles), 0);
218            }
219            canvas->save();
220            for (size_t fill = 0; fill < SK_ARRAY_COUNT(gFills); ++fill) {
221                if (0 < fill) {
222                    canvas->translate(0, rect.height() + 40 * SK_Scalar1);
223                }
224                canvas->save();
225                for (size_t style = 0; style < SK_ARRAY_COUNT(gStyles); ++style) {
226                    if (0 < style) {
227                        canvas->translate(rect.width() + 40 * SK_Scalar1, 0);
228                    }
229
230                    SkColor color = 0xff007000;
231                    this->drawPath(path.fPath, canvas, color, rect,
232                                    gCaps[cap].fCap, gCaps[cap].fJoin, gStyles[style].fStyle,
233                                    gFills[fill].fFill, SK_Scalar1*10);
234
235                    SkPaint rectPaint;
236                    rectPaint.setColor(SK_ColorBLACK);
237                    rectPaint.setStyle(SkPaint::kStroke_Style);
238                    rectPaint.setStrokeWidth(-1);
239                    rectPaint.setAntiAlias(true);
240                    canvas->drawRect(rect, rectPaint);
241
242                    SkPaint labelPaint;
243                    labelPaint.setColor(color);
244                    labelPaint.setAntiAlias(true);
245                    sk_tool_utils::set_portable_typeface(&labelPaint);
246                    labelPaint.setTextSize(10 * SK_Scalar1);
247                    canvas->drawString(gStyles[style].fName,
248                                       0, rect.height() + 12 * SK_Scalar1,
249                                       labelPaint);
250                    canvas->drawString(gFills[fill].fName,
251                                       0, rect.height() + 24 * SK_Scalar1,
252                                       labelPaint);
253                    canvas->drawString(gCaps[cap].fName,
254                                       0, rect.height() + 36 * SK_Scalar1,
255                                       labelPaint);
256                }
257                canvas->restore();
258            }
259            canvas->restore();
260        }
261        canvas->restore();
262        canvas->restore();
263    }
264
265private:
266    typedef skiagm::GM INHERITED;
267};
268
269class CubicClosePathGM : public skiagm::GM {
270public:
271    CubicClosePathGM() {}
272
273protected:
274
275    SkString onShortName() {
276        return SkString("cubicclosepath");
277    }
278
279    SkISize onISize() { return SkISize::Make(1240, 390); }
280
281    void drawPath(SkPath& path,SkCanvas* canvas,SkColor color,
282                  const SkRect& clip,SkPaint::Cap cap, SkPaint::Join join,
283                  SkPaint::Style style, SkPath::FillType fill,
284                  SkScalar strokeWidth) {
285        path.setFillType(fill);
286        SkPaint paint;
287        paint.setStrokeCap(cap);
288        paint.setStrokeWidth(strokeWidth);
289        paint.setStrokeJoin(join);
290        paint.setColor(color);
291        paint.setStyle(style);
292        canvas->save();
293        canvas->clipRect(clip);
294        canvas->drawPath(path, paint);
295        canvas->restore();
296    }
297
298    virtual void onDraw(SkCanvas* canvas) {
299        struct FillAndName {
300            SkPath::FillType fFill;
301            const char*      fName;
302        };
303        constexpr FillAndName gFills[] = {
304            {SkPath::kWinding_FillType, "Winding"},
305            {SkPath::kEvenOdd_FillType, "Even / Odd"},
306            {SkPath::kInverseWinding_FillType, "Inverse Winding"},
307            {SkPath::kInverseEvenOdd_FillType, "Inverse Even / Odd"},
308        };
309        struct StyleAndName {
310            SkPaint::Style fStyle;
311            const char*    fName;
312        };
313        constexpr StyleAndName gStyles[] = {
314            {SkPaint::kFill_Style, "Fill"},
315            {SkPaint::kStroke_Style, "Stroke"},
316            {SkPaint::kStrokeAndFill_Style, "Stroke And Fill"},
317        };
318        struct CapAndName {
319            SkPaint::Cap  fCap;
320            SkPaint::Join fJoin;
321            const char*   fName;
322        };
323        constexpr CapAndName gCaps[] = {
324            {SkPaint::kButt_Cap, SkPaint::kBevel_Join, "Butt"},
325            {SkPaint::kRound_Cap, SkPaint::kRound_Join, "Round"},
326            {SkPaint::kSquare_Cap, SkPaint::kBevel_Join, "Square"}
327        };
328        struct PathAndName {
329            SkPath      fPath;
330            const char* fName;
331        };
332        PathAndName path;
333        path.fPath.moveTo(25*SK_Scalar1, 10*SK_Scalar1);
334        path.fPath.cubicTo(40*SK_Scalar1, 20*SK_Scalar1,
335                           60*SK_Scalar1, 20*SK_Scalar1,
336                           75*SK_Scalar1, 10*SK_Scalar1);
337        path.fPath.close();
338        path.fName = "moveTo-cubic-close";
339
340        SkPaint titlePaint;
341        titlePaint.setColor(SK_ColorBLACK);
342        titlePaint.setAntiAlias(true);
343        sk_tool_utils::set_portable_typeface(&titlePaint);
344        titlePaint.setTextSize(15 * SK_Scalar1);
345        const char title[] = "Cubic Closed Drawn Into Rectangle Clips With "
346                             "Indicated Style, Fill and Linecaps, with stroke width 10";
347        canvas->drawString(title,
348                           20 * SK_Scalar1,
349                           20 * SK_Scalar1,
350                           titlePaint);
351
352        SkRandom rand;
353        SkRect rect = SkRect::MakeWH(100*SK_Scalar1, 30*SK_Scalar1);
354        canvas->save();
355        canvas->translate(10 * SK_Scalar1, 30 * SK_Scalar1);
356        canvas->save();
357        for (size_t cap = 0; cap < SK_ARRAY_COUNT(gCaps); ++cap) {
358            if (0 < cap) {
359                canvas->translate((rect.width() + 40 * SK_Scalar1) * SK_ARRAY_COUNT(gStyles), 0);
360            }
361            canvas->save();
362            for (size_t fill = 0; fill < SK_ARRAY_COUNT(gFills); ++fill) {
363                if (0 < fill) {
364                    canvas->translate(0, rect.height() + 40 * SK_Scalar1);
365                }
366                canvas->save();
367                for (size_t style = 0; style < SK_ARRAY_COUNT(gStyles); ++style) {
368                    if (0 < style) {
369                        canvas->translate(rect.width() + 40 * SK_Scalar1, 0);
370                    }
371
372                    SkColor color = 0xff007000;
373                    this->drawPath(path.fPath, canvas, color, rect,
374                                    gCaps[cap].fCap, gCaps[cap].fJoin, gStyles[style].fStyle,
375                                    gFills[fill].fFill, SK_Scalar1*10);
376
377                    SkPaint rectPaint;
378                    rectPaint.setColor(SK_ColorBLACK);
379                    rectPaint.setStyle(SkPaint::kStroke_Style);
380                    rectPaint.setStrokeWidth(-1);
381                    rectPaint.setAntiAlias(true);
382                    canvas->drawRect(rect, rectPaint);
383
384                    SkPaint labelPaint;
385                    labelPaint.setColor(color);
386                    labelPaint.setAntiAlias(true);
387                    sk_tool_utils::set_portable_typeface(&labelPaint);
388                    labelPaint.setTextSize(10 * SK_Scalar1);
389                    canvas->drawString(gStyles[style].fName,
390                                       0, rect.height() + 12 * SK_Scalar1,
391                                       labelPaint);
392                    canvas->drawString(gFills[fill].fName,
393                                       0, rect.height() + 24 * SK_Scalar1,
394                                       labelPaint);
395                    canvas->drawString(gCaps[cap].fName,
396                                       0, rect.height() + 36 * SK_Scalar1,
397                                       labelPaint);
398                }
399                canvas->restore();
400            }
401            canvas->restore();
402        }
403        canvas->restore();
404        canvas->restore();
405    }
406
407private:
408    typedef skiagm::GM INHERITED;
409};
410
411DEF_SIMPLE_GM(bug5099, canvas, 50, 50) {
412    SkPaint p;
413    p.setColor(SK_ColorRED);
414    p.setAntiAlias(true);
415    p.setStyle(SkPaint::kStroke_Style);
416    p.setStrokeWidth(10);
417
418    SkPath path;
419    path.moveTo(6, 27);
420    path.cubicTo(31.5f, 1.5f, 3.5f, 4.5f, 29, 29);
421    canvas->drawPath(path, p);
422}
423
424DEF_SIMPLE_GM(bug6083, canvas, 100, 50) {
425    SkPaint p;
426    p.setColor(SK_ColorRED);
427    p.setAntiAlias(true);
428    p.setStyle(SkPaint::kStroke_Style);
429    p.setStrokeWidth(15);
430    canvas->translate(-500, -130);
431    SkPath path;
432    path.moveTo(500.988f, 155.200f);
433    path.lineTo(526.109f, 155.200f);
434    SkPoint p1 = { 526.109f, 155.200f };
435    SkPoint p2 = { 525.968f, 212.968f };
436    SkPoint p3 = { 526.109f, 241.840f };
437    path.cubicTo(p1, p2, p3);
438    canvas->drawPath(path, p);
439    canvas->translate(50, 0);
440    path.reset();
441    p2.set(525.968f, 213.172f);
442    path.moveTo(500.988f, 155.200f);
443    path.lineTo(526.109f, 155.200f);
444    path.cubicTo(p1, p2, p3);
445    canvas->drawPath(path, p);
446}
447
448//////////////////////////////////////////////////////////////////////////////
449
450DEF_GM( return new CubicPathGM; )
451DEF_GM( return new CubicClosePathGM; )
452DEF_GM( return new ClippedCubicGM; )
453DEF_GM( return new ClippedCubic2GM; )
454