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