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 "SkBenchmark.h"
9#include "SkAAClip.h"
10#include "SkPath.h"
11#include "SkRegion.h"
12#include "SkString.h"
13#include "SkCanvas.h"
14#include "SkRandom.h"
15
16////////////////////////////////////////////////////////////////////////////////
17// This bench tests out AA/BW clipping via canvas' clipPath and clipRect calls
18class AAClipBench : public SkBenchmark {
19    SkString fName;
20    SkPath   fClipPath;
21    SkRect   fClipRect;
22    SkRect   fDrawRect;
23    bool     fDoPath;
24    bool     fDoAA;
25
26    enum {
27        N = SkBENCHLOOP(200),
28    };
29
30public:
31    AAClipBench(void* param, bool doPath, bool doAA)
32        : INHERITED(param)
33        , fDoPath(doPath)
34        , fDoAA(doAA) {
35
36        fName.printf("aaclip_%s_%s",
37                     doPath ? "path" : "rect",
38                     doAA ? "AA" : "BW");
39
40        fClipRect.set(SkFloatToScalar(10.5f), SkFloatToScalar(10.5f),
41                      SkFloatToScalar(50.5f), SkFloatToScalar(50.5f));
42        fClipPath.addRoundRect(fClipRect, SkIntToScalar(10), SkIntToScalar(10));
43        fDrawRect.set(SkIntToScalar(0), SkIntToScalar(0),
44                      SkIntToScalar(100), SkIntToScalar(100));
45
46        SkASSERT(fClipPath.isConvex());
47    }
48
49protected:
50    virtual const char* onGetName() { return fName.c_str(); }
51    virtual void onDraw(SkCanvas* canvas) {
52
53        SkPaint paint;
54        this->setupPaint(&paint);
55
56        for (int i = 0; i < N; ++i) {
57            // jostle the clip regions each time to prevent caching
58            fClipRect.offset((i % 2) == 0 ? SkIntToScalar(10) : SkIntToScalar(-10), 0);
59            fClipPath.reset();
60            fClipPath.addRoundRect(fClipRect,
61                                   SkIntToScalar(5), SkIntToScalar(5));
62            SkASSERT(fClipPath.isConvex());
63
64            canvas->save();
65#if 1
66            if (fDoPath) {
67                canvas->clipPath(fClipPath, SkRegion::kReplace_Op, fDoAA);
68            } else {
69                canvas->clipRect(fClipRect, SkRegion::kReplace_Op, fDoAA);
70            }
71
72            canvas->drawRect(fDrawRect, paint);
73#else
74            // this path tests out directly draw the clip primitive
75            // use it to comparing just drawing the clip vs. drawing using
76            // the clip
77            if (fDoPath) {
78                canvas->drawPath(fClipPath, paint);
79            } else {
80                canvas->drawRect(fClipRect, paint);
81            }
82#endif
83            canvas->restore();
84        }
85    }
86private:
87    typedef SkBenchmark INHERITED;
88};
89
90////////////////////////////////////////////////////////////////////////////////
91// This bench tests out nested clip stacks. It is intended to simulate
92// how WebKit nests clips.
93class NestedAAClipBench : public SkBenchmark {
94    SkString fName;
95    bool     fDoAA;
96    SkRect   fDrawRect;
97    SkRandom fRandom;
98
99    static const int kNumDraws = SkBENCHLOOP(2);
100    static const int kNestingDepth = 3;
101    static const int kImageSize = 400;
102
103    SkPoint fSizes[kNestingDepth+1];
104
105public:
106    NestedAAClipBench(void* param, bool doAA)
107        : INHERITED(param)
108        , fDoAA(doAA) {
109
110        fName.printf("nested_aaclip_%s", doAA ? "AA" : "BW");
111
112        fDrawRect = SkRect::MakeLTRB(0, 0,
113                                     SkIntToScalar(kImageSize),
114                                     SkIntToScalar(kImageSize));
115
116        fSizes[0].set(SkIntToScalar(kImageSize), SkIntToScalar(kImageSize));
117
118        for (int i = 1; i < kNestingDepth+1; ++i) {
119            fSizes[i].set(fSizes[i-1].fX/2, fSizes[i-1].fY/2);
120        }
121    }
122
123protected:
124    virtual const char* onGetName() { return fName.c_str(); }
125
126
127    void recurse(SkCanvas* canvas,
128                 int depth,
129                 const SkPoint& offset) {
130
131            canvas->save();
132
133            SkRect temp = SkRect::MakeLTRB(0, 0,
134                                           fSizes[depth].fX, fSizes[depth].fY);
135            temp.offset(offset);
136
137            SkPath path;
138            path.addRoundRect(temp, SkIntToScalar(3), SkIntToScalar(3));
139            SkASSERT(path.isConvex());
140
141            canvas->clipPath(path,
142                             0 == depth ? SkRegion::kReplace_Op :
143                                          SkRegion::kIntersect_Op,
144                             fDoAA);
145
146            if (kNestingDepth == depth) {
147                // we only draw the draw rect at the lowest nesting level
148                SkPaint paint;
149                paint.setColor(0xff000000 | fRandom.nextU());
150                canvas->drawRect(fDrawRect, paint);
151            } else {
152                SkPoint childOffset = offset;
153                this->recurse(canvas, depth+1, childOffset);
154
155                childOffset += fSizes[depth+1];
156                this->recurse(canvas, depth+1, childOffset);
157
158                childOffset.fX = offset.fX + fSizes[depth+1].fX;
159                childOffset.fY = offset.fY;
160                this->recurse(canvas, depth+1, childOffset);
161
162                childOffset.fX = offset.fX;
163                childOffset.fY = offset.fY + fSizes[depth+1].fY;
164                this->recurse(canvas, depth+1, childOffset);
165            }
166
167            canvas->restore();
168    }
169
170    virtual void onDraw(SkCanvas* canvas) {
171
172        for (int i = 0; i < kNumDraws; ++i) {
173            SkPoint offset = SkPoint::Make(0, 0);
174            this->recurse(canvas, 0, offset);
175        }
176    }
177
178private:
179    typedef SkBenchmark INHERITED;
180};
181
182////////////////////////////////////////////////////////////////////////////////
183class AAClipBuilderBench : public SkBenchmark {
184    SkString fName;
185    SkPath   fPath;
186    SkRect   fRect;
187    SkRegion fRegion;
188    bool     fDoPath;
189    bool     fDoAA;
190
191    enum {
192        N = SkBENCHLOOP(200),
193    };
194
195public:
196    AAClipBuilderBench(void* param, bool doPath, bool doAA) : INHERITED(param) {
197        fDoPath = doPath;
198        fDoAA = doAA;
199
200        fName.printf("aaclip_build_%s_%s", doPath ? "path" : "rect",
201                     doAA ? "AA" : "BW");
202
203        fRegion.setRect(0, 0, 640, 480);
204        fRect.set(fRegion.getBounds());
205        fRect.inset(SK_Scalar1/4, SK_Scalar1/4);
206        fPath.addRoundRect(fRect, SkIntToScalar(20), SkIntToScalar(20));
207    }
208
209protected:
210    virtual const char* onGetName() { return fName.c_str(); }
211    virtual void onDraw(SkCanvas*) {
212        SkPaint paint;
213        this->setupPaint(&paint);
214
215        for (int i = 0; i < N; ++i) {
216            SkAAClip clip;
217            if (fDoPath) {
218                clip.setPath(fPath, &fRegion, fDoAA);
219            } else {
220                clip.setRect(fRect, fDoAA);
221            }
222        }
223    }
224private:
225    typedef SkBenchmark INHERITED;
226};
227
228////////////////////////////////////////////////////////////////////////////////
229class AAClipRegionBench : public SkBenchmark {
230public:
231    AAClipRegionBench(void* param) : INHERITED(param) {
232        SkPath path;
233        // test conversion of a complex clip to a aaclip
234        path.addCircle(0, 0, SkIntToScalar(200));
235        path.addCircle(0, 0, SkIntToScalar(180));
236        // evenodd means we've constructed basically a stroked circle
237        path.setFillType(SkPath::kEvenOdd_FillType);
238
239        SkIRect bounds;
240        path.getBounds().roundOut(&bounds);
241        fRegion.setPath(path, SkRegion(bounds));
242    }
243
244protected:
245    virtual const char* onGetName() { return "aaclip_setregion"; }
246    virtual void onDraw(SkCanvas*) {
247        for (int i = 0; i < N; ++i) {
248            SkAAClip clip;
249            clip.setRegion(fRegion);
250        }
251    }
252
253private:
254    enum {
255        N = SkBENCHLOOP(400),
256    };
257    SkRegion fRegion;
258    typedef SkBenchmark INHERITED;
259};
260
261////////////////////////////////////////////////////////////////////////////////
262
263static SkBenchmark* Fact0(void* p) { return SkNEW_ARGS(AAClipBuilderBench, (p, false, false)); }
264static SkBenchmark* Fact1(void* p) { return SkNEW_ARGS(AAClipBuilderBench, (p, false, true)); }
265static SkBenchmark* Fact2(void* p) { return SkNEW_ARGS(AAClipBuilderBench, (p, true, false)); }
266static SkBenchmark* Fact3(void* p) { return SkNEW_ARGS(AAClipBuilderBench, (p, true, true)); }
267
268static BenchRegistry gReg0(Fact0);
269static BenchRegistry gReg1(Fact1);
270static BenchRegistry gReg2(Fact2);
271static BenchRegistry gReg3(Fact3);
272
273static SkBenchmark* Fact01(void* p) { return SkNEW_ARGS(AAClipRegionBench, (p)); }
274static BenchRegistry gReg01(Fact01);
275
276static SkBenchmark* Fact000(void* p) { return SkNEW_ARGS(AAClipBench, (p, false, false)); }
277static SkBenchmark* Fact001(void* p) { return SkNEW_ARGS(AAClipBench, (p, false, true)); }
278static SkBenchmark* Fact002(void* p) { return SkNEW_ARGS(AAClipBench, (p, true, false)); }
279static SkBenchmark* Fact003(void* p) { return SkNEW_ARGS(AAClipBench, (p, true, true)); }
280
281static BenchRegistry gReg000(Fact000);
282static BenchRegistry gReg001(Fact001);
283static BenchRegistry gReg002(Fact002);
284static BenchRegistry gReg003(Fact003);
285
286static SkBenchmark* Fact004(void* p) { return SkNEW_ARGS(NestedAAClipBench, (p, false)); }
287static SkBenchmark* Fact005(void* p) { return SkNEW_ARGS(NestedAAClipBench, (p, true)); }
288
289static BenchRegistry gReg004(Fact004);
290static BenchRegistry gReg005(Fact005);
291