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->drawText(title, strlen(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->drawText(gStyles[style].fName,
248                                        strlen(gStyles[style].fName),
249                                        0, rect.height() + 12 * SK_Scalar1,
250                                        labelPaint);
251                    canvas->drawText(gFills[fill].fName,
252                                        strlen(gFills[fill].fName),
253                                        0, rect.height() + 24 * SK_Scalar1,
254                                        labelPaint);
255                    canvas->drawText(gCaps[cap].fName,
256                                        strlen(gCaps[cap].fName),
257                                        0, rect.height() + 36 * SK_Scalar1,
258                                        labelPaint);
259                }
260                canvas->restore();
261            }
262            canvas->restore();
263        }
264        canvas->restore();
265        canvas->restore();
266    }
267
268private:
269    typedef skiagm::GM INHERITED;
270};
271
272class CubicClosePathGM : public skiagm::GM {
273public:
274    CubicClosePathGM() {}
275
276protected:
277
278    SkString onShortName() {
279        return SkString("cubicclosepath");
280    }
281
282    SkISize onISize() { return SkISize::Make(1240, 390); }
283
284    void drawPath(SkPath& path,SkCanvas* canvas,SkColor color,
285                  const SkRect& clip,SkPaint::Cap cap, SkPaint::Join join,
286                  SkPaint::Style style, SkPath::FillType fill,
287                  SkScalar strokeWidth) {
288        path.setFillType(fill);
289        SkPaint paint;
290        paint.setStrokeCap(cap);
291        paint.setStrokeWidth(strokeWidth);
292        paint.setStrokeJoin(join);
293        paint.setColor(color);
294        paint.setStyle(style);
295        canvas->save();
296        canvas->clipRect(clip);
297        canvas->drawPath(path, paint);
298        canvas->restore();
299    }
300
301    virtual void onDraw(SkCanvas* canvas) {
302        struct FillAndName {
303            SkPath::FillType fFill;
304            const char*      fName;
305        };
306        constexpr FillAndName gFills[] = {
307            {SkPath::kWinding_FillType, "Winding"},
308            {SkPath::kEvenOdd_FillType, "Even / Odd"},
309            {SkPath::kInverseWinding_FillType, "Inverse Winding"},
310            {SkPath::kInverseEvenOdd_FillType, "Inverse Even / Odd"},
311        };
312        struct StyleAndName {
313            SkPaint::Style fStyle;
314            const char*    fName;
315        };
316        constexpr StyleAndName gStyles[] = {
317            {SkPaint::kFill_Style, "Fill"},
318            {SkPaint::kStroke_Style, "Stroke"},
319            {SkPaint::kStrokeAndFill_Style, "Stroke And Fill"},
320        };
321        struct CapAndName {
322            SkPaint::Cap  fCap;
323            SkPaint::Join fJoin;
324            const char*   fName;
325        };
326        constexpr CapAndName gCaps[] = {
327            {SkPaint::kButt_Cap, SkPaint::kBevel_Join, "Butt"},
328            {SkPaint::kRound_Cap, SkPaint::kRound_Join, "Round"},
329            {SkPaint::kSquare_Cap, SkPaint::kBevel_Join, "Square"}
330        };
331        struct PathAndName {
332            SkPath      fPath;
333            const char* fName;
334        };
335        PathAndName path;
336        path.fPath.moveTo(25*SK_Scalar1, 10*SK_Scalar1);
337        path.fPath.cubicTo(40*SK_Scalar1, 20*SK_Scalar1,
338                           60*SK_Scalar1, 20*SK_Scalar1,
339                           75*SK_Scalar1, 10*SK_Scalar1);
340        path.fPath.close();
341        path.fName = "moveTo-cubic-close";
342
343        SkPaint titlePaint;
344        titlePaint.setColor(SK_ColorBLACK);
345        titlePaint.setAntiAlias(true);
346        sk_tool_utils::set_portable_typeface(&titlePaint);
347        titlePaint.setTextSize(15 * SK_Scalar1);
348        const char title[] = "Cubic Closed Drawn Into Rectangle Clips With "
349                             "Indicated Style, Fill and Linecaps, with stroke width 10";
350        canvas->drawText(title, strlen(title),
351                            20 * SK_Scalar1,
352                            20 * SK_Scalar1,
353                            titlePaint);
354
355        SkRandom rand;
356        SkRect rect = SkRect::MakeWH(100*SK_Scalar1, 30*SK_Scalar1);
357        canvas->save();
358        canvas->translate(10 * SK_Scalar1, 30 * SK_Scalar1);
359        canvas->save();
360        for (size_t cap = 0; cap < SK_ARRAY_COUNT(gCaps); ++cap) {
361            if (0 < cap) {
362                canvas->translate((rect.width() + 40 * SK_Scalar1) * SK_ARRAY_COUNT(gStyles), 0);
363            }
364            canvas->save();
365            for (size_t fill = 0; fill < SK_ARRAY_COUNT(gFills); ++fill) {
366                if (0 < fill) {
367                    canvas->translate(0, rect.height() + 40 * SK_Scalar1);
368                }
369                canvas->save();
370                for (size_t style = 0; style < SK_ARRAY_COUNT(gStyles); ++style) {
371                    if (0 < style) {
372                        canvas->translate(rect.width() + 40 * SK_Scalar1, 0);
373                    }
374
375                    SkColor color = 0xff007000;
376                    this->drawPath(path.fPath, canvas, color, rect,
377                                    gCaps[cap].fCap, gCaps[cap].fJoin, gStyles[style].fStyle,
378                                    gFills[fill].fFill, SK_Scalar1*10);
379
380                    SkPaint rectPaint;
381                    rectPaint.setColor(SK_ColorBLACK);
382                    rectPaint.setStyle(SkPaint::kStroke_Style);
383                    rectPaint.setStrokeWidth(-1);
384                    rectPaint.setAntiAlias(true);
385                    canvas->drawRect(rect, rectPaint);
386
387                    SkPaint labelPaint;
388                    labelPaint.setColor(color);
389                    labelPaint.setAntiAlias(true);
390                    sk_tool_utils::set_portable_typeface(&labelPaint);
391                    labelPaint.setTextSize(10 * SK_Scalar1);
392                    canvas->drawText(gStyles[style].fName,
393                                        strlen(gStyles[style].fName),
394                                        0, rect.height() + 12 * SK_Scalar1,
395                                        labelPaint);
396                    canvas->drawText(gFills[fill].fName,
397                                        strlen(gFills[fill].fName),
398                                        0, rect.height() + 24 * SK_Scalar1,
399                                        labelPaint);
400                    canvas->drawText(gCaps[cap].fName,
401                                        strlen(gCaps[cap].fName),
402                                        0, rect.height() + 36 * SK_Scalar1,
403                                        labelPaint);
404                }
405                canvas->restore();
406            }
407            canvas->restore();
408        }
409        canvas->restore();
410        canvas->restore();
411    }
412
413private:
414    typedef skiagm::GM INHERITED;
415};
416
417DEF_SIMPLE_GM(bug5099, canvas, 50, 50) {
418    SkPaint p;
419    p.setColor(SK_ColorRED);
420    p.setAntiAlias(true);
421    p.setStyle(SkPaint::kStroke_Style);
422    p.setStrokeWidth(10);
423
424    SkPath path;
425    path.moveTo(6, 27);
426    path.cubicTo(31.5f, 1.5f, 3.5f, 4.5f, 29, 29);
427    canvas->drawPath(path, p);
428}
429
430DEF_SIMPLE_GM(bug6083, canvas, 100, 50) {
431    SkPaint p;
432    p.setColor(SK_ColorRED);
433    p.setAntiAlias(true);
434    p.setStyle(SkPaint::kStroke_Style);
435    p.setStrokeWidth(15);
436    canvas->translate(-500, -130);
437    SkPath path;
438    path.moveTo(500.988f, 155.200f);
439    path.lineTo(526.109f, 155.200f);
440    SkPoint p1 = { 526.109f, 155.200f };
441    SkPoint p2 = { 525.968f, 212.968f };
442    SkPoint p3 = { 526.109f, 241.840f };
443    path.cubicTo(p1, p2, p3);
444    canvas->drawPath(path, p);
445    canvas->translate(50, 0);
446    path.reset();
447    p2.set(525.968f, 213.172f);
448    path.moveTo(500.988f, 155.200f);
449    path.lineTo(526.109f, 155.200f);
450    path.cubicTo(p1, p2, p3);
451    canvas->drawPath(path, p);
452}
453
454//////////////////////////////////////////////////////////////////////////////
455
456DEF_GM( return new CubicPathGM; )
457DEF_GM( return new CubicClosePathGM; )
458DEF_GM( return new ClippedCubicGM; )
459DEF_GM( return new ClippedCubic2GM; )
460