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.
17 class ClippedCubicGM : public skiagm::GM {
18 public:
ClippedCubicGM()19     ClippedCubicGM() {}
20 
21 protected:
22 
onShortName()23     SkString onShortName() {
24         return SkString("clippedcubic");
25     }
26 
onISize()27     SkISize onISize() { return SkISize::Make(1240, 390); }
28 
onDraw(SkCanvas * canvas)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 
53 private:
54     typedef skiagm::GM INHERITED;
55 };
56 
57 
58 class ClippedCubic2GM : public skiagm::GM {
59 public:
ClippedCubic2GM()60     ClippedCubic2GM() {}
61 
62 protected:
63 
onShortName()64     SkString onShortName() override {
65         return SkString("clippedcubic2");
66     }
67 
onISize()68     SkISize onISize() override { return SkISize::Make(1240, 390); }
69 
onDraw(SkCanvas * canvas)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 
drawOne(SkCanvas * canvas,const SkPath & path,const SkRect & clip)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 
onOnceBeforeDraw()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;
123 private:
124     typedef skiagm::GM INHERITED;
125 };
126 
127 
128 class CubicPathGM : public skiagm::GM {
129 public:
CubicPathGM()130     CubicPathGM() {}
131 
132 protected:
133 
onShortName()134     SkString onShortName() {
135         return SkString("cubicpath");
136     }
137 
onISize()138     SkISize onISize() { return SkISize::Make(1240, 390); }
139 
drawPath(SkPath & path,SkCanvas * canvas,SkColor color,const SkRect & clip,SkPaint::Cap cap,SkPaint::Join join,SkPaint::Style style,SkPath::FillType fill,SkScalar strokeWidth)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 
onDraw(SkCanvas * canvas)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         SkFont font(sk_tool_utils::create_portable_typeface(), 15);
202         const char title[] = "Cubic Drawn Into Rectangle Clips With "
203                              "Indicated Style, Fill and Linecaps, with stroke width 10";
204         canvas->drawString(title, 20, 20, font, titlePaint);
205 
206         SkRandom rand;
207         SkRect rect = SkRect::MakeWH(100*SK_Scalar1, 30*SK_Scalar1);
208         canvas->save();
209         canvas->translate(10 * SK_Scalar1, 30 * SK_Scalar1);
210         canvas->save();
211         for (size_t cap = 0; cap < SK_ARRAY_COUNT(gCaps); ++cap) {
212             if (0 < cap) {
213                 canvas->translate((rect.width() + 40 * SK_Scalar1) * SK_ARRAY_COUNT(gStyles), 0);
214             }
215             canvas->save();
216             for (size_t fill = 0; fill < SK_ARRAY_COUNT(gFills); ++fill) {
217                 if (0 < fill) {
218                     canvas->translate(0, rect.height() + 40 * SK_Scalar1);
219                 }
220                 canvas->save();
221                 for (size_t style = 0; style < SK_ARRAY_COUNT(gStyles); ++style) {
222                     if (0 < style) {
223                         canvas->translate(rect.width() + 40 * SK_Scalar1, 0);
224                     }
225 
226                     SkColor color = 0xff007000;
227                     this->drawPath(path.fPath, canvas, color, rect,
228                                     gCaps[cap].fCap, gCaps[cap].fJoin, gStyles[style].fStyle,
229                                     gFills[fill].fFill, SK_Scalar1*10);
230 
231                     SkPaint rectPaint;
232                     rectPaint.setColor(SK_ColorBLACK);
233                     rectPaint.setStyle(SkPaint::kStroke_Style);
234                     rectPaint.setStrokeWidth(-1);
235                     rectPaint.setAntiAlias(true);
236                     canvas->drawRect(rect, rectPaint);
237 
238                     SkPaint labelPaint;
239                     labelPaint.setColor(color);
240                     font.setSize(10);
241                     canvas->drawString(gStyles[style].fName, 0, rect.height() + 12, font, labelPaint);
242                     canvas->drawString(gFills[fill].fName, 0, rect.height() + 24, font, labelPaint);
243                     canvas->drawString(gCaps[cap].fName, 0, rect.height() + 36, font, labelPaint);
244                 }
245                 canvas->restore();
246             }
247             canvas->restore();
248         }
249         canvas->restore();
250         canvas->restore();
251     }
252 
253 private:
254     typedef skiagm::GM INHERITED;
255 };
256 
257 class CubicClosePathGM : public skiagm::GM {
258 public:
CubicClosePathGM()259     CubicClosePathGM() {}
260 
261 protected:
262 
onShortName()263     SkString onShortName() {
264         return SkString("cubicclosepath");
265     }
266 
onISize()267     SkISize onISize() { return SkISize::Make(1240, 390); }
268 
drawPath(SkPath & path,SkCanvas * canvas,SkColor color,const SkRect & clip,SkPaint::Cap cap,SkPaint::Join join,SkPaint::Style style,SkPath::FillType fill,SkScalar strokeWidth)269     void drawPath(SkPath& path,SkCanvas* canvas,SkColor color,
270                   const SkRect& clip,SkPaint::Cap cap, SkPaint::Join join,
271                   SkPaint::Style style, SkPath::FillType fill,
272                   SkScalar strokeWidth) {
273         path.setFillType(fill);
274         SkPaint paint;
275         paint.setStrokeCap(cap);
276         paint.setStrokeWidth(strokeWidth);
277         paint.setStrokeJoin(join);
278         paint.setColor(color);
279         paint.setStyle(style);
280         canvas->save();
281         canvas->clipRect(clip);
282         canvas->drawPath(path, paint);
283         canvas->restore();
284     }
285 
onDraw(SkCanvas * canvas)286     virtual void onDraw(SkCanvas* canvas) {
287         struct FillAndName {
288             SkPath::FillType fFill;
289             const char*      fName;
290         };
291         constexpr FillAndName gFills[] = {
292             {SkPath::kWinding_FillType, "Winding"},
293             {SkPath::kEvenOdd_FillType, "Even / Odd"},
294             {SkPath::kInverseWinding_FillType, "Inverse Winding"},
295             {SkPath::kInverseEvenOdd_FillType, "Inverse Even / Odd"},
296         };
297         struct StyleAndName {
298             SkPaint::Style fStyle;
299             const char*    fName;
300         };
301         constexpr StyleAndName gStyles[] = {
302             {SkPaint::kFill_Style, "Fill"},
303             {SkPaint::kStroke_Style, "Stroke"},
304             {SkPaint::kStrokeAndFill_Style, "Stroke And Fill"},
305         };
306         struct CapAndName {
307             SkPaint::Cap  fCap;
308             SkPaint::Join fJoin;
309             const char*   fName;
310         };
311         constexpr CapAndName gCaps[] = {
312             {SkPaint::kButt_Cap, SkPaint::kBevel_Join, "Butt"},
313             {SkPaint::kRound_Cap, SkPaint::kRound_Join, "Round"},
314             {SkPaint::kSquare_Cap, SkPaint::kBevel_Join, "Square"}
315         };
316         struct PathAndName {
317             SkPath      fPath;
318             const char* fName;
319         };
320         PathAndName path;
321         path.fPath.moveTo(25*SK_Scalar1, 10*SK_Scalar1);
322         path.fPath.cubicTo(40*SK_Scalar1, 20*SK_Scalar1,
323                            60*SK_Scalar1, 20*SK_Scalar1,
324                            75*SK_Scalar1, 10*SK_Scalar1);
325         path.fPath.close();
326         path.fName = "moveTo-cubic-close";
327 
328         SkPaint titlePaint;
329         titlePaint.setColor(SK_ColorBLACK);
330         titlePaint.setAntiAlias(true);
331         SkFont font(sk_tool_utils::create_portable_typeface(), 15);
332         const char title[] = "Cubic Closed Drawn Into Rectangle Clips With "
333                              "Indicated Style, Fill and Linecaps, with stroke width 10";
334         canvas->drawString(title, 20, 20, font, titlePaint);
335 
336         SkRandom rand;
337         SkRect rect = SkRect::MakeWH(100*SK_Scalar1, 30*SK_Scalar1);
338         canvas->save();
339         canvas->translate(10 * SK_Scalar1, 30 * SK_Scalar1);
340         canvas->save();
341         for (size_t cap = 0; cap < SK_ARRAY_COUNT(gCaps); ++cap) {
342             if (0 < cap) {
343                 canvas->translate((rect.width() + 40 * SK_Scalar1) * SK_ARRAY_COUNT(gStyles), 0);
344             }
345             canvas->save();
346             for (size_t fill = 0; fill < SK_ARRAY_COUNT(gFills); ++fill) {
347                 if (0 < fill) {
348                     canvas->translate(0, rect.height() + 40 * SK_Scalar1);
349                 }
350                 canvas->save();
351                 for (size_t style = 0; style < SK_ARRAY_COUNT(gStyles); ++style) {
352                     if (0 < style) {
353                         canvas->translate(rect.width() + 40 * SK_Scalar1, 0);
354                     }
355 
356                     SkColor color = 0xff007000;
357                     this->drawPath(path.fPath, canvas, color, rect,
358                                     gCaps[cap].fCap, gCaps[cap].fJoin, gStyles[style].fStyle,
359                                     gFills[fill].fFill, SK_Scalar1*10);
360 
361                     SkPaint rectPaint;
362                     rectPaint.setColor(SK_ColorBLACK);
363                     rectPaint.setStyle(SkPaint::kStroke_Style);
364                     rectPaint.setStrokeWidth(-1);
365                     rectPaint.setAntiAlias(true);
366                     canvas->drawRect(rect, rectPaint);
367 
368                     SkPaint labelPaint;
369                     labelPaint.setColor(color);
370                     labelPaint.setAntiAlias(true);
371                     font.setSize(10);
372                     canvas->drawString(gStyles[style].fName, 0, rect.height() + 12, font, labelPaint);
373                     canvas->drawString(gFills[fill].fName, 0, rect.height() + 24, font, labelPaint);
374                     canvas->drawString(gCaps[cap].fName, 0, rect.height() + 36, font, labelPaint);
375                 }
376                 canvas->restore();
377             }
378             canvas->restore();
379         }
380         canvas->restore();
381         canvas->restore();
382     }
383 
384 private:
385     typedef skiagm::GM INHERITED;
386 };
387 
388 DEF_SIMPLE_GM(bug5099, canvas, 50, 50) {
389     SkPaint p;
390     p.setColor(SK_ColorRED);
391     p.setAntiAlias(true);
392     p.setStyle(SkPaint::kStroke_Style);
393     p.setStrokeWidth(10);
394 
395     SkPath path;
396     path.moveTo(6, 27);
397     path.cubicTo(31.5f, 1.5f, 3.5f, 4.5f, 29, 29);
398     canvas->drawPath(path, p);
399 }
400 
401 DEF_SIMPLE_GM(bug6083, canvas, 100, 50) {
402     SkPaint p;
403     p.setColor(SK_ColorRED);
404     p.setAntiAlias(true);
405     p.setStyle(SkPaint::kStroke_Style);
406     p.setStrokeWidth(15);
407     canvas->translate(-500, -130);
408     SkPath path;
409     path.moveTo(500.988f, 155.200f);
410     path.lineTo(526.109f, 155.200f);
411     SkPoint p1 = { 526.109f, 155.200f };
412     SkPoint p2 = { 525.968f, 212.968f };
413     SkPoint p3 = { 526.109f, 241.840f };
414     path.cubicTo(p1, p2, p3);
415     canvas->drawPath(path, p);
416     canvas->translate(50, 0);
417     path.reset();
418     p2.set(525.968f, 213.172f);
419     path.moveTo(500.988f, 155.200f);
420     path.lineTo(526.109f, 155.200f);
421     path.cubicTo(p1, p2, p3);
422     canvas->drawPath(path, p);
423 }
424 
425 //////////////////////////////////////////////////////////////////////////////
426 
427 DEF_GM( return new CubicPathGM; )
428 DEF_GM( return new CubicClosePathGM; )
429 DEF_GM( return new ClippedCubicGM; )
430 DEF_GM( return new ClippedCubic2GM; )
431