xref: /aosp_15_r20/external/skia/gm/strokes.cpp (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
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/SkPathEffect.h"
14 #include "include/core/SkPathUtils.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/SkTypes.h"
21 #include "include/effects/SkDashPathEffect.h"
22 #include "include/utils/SkParsePath.h"
23 #include "src/base/SkFloatBits.h"
24 #include "src/base/SkRandom.h"
25 #include "tools/ToolUtils.h"
26 
27 #include <string.h>
28 
29 #if defined(SK_GRAPHITE)
30 #include "include/gpu/graphite/ContextOptions.h"
31 #endif
32 
33 #define W   400
34 #define H   400
35 #define N   50
36 
37 constexpr SkScalar SW = SkIntToScalar(W);
38 constexpr SkScalar SH = SkIntToScalar(H);
39 
rnd_rect(SkRect * r,SkPaint * paint,SkRandom & rand)40 static void rnd_rect(SkRect* r, SkPaint* paint, SkRandom& rand) {
41     SkScalar x = rand.nextUScalar1() * W;
42     SkScalar y = rand.nextUScalar1() * H;
43     SkScalar w = rand.nextUScalar1() * (W >> 2);
44     SkScalar h = rand.nextUScalar1() * (H >> 2);
45     SkScalar hoffset = rand.nextSScalar1();
46     SkScalar woffset = rand.nextSScalar1();
47 
48     r->setXYWH(x, y, w, h);
49     r->offset(-w/2 + woffset, -h/2 + hoffset);
50 
51     paint->setColor(rand.nextU());
52     paint->setAlphaf(1.0f);
53 }
54 
55 
56 class StrokesGM : public skiagm::GM {
57 public:
StrokesGM()58     StrokesGM() {}
59 
60 protected:
getName() const61     SkString getName() const override { return SkString("strokes_round"); }
62 
getISize()63     SkISize getISize() override { return SkISize::Make(W, H * 2); }
64 
65 #if defined(SK_GRAPHITE)
modifyGraphiteContextOptions(skgpu::graphite::ContextOptions * options) const66     void modifyGraphiteContextOptions(skgpu::graphite::ContextOptions* options) const override {
67         options->fMaxPathAtlasTextureSize = 0;
68         options->fAllowMultipleAtlasTextures = false;
69     }
70 #endif
71 
onDraw(SkCanvas * canvas)72     void onDraw(SkCanvas* canvas) override {
73         SkPaint paint;
74         paint.setStyle(SkPaint::kStroke_Style);
75         paint.setStrokeWidth(SkIntToScalar(9)/2);
76 
77         for (int y = 0; y < 2; y++) {
78             paint.setAntiAlias(!!y);
79             SkAutoCanvasRestore acr(canvas, true);
80             canvas->translate(0, SH * y);
81             canvas->clipRect(SkRect::MakeLTRB(
82                                               SkIntToScalar(2), SkIntToScalar(2)
83                                               , SW - SkIntToScalar(2), SH - SkIntToScalar(2)
84                                               ));
85 
86             SkRandom rand;
87             for (int i = 0; i < N; i++) {
88                 SkRect r;
89                 rnd_rect(&r, &paint, rand);
90                 canvas->drawOval(r, paint);
91                 rnd_rect(&r, &paint, rand);
92                 canvas->drawRoundRect(r, r.width()/4, r.height()/4, paint);
93                 rnd_rect(&r, &paint, rand);
94             }
95         }
96     }
97 
98 private:
99     using INHERITED = skiagm::GM;
100 };
101 
102 /* See
103    https://code.google.com/p/chromium/issues/detail?id=422974          and
104    http://jsfiddle.net/1xnku3sg/2/
105  */
106 class ZeroLenStrokesGM : public skiagm::GM {
107     SkPath fMoveHfPath, fMoveZfPath, fDashedfPath, fRefPath[4];
108     SkPath fCubicPath, fQuadPath, fLinePath;
109 protected:
onOnceBeforeDraw()110     void onOnceBeforeDraw() override {
111 
112         SkAssertResult(SkParsePath::FromSVGString("M0,0h0M10,0h0M20,0h0", &fMoveHfPath));
113         SkAssertResult(SkParsePath::FromSVGString("M0,0zM10,0zM20,0z", &fMoveZfPath));
114         SkAssertResult(SkParsePath::FromSVGString("M0,0h25", &fDashedfPath));
115         SkAssertResult(SkParsePath::FromSVGString("M 0 0 C 0 0 0 0 0 0", &fCubicPath));
116         SkAssertResult(SkParsePath::FromSVGString("M 0 0 Q 0 0 0 0", &fQuadPath));
117         SkAssertResult(SkParsePath::FromSVGString("M 0 0 L 0 0", &fLinePath));
118 
119         for (int i = 0; i < 3; ++i) {
120             fRefPath[0].addCircle(i * 10.f, 0, 5);
121             fRefPath[1].addCircle(i * 10.f, 0, 10);
122             fRefPath[2].addRect(i * 10.f - 4, -2, i * 10.f + 4, 6);
123             fRefPath[3].addRect(i * 10.f - 10, -10, i * 10.f + 10, 10);
124         }
125     }
126 
getName() const127     SkString getName() const override { return SkString("zeroPath"); }
128 
getISize()129     SkISize getISize() override { return SkISize::Make(W, H * 2); }
130 
onDraw(SkCanvas * canvas)131     void onDraw(SkCanvas* canvas) override {
132         SkPaint fillPaint, strokePaint, dashPaint;
133         fillPaint.setAntiAlias(true);
134         strokePaint = fillPaint;
135         strokePaint.setStyle(SkPaint::kStroke_Style);
136         for (int i = 0; i < 2; ++i) {
137             fillPaint.setAlphaf(1.0f);
138             strokePaint.setAlphaf(1.0f);
139             strokePaint.setStrokeWidth(i ? 8.f : 10.f);
140             strokePaint.setStrokeCap(i ? SkPaint::kSquare_Cap : SkPaint::kRound_Cap);
141             canvas->save();
142             canvas->translate(10 + i * 100.f, 10);
143             canvas->drawPath(fMoveHfPath, strokePaint);
144             canvas->translate(0, 20);
145             canvas->drawPath(fMoveZfPath, strokePaint);
146             dashPaint = strokePaint;
147             const SkScalar intervals[] = { 0, 10 };
148             dashPaint.setPathEffect(SkDashPathEffect::Make(intervals, 2, 0));
149             SkPath fillPath;
150             skpathutils::FillPathWithPaint(fDashedfPath, dashPaint, &fillPath);
151             canvas->translate(0, 20);
152             canvas->drawPath(fDashedfPath, dashPaint);
153             canvas->translate(0, 20);
154             canvas->drawPath(fRefPath[i * 2], fillPaint);
155             strokePaint.setStrokeWidth(20);
156             strokePaint.setAlphaf(0.5f);
157             canvas->translate(0, 50);
158             canvas->drawPath(fMoveHfPath, strokePaint);
159             canvas->translate(0, 30);
160             canvas->drawPath(fMoveZfPath, strokePaint);
161             canvas->translate(0, 30);
162             fillPaint.setAlphaf(0.5f);
163             canvas->drawPath(fRefPath[1 + i * 2], fillPaint);
164             canvas->translate(0, 30);
165             canvas->drawPath(fCubicPath, strokePaint);
166             canvas->translate(0, 30);
167             canvas->drawPath(fQuadPath, strokePaint);
168             canvas->translate(0, 30);
169             canvas->drawPath(fLinePath, strokePaint);
170             canvas->restore();
171         }
172     }
173 
174 private:
175     using INHERITED = skiagm::GM;
176 };
177 
178 class TeenyStrokesGM : public skiagm::GM {
getName() const179     SkString getName() const override { return SkString("teenyStrokes"); }
180 
getISize()181     SkISize getISize() override { return SkISize::Make(W, H * 2); }
182 
line(SkScalar scale,SkCanvas * canvas,SkColor color)183     static void line(SkScalar scale, SkCanvas* canvas, SkColor color) {
184         SkPaint p;
185         p.setAntiAlias(true);
186         p.setStyle(SkPaint::kStroke_Style);
187         p.setColor(color);
188         canvas->translate(50, 0);
189         canvas->save();
190         p.setStrokeWidth(scale * 5);
191         canvas->scale(1 / scale, 1 / scale);
192         canvas->drawLine(20 * scale, 20 * scale, 20 * scale, 100 * scale, p);
193         canvas->drawLine(20 * scale, 20 * scale, 100 * scale, 100 * scale, p);
194         canvas->restore();
195     }
196 
onDraw(SkCanvas * canvas)197     void onDraw(SkCanvas* canvas) override {
198         line(0.00005f, canvas, SK_ColorBLACK);
199         line(0.000045f, canvas, SK_ColorRED);
200         line(0.0000035f, canvas, SK_ColorGREEN);
201         line(0.000003f, canvas, SK_ColorBLUE);
202         line(0.000002f, canvas, SK_ColorBLACK);
203     }
204 private:
205     using INHERITED = skiagm::GM;
206 };
207 
208 DEF_SIMPLE_GM(CubicStroke, canvas, 384, 384) {
209     SkPaint p;
210     p.setAntiAlias(true);
211     p.setStyle(SkPaint::kStroke_Style);
212     p.setStrokeWidth(1.0720f);
213     SkPath path;
214     path.moveTo(-6000,-6000);
215     path.cubicTo(-3500,5500,-500,5500,2500,-6500);
216     canvas->drawPath(path, p);
217     p.setStrokeWidth(1.0721f);
218     canvas->translate(10, 10);
219     canvas->drawPath(path, p);
220     p.setStrokeWidth(1.0722f);
221     canvas->translate(10, 10);
222     canvas->drawPath(path, p);
223 }
224 
225 DEF_SIMPLE_GM(zerolinestroke, canvas, 90, 120) {
226     SkPaint paint;
227     paint.setStyle(SkPaint::kStroke_Style);
228     paint.setStrokeWidth(20);
229     paint.setAntiAlias(true);
230     paint.setStrokeCap(SkPaint::kRound_Cap);
231 
232     SkPath path;
233     path.moveTo(30, 90);
234     path.lineTo(30, 90);
235     path.lineTo(60, 90);
236     path.lineTo(60, 90);
237     canvas->drawPath(path, paint);
238 
239     path.reset();
240     path.moveTo(30, 30);
241     path.lineTo(60, 30);
242     canvas->drawPath(path, paint);
243 
244     path.reset();
245     path.moveTo(30, 60);
246     path.lineTo(30, 60);
247     path.lineTo(60, 60);
248     canvas->drawPath(path, paint);
249 }
250 
251 DEF_SIMPLE_GM(quadcap, canvas, 200, 200) {
252     SkPaint p;
253     p.setAntiAlias(true);
254     p.setStyle(SkPaint::kStroke_Style);
255     p.setStrokeWidth(0);
256     SkPath path;
257     SkPoint pts[] = {{105.738571f,13.126318f},
258             {105.738571f,13.126318f},
259             {123.753784f,1.f}};
260     SkVector tangent = pts[1] - pts[2];
261     tangent.normalize();
262     SkPoint pts2[3];
263     memcpy(pts2, pts, sizeof(pts));
264     const SkScalar capOutset = SK_ScalarPI / 8;
265     pts2[0].fX += tangent.fX * capOutset;
266     pts2[0].fY += tangent.fY * capOutset;
267     pts2[1].fX += tangent.fX * capOutset;
268     pts2[1].fY += tangent.fY * capOutset;
269     pts2[2].fX += -tangent.fX * capOutset;
270     pts2[2].fY += -tangent.fY * capOutset;
271     path.moveTo(pts2[0]);
272     path.quadTo(pts2[1], pts2[2]);
273     canvas->drawPath(path, p);
274 
275     path.reset();
276     path.moveTo(pts[0]);
277     path.quadTo(pts[1], pts[2]);
278     p.setStrokeCap(SkPaint::kRound_Cap);
279     canvas->translate(30, 0);
280     canvas->drawPath(path, p);
281 }
282 
283 class Strokes2GM : public skiagm::GM {
284     SkPath fPath;
285 protected:
onOnceBeforeDraw()286     void onOnceBeforeDraw() override {
287         SkRandom rand;
288         fPath.moveTo(0, 0);
289         for (int i = 0; i < 13; i++) {
290             SkScalar x = rand.nextUScalar1() * (W >> 1);
291             SkScalar y = rand.nextUScalar1() * (H >> 1);
292             fPath.lineTo(x, y);
293         }
294     }
295 
getName() const296     SkString getName() const override { return SkString("strokes_poly"); }
297 
getISize()298     SkISize getISize() override { return SkISize::Make(W, H * 2); }
299 
onDraw(SkCanvas * canvas)300     void onDraw(SkCanvas* canvas) override {
301         canvas->drawColor(SK_ColorWHITE);
302 
303         SkPaint paint;
304         paint.setStyle(SkPaint::kStroke_Style);
305         paint.setStrokeWidth(SkIntToScalar(9)/2);
306 
307         for (int y = 0; y < 2; y++) {
308             paint.setAntiAlias(!!y);
309             SkAutoCanvasRestore acr(canvas, true);
310             canvas->translate(0, SH * y);
311             canvas->clipRect(SkRect::MakeLTRB(SkIntToScalar(2),
312                                               SkIntToScalar(2),
313                                               SW - SkIntToScalar(2),
314                                               SH - SkIntToScalar(2)));
315 
316             SkRandom rand;
317             for (int i = 0; i < N/2; i++) {
318                 SkRect r;
319                 rnd_rect(&r, &paint, rand);
320                 canvas->rotate(SkIntToScalar(15), SW/2, SH/2);
321                 canvas->drawPath(fPath, paint);
322             }
323         }
324     }
325 
326 private:
327     using INHERITED = skiagm::GM;
328 };
329 
330 //////////////////////////////////////////////////////////////////////////////
331 
inset(const SkRect & r)332 static SkRect inset(const SkRect& r) {
333     SkRect rr(r);
334     rr.inset(r.width()/10, r.height()/10);
335     return rr;
336 }
337 
338 class Strokes3GM : public skiagm::GM {
make0(SkPath * path,const SkRect & bounds,SkString * title)339     static void make0(SkPath* path, const SkRect& bounds, SkString* title) {
340         path->addRect(bounds, SkPathDirection::kCW);
341         path->addRect(inset(bounds), SkPathDirection::kCW);
342         title->set("CW CW");
343     }
344 
make1(SkPath * path,const SkRect & bounds,SkString * title)345     static void make1(SkPath* path, const SkRect& bounds, SkString* title) {
346         path->addRect(bounds, SkPathDirection::kCW);
347         path->addRect(inset(bounds), SkPathDirection::kCCW);
348         title->set("CW CCW");
349     }
350 
make2(SkPath * path,const SkRect & bounds,SkString * title)351     static void make2(SkPath* path, const SkRect& bounds, SkString* title) {
352         path->addOval(bounds, SkPathDirection::kCW);
353         path->addOval(inset(bounds), SkPathDirection::kCW);
354         title->set("CW CW");
355     }
356 
make3(SkPath * path,const SkRect & bounds,SkString * title)357     static void make3(SkPath* path, const SkRect& bounds, SkString* title) {
358         path->addOval(bounds, SkPathDirection::kCW);
359         path->addOval(inset(bounds), SkPathDirection::kCCW);
360         title->set("CW CCW");
361     }
362 
make4(SkPath * path,const SkRect & bounds,SkString * title)363     static void make4(SkPath* path, const SkRect& bounds, SkString* title) {
364         path->addRect(bounds, SkPathDirection::kCW);
365         SkRect r = bounds;
366         r.inset(bounds.width() / 10, -bounds.height() / 10);
367         path->addOval(r, SkPathDirection::kCW);
368         title->set("CW CW");
369     }
370 
make5(SkPath * path,const SkRect & bounds,SkString * title)371     static void make5(SkPath* path, const SkRect& bounds, SkString* title) {
372         path->addRect(bounds, SkPathDirection::kCW);
373         SkRect r = bounds;
374         r.inset(bounds.width() / 10, -bounds.height() / 10);
375         path->addOval(r, SkPathDirection::kCCW);
376         title->set("CW CCW");
377     }
378 
379 public:
Strokes3GM()380     Strokes3GM() {}
381 
382 protected:
getName() const383     SkString getName() const override { return SkString("strokes3"); }
384 
getISize()385     SkISize getISize() override { return SkISize::Make(1500, 1500); }
386 
onDraw(SkCanvas * canvas)387     void onDraw(SkCanvas* canvas) override {
388         SkPaint origPaint;
389         origPaint.setAntiAlias(true);
390         origPaint.setStyle(SkPaint::kStroke_Style);
391         SkPaint fillPaint(origPaint);
392         fillPaint.setColor(SK_ColorRED);
393         SkPaint strokePaint(origPaint);
394         strokePaint.setColor(ToolUtils::color_to_565(0xFF4444FF));
395 
396         void (*procs[])(SkPath*, const SkRect&, SkString*) = {
397             make0, make1, make2, make3, make4, make5
398         };
399 
400         canvas->translate(SkIntToScalar(20), SkIntToScalar(80));
401 
402         SkRect bounds = SkRect::MakeWH(SkIntToScalar(50), SkIntToScalar(50));
403         SkScalar dx = bounds.width() * 4/3;
404         SkScalar dy = bounds.height() * 5;
405 
406         for (size_t i = 0; i < std::size(procs); ++i) {
407             SkPath orig;
408             SkString str;
409             procs[i](&orig, bounds, &str);
410 
411             canvas->save();
412             for (int j = 0; j < 13; ++j) {
413                 strokePaint.setStrokeWidth(SK_Scalar1 * j * j);
414                 canvas->drawPath(orig, strokePaint);
415                 canvas->drawPath(orig, origPaint);
416                 SkPath fill;
417                 skpathutils::FillPathWithPaint(orig, strokePaint, &fill);
418                 canvas->drawPath(fill, fillPaint);
419                 canvas->translate(dx + strokePaint.getStrokeWidth(), 0);
420             }
421             canvas->restore();
422             canvas->translate(0, dy);
423         }
424     }
425 
426 private:
427     using INHERITED = skiagm::GM;
428 };
429 
430 class Strokes4GM : public skiagm::GM {
431 public:
Strokes4GM()432     Strokes4GM() {}
433 
434 protected:
getName() const435     SkString getName() const override { return SkString("strokes_zoomed"); }
436 
getISize()437     SkISize getISize() override { return SkISize::Make(W, H * 2); }
438 
onDraw(SkCanvas * canvas)439     void onDraw(SkCanvas* canvas) override {
440         SkPaint paint;
441         paint.setStyle(SkPaint::kStroke_Style);
442         paint.setStrokeWidth(0.055f);
443 
444         canvas->scale(1000, 1000);
445         canvas->drawCircle(0, 2, 1.97f, paint);
446     }
447 
448 private:
449     using INHERITED = skiagm::GM;
450 };
451 
452 // Test stroking for curves that produce degenerate tangents when t is 0 or 1 (see bug 4191)
453 class Strokes5GM : public skiagm::GM {
454 public:
Strokes5GM()455     Strokes5GM() {}
456 
457 protected:
getName() const458     SkString getName() const override { return SkString("zero_control_stroke"); }
459 
getISize()460     SkISize getISize() override { return SkISize::Make(W, H * 2); }
461 
onDraw(SkCanvas * canvas)462     void onDraw(SkCanvas* canvas) override {
463         SkPaint p;
464         p.setColor(SK_ColorRED);
465         p.setAntiAlias(true);
466         p.setStyle(SkPaint::kStroke_Style);
467         p.setStrokeWidth(40);
468         p.setStrokeCap(SkPaint::kButt_Cap);
469 
470         SkPath path;
471         path.moveTo(157.474f,111.753f);
472         path.cubicTo(128.5f,111.5f,35.5f,29.5f,35.5f,29.5f);
473         canvas->drawPath(path, p);
474         path.reset();
475         path.moveTo(250, 50);
476         path.quadTo(280, 80, 280, 80);
477         canvas->drawPath(path, p);
478         path.reset();
479         path.moveTo(150, 50);
480         path.conicTo(180, 80, 180, 80, 0.707f);
481         canvas->drawPath(path, p);
482 
483         path.reset();
484         path.moveTo(157.474f,311.753f);
485         path.cubicTo(157.474f,311.753f,85.5f,229.5f,35.5f,229.5f);
486         canvas->drawPath(path, p);
487         path.reset();
488         path.moveTo(280, 250);
489         path.quadTo(280, 250, 310, 280);
490         canvas->drawPath(path, p);
491         path.reset();
492         path.moveTo(180, 250);
493         path.conicTo(180, 250, 210, 280, 0.707f);
494         canvas->drawPath(path, p);
495     }
496 
497 private:
498     using INHERITED = skiagm::GM;
499 };
500 
501 
502 //////////////////////////////////////////////////////////////////////////////
503 
504 DEF_GM( return new StrokesGM; )
DEF_GM(return new Strokes2GM;)505 DEF_GM( return new Strokes2GM; )
506 DEF_GM( return new Strokes3GM; )
507 DEF_GM( return new Strokes4GM; )
508 DEF_GM( return new Strokes5GM; )
509 
510 DEF_GM( return new ZeroLenStrokesGM; )
511 DEF_GM( return new TeenyStrokesGM; )
512 
513 DEF_SIMPLE_GM(zerolinedash, canvas, 256, 256) {
514     canvas->clear(SK_ColorWHITE);
515 
516     SkPaint paint;
517     paint.setColor(SkColorSetARGB(255, 0, 0, 0));
518     paint.setStrokeWidth(11);
519     paint.setStrokeCap(SkPaint::kRound_Cap);
520     paint.setStrokeJoin(SkPaint::kBevel_Join);
521 
522     SkScalar dash_pattern[] = {1, 5};
523     paint.setPathEffect(SkDashPathEffect::Make(dash_pattern, 2, 0));
524 
525     canvas->drawLine(100, 100, 100, 100, paint);
526 }
527 
528 #ifdef PDF_IS_FIXED_SO_THIS_DOESNT_BREAK_IT
529 DEF_SIMPLE_GM(longrect_dash, canvas, 250, 250) {
530     canvas->clear(SK_ColorWHITE);
531 
532     SkPaint paint;
533     paint.setColor(SkColorSetARGB(255, 0, 0, 0));
534     paint.setStrokeWidth(5);
535     paint.setStrokeCap(SkPaint::kRound_Cap);
536     paint.setStrokeJoin(SkPaint::kBevel_Join);
537     paint.setStyle(SkPaint::kStroke_Style);
538     SkScalar dash_pattern[] = {1, 5};
539     paint.setPathEffect(SkDashPathEffect::Make(dash_pattern, 2, 0));
540     // try all combinations of stretching bounds
541     for (auto left : { 20.f, -100001.f } ) {
542         for (auto top : { 20.f, -100001.f } ) {
543             for (auto right : { 40.f, 100001.f } ) {
544                 for (auto bottom : { 40.f, 100001.f } ) {
545                     canvas->save();
546                     canvas->clipRect({10, 10, 50, 50});
547                     canvas->drawRect({left, top, right, bottom}, paint);
548                     canvas->restore();
549                     canvas->translate(60, 0);
550                }
551             }
552             canvas->translate(-60 * 4, 60);
553         }
554     }
555 }
556 #endif
557 
558 DEF_SIMPLE_GM(inner_join_geometry, canvas, 1000, 700) {
559     // These paths trigger cases where we must add inner join geometry.
560     // skbug.com/11964
561     const SkPoint pathPoints[] = {
562         /*moveTo*/  /*lineTo*/  /*lineTo*/
563         {119,  71}, {129, 151}, {230,  24},
564         {200, 144}, {129, 151}, {230,  24},
565         {192, 176}, {224, 175}, {281, 103},
566         {233, 205}, {224, 175}, {281, 103},
567         {121, 216}, {234, 189}, {195, 147},
568         {141, 216}, {254, 189}, {238, 250},
569         {159, 202}, {269, 197}, {289, 165},
570         {159, 202}, {269, 197}, {287, 227},
571     };
572 
573     SkPaint pathPaint;
574     pathPaint.setStroke(true);
575     pathPaint.setAntiAlias(true);
576     pathPaint.setStrokeWidth(100);
577 
578     SkPaint skeletonPaint;
579     skeletonPaint.setStroke(true);
580     skeletonPaint.setAntiAlias(true);
581     skeletonPaint.setStrokeWidth(0);
582     skeletonPaint.setColor(SK_ColorRED);
583 
584     canvas->translate(0, 50);
585     for (size_t i = 0; i < std::size(pathPoints) / 3; i++) {
586         auto path = SkPath::Polygon(pathPoints + i * 3, 3, false);
587         canvas->drawPath(path, pathPaint);
588 
589         SkPath fillPath;
590         skpathutils::FillPathWithPaint(path, pathPaint, &fillPath);
591         canvas->drawPath(fillPath, skeletonPaint);
592 
593         canvas->translate(200, 0);
594         if ((i + 1) % 4 == 0) {
595             canvas->translate(-800, 200);
596         }
597     }
598 }
599 
600 DEF_SIMPLE_GM(skbug12244, canvas, 150, 150) {
601     // Should look like a stroked triangle; these vertices are the results of the SkStroker
602     // but we draw as a filled path in order to highlight that it's the GPU triangulating path
603     // renderer that's the source of the problem, and not the stroking operation. The original
604     // path was a simple:
605     // m(0,0), l(100, 40), l(0, 80), l(0,0) with a stroke width of 15px
606     SkPath path;
607     path.moveTo(2.7854299545288085938, -6.9635753631591796875);
608     path.lineTo( 120.194366455078125,                   40);
609     path.lineTo(-7.5000004768371582031, 91.07775115966796875);
610     path.lineTo(-7.5000004768371582031, -11.077748298645019531);
611     path.lineTo(2.7854299545288085938, -6.9635753631591796875);
612     path.moveTo(-2.7854299545288085938, 6.9635753631591796875);
613     path.lineTo(                   0,                    0);
614     path.lineTo(                 7.5,                    0);
615     path.lineTo(7.5000004768371582031, 68.92224884033203125);
616     path.lineTo(  79.805633544921875,                   40);
617     path.lineTo(-2.7854299545288085938, 6.9635753631591796875);
618 
619     SkPaint p;
620     p.setColor(SK_ColorGREEN);
621 
622     canvas->translate(20.f, 20.f);
623     canvas->drawPath(path, p);
624 }
625 
626 DEF_SIMPLE_GM(b_340982297, canvas, 80, 50) {
627     SkPaint paint;
628     paint.setAntiAlias(true);
629 
630     SkPath path;
631     path.moveTo(30.23983f, 48.5674667f);
632     path.lineTo(1.30884242f, 45.5222702f);
633     path.lineTo(2.97688866f, 29.6749554f);
634     path.lineTo(17.4423828f, 31.1975555f);
635     path.lineTo(2.94269657f, 30.0452003f);
636     path.lineTo(4.38597536f, 11.8849154f);
637     path.lineTo(33.3853493f, 14.1896257f);
638     path.close();
639 
640     canvas->drawPath(path, paint);
641 
642     path.reset();
643     path.moveTo(73.3853455f, 4.18963623f);
644     path.lineTo(69.995636f, 39.1360626f);
645     path.lineTo(42.83145142f, 21.056778f);
646     path.lineTo(42.97689819f, 19.6749573f);
647     path.lineTo(57.4423828f, 21.1975555f);
648     path.lineTo(42.94268799f, 20.0451965f);
649     path.lineTo(44.38595581f, 1.88491821f);
650     path.close();
651 
652     canvas->drawPath(path, paint);
653 }
654