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