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/SkPaint.h"
12 #include "include/core/SkPath.h"
13 #include "include/core/SkRect.h"
14 #include "include/core/SkScalar.h"
15 #include "include/core/SkSize.h"
16 #include "include/core/SkString.h"
17 #include "include/core/SkTypes.h"
18
19 typedef SkScalar (*MakePathProc)(SkPath*);
20
make_frame(SkPath * path)21 static SkScalar make_frame(SkPath* path) {
22 SkRect r = { SkIntToScalar(10), SkIntToScalar(10),
23 SkIntToScalar(630), SkIntToScalar(470) };
24 path->addRoundRect(r, SkIntToScalar(15), SkIntToScalar(15));
25
26 SkPaint paint;
27 paint.setStyle(SkPaint::kStroke_Style);
28 paint.setStrokeWidth(SkIntToScalar(5));
29 paint.getFillPath(*path, path);
30 return SkIntToScalar(15);
31 }
32
make_triangle(SkPath * path)33 static SkScalar make_triangle(SkPath* path) {
34 constexpr int gCoord[] = {
35 10, 20, 15, 5, 30, 30
36 };
37 path->moveTo(SkIntToScalar(gCoord[0]), SkIntToScalar(gCoord[1]));
38 path->lineTo(SkIntToScalar(gCoord[2]), SkIntToScalar(gCoord[3]));
39 path->lineTo(SkIntToScalar(gCoord[4]), SkIntToScalar(gCoord[5]));
40 path->close();
41 path->offset(SkIntToScalar(10), SkIntToScalar(0));
42 return SkIntToScalar(30);
43 }
44
make_rect(SkPath * path)45 static SkScalar make_rect(SkPath* path) {
46 SkRect r = { SkIntToScalar(10), SkIntToScalar(10),
47 SkIntToScalar(30), SkIntToScalar(30) };
48 path->addRect(r);
49 path->offset(SkIntToScalar(10), SkIntToScalar(0));
50 return SkIntToScalar(30);
51 }
52
make_oval(SkPath * path)53 static SkScalar make_oval(SkPath* path) {
54 SkRect r = { SkIntToScalar(10), SkIntToScalar(10),
55 SkIntToScalar(30), SkIntToScalar(30) };
56 path->addOval(r);
57 path->offset(SkIntToScalar(10), SkIntToScalar(0));
58 return SkIntToScalar(30);
59 }
60
make_sawtooth(SkPath * path,int teeth)61 static SkScalar make_sawtooth(SkPath* path, int teeth) {
62 SkScalar x = SkIntToScalar(20);
63 SkScalar y = SkIntToScalar(20);
64 const SkScalar x0 = x;
65 const SkScalar dx = SkIntToScalar(5);
66 const SkScalar dy = SkIntToScalar(10);
67
68 path->moveTo(x, y);
69 for (int i = 0; i < teeth; i++) {
70 x += dx;
71 path->lineTo(x, y - dy);
72 x += dx;
73 path->lineTo(x, y + dy);
74 }
75 path->lineTo(x, y + (2 * dy));
76 path->lineTo(x0, y + (2 * dy));
77 path->close();
78 return SkIntToScalar(30);
79 }
80
make_sawtooth_3(SkPath * path)81 static SkScalar make_sawtooth_3(SkPath* path) { return make_sawtooth(path, 3); }
make_sawtooth_32(SkPath * path)82 static SkScalar make_sawtooth_32(SkPath* path) { return make_sawtooth(path, 32); }
83
make_house(SkPath * path)84 static SkScalar make_house(SkPath* path) {
85 path->moveTo(21, 23);
86 path->lineTo(21, 11.534f);
87 path->lineTo(22.327f, 12.741f);
88 path->lineTo(23.673f, 11.261f);
89 path->lineTo(12, 0.648f);
90 path->lineTo(8, 4.285f);
91 path->lineTo(8, 2);
92 path->lineTo(4, 2);
93 path->lineTo(4, 7.921f);
94 path->lineTo(0.327f, 11.26f);
95 path->lineTo(1.673f, 12.74f);
96 path->lineTo(3, 11.534f);
97 path->lineTo(3, 23);
98 path->lineTo(11, 23);
99 path->lineTo(11, 18);
100 path->lineTo(13, 18);
101 path->lineTo(13, 23);
102 path->lineTo(21, 23);
103 path->close();
104 path->lineTo(9, 16);
105 path->lineTo(9, 21);
106 path->lineTo(5, 21);
107 path->lineTo(5, 9.715f);
108 path->lineTo(12, 3.351f);
109 path->lineTo(19, 9.715f);
110 path->lineTo(19, 21);
111 path->lineTo(15, 21);
112 path->lineTo(15, 16);
113 path->lineTo(9, 16);
114 path->close();
115 path->offset(20, 0);
116 return SkIntToScalar(30);
117 }
118
make_star(SkPath * path,int n)119 static SkScalar make_star(SkPath* path, int n) {
120 const SkScalar c = SkIntToScalar(45);
121 const SkScalar r = SkIntToScalar(20);
122
123 SkScalar rad = -SK_ScalarPI / 2;
124 const SkScalar drad = (n >> 1) * SK_ScalarPI * 2 / n;
125
126 path->moveTo(c, c - r);
127 for (int i = 1; i < n; i++) {
128 rad += drad;
129 path->lineTo(c + SkScalarCos(rad) * r, c + SkScalarSin(rad) * r);
130 }
131 path->close();
132 return r * 2 * 6 / 5;
133 }
134
make_star_5(SkPath * path)135 static SkScalar make_star_5(SkPath* path) { return make_star(path, 5); }
make_star_13(SkPath * path)136 static SkScalar make_star_13(SkPath* path) { return make_star(path, 13); }
137
138 // We don't expect any output from this path.
make_line(SkPath * path)139 static SkScalar make_line(SkPath* path) {
140 path->moveTo(SkIntToScalar(30), SkIntToScalar(30));
141 path->lineTo(SkIntToScalar(120), SkIntToScalar(40));
142 path->close();
143 path->moveTo(SkIntToScalar(150), SkIntToScalar(30));
144 path->lineTo(SkIntToScalar(150), SkIntToScalar(30));
145 path->lineTo(SkIntToScalar(300), SkIntToScalar(40));
146 path->close();
147 return SkIntToScalar(40);
148 }
149
make_info(SkPath * path)150 static void make_info(SkPath* path) {
151 path->moveTo(24, 4);
152 path->cubicTo(12.94999980926514f,
153 4,
154 4,
155 12.94999980926514f,
156 4,
157 24);
158 path->cubicTo(4,
159 35.04999923706055f,
160 12.94999980926514f,
161 44,
162 24,
163 44);
164 path->cubicTo(35.04999923706055f,
165 44,
166 44,
167 35.04999923706055f,
168 44,
169 24);
170 path->cubicTo(44,
171 12.95000076293945f,
172 35.04999923706055f,
173 4,
174 24,
175 4);
176 path->close();
177 path->moveTo(26, 34);
178 path->lineTo(22, 34);
179 path->lineTo(22, 22);
180 path->lineTo(26, 22);
181 path->lineTo(26, 34);
182 path->close();
183 path->moveTo(26, 18);
184 path->lineTo(22, 18);
185 path->lineTo(22, 14);
186 path->lineTo(26, 14);
187 path->lineTo(26, 18);
188 path->close();
189 }
190
make_accessibility(SkPath * path)191 static void make_accessibility(SkPath* path) {
192 path->moveTo(12, 2);
193 path->cubicTo(13.10000038146973f,
194 2,
195 14,
196 2.900000095367432f,
197 14,
198 4);
199 path->cubicTo(14,
200 5.099999904632568f,
201 13.10000038146973f,
202 6,
203 12,
204 6);
205 path->cubicTo(10.89999961853027f,
206 6,
207 10,
208 5.099999904632568f,
209 10,
210 4);
211 path->cubicTo(10,
212 2.900000095367432f,
213 10.89999961853027f,
214 2,
215 12,
216 2);
217 path->close();
218 path->moveTo(21, 9);
219 path->lineTo(15, 9);
220 path->lineTo(15, 22);
221 path->lineTo(13, 22);
222 path->lineTo(13, 16);
223 path->lineTo(11, 16);
224 path->lineTo(11, 22);
225 path->lineTo(9, 22);
226 path->lineTo(9, 9);
227 path->lineTo(3, 9);
228 path->lineTo(3, 7);
229 path->lineTo(21, 7);
230 path->lineTo(21, 9);
231 path->close();
232 }
233
234 // test case for http://crbug.com/695196
make_visualizer(SkPath * path)235 static void make_visualizer(SkPath* path) {
236 path->moveTo(1.9520f, 2.0000f);
237 path->conicTo(1.5573f, 1.9992f, 1.2782f, 2.2782f, 0.9235f);
238 path->conicTo(0.9992f, 2.5573f, 1.0000f, 2.9520f, 0.9235f);
239 path->lineTo(1.0000f, 5.4300f);
240 path->lineTo(17.0000f, 5.4300f);
241 path->lineTo(17.0000f, 2.9520f);
242 path->conicTo(17.0008f, 2.5573f, 16.7218f, 2.2782f, 0.9235f);
243 path->conicTo(16.4427f, 1.9992f, 16.0480f, 2.0000f, 0.9235f);
244 path->lineTo(1.9520f, 2.0000f);
245 path->close();
246 path->moveTo(2.7140f, 3.1430f);
247 path->conicTo(3.0547f, 3.1287f, 3.2292f, 3.4216f, 0.8590f);
248 path->conicTo(3.4038f, 3.7145f, 3.2292f, 4.0074f, 0.8590f);
249 path->conicTo(3.0547f, 4.3003f, 2.7140f, 4.2860f, 0.8590f);
250 path->conicTo(2.1659f, 4.2631f, 2.1659f, 3.7145f, 0.7217f);
251 path->conicTo(2.1659f, 3.1659f, 2.7140f, 3.1430f, 0.7217f);
252 path->lineTo(2.7140f, 3.1430f);
253 path->close();
254 path->moveTo(5.0000f, 3.1430f);
255 path->conicTo(5.3407f, 3.1287f, 5.5152f, 3.4216f, 0.8590f);
256 path->conicTo(5.6898f, 3.7145f, 5.5152f, 4.0074f, 0.8590f);
257 path->conicTo(5.3407f, 4.3003f, 5.0000f, 4.2860f, 0.8590f);
258 path->conicTo(4.4519f, 4.2631f, 4.4519f, 3.7145f, 0.7217f);
259 path->conicTo(4.4519f, 3.1659f, 5.0000f, 3.1430f, 0.7217f);
260 path->lineTo(5.0000f, 3.1430f);
261 path->close();
262 path->moveTo(7.2860f, 3.1430f);
263 path->conicTo(7.6267f, 3.1287f, 7.8012f, 3.4216f, 0.8590f);
264 path->conicTo(7.9758f, 3.7145f, 7.8012f, 4.0074f, 0.8590f);
265 path->conicTo(7.6267f, 4.3003f, 7.2860f, 4.2860f, 0.8590f);
266 path->conicTo(6.7379f, 4.2631f, 6.7379f, 3.7145f, 0.7217f);
267 path->conicTo(6.7379f, 3.1659f, 7.2860f, 3.1430f, 0.7217f);
268 path->close();
269 path->moveTo(1.0000f, 6.1900f);
270 path->lineTo(1.0000f, 14.3810f);
271 path->conicTo(0.9992f, 14.7757f, 1.2782f, 15.0548f, 0.9235f);
272 path->conicTo(1.5573f, 15.3338f, 1.9520f, 15.3330f, 0.9235f);
273 path->lineTo(16.0480f, 15.3330f);
274 path->conicTo(16.4427f, 15.3338f, 16.7218f, 15.0548f, 0.9235f);
275 path->conicTo(17.0008f, 14.7757f, 17.0000f, 14.3810f, 0.9235f);
276 path->lineTo(17.0000f, 6.1910f);
277 path->lineTo(1.0000f, 6.1910f);
278 path->lineTo(1.0000f, 6.1900f);
279 path->close();
280 }
281
282 constexpr MakePathProc gProcs[] = {
283 make_frame,
284 make_triangle,
285 make_rect,
286 make_oval,
287 make_sawtooth_32,
288 make_star_5,
289 make_star_13,
290 make_line,
291 make_house,
292 make_sawtooth_3,
293 };
294
295 #define N SK_ARRAY_COUNT(gProcs)
296
297 class PathFillGM : public skiagm::GM {
298 SkPath fPath[N];
299 SkScalar fDY[N];
300 SkPath fInfoPath;
301 SkPath fAccessibilityPath;
302 SkPath fVisualizerPath;
303 protected:
onOnceBeforeDraw()304 void onOnceBeforeDraw() override {
305 for (size_t i = 0; i < N; i++) {
306 fDY[i] = gProcs[i](&fPath[i]);
307 }
308
309 make_info(&fInfoPath);
310 make_accessibility(&fAccessibilityPath);
311 make_visualizer(&fVisualizerPath);
312 }
313
314
onShortName()315 SkString onShortName() override {
316 return SkString("pathfill");
317 }
318
onISize()319 SkISize onISize() override {
320 return SkISize::Make(640, 480);
321 }
322
onDraw(SkCanvas * canvas)323 void onDraw(SkCanvas* canvas) override {
324 SkPaint paint;
325 paint.setAntiAlias(true);
326
327 for (size_t i = 0; i < N; i++) {
328 canvas->drawPath(fPath[i], paint);
329 canvas->translate(SkIntToScalar(0), fDY[i]);
330 }
331
332 canvas->save();
333 canvas->scale(0.300000011920929f, 0.300000011920929f);
334 canvas->translate(50, 50);
335 canvas->drawPath(fInfoPath, paint);
336 canvas->restore();
337
338 canvas->scale(2, 2);
339 canvas->translate(5, 15);
340 canvas->drawPath(fAccessibilityPath, paint);
341
342 canvas->scale(0.5f, 0.5f);
343 canvas->translate(5, 50);
344 canvas->drawPath(fVisualizerPath, paint);
345 }
346
347 private:
348 typedef skiagm::GM INHERITED;
349 };
350
351 // test inverse-fill w/ a clip that completely excludes the geometry
352 class PathInverseFillGM : public skiagm::GM {
353 SkPath fPath[N];
354 SkScalar fDY[N];
355 protected:
onOnceBeforeDraw()356 void onOnceBeforeDraw() override {
357 for (size_t i = 0; i < N; i++) {
358 fDY[i] = gProcs[i](&fPath[i]);
359 }
360 }
361
onShortName()362 SkString onShortName() override {
363 return SkString("pathinvfill");
364 }
365
onISize()366 SkISize onISize() override {
367 return SkISize::Make(450, 220);
368 }
369
show(SkCanvas * canvas,const SkPath & path,const SkPaint & paint,const SkRect * clip,SkScalar top,const SkScalar bottom)370 static void show(SkCanvas* canvas, const SkPath& path, const SkPaint& paint,
371 const SkRect* clip, SkScalar top, const SkScalar bottom) {
372 canvas->save();
373 if (clip) {
374 SkRect r = *clip;
375 r.fTop = top;
376 r.fBottom = bottom;
377 canvas->clipRect(r);
378 }
379 canvas->drawPath(path, paint);
380 canvas->restore();
381 }
382
onDraw(SkCanvas * canvas)383 void onDraw(SkCanvas* canvas) override {
384 SkPath path;
385
386 path.addCircle(SkIntToScalar(50), SkIntToScalar(50), SkIntToScalar(40));
387 path.toggleInverseFillType();
388
389 SkRect clipR = { 0, 0, SkIntToScalar(100), SkIntToScalar(200) };
390
391 canvas->translate(SkIntToScalar(10), SkIntToScalar(10));
392
393 for (int doclip = 0; doclip <= 1; ++doclip) {
394 for (int aa = 0; aa <= 1; ++aa) {
395 SkPaint paint;
396 paint.setAntiAlias(SkToBool(aa));
397
398 canvas->save();
399 canvas->clipRect(clipR);
400
401 const SkRect* clipPtr = doclip ? &clipR : nullptr;
402
403 show(canvas, path, paint, clipPtr, clipR.fTop, clipR.centerY());
404 show(canvas, path, paint, clipPtr, clipR.centerY(), clipR.fBottom);
405
406 canvas->restore();
407 canvas->translate(SkIntToScalar(110), 0);
408 }
409 }
410 }
411
412 private:
413 typedef skiagm::GM INHERITED;
414 };
415
416 DEF_SIMPLE_GM(rotatedcubicpath, canvas, 200, 200) {
417 SkPaint p;
418 p.setAntiAlias(true);
419 p.setStyle(SkPaint::kFill_Style);
420
421 canvas->translate(50, 50);
422 SkPath path;
423 path.moveTo(48,-23);
424 path.cubicTo(48,-29.5, 6,-30, 6,-30);
425 path.cubicTo(6,-30, 2,0, 2,0);
426 path.cubicTo(2,0, 44,-21.5, 48,-23);
427 path.close();
428
429 p.setColor(SK_ColorBLUE);
430 canvas->drawPath(path, p);
431
432 // Rotated path, which is not antialiased on GPU
433 p.setColor(SK_ColorRED);
434 canvas->rotate(90);
435 canvas->drawPath(path, p);
436 }
437
438 ///////////////////////////////////////////////////////////////////////////////
439
440 DEF_GM( return new PathFillGM; )
DEF_GM(return new PathInverseFillGM;)441 DEF_GM( return new PathInverseFillGM; )
442
443 DEF_SIMPLE_GM(bug7792, canvas, 800, 800) {
444 // from skbug.com/7792 bug description
445 SkPaint p;
446 SkPath path;
447 path.moveTo(10, 10);
448 path.moveTo(75, 75);
449 path.lineTo(150, 75);
450 path.lineTo(150, 150);
451 path.lineTo(75, 150);
452 canvas->drawPath(path, p);
453 // from skbug.com/7792#c3
454 canvas->translate(200, 0);
455 path.reset();
456 path.moveTo(75, 50);
457 path.moveTo(100, 75);
458 path.lineTo(150, 75);
459 path.lineTo(150, 150);
460 path.lineTo(75, 150);
461 path.lineTo(75, 50);
462 path.close();
463 canvas->drawPath(path, p);
464 // from skbug.com/7792#c9
465 canvas->translate(200, 0);
466 path.reset();
467 path.moveTo(10, 10);
468 path.moveTo(75, 75);
469 path.lineTo(150, 75);
470 path.lineTo(150, 150);
471 path.lineTo(75, 150);
472 path.close();
473 canvas->drawPath(path, p);
474 // from skbug.com/7792#c11
475 canvas->translate(-200 * 2, 200);
476 path.reset();
477 path.moveTo(75, 150);
478 path.lineTo(75, 75);
479 path.lineTo(150, 75);
480 path.lineTo(150, 150);
481 path.lineTo(75, 150);
482 path.moveTo(75, 150);
483 canvas->drawPath(path, p);
484 // from skbug.com/7792#c14
485 canvas->translate(200, 0);
486 path.reset();
487 path.moveTo(250, 75);
488 path.moveTo(250, 75);
489 path.moveTo(250, 75);
490 path.moveTo(100, 75);
491 path.lineTo(150, 75);
492 path.lineTo(150, 150);
493 path.lineTo(75, 150);
494 path.lineTo(75, 75);
495 path.close();
496 path.lineTo(0, 0);
497 path.close();
498 canvas->drawPath(path, p);
499 // from skbug.com/7792#c15
500 canvas->translate(200, 0);
501 path.reset();
502 path.moveTo(75, 75);
503 path.lineTo(150, 75);
504 path.lineTo(150, 150);
505 path.lineTo(75, 150);
506 path.moveTo(250, 75);
507 canvas->drawPath(path, p);
508 // from skbug.com/7792#c17
509 canvas->translate(-200 * 2, 200);
510 path.reset();
511 path.moveTo(75, 10);
512 path.moveTo(75, 75);
513 path.lineTo(150, 75);
514 path.lineTo(150, 150);
515 path.lineTo(75, 150);
516 path.lineTo(75, 10);
517 path.close();
518 canvas->drawPath(path, p);
519 // from skbug.com/7792#c19
520 canvas->translate(200, 0);
521 path.reset();
522 path.moveTo(75, 75);
523 path.lineTo(75, 75);
524 path.lineTo(75, 75);
525 path.lineTo(75, 75);
526 path.lineTo(150, 75);
527 path.lineTo(150, 150);
528 path.lineTo(75, 150);
529 path.close();
530 path.moveTo(10, 10);
531 path.lineTo(30, 10);
532 path.lineTo(10, 30);
533 canvas->drawPath(path, p);
534 // from skbug.com/7792#c23
535 canvas->translate(200, 0);
536 path.reset();
537 path.moveTo(75, 75);
538 path.lineTo(75, 75);
539 path.moveTo(75, 75);
540 path.lineTo(75, 75);
541 path.lineTo(150, 75);
542 path.lineTo(150, 150);
543 path.lineTo(75, 150);
544 path.close();
545 canvas->drawPath(path, p);
546 // from skbug.com/7792#c29
547 canvas->translate(-200 * 2, 200);
548 path.reset();
549 path.moveTo(75, 75);
550 path.lineTo(150, 75);
551 path.lineTo(150, 150);
552 path.lineTo(75, 150);
553 path.lineTo(75, 250);
554 path.moveTo(75, 75);
555 path.close();
556 canvas->drawPath(path, p);
557 // from skbug.com/7792#c31
558 canvas->translate(200, 0);
559 path.reset();
560 path.moveTo(75, 75);
561 path.lineTo(150, 75);
562 path.lineTo(150, 150);
563 path.lineTo(75, 150);
564 path.lineTo(75, 10);
565 path.moveTo(75, 75);
566 path.close();
567 canvas->drawPath(path, p);
568 // from skbug.com/7792#c36
569 canvas->translate(200, 0);
570 path.reset();
571 path.moveTo(75, 75);
572 path.lineTo(150, 75);
573 path.lineTo(150, 150);
574 path.lineTo(10, 150);
575 path.moveTo(75, 75);
576 path.lineTo(75, 75);
577 canvas->drawPath(path, p);
578 // from skbug.com/7792#c39
579 canvas->translate(200, -200 * 3);
580 path.reset();
581 path.moveTo(150, 75);
582 path.lineTo(150, 150);
583 path.lineTo(75, 150);
584 path.lineTo(75, 100);
585 canvas->drawPath(path, p);
586 // from zero_length_paths_aa
587 canvas->translate(0, 200);
588 path.reset();
589 path.moveTo(150, 100);
590 path.lineTo(150, 100);
591 path.lineTo(150, 150);
592 path.lineTo(75, 150);
593 path.lineTo(75, 100);
594 path.lineTo(75, 75);
595 path.lineTo(150, 75);
596 path.close();
597 canvas->drawPath(path, p);
598 // from skbug.com/7792#c41
599 canvas->translate(0, 200);
600 path.reset();
601 path.moveTo(75, 75);
602 path.lineTo(150, 75);
603 path.lineTo(150, 150);
604 path.lineTo(140, 150);
605 path.lineTo(140, 75);
606 path.moveTo(75, 75);
607 path.close();
608 canvas->drawPath(path, p);
609 // from skbug.com/7792#c53
610 canvas->translate(0, 200);
611 path.reset();
612 path.moveTo(75, 75);
613 path.lineTo(150, 75);
614 path.lineTo(150, 150);
615 path.lineTo(140, 150);
616 path.lineTo(140, 75);
617 path.moveTo(75, 75);
618 path.close();
619 canvas->drawPath(path, p);
620 }
621