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