Flutter Engine
The Flutter Engine
Loading...
Searching...
No Matches
PathBench.cpp
Go to the documentation of this file.
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 "bench/Benchmark.h"
13#include "include/core/SkPath.h"
20#include "src/base/SkRandom.h"
21
22#include "src/core/SkDraw.h"
24
25using namespace skia_private;
26
27enum Flags {
28 kStroke_Flag = 1 << 0,
29 kBig_Flag = 1 << 1
30};
31
32#define FLAGS00 Flags(0)
33#define FLAGS01 Flags(kStroke_Flag)
34#define FLAGS10 Flags(kBig_Flag)
35#define FLAGS11 Flags(kStroke_Flag | kBig_Flag)
36
37class PathBench : public Benchmark {
38 SkPaint fPaint;
39 SkString fName;
40 Flags fFlags;
41public:
48
49 virtual void appendName(SkString*) = 0;
50 virtual void makePath(SkPath*) = 0;
51 virtual int complexity() { return 0; }
52
53protected:
54 const char* onGetName() override {
55 fName.printf("path_%s_%s_",
56 fFlags & kStroke_Flag ? "stroke" : "fill",
57 fFlags & kBig_Flag ? "big" : "small");
58 this->appendName(&fName);
59 return fName.c_str();
60 }
61
62 void onDraw(int loops, SkCanvas* canvas) override {
63 SkPaint paint(fPaint);
64 this->setupPaint(&paint);
65
66 SkPath path;
67 this->makePath(&path);
68 if (fFlags & kBig_Flag) {
69 const SkMatrix m = SkMatrix::Scale(10, 10);
70 path.transform(m);
71 }
72
73 for (int i = 0; i < loops; i++) {
74 canvas->drawPath(path, paint);
75 }
76 }
77
78private:
79 using INHERITED = Benchmark;
80};
81
83public:
85
86 void appendName(SkString* name) override {
87 name->append("triangle");
88 }
89 void makePath(SkPath* path) override {
90 static const int gCoord[] = {
91 10, 10, 15, 5, 20, 20
92 };
93 path->moveTo(SkIntToScalar(gCoord[0]), SkIntToScalar(gCoord[1]));
94 path->lineTo(SkIntToScalar(gCoord[2]), SkIntToScalar(gCoord[3]));
95 path->lineTo(SkIntToScalar(gCoord[4]), SkIntToScalar(gCoord[5]));
96 path->close();
97 }
98private:
99 using INHERITED = PathBench;
100};
101
102class RectPathBench : public PathBench {
103public:
105
106 void appendName(SkString* name) override {
107 name->append("rect");
108 }
109 void makePath(SkPath* path) override {
110 SkRect r = { 10, 10, 20, 20 };
111 path->addRect(r);
112 }
113private:
114 using INHERITED = PathBench;
115};
116
118public:
119 RotatedRectBench(Flags flags, bool aa, int degrees) : INHERITED(flags) {
120 fAA = aa;
121 fDegrees = degrees;
122 }
123
124 void appendName(SkString* name) override {
125 SkString suffix;
126 suffix.printf("rotated_rect_%s_%d", fAA ? "aa" : "noaa", fDegrees);
127 name->append(suffix);
128 }
129
130 void makePath(SkPath* path) override {
131 SkRect r = { 10, 10, 20, 20 };
132 path->addRect(r);
133 SkMatrix rotateMatrix;
134 rotateMatrix.setRotate((SkScalar)fDegrees);
135 path->transform(rotateMatrix);
136 }
137
138 void setupPaint(SkPaint* paint) override {
140 paint->setAntiAlias(fAA);
141 }
142private:
143 using INHERITED = PathBench;
144 int fDegrees;
145 bool fAA;
146};
147
148class OvalPathBench : public PathBench {
149public:
151
152 void appendName(SkString* name) override {
153 name->append("oval");
154 }
155 void makePath(SkPath* path) override {
156 SkRect r = { 10, 10, 23, 20 };
157 path->addOval(r);
158 }
159private:
160 using INHERITED = PathBench;
161};
162
164public:
166
167 void appendName(SkString* name) override {
168 name->append("circle");
169 }
170 void makePath(SkPath* path) override {
171 path->addCircle(SkIntToScalar(20), SkIntToScalar(20),
172 SkIntToScalar(10));
173 }
174private:
175 using INHERITED = PathBench;
176};
177
179public:
181
182 void appendName(SkString* name) override {
183 name->append("nonaacircle");
184 }
185
186 void setupPaint(SkPaint* paint) override {
188 paint->setAntiAlias(false);
189 }
190
191private:
192 using INHERITED = CirclePathBench;
193};
194
195// Test max speedup of Analytic AA for concave paths
197public:
199
200 void appendName(SkString* name) override {
201 name->append("concave_aaa");
202 }
203
204 void makePath(SkPath* path) override {
205 path->moveTo(10, 10);
206 path->lineTo(15, 10);
207 path->lineTo(15, 5);
208 path->lineTo(40, 40);
209 path->close();
210 }
211
212private:
213 using INHERITED = PathBench;
214};
215
216// Test max speedup of Analytic AA for convex paths
218public:
220
221 void appendName(SkString* name) override {
222 name->append("convex_aaa");
223 }
224
225 void makePath(SkPath* path) override {
226 path->moveTo(10, 10);
227 path->lineTo(15, 10);
228 path->lineTo(40, 50);
229 path->close();
230 }
231
232private:
233 using INHERITED = PathBench;
234};
235
237public:
239
240 void appendName(SkString* name) override {
241 name->append("sawtooth");
242 }
243 void makePath(SkPath* path) override {
246 const SkScalar x0 = x;
247 const SkScalar dx = SK_Scalar1 * 5;
248 const SkScalar dy = SK_Scalar1 * 10;
249
250 path->moveTo(x, y);
251 for (int i = 0; i < 32; i++) {
252 x += dx;
253 path->lineTo(x, y - dy);
254 x += dx;
255 path->lineTo(x, y + dy);
256 }
257 path->lineTo(x, y + 2 * dy);
258 path->lineTo(x0, y + 2 * dy);
259 path->close();
260 }
261 int complexity() override { return 1; }
262private:
263 using INHERITED = PathBench;
264};
265
267public:
269
270 void appendName(SkString* name) override {
271 name->append("long_curved");
272 }
273 void makePath(SkPath* path) override {
274 SkRandom rand (12);
275 int i;
276 for (i = 0; i < 100; i++) {
277 path->quadTo(rand.nextUScalar1() * 640, rand.nextUScalar1() * 480,
278 rand.nextUScalar1() * 640, rand.nextUScalar1() * 480);
279 }
280 path->close();
281 }
282 int complexity() override { return 2; }
283private:
284 using INHERITED = PathBench;
285};
286
288public:
290
291 void appendName(SkString* name) override {
292 name->append("long_line");
293 }
294 void makePath(SkPath* path) override {
295 SkRandom rand;
296 path->moveTo(rand.nextUScalar1() * 640, rand.nextUScalar1() * 480);
297 for (size_t i = 1; i < 100; i++) {
298 path->lineTo(rand.nextUScalar1() * 640, rand.nextUScalar1() * 480);
299 }
300 }
301 int complexity() override { return 2; }
302private:
303 using INHERITED = PathBench;
304};
305
307public:
308 bool isSuitableFor(Backend backend) override {
310 }
311
312protected:
313 void createData(int minVerbs,
314 int maxVerbs,
315 bool allowMoves = true,
316 SkRect* bounds = nullptr) {
317 SkRect tempBounds;
318 if (nullptr == bounds) {
319 tempBounds.setXYWH(0, 0, SK_Scalar1, SK_Scalar1);
320 bounds = &tempBounds;
321 }
322 fVerbCnts.reset(kNumVerbCnts);
323 for (int i = 0; i < kNumVerbCnts; ++i) {
324 fVerbCnts[i] = fRandom.nextRangeU(minVerbs, maxVerbs + 1);
325 }
326 fVerbs.reset(kNumVerbs);
327 for (int i = 0; i < kNumVerbs; ++i) {
328 do {
329 fVerbs[i] = static_cast<SkPath::Verb>(fRandom.nextULessThan(SkPath::kDone_Verb));
330 } while (!allowMoves && SkPath::kMove_Verb == fVerbs[i]);
331 }
332 fPoints.reset(kNumPoints);
333 for (int i = 0; i < kNumPoints; ++i) {
334 fPoints[i].set(fRandom.nextRangeScalar(bounds->fLeft, bounds->fRight),
335 fRandom.nextRangeScalar(bounds->fTop, bounds->fBottom));
336 }
337 this->restartMakingPaths();
338 }
339
341 fCurrPath = 0;
342 fCurrVerb = 0;
343 fCurrPoint = 0;
344 }
345
346 void makePath(SkPath* path) {
347 int vCount = fVerbCnts[(fCurrPath++) & (kNumVerbCnts - 1)];
348 for (int v = 0; v < vCount; ++v) {
349 int verb = fVerbs[(fCurrVerb++) & (kNumVerbs - 1)];
350 switch (verb) {
352 path->moveTo(fPoints[(fCurrPoint++) & (kNumPoints - 1)]);
353 break;
355 path->lineTo(fPoints[(fCurrPoint++) & (kNumPoints - 1)]);
356 break;
358 path->quadTo(fPoints[(fCurrPoint + 0) & (kNumPoints - 1)],
359 fPoints[(fCurrPoint + 1) & (kNumPoints - 1)]);
360 fCurrPoint += 2;
361 break;
363 path->conicTo(fPoints[(fCurrPoint + 0) & (kNumPoints - 1)],
364 fPoints[(fCurrPoint + 1) & (kNumPoints - 1)],
366 fCurrPoint += 2;
367 break;
369 path->cubicTo(fPoints[(fCurrPoint + 0) & (kNumPoints - 1)],
370 fPoints[(fCurrPoint + 1) & (kNumPoints - 1)],
371 fPoints[(fCurrPoint + 2) & (kNumPoints - 1)]);
372 fCurrPoint += 3;
373 break;
375 path->close();
376 break;
377 default:
378 SkDEBUGFAIL("Unexpected path verb");
379 break;
380 }
381 }
382 }
383
385 fVerbCnts.reset(0);
386 fVerbs.reset(0);
387 fPoints.reset(0);
388 }
389
390private:
391 enum {
392 // these should all be pow 2
393 kNumVerbCnts = 1 << 5,
394 kNumVerbs = 1 << 5,
395 kNumPoints = 1 << 5,
396 };
397 AutoTArray<int> fVerbCnts;
399 AutoTArray<SkPoint> fPoints;
400 int fCurrPath;
401 int fCurrVerb;
402 int fCurrPoint;
403 SkRandom fRandom;
404 using INHERITED = Benchmark;
405};
406
408public:
411
412protected:
413 const char* onGetName() override {
414 return "path_create";
415 }
416
417 void onDelayedSetup() override {
418 this->createData(10, 100);
419 }
420
421 void onDraw(int loops, SkCanvas*) override {
422 for (int i = 0; i < loops; ++i) {
423 if (i % 1000 == 0) {
424 fPath.reset(); // PathRef memory can grow without bound otherwise.
425 }
426 this->makePath(&fPath);
427 }
428 this->restartMakingPaths();
429 }
430
431private:
432 SkPath fPath;
433
434 using INHERITED = RandomPathBench;
435};
436
438public:
440 }
441
442protected:
443 const char* onGetName() override {
444 return "path_copy";
445 }
446 void onDelayedSetup() override {
447 this->createData(10, 100);
448 fPaths.reset(kPathCnt);
449 fCopies.reset(kPathCnt);
450 for (int i = 0; i < kPathCnt; ++i) {
451 this->makePath(&fPaths[i]);
452 }
453 this->finishedMakingPaths();
454 }
455 void onDraw(int loops, SkCanvas*) override {
456 for (int i = 0; i < loops; ++i) {
457 int idx = i & (kPathCnt - 1);
458 fCopies[idx] = fPaths[idx];
459 }
460 }
461
462private:
463 enum {
464 // must be a pow 2
465 kPathCnt = 1 << 5,
466 };
467 AutoTArray<SkPath> fPaths;
468 AutoTArray<SkPath> fCopies;
469
470 using INHERITED = RandomPathBench;
471};
472
474public:
475 PathTransformBench(bool inPlace) : fInPlace(inPlace) {}
476
477protected:
478 const char* onGetName() override {
479 return fInPlace ? "path_transform_in_place" : "path_transform_copy";
480 }
481
482 void onDelayedSetup() override {
483 fMatrix.setScale(5 * SK_Scalar1, 6 * SK_Scalar1);
484 this->createData(10, 100);
485 fPaths.reset(kPathCnt);
486 for (int i = 0; i < kPathCnt; ++i) {
487 this->makePath(&fPaths[i]);
488 }
489 this->finishedMakingPaths();
490 if (!fInPlace) {
491 fTransformed.reset(kPathCnt);
492 }
493 }
494
495 void onDraw(int loops, SkCanvas*) override {
496 if (fInPlace) {
497 for (int i = 0; i < loops; ++i) {
498 fPaths[i & (kPathCnt - 1)].transform(fMatrix);
499 }
500 } else {
501 for (int i = 0; i < loops; ++i) {
502 int idx = i & (kPathCnt - 1);
503 fPaths[idx].transform(fMatrix, &fTransformed[idx]);
504 }
505 }
506 }
507
508private:
509 enum {
510 // must be a pow 2
511 kPathCnt = 1 << 5,
512 };
513 AutoTArray<SkPath> fPaths;
514 AutoTArray<SkPath> fTransformed;
515
516 SkMatrix fMatrix;
517 bool fInPlace;
518 using INHERITED = RandomPathBench;
519};
520
522public:
524
525protected:
526 const char* onGetName() override {
527 return "path_equality_50%";
528 }
529
530 void onDelayedSetup() override {
531 fParity = 0;
532 this->createData(10, 100);
533 fPaths.reset(kPathCnt);
534 fCopies.reset(kPathCnt);
535 for (int i = 0; i < kPathCnt; ++i) {
536 this->makePath(&fPaths[i]);
537 fCopies[i] = fPaths[i];
538 }
539 this->finishedMakingPaths();
540 }
541
542 void onDraw(int loops, SkCanvas*) override {
543 for (int i = 0; i < loops; ++i) {
544 int idx = i & (kPathCnt - 1);
545 fParity ^= (fPaths[idx] == fCopies[idx & ~0x1]);
546 }
547 }
548
549private:
550 bool fParity; // attempt to keep compiler from optimizing out the ==
551 enum {
552 // must be a pow 2
553 kPathCnt = 1 << 5,
554 };
555 AutoTArray<SkPath> fPaths;
556 AutoTArray<SkPath> fCopies;
557 using INHERITED = RandomPathBench;
558};
559
561public:
569
571 fMatrix.setRotate(60 * SK_Scalar1);
572 }
573
574protected:
575 const char* onGetName() override {
576 switch (fType) {
577 case kAdd_AddType:
578 return "path_add_path";
580 return "path_add_path_trans";
582 return "path_add_path_matrix";
584 return "path_reverse_add_path";
586 return "path_reverse_path_to";
587 default:
588 SkDEBUGFAIL("Bad add type");
589 return "";
590 }
591 }
592
593 void onDelayedSetup() override {
594 // reversePathTo assumes a single contour path.
595 bool allowMoves = kReversePathTo_AddType != fType;
596 this->createData(10, 100, allowMoves);
597 fPaths0.reset(kPathCnt);
598 fPaths1.reset(kPathCnt);
599 for (int i = 0; i < kPathCnt; ++i) {
600 this->makePath(&fPaths0[i]);
601 this->makePath(&fPaths1[i]);
602 }
603 this->finishedMakingPaths();
604 }
605
606 void onDraw(int loops, SkCanvas*) override {
607 switch (fType) {
608 case kAdd_AddType:
609 for (int i = 0; i < loops; ++i) {
610 int idx = i & (kPathCnt - 1);
611 SkPath result = fPaths0[idx];
612 result.addPath(fPaths1[idx]);
613 }
614 break;
616 for (int i = 0; i < loops; ++i) {
617 int idx = i & (kPathCnt - 1);
618 SkPath result = fPaths0[idx];
619 result.addPath(fPaths1[idx], 2 * SK_Scalar1, 5 * SK_Scalar1);
620 }
621 break;
623 for (int i = 0; i < loops; ++i) {
624 int idx = i & (kPathCnt - 1);
625 SkPath result = fPaths0[idx];
626 result.addPath(fPaths1[idx], fMatrix);
627 }
628 break;
630 for (int i = 0; i < loops; ++i) {
631 int idx = i & (kPathCnt - 1);
632 SkPath result = fPaths0[idx];
633 result.reverseAddPath(fPaths1[idx]);
634 }
635 break;
637 for (int i = 0; i < loops; ++i) {
638 int idx = i & (kPathCnt - 1);
639 SkPath result = fPaths0[idx];
640 result.reversePathTo(fPaths1[idx]);
641 }
642 break;
643 }
644 }
645
646private:
647 AddType fType; // or reverseAddPath
648 enum {
649 // must be a pow 2
650 kPathCnt = 1 << 5,
651 };
652 AutoTArray<SkPath> fPaths0;
653 AutoTArray<SkPath> fPaths1;
654 SkMatrix fMatrix;
655 using INHERITED = RandomPathBench;
656};
657
658
659class CirclesBench : public Benchmark {
660protected:
663
664public:
666 fName.printf("circles_%s", fFlags & kStroke_Flag ? "stroke" : "fill");
667 }
668
669protected:
670 const char* onGetName() override {
671 return fName.c_str();
672 }
673
674 void onDraw(int loops, SkCanvas* canvas) override {
676
677 paint.setColor(SK_ColorBLACK);
678 paint.setAntiAlias(true);
679 if (fFlags & kStroke_Flag) {
681 }
682
683 SkRandom rand;
684
685 SkRect r;
686
687 for (int i = 0; i < loops; ++i) {
688 SkScalar radius = rand.nextUScalar1() * 3;
689 r.fLeft = rand.nextUScalar1() * 300;
690 r.fTop = rand.nextUScalar1() * 300;
691 r.fRight = r.fLeft + 2 * radius;
692 r.fBottom = r.fTop + 2 * radius;
693
694 if (fFlags & kStroke_Flag) {
695 paint.setStrokeWidth(rand.nextUScalar1() * 5.0f);
696 }
697
698 SkPath temp;
699
700 // mimic how Chrome does circles
701 temp.arcTo(r, 0, 0, false);
703 temp.arcTo(r, 360, 0, true);
704 temp.close();
705
706 canvas->drawPath(temp, paint);
707 }
708 }
709
710private:
711 using INHERITED = Benchmark;
712};
713
714
715// Chrome creates its own round rects with each corner possibly being different.
716// In its "zero radius" incarnation it creates degenerate round rects.
717// Note: PathTest::test_arb_round_rect_is_convex and
718// test_arb_zero_rad_round_rect_is_rect perform almost exactly
719// the same test (but with no drawing)
721protected:
723
724public:
725 ArbRoundRectBench(bool zeroRad) : fZeroRad(zeroRad) {
726 if (zeroRad) {
727 fName.printf("zeroradroundrect");
728 } else {
729 fName.printf("arbroundrect");
730 }
731 }
732
733protected:
734 const char* onGetName() override {
735 return fName.c_str();
736 }
737
738 static void add_corner_arc(SkPath* path, const SkRect& rect,
739 SkScalar xIn, SkScalar yIn,
740 int startAngle)
741 {
742
743 SkScalar rx = std::min(rect.width(), xIn);
744 SkScalar ry = std::min(rect.height(), yIn);
745
746 SkRect arcRect;
747 arcRect.setLTRB(-rx, -ry, rx, ry);
748 switch (startAngle) {
749 case 0:
750 arcRect.offset(rect.fRight - arcRect.fRight, rect.fBottom - arcRect.fBottom);
751 break;
752 case 90:
753 arcRect.offset(rect.fLeft - arcRect.fLeft, rect.fBottom - arcRect.fBottom);
754 break;
755 case 180:
756 arcRect.offset(rect.fLeft - arcRect.fLeft, rect.fTop - arcRect.fTop);
757 break;
758 case 270:
759 arcRect.offset(rect.fRight - arcRect.fRight, rect.fTop - arcRect.fTop);
760 break;
761 default:
762 break;
763 }
764
765 path->arcTo(arcRect, SkIntToScalar(startAngle), SkIntToScalar(90), false);
766 }
767
768 static void make_arb_round_rect(SkPath* path, const SkRect& r,
769 SkScalar xCorner, SkScalar yCorner) {
770 // we are lazy here and use the same x & y for each corner
771 add_corner_arc(path, r, xCorner, yCorner, 270);
772 add_corner_arc(path, r, xCorner, yCorner, 0);
773 add_corner_arc(path, r, xCorner, yCorner, 90);
774 add_corner_arc(path, r, xCorner, yCorner, 180);
775 path->close();
776
777 SkASSERT(path->isConvex());
778 }
779
780 void onDraw(int loops, SkCanvas* canvas) override {
781 SkRandom rand;
782 SkRect r;
783
784 for (int i = 0; i < loops; ++i) {
786 paint.setColor(0xff000000 | rand.nextU());
787 paint.setAntiAlias(true);
788
789 SkScalar size = rand.nextUScalar1() * 30;
790 if (size < SK_Scalar1) {
791 continue;
792 }
793 r.fLeft = rand.nextUScalar1() * 300;
794 r.fTop = rand.nextUScalar1() * 300;
795 r.fRight = r.fLeft + 2 * size;
796 r.fBottom = r.fTop + 2 * size;
797
798 SkPath temp;
799
800 if (fZeroRad) {
801 make_arb_round_rect(&temp, r, 0, 0);
802
803 SkASSERT(temp.isRect(nullptr));
804 } else {
805 make_arb_round_rect(&temp, r, r.width() / 10, r.height() / 15);
806 }
807
808 canvas->drawPath(temp, paint);
809 }
810 }
811
812private:
813 bool fZeroRad; // should 0 radius rounds rects be tested?
814
815 using INHERITED = Benchmark;
816};
817
819public:
825
827 fParity = false;
828 fName = "conservatively_contains_";
829 switch (type) {
830 case kRect_Type:
831 fName.append("rect");
832 fPath.addRect(kBaseRect);
833 break;
834 case kRoundRect_Type:
835 fName.append("round_rect");
836 fPath.addRoundRect(kBaseRect, kRRRadii[0], kRRRadii[1]);
837 break;
838 case kOval_Type:
839 fName.append("oval");
840 fPath.addOval(kBaseRect);
841 break;
842 }
843 }
844
845 bool isSuitableFor(Backend backend) override {
847 }
848
849private:
850 const char* onGetName() override {
851 return fName.c_str();
852 }
853
854 void onDraw(int loops, SkCanvas*) override {
855 for (int i = 0; i < loops; ++i) {
856 const SkRect& rect = fQueryRects[i % kQueryRectCnt];
857 fParity = fParity != fPath.conservativelyContainsRect(rect);
858 }
859 }
860
861 void onDelayedSetup() override {
862 fQueryRects.resize(kQueryRectCnt);
863
864 SkRandom rand;
865 for (int i = 0; i < kQueryRectCnt; ++i) {
866 SkSize size;
867 SkPoint xy;
868 size.fWidth = rand.nextRangeScalar(kQueryMin.fWidth, kQueryMax.fWidth);
869 size.fHeight = rand.nextRangeScalar(kQueryMin.fHeight, kQueryMax.fHeight);
870 xy.fX = rand.nextRangeScalar(kBounds.fLeft, kBounds.fRight - size.fWidth);
871 xy.fY = rand.nextRangeScalar(kBounds.fTop, kBounds.fBottom - size.fHeight);
872
873 fQueryRects[i] = SkRect::MakeXYWH(xy.fX, xy.fY, size.fWidth, size.fHeight);
874 }
875 }
876
877 enum {
878 kQueryRectCnt = 400,
879 };
880 static const SkRect kBounds; // bounds for all random query rects
881 static const SkSize kQueryMin; // minimum query rect size, should be <= kQueryMax
882 static const SkSize kQueryMax; // max query rect size, should < kBounds
883 static const SkRect kBaseRect; // rect that is used to construct the path
884 static const SkScalar kRRRadii[2]; // x and y radii for round rect
885
886 SkString fName;
887 SkPath fPath;
888 bool fParity;
889 SkTDArray<SkRect> fQueryRects;
890
891 using INHERITED = Benchmark;
892};
893
894///////////////////////////////////////////////////////////////////////////////
895
896#include "src/core/SkGeometry.h"
897
899protected:
902public:
903 ConicBench_Chop() : fName("conic-chop") {
904 fRQ.fPts[0].set(0, 0);
905 fRQ.fPts[1].set(100, 0);
906 fRQ.fPts[2].set(100, 100);
908 }
909
910 bool isSuitableFor(Backend backend) override {
912 }
913
914private:
915 const char* onGetName() override { return fName.c_str(); }
916
917 void onDraw(int loops, SkCanvas*) override {
918 for (int i = 0; i < loops; ++i) {
919 fRQ.chop(fDst);
920 }
921 }
922
923 using INHERITED = Benchmark;
924};
925DEF_BENCH( return new ConicBench_Chop; )
926
928 const bool fUseV2;
929public:
930 ConicBench_EvalPos(bool useV2) : fUseV2(useV2) {
931 fName.printf("conic-eval-pos%d", useV2);
932 }
933 void onDraw(int loops, SkCanvas*) override {
934 if (fUseV2) {
935 for (int i = 0; i < loops; ++i) {
936 for (int j = 0; j < 1000; ++j) {
937 fDst[0].fPts[0] = fRQ.evalAt(0.4f);
938 }
939 }
940 } else {
941 for (int i = 0; i < loops; ++i) {
942 for (int j = 0; j < 1000; ++j) {
943 fRQ.evalAt(0.4f, &fDst[0].fPts[0], nullptr);
944 }
945 }
946 }
947 }
948};
949DEF_BENCH( return new ConicBench_EvalPos(false); )
950DEF_BENCH( return new ConicBench_EvalPos(true); )
951
953 const bool fUseV2;
954public:
955 ConicBench_EvalTan(bool useV2) : fUseV2(useV2) {
956 fName.printf("conic-eval-tan%d", useV2);
957 }
958 void onDraw(int loops, SkCanvas*) override {
959 if (fUseV2) {
960 for (int i = 0; i < loops; ++i) {
961 for (int j = 0; j < 1000; ++j) {
962 fDst[0].fPts[0] = fRQ.evalTangentAt(0.4f);
963 }
964 }
965 } else {
966 for (int i = 0; i < loops; ++i) {
967 for (int j = 0; j < 1000; ++j) {
968 fRQ.evalAt(0.4f, nullptr, &fDst[0].fPts[0]);
969 }
970 }
971 }
972 }
973};
974DEF_BENCH( return new ConicBench_EvalTan(false); )
975DEF_BENCH( return new ConicBench_EvalTan(true); )
976
978protected:
980
981public:
982 ConicBench_TinyError() : fName("conic-tinyerror") {}
983
984protected:
985 const char* onGetName() override { return fName.c_str(); }
986
987 void onDraw(int loops, SkCanvas*) override {
989 paint.setColor(SK_ColorBLACK);
990 paint.setAntiAlias(true);
992 paint.setStrokeWidth(2);
993
994 SkPath path;
995 path.moveTo(-100, 1);
996 path.cubicTo(-101, 1, -118, -47, -138, -44);
997
998 // The large y scale factor produces a tiny error threshold.
999 const SkMatrix mtx = SkMatrix::MakeAll(3.07294035f, 0.833333373f, 361.111115f, 0.0f,
1000 6222222.5f, 28333.334f, 0.0f, 0.0f, 1.0f);
1001 for (int i = 0; i < loops; ++i) {
1002 SkPath dst;
1003 skpathutils::FillPathWithPaint(path, paint, &dst, nullptr,
1005 }
1006 }
1007
1008private:
1009 using INHERITED = Benchmark;
1010};
1011DEF_BENCH( return new ConicBench_TinyError; )
1012
1013///////////////////////////////////////////////////////////////////////////////
1014
1015static void rand_conic(SkConic* conic, SkRandom& rand) {
1016 for (int i = 0; i < 3; ++i) {
1017 conic->fPts[i].set(rand.nextUScalar1() * 100, rand.nextUScalar1() * 100);
1018 }
1019 if (rand.nextUScalar1() > 0.5f) {
1020 conic->fW = rand.nextUScalar1();
1021 } else {
1022 conic->fW = 1 + rand.nextUScalar1() * 4;
1023 }
1024}
1025
1026class ConicBench : public Benchmark {
1027public:
1029 SkRandom rand;
1030 for (int i = 0; i < CONICS; ++i) {
1031 rand_conic(&fConics[i], rand);
1032 }
1033 }
1034
1035 bool isSuitableFor(Backend backend) override {
1037 }
1038
1039protected:
1040 enum {
1041 CONICS = 100
1044
1045private:
1046 using INHERITED = Benchmark;
1047};
1048
1050public:
1052
1053protected:
1054 const char* onGetName() override {
1055 return "conic-compute-error";
1056 }
1057
1058 void onDraw(int loops, SkCanvas*) override {
1059 SkVector err;
1060 for (int i = 0; i < loops; ++i) {
1061 for (int j = 0; j < CONICS; ++j) {
1062 fConics[j].computeAsQuadError(&err);
1063 }
1064 }
1065 }
1066
1067private:
1068 using INHERITED = ConicBench;
1069};
1070
1072public:
1074
1075protected:
1076 const char* onGetName() override {
1077 return "conic-asQuadTol";
1078 }
1079
1080 void onDraw(int loops, SkCanvas*) override {
1081 for (int i = 0; i < loops; ++i) {
1082 for (int j = 0; j < CONICS; ++j) {
1084 }
1085 }
1086 }
1087
1088private:
1089 using INHERITED = ConicBench;
1090};
1091
1093public:
1095
1096protected:
1097 const char* onGetName() override {
1098 return "conic-quadPow2";
1099 }
1100
1101 void onDraw(int loops, SkCanvas*) override {
1102 for (int i = 0; i < loops; ++i) {
1103 for (int j = 0; j < CONICS; ++j) {
1105 }
1106 }
1107 }
1108
1109private:
1110 using INHERITED = ConicBench;
1111};
1112
1113///////////////////////////////////////////////////////////////////////////////
1114
1116 SkPath fPath;
1117 SkString fName;
1118 SkRect (*fProc)(const SkPath&);
1119
1120public:
1121 TightBoundsBench(SkRect (*proc)(const SkPath&), const char suffix[]) : fProc(proc) {
1122 fName.printf("tight_bounds_%s", suffix);
1123
1124 const int N = 100;
1125 SkRandom rand;
1126 for (int i = 0; i < N; ++i) {
1127 fPath.moveTo(rand.nextF()*100, rand.nextF()*100);
1128 fPath.lineTo(rand.nextF()*100, rand.nextF()*100);
1129 fPath.quadTo(rand.nextF()*100, rand.nextF()*100, rand.nextF()*100, rand.nextF()*100);
1130 fPath.conicTo(rand.nextF()*100, rand.nextF()*100, rand.nextF()*100, rand.nextF()*100,
1131 rand.nextF()*10);
1132 fPath.cubicTo(rand.nextF()*100, rand.nextF()*100, rand.nextF()*100, rand.nextF()*100,
1133 rand.nextF()*100, rand.nextF()*100);
1134 }
1135 }
1136
1137protected:
1138 bool isSuitableFor(Backend backend) override {
1140 }
1141
1142 const char* onGetName() override { return fName.c_str(); }
1143
1144 void onDraw(int loops, SkCanvas* canvas) override {
1145 for (int i = 0; i < loops*100; ++i) {
1146 fProc(fPath);
1147 }
1148 }
1149
1150private:
1151 using INHERITED = Benchmark;
1152};
1153
1154
1155const SkRect ConservativelyContainsBench::kBounds = SkRect::MakeWH(SkIntToScalar(100), SkIntToScalar(100));
1156const SkSize ConservativelyContainsBench::kQueryMin = {SkIntToScalar(1), SkIntToScalar(1)};
1157const SkSize ConservativelyContainsBench::kQueryMax = {SkIntToScalar(40), SkIntToScalar(40)};
1158const SkRect ConservativelyContainsBench::kBaseRect = SkRect::MakeXYWH(SkIntToScalar(25), SkIntToScalar(25), SkIntToScalar(50), SkIntToScalar(50));
1159const SkScalar ConservativelyContainsBench::kRRRadii[2] = {SkIntToScalar(5), SkIntToScalar(10)};
1160
1161DEF_BENCH( return new TrianglePathBench(FLAGS00); )
1162DEF_BENCH( return new TrianglePathBench(FLAGS01); )
1163DEF_BENCH( return new TrianglePathBench(FLAGS10); )
1164DEF_BENCH( return new TrianglePathBench(FLAGS11); )
1165
1166DEF_BENCH( return new RectPathBench(FLAGS00); )
1167DEF_BENCH( return new RectPathBench(FLAGS01); )
1168DEF_BENCH( return new RectPathBench(FLAGS10); )
1169DEF_BENCH( return new RectPathBench(FLAGS11); )
1170
1171DEF_BENCH( return new RotatedRectBench(FLAGS00, false, 45));
1172DEF_BENCH( return new RotatedRectBench(FLAGS10, false, 45));
1173DEF_BENCH( return new RotatedRectBench(FLAGS00, true, 45));
1174DEF_BENCH( return new RotatedRectBench(FLAGS10, true, 45));
1175
1176DEF_BENCH( return new OvalPathBench(FLAGS00); )
1177DEF_BENCH( return new OvalPathBench(FLAGS01); )
1178DEF_BENCH( return new OvalPathBench(FLAGS10); )
1179DEF_BENCH( return new OvalPathBench(FLAGS11); )
1180
1181DEF_BENCH( return new CirclePathBench(FLAGS00); )
1182DEF_BENCH( return new CirclePathBench(FLAGS01); )
1183DEF_BENCH( return new CirclePathBench(FLAGS10); )
1184DEF_BENCH( return new CirclePathBench(FLAGS11); )
1185
1188
1191DEF_BENCH( return new AAAConvexPathBench(FLAGS00); )
1192DEF_BENCH( return new AAAConvexPathBench(FLAGS10); )
1193
1194DEF_BENCH( return new SawToothPathBench(FLAGS00); )
1195DEF_BENCH( return new SawToothPathBench(FLAGS01); )
1196
1199DEF_BENCH( return new LongLinePathBench(FLAGS00); )
1200DEF_BENCH( return new LongLinePathBench(FLAGS01); )
1201
1202DEF_BENCH( return new PathCreateBench(); )
1203DEF_BENCH( return new PathCopyBench(); )
1204DEF_BENCH( return new PathTransformBench(true); )
1205DEF_BENCH( return new PathTransformBench(false); )
1206DEF_BENCH( return new PathEqualityBench(); )
1207
1213
1214DEF_BENCH( return new CirclesBench(FLAGS00); )
1215DEF_BENCH( return new CirclesBench(FLAGS01); )
1216DEF_BENCH( return new ArbRoundRectBench(false); )
1217DEF_BENCH( return new ArbRoundRectBench(true); )
1221
1222#include "include/pathops/SkPathOps.h"
1223#include "src/core/SkPathPriv.h"
1224
1225DEF_BENCH( return new TightBoundsBench([](const SkPath& path){ return path.computeTightBounds();},
1226 "priv"); )
1227DEF_BENCH( return new TightBoundsBench([](const SkPath& path) {
1228 SkRect bounds; TightBounds(path, &bounds); return bounds;
1229 }, "pathops"); )
1230
1231// These seem to be optimized away, which is troublesome for timing.
1232/*
1233DEF_BENCH( return new ConicBench_Chop5() )
1234DEF_BENCH( return new ConicBench_ComputeError() )
1235DEF_BENCH( return new ConicBench_asQuadTol() )
1236DEF_BENCH( return new ConicBench_quadPow2() )
1237*/
1238
1240protected:
1243 const bool fAA;
1244
1245public:
1246 CommonConvexBench(int w, int h, bool forceConcave, bool aa) : fAA(aa) {
1247 fName.printf("convex_path_%d_%d_%d_%d", w, h, forceConcave, aa);
1248
1249 SkRect r = SkRect::MakeXYWH(10, 10, w*1.0f, h*1.0f);
1250 fPath.addRRect(SkRRect::MakeRectXY(r, w/8.0f, h/8.0f));
1251
1252 if (forceConcave) {
1255 } else {
1257 }
1258 }
1259
1260protected:
1261 const char* onGetName() override {
1262 return fName.c_str();
1263 }
1264
1265 void onDraw(int loops, SkCanvas* canvas) override {
1266 SkPaint paint;
1267 paint.setAntiAlias(fAA);
1268
1269 for (int i = 0; i < loops; ++i) {
1270 for (int inner = 0; inner < 100; ++inner) {
1271 canvas->drawPath(fPath, paint);
1272 }
1273 }
1274 }
1275
1276private:
1277 using INHERITED = Benchmark;
1278};
1279
1280DEF_BENCH( return new CommonConvexBench( 16, 16, false, false); )
1281DEF_BENCH( return new CommonConvexBench( 16, 16, true, false); )
1282DEF_BENCH( return new CommonConvexBench( 16, 16, false, true); )
1283DEF_BENCH( return new CommonConvexBench( 16, 16, true, true); )
1284
1285DEF_BENCH( return new CommonConvexBench(200, 16, false, false); )
1286DEF_BENCH( return new CommonConvexBench(200, 16, true, false); )
1287DEF_BENCH( return new CommonConvexBench(200, 16, false, true); )
1288DEF_BENCH( return new CommonConvexBench(200, 16, true, true); )
SkPoint fPts[2]
SkPath fPath
#define DEF_BENCH(code)
Definition Benchmark.h:20
const char * backend
const char * fName
#define FLAGS01
Definition PathBench.cpp:33
#define FLAGS11
Definition PathBench.cpp:35
Flags
Definition PathBench.cpp:27
@ kBig_Flag
Definition PathBench.cpp:29
@ kStroke_Flag
Definition PathBench.cpp:28
#define FLAGS00
Definition PathBench.cpp:32
#define FLAGS10
Definition PathBench.cpp:34
static void rand_conic(SkConic *conic, SkRandom &rand)
uint16_t fFlags
#define SkDEBUGFAIL(message)
Definition SkAssert.h:118
#define SkASSERT(cond)
Definition SkAssert.h:116
constexpr SkColor SK_ColorBLACK
Definition SkColor.h:103
bool SK_API TightBounds(const SkPath &path, SkRect *result)
#define INHERITED(method,...)
#define SK_Scalar1
Definition SkScalar.h:18
#define SK_ScalarHalf
Definition SkScalar.h:19
#define SkIntToScalar(x)
Definition SkScalar.h:57
#define SkScalarCos(radians)
Definition SkScalar.h:46
#define SK_ScalarPI
Definition SkScalar.h:21
#define N
Definition beziers.cpp:19
AAAConcavePathBench(Flags flags)
void appendName(SkString *name) override
void makePath(SkPath *path) override
void appendName(SkString *name) override
AAAConvexPathBench(Flags flags)
void makePath(SkPath *path) override
static void make_arb_round_rect(SkPath *path, const SkRect &r, SkScalar xCorner, SkScalar yCorner)
const char * onGetName() override
ArbRoundRectBench(bool zeroRad)
void onDraw(int loops, SkCanvas *canvas) override
static void add_corner_arc(SkPath *path, const SkRect &rect, SkScalar xIn, SkScalar yIn, int startAngle)
virtual void setupPaint(SkPaint *paint)
Definition Benchmark.cpp:55
void makePath(SkPath *path) override
void appendName(SkString *name) override
CirclePathBench(Flags flags)
void onDraw(int loops, SkCanvas *canvas) override
const char * onGetName() override
CirclesBench(Flags flags)
SkString fName
void onDraw(int loops, SkCanvas *canvas) override
CommonConvexBench(int w, int h, bool forceConcave, bool aa)
const char * onGetName() override
SkConic fDst[2]
void onDraw(int loops, SkCanvas *) override
const char * onGetName() override
bool isSuitableFor(Backend backend) override
void onDraw(int loops, SkCanvas *) override
const char * onGetName() override
void onDraw(int loops, SkCanvas *) override
ConicBench_EvalPos(bool useV2)
ConicBench_EvalTan(bool useV2)
void onDraw(int loops, SkCanvas *) override
const char * onGetName() override
void onDraw(int loops, SkCanvas *) override
void onDraw(int loops, SkCanvas *) override
const char * onGetName() override
const char * onGetName() override
void onDraw(int loops, SkCanvas *) override
bool isSuitableFor(Backend backend) override
SkConic fConics[CONICS]
ConservativelyContainsBench(Type type)
void onDelayedSetup() override
const char * onGetName() override
bool isSuitableFor(Backend backend) override
void onDraw(int loops, SkCanvas *) override
void appendName(SkString *name) override
LongCurvedPathBench(Flags flags)
void makePath(SkPath *path) override
int complexity() override
int complexity() override
void makePath(SkPath *path) override
LongLinePathBench(Flags flags)
void appendName(SkString *name) override
void appendName(SkString *name) override
void setupPaint(SkPaint *paint) override
NonAACirclePathBench(Flags flags)
void appendName(SkString *name) override
void makePath(SkPath *path) override
OvalPathBench(Flags flags)
PathBench(Flags flags)
Definition PathBench.cpp:42
const char * onGetName() override
Definition PathBench.cpp:54
virtual int complexity()
Definition PathBench.cpp:51
virtual void appendName(SkString *)=0
void onDraw(int loops, SkCanvas *canvas) override
Definition PathBench.cpp:62
virtual void makePath(SkPath *)=0
void onDelayedSetup() override
const char * onGetName() override
void onDraw(int loops, SkCanvas *) override
void onDelayedSetup() override
void onDraw(int loops, SkCanvas *) override
const char * onGetName() override
const char * onGetName() override
void onDraw(int loops, SkCanvas *) override
void onDelayedSetup() override
void onDraw(int loops, SkCanvas *) override
const char * onGetName() override
PathTransformBench(bool inPlace)
void onDelayedSetup() override
void makePath(SkPath *path)
void createData(int minVerbs, int maxVerbs, bool allowMoves=true, SkRect *bounds=nullptr)
bool isSuitableFor(Backend backend) override
void finishedMakingPaths()
void restartMakingPaths()
void appendName(SkString *name) override
void makePath(SkPath *path) override
RectPathBench(Flags flags)
void makePath(SkPath *path) override
void appendName(SkString *name) override
RotatedRectBench(Flags flags, bool aa, int degrees)
void setupPaint(SkPaint *paint) override
void makePath(SkPath *path) override
SawToothPathBench(Flags flags)
void appendName(SkString *name) override
int complexity() override
const char * onGetName() override
SkBench_AddPathTest(AddType type)
void onDraw(int loops, SkCanvas *) override
void onDelayedSetup() override
void drawPath(const SkPath &path, const SkPaint &paint)
static SkScalar ComputeResScaleForStroking(const SkMatrix &matrix)
static SkMatrix Scale(SkScalar sx, SkScalar sy)
Definition SkMatrix.h:75
static SkMatrix MakeAll(SkScalar scaleX, SkScalar skewX, SkScalar transX, SkScalar skewY, SkScalar scaleY, SkScalar transY, SkScalar pers0, SkScalar pers1, SkScalar pers2)
Definition SkMatrix.h:179
SkMatrix & setScale(SkScalar sx, SkScalar sy, SkScalar px, SkScalar py)
Definition SkMatrix.cpp:296
SkMatrix & setRotate(SkScalar degrees, SkScalar px, SkScalar py)
Definition SkMatrix.cpp:452
void setStyle(Style style)
Definition SkPaint.cpp:105
@ kStroke_Style
set to stroke geometry
Definition SkPaint.h:194
@ kFill_Style
set to fill geometry
Definition SkPaint.h:193
void setStrokeJoin(Join join)
Definition SkPaint.cpp:189
@ kBevel_Join
connects outside edges
Definition SkPaint.h:361
void setStrokeWidth(SkScalar width)
Definition SkPaint.cpp:159
static void SetConvexity(const SkPath &path, SkPathConvexity c)
Definition SkPathPriv.h:413
bool conservativelyContainsRect(const SkRect &rect) const
Definition SkPath.cpp:289
SkPath & arcTo(const SkRect &oval, SkScalar startAngle, SkScalar sweepAngle, bool forceMoveTo)
Definition SkPath.cpp:1156
SkPath & moveTo(SkScalar x, SkScalar y)
Definition SkPath.cpp:678
SkPath & lineTo(SkScalar x, SkScalar y)
Definition SkPath.cpp:718
SkPath & addRRect(const SkRRect &rrect, SkPathDirection dir=SkPathDirection::kCW)
Definition SkPath.cpp:990
SkPath & reset()
Definition SkPath.cpp:360
SkPath & quadTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2)
Definition SkPath.cpp:736
SkPath & addRoundRect(const SkRect &rect, SkScalar rx, SkScalar ry, SkPathDirection dir=SkPathDirection::kCW)
Definition SkPath.cpp:1088
bool isConvex() const
Definition SkPath.cpp:416
@ kClose_Verb
Definition SkPath.h:1463
@ kMove_Verb
Definition SkPath.h:1458
@ kConic_Verb
Definition SkPath.h:1461
@ kDone_Verb
Definition SkPath.h:1464
@ kCubic_Verb
Definition SkPath.h:1462
@ kQuad_Verb
Definition SkPath.h:1460
@ kLine_Verb
Definition SkPath.h:1459
SkPath & addOval(const SkRect &oval, SkPathDirection dir=SkPathDirection::kCW)
Definition SkPath.cpp:1101
SkPath & cubicTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2, SkScalar x3, SkScalar y3)
Definition SkPath.cpp:789
SkPath & conicTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2, SkScalar w)
Definition SkPath.cpp:756
SkPath & close()
Definition SkPath.cpp:813
bool isRect(SkRect *rect, bool *isClosed=nullptr, SkPathDirection *direction=nullptr) const
Definition SkPath.cpp:506
SkPath & addRect(const SkRect &rect, SkPathDirection dir, unsigned start)
Definition SkPath.cpp:854
static SkRRect MakeRectXY(const SkRect &rect, SkScalar xRad, SkScalar yRad)
Definition SkRRect.h:180
uint32_t nextU()
Definition SkRandom.h:42
float nextF()
Definition SkRandom.h:55
SkScalar nextUScalar1()
Definition SkRandom.h:101
SkScalar nextRangeScalar(SkScalar min, SkScalar max)
Definition SkRandom.h:106
uint32_t nextULessThan(uint32_t count)
Definition SkRandom.h:93
uint32_t nextRangeU(uint32_t min, uint32_t max)
Definition SkRandom.h:80
void printf(const char format[],...) SK_PRINTF_LIKE(2
Definition SkString.cpp:534
void append(const char text[])
Definition SkString.h:203
const char * c_str() const
Definition SkString.h:133
void resize(int count)
Definition SkTDArray.h:183
bool isSuitableFor(Backend backend) override
void onDraw(int loops, SkCanvas *canvas) override
const char * onGetName() override
TightBoundsBench(SkRect(*proc)(const SkPath &), const char suffix[])
void makePath(SkPath *path) override
Definition PathBench.cpp:89
TrianglePathBench(Flags flags)
Definition PathBench.cpp:84
void appendName(SkString *name) override
Definition PathBench.cpp:86
void reset(size_t count=0)
const Paint & paint
float SkScalar
Definition extension.cpp:12
FlutterSemanticsFlag flags
GAsyncResult * result
const char * name
Definition fuchsia.cc:50
double y
double x
Optional< SkRect > bounds
Definition SkRecords.h:189
SK_API bool FillPathWithPaint(const SkPath &src, const SkPaint &paint, SkPath *dst, const SkRect *cullRect, SkScalar resScale=1)
static SkColor4f transform(SkColor4f c, SkColorSpace *src, SkColorSpace *dst)
Definition p3.cpp:47
SkScalar w
SkScalar h
int SK_SPI computeQuadPOW2(SkScalar tol) const
SkScalar fW
Definition SkGeometry.h:337
void computeAsQuadError(SkVector *err) const
bool asQuadTol(SkScalar tol) const
void chop(SkConic dst[2]) const
SkPoint fPts[3]
Definition SkGeometry.h:336
float fX
x-axis value
void set(float x, float y)
float fY
y-axis value
SkScalar fBottom
larger y-axis bounds
Definition extension.cpp:17
void setXYWH(float x, float y, float width, float height)
Definition SkRect.h:931
SkScalar fLeft
smaller x-axis bounds
Definition extension.cpp:14
static constexpr SkRect MakeXYWH(float x, float y, float w, float h)
Definition SkRect.h:659
SkScalar fRight
larger x-axis bounds
Definition extension.cpp:16
void offset(float dx, float dy)
Definition SkRect.h:1016
constexpr float height() const
Definition SkRect.h:769
void setLTRB(float left, float top, float right, float bottom)
Definition SkRect.h:865
constexpr float width() const
Definition SkRect.h:762
static constexpr SkRect MakeWH(float w, float h)
Definition SkRect.h:609
SkScalar fTop
smaller y-axis bounds
Definition extension.cpp:15
SkScalar fHeight
Definition SkSize.h:54
SkScalar fWidth
Definition SkSize.h:53