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