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