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