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 "SkRandom.h"
14#include "SkString.h"
15#include "sk_tool_utils.h"
16
17/*  Variants for bitmaps
18
19    - src depth (32 w+w/o alpha), 565, 4444, index, a8
20    - paint options: filtering, dither, alpha
21    - matrix options: translate, scale, rotate, persp
22    - tiling: none, repeat, mirror, clamp
23
24 */
25
26class BitmapBench : public Benchmark {
27    const SkColorType   fColorType;
28    const SkAlphaType   fAlphaType;
29    const bool          fForceUpdate; //bitmap marked as dirty before each draw. forces bitmap to be updated on device cache
30    const bool          fIsVolatile;
31    const bool          fDoScale;
32
33    SkBitmap            fBitmap;
34    SkPaint             fPaint;
35    SkString            fName;
36
37    enum { W = 128 };
38    enum { H = 128 };
39public:
40    BitmapBench(SkColorType ct, SkAlphaType at, bool forceUpdate, bool isVolatile, bool doScale)
41        : fColorType(ct)
42        , fAlphaType(at)
43        , fForceUpdate(forceUpdate)
44        , fIsVolatile(isVolatile)
45        , fDoScale(doScale)
46    {}
47
48protected:
49    const char* onGetName() override {
50        fName.set("bitmap");
51        fName.appendf("_%s%s", sk_tool_utils::colortype_name(fColorType),
52                      kOpaque_SkAlphaType == fAlphaType ? "" : "_A");
53        if (fDoScale) {
54            fName.append("_scale");
55        }
56        if (fForceUpdate) {
57            fName.append("_update");
58        }
59        if (fIsVolatile) {
60            fName.append("_volatile");
61        }
62
63        return fName.c_str();
64    }
65
66    void onDelayedSetup() override {
67        SkBitmap bm;
68
69        bm.allocPixels(SkImageInfo::Make(W, H, fColorType, fAlphaType));
70        bm.eraseColor(kOpaque_SkAlphaType == fAlphaType ? SK_ColorBLACK : 0);
71
72        this->onDrawIntoBitmap(bm);
73
74        fBitmap = bm;
75        fBitmap.setIsVolatile(fIsVolatile);
76    }
77
78    void onDraw(int loops, SkCanvas* canvas) override {
79        if (fDoScale) {
80            canvas->scale(.99f, .99f);
81        }
82        SkIPoint dim = this->getSize();
83        SkRandom rand;
84
85        SkPaint paint(fPaint);
86        this->setupPaint(&paint);
87
88        const SkBitmap& bitmap = fBitmap;
89        const SkScalar x0 = SkIntToScalar(-bitmap.width() / 2);
90        const SkScalar y0 = SkIntToScalar(-bitmap.height() / 2);
91
92        for (int i = 0; i < loops; i++) {
93            SkScalar x = x0 + rand.nextUScalar1() * dim.fX;
94            SkScalar y = y0 + rand.nextUScalar1() * dim.fY;
95
96            if (fForceUpdate)
97                bitmap.notifyPixelsChanged();
98
99            canvas->drawBitmap(bitmap, x, y, &paint);
100        }
101    }
102
103     virtual void onDrawIntoBitmap(const SkBitmap& bm) {
104        const int w = bm.width();
105        const int h = bm.height();
106
107        SkCanvas canvas(bm);
108        SkPaint p;
109        p.setAntiAlias(true);
110        p.setColor(SK_ColorRED);
111        canvas.drawCircle(SkIntToScalar(w)/2, SkIntToScalar(h)/2,
112                          SkIntToScalar(SkMin32(w, h))*3/8, p);
113
114        SkRect r;
115        r.set(0, 0, SkIntToScalar(w), SkIntToScalar(h));
116        p.setStyle(SkPaint::kStroke_Style);
117        p.setStrokeWidth(SkIntToScalar(4));
118        p.setColor(SK_ColorBLUE);
119        canvas.drawRect(r, p);
120    }
121
122private:
123    typedef Benchmark INHERITED;
124};
125
126/** Explicitly invoke some filter types to improve coverage of acceleration
127    procs. */
128
129enum Flags {
130    kScale_Flag             = 1 << 0,
131    kRotate_Flag            = 1 << 1,
132    kBilerp_Flag            = 1 << 2,
133    kBicubic_Flag           = 1 << 3,
134};
135
136static bool isBilerp(uint32_t flags) {
137    return (flags & (kBilerp_Flag | kBicubic_Flag)) == (kBilerp_Flag);
138}
139
140static bool isBicubic(uint32_t flags) {
141    return (flags & (kBilerp_Flag | kBicubic_Flag)) == (kBilerp_Flag | kBicubic_Flag);
142}
143
144class FilterBitmapBench : public BitmapBench {
145    uint32_t    fFlags;
146    SkString    fFullName;
147public:
148    FilterBitmapBench(SkColorType ct, SkAlphaType at,
149                      bool forceUpdate, bool isVolitile, uint32_t flags)
150        : INHERITED(ct, at, forceUpdate, isVolitile, false)
151        , fFlags(flags) {
152    }
153
154protected:
155    const char* onGetName() override {
156        fFullName.set(INHERITED::onGetName());
157        if (fFlags & kScale_Flag) {
158            fFullName.append("_scale");
159        }
160        if (fFlags & kRotate_Flag) {
161            fFullName.append("_rotate");
162        }
163        if (isBilerp(fFlags)) {
164            fFullName.append("_bilerp");
165        } else if (isBicubic(fFlags)) {
166            fFullName.append("_bicubic");
167        }
168
169        return fFullName.c_str();
170    }
171
172    void onDraw(int loops, SkCanvas* canvas) override {
173        SkISize dim = canvas->getBaseLayerSize();
174        if (fFlags & kScale_Flag) {
175            const SkScalar x = SkIntToScalar(dim.fWidth) / 2;
176            const SkScalar y = SkIntToScalar(dim.fHeight) / 2;
177
178            canvas->translate(x, y);
179            // just enough so we can't take the sprite case
180            canvas->scale(SK_Scalar1 * 99/100, SK_Scalar1 * 99/100);
181            canvas->translate(-x, -y);
182        }
183        if (fFlags & kRotate_Flag) {
184            const SkScalar x = SkIntToScalar(dim.fWidth) / 2;
185            const SkScalar y = SkIntToScalar(dim.fHeight) / 2;
186            canvas->rotate(SkIntToScalar(35), x, y);
187        }
188        INHERITED::onDraw(loops, canvas);
189    }
190
191    void setupPaint(SkPaint* paint) override {
192        this->INHERITED::setupPaint(paint);
193
194        int index = 0;
195        if (fFlags & kBilerp_Flag) {
196            index |= 1;
197        }
198        if (fFlags & kBicubic_Flag) {
199            index |= 2;
200        }
201        static const SkFilterQuality gQualitys[] = {
202            kNone_SkFilterQuality,
203            kLow_SkFilterQuality,
204            kMedium_SkFilterQuality,
205            kHigh_SkFilterQuality
206        };
207        paint->setFilterQuality(gQualitys[index]);
208}
209
210private:
211    typedef BitmapBench INHERITED;
212};
213
214/** Verify optimizations that test source alpha values. */
215
216class SourceAlphaBitmapBench : public BitmapBench {
217public:
218    enum SourceAlpha { kOpaque_SourceAlpha, kTransparent_SourceAlpha,
219                       kTwoStripes_SourceAlpha, kThreeStripes_SourceAlpha};
220private:
221    SkString    fFullName;
222    SourceAlpha fSourceAlpha;
223public:
224    SourceAlphaBitmapBench(SourceAlpha alpha, SkColorType ct,
225                bool forceUpdate = false, bool bitmapVolatile = false)
226        : INHERITED(ct, kPremul_SkAlphaType, forceUpdate, bitmapVolatile, false)
227        , fSourceAlpha(alpha) {
228    }
229
230protected:
231    const char* onGetName() override {
232        fFullName.set(INHERITED::onGetName());
233
234        if (fSourceAlpha == kOpaque_SourceAlpha) {
235                fFullName.append("_source_opaque");
236        } else if (fSourceAlpha == kTransparent_SourceAlpha) {
237                fFullName.append("_source_transparent");
238        } else if (fSourceAlpha == kTwoStripes_SourceAlpha) {
239                fFullName.append("_source_stripes_two");
240        } else if (fSourceAlpha == kThreeStripes_SourceAlpha) {
241                fFullName.append("_source_stripes_three");
242        }
243
244        return fFullName.c_str();
245    }
246
247    void onDrawIntoBitmap(const SkBitmap& bm) override {
248        const int w = bm.width();
249        const int h = bm.height();
250
251        if (kOpaque_SourceAlpha == fSourceAlpha) {
252            bm.eraseColor(SK_ColorBLACK);
253        } else if (kTransparent_SourceAlpha == fSourceAlpha) {
254            bm.eraseColor(0);
255        } else if (kTwoStripes_SourceAlpha == fSourceAlpha) {
256            bm.eraseColor(0);
257
258            SkCanvas canvas(bm);
259            SkPaint p;
260            p.setAntiAlias(false);
261            p.setStyle(SkPaint::kFill_Style);
262            p.setColor(SK_ColorRED);
263
264            // Draw red vertical stripes on transparent background
265            SkRect r;
266            for (int x = 0; x < w; x+=2)
267            {
268                r.set(SkIntToScalar(x), 0, SkIntToScalar(x+1), SkIntToScalar(h));
269                canvas.drawRect(r, p);
270            }
271
272        } else if (kThreeStripes_SourceAlpha == fSourceAlpha) {
273            bm.eraseColor(0);
274
275            SkCanvas canvas(bm);
276            SkPaint p;
277            p.setAntiAlias(false);
278            p.setStyle(SkPaint::kFill_Style);
279
280            // Draw vertical stripes on transparent background with a pattern
281            // where the first pixel is fully transparent, the next is semi-transparent
282            // and the third is fully opaque.
283            SkRect r;
284            for (int x = 0; x < w; x++)
285            {
286                if (x % 3 == 0) {
287                    continue; // Keep transparent
288                } else if (x % 3 == 1) {
289                    p.setColor(SkColorSetARGB(127, 127, 127, 127)); // Semi-transparent
290                } else if (x % 3 == 2) {
291                    p.setColor(SK_ColorRED); // Opaque
292                }
293                r.set(SkIntToScalar(x), 0, SkIntToScalar(x+1), SkIntToScalar(h));
294                canvas.drawRect(r, p);
295            }
296        }
297    }
298
299private:
300    typedef BitmapBench INHERITED;
301};
302
303DEF_BENCH( return new BitmapBench(kN32_SkColorType, kPremul_SkAlphaType, false, false, false); )
304DEF_BENCH( return new BitmapBench(kN32_SkColorType, kOpaque_SkAlphaType, false, false, false); )
305DEF_BENCH( return new BitmapBench(kN32_SkColorType, kOpaque_SkAlphaType, false, false, true); )
306DEF_BENCH( return new BitmapBench(kRGB_565_SkColorType, kOpaque_SkAlphaType, false, false, false); )
307DEF_BENCH( return new BitmapBench(kN32_SkColorType, kOpaque_SkAlphaType, true, true, false); )
308DEF_BENCH( return new BitmapBench(kN32_SkColorType, kOpaque_SkAlphaType, true, false, false); )
309
310// scale filter -> S32_opaque_D32_filter_DX_{SSE2,SSSE3} and Fact9 is also for S32_D16_filter_DX_SSE2
311DEF_BENCH( return new FilterBitmapBench(kN32_SkColorType, kPremul_SkAlphaType, false, false, kScale_Flag | kBilerp_Flag); )
312DEF_BENCH( return new FilterBitmapBench(kN32_SkColorType, kOpaque_SkAlphaType, false, false, kScale_Flag | kBilerp_Flag); )
313DEF_BENCH( return new FilterBitmapBench(kN32_SkColorType, kOpaque_SkAlphaType, true, true, kScale_Flag | kBilerp_Flag); )
314DEF_BENCH( return new FilterBitmapBench(kN32_SkColorType, kOpaque_SkAlphaType, true, false, kScale_Flag | kBilerp_Flag); )
315
316// The following two cases test the performance regression of b/70172912 .
317DEF_BENCH( return new FilterBitmapBench(kRGB_565_SkColorType, kOpaque_SkAlphaType, false, false, kScale_Flag | kBilerp_Flag); )
318DEF_BENCH( return new BitmapBench(kRGB_565_SkColorType, kOpaque_SkAlphaType, false, false, true); )
319
320// scale rotate filter -> S32_opaque_D32_filter_DXDY_{SSE2,SSSE3}
321DEF_BENCH( return new FilterBitmapBench(kN32_SkColorType, kPremul_SkAlphaType, false, false, kScale_Flag | kRotate_Flag | kBilerp_Flag); )
322DEF_BENCH( return new FilterBitmapBench(kN32_SkColorType, kOpaque_SkAlphaType, false, false, kScale_Flag | kRotate_Flag | kBilerp_Flag); )
323DEF_BENCH( return new FilterBitmapBench(kN32_SkColorType, kOpaque_SkAlphaType, true, true, kScale_Flag | kRotate_Flag | kBilerp_Flag); )
324DEF_BENCH( return new FilterBitmapBench(kN32_SkColorType, kOpaque_SkAlphaType, true, false, kScale_Flag | kRotate_Flag | kBilerp_Flag); )
325
326DEF_BENCH( return new FilterBitmapBench(kN32_SkColorType, kPremul_SkAlphaType, false, false, kScale_Flag | kBilerp_Flag | kBicubic_Flag); )
327DEF_BENCH( return new FilterBitmapBench(kN32_SkColorType, kPremul_SkAlphaType, false, false, kScale_Flag | kRotate_Flag | kBilerp_Flag | kBicubic_Flag); )
328
329// source alpha tests -> S32A_Opaque_BlitRow32_{arm,neon}
330DEF_BENCH( return new SourceAlphaBitmapBench(SourceAlphaBitmapBench::kOpaque_SourceAlpha, kN32_SkColorType); )
331DEF_BENCH( return new SourceAlphaBitmapBench(SourceAlphaBitmapBench::kTransparent_SourceAlpha, kN32_SkColorType); )
332DEF_BENCH( return new SourceAlphaBitmapBench(SourceAlphaBitmapBench::kTwoStripes_SourceAlpha, kN32_SkColorType); )
333DEF_BENCH( return new SourceAlphaBitmapBench(SourceAlphaBitmapBench::kThreeStripes_SourceAlpha, kN32_SkColorType); )
334