blob: d8363fcb9dc7e7f05152503cd502a69076149ebd [file] [log] [blame]
epoger@google.comec3ed6a2011-07-28 14:26:00 +00001
2/*
3 * Copyright 2011 Google Inc.
4 *
5 * Use of this source code is governed by a BSD-style license that can be
6 * found in the LICENSE file.
7 */
reed@android.comf523e252009-01-26 23:15:37 +00008#include "SkBenchmark.h"
9#include "SkBitmap.h"
10#include "SkPaint.h"
11#include "SkCanvas.h"
12#include "SkColorPriv.h"
13#include "SkRandom.h"
14#include "SkString.h"
15
reed@android.comf523e252009-01-26 23:15:37 +000016static const char* gConfigName[] = {
17 "ERROR", "a1", "a8", "index8", "565", "4444", "8888"
18};
19
reed@android.comf523e252009-01-26 23:15:37 +000020static int conv6ToByte(int x) {
21 return x * 0xFF / 5;
22}
23
24static int convByteTo6(int x) {
25 return x * 5 / 255;
26}
27
28static uint8_t compute666Index(SkPMColor c) {
29 int r = SkGetPackedR32(c);
30 int g = SkGetPackedG32(c);
31 int b = SkGetPackedB32(c);
rmistry@google.comfbfcd562012-08-23 18:09:54 +000032
reed@android.comf523e252009-01-26 23:15:37 +000033 return convByteTo6(r) * 36 + convByteTo6(g) * 6 + convByteTo6(b);
34}
35
36static void convertToIndex666(const SkBitmap& src, SkBitmap* dst) {
37 SkColorTable* ctable = new SkColorTable(216);
38 SkPMColor* colors = ctable->lockColors();
39 // rrr ggg bbb
40 for (int r = 0; r < 6; r++) {
41 int rr = conv6ToByte(r);
42 for (int g = 0; g < 6; g++) {
43 int gg = conv6ToByte(g);
44 for (int b = 0; b < 6; b++) {
45 int bb = conv6ToByte(b);
46 *colors++ = SkPreMultiplyARGB(0xFF, rr, gg, bb);
47 }
48 }
49 }
50 ctable->unlockColors(true);
51 dst->setConfig(SkBitmap::kIndex8_Config, src.width(), src.height());
52 dst->allocPixels(ctable);
53 ctable->unref();
rmistry@google.comfbfcd562012-08-23 18:09:54 +000054
reed@android.comf523e252009-01-26 23:15:37 +000055 SkAutoLockPixels alps(src);
56 SkAutoLockPixels alpd(*dst);
57
58 for (int y = 0; y < src.height(); y++) {
59 const SkPMColor* srcP = src.getAddr32(0, y);
60 uint8_t* dstP = dst->getAddr8(0, y);
61 for (int x = src.width() - 1; x >= 0; --x) {
62 *dstP++ = compute666Index(*srcP++);
63 }
64 }
65}
66
67/* Variants for bitmaps
rmistry@google.comfbfcd562012-08-23 18:09:54 +000068
reed@android.comf523e252009-01-26 23:15:37 +000069 - src depth (32 w+w/o alpha), 565, 4444, index, a8
70 - paint options: filtering, dither, alpha
71 - matrix options: translate, scale, rotate, persp
72 - tiling: none, repeat, mirror, clamp
rmistry@google.comfbfcd562012-08-23 18:09:54 +000073
reed@android.comf523e252009-01-26 23:15:37 +000074 */
75
76class BitmapBench : public SkBenchmark {
77 SkBitmap fBitmap;
78 SkPaint fPaint;
reed@android.com11ec1862009-10-19 19:01:45 +000079 bool fIsOpaque;
junov@google.com4ee7ae52011-06-30 17:30:49 +000080 bool fForceUpdate; //bitmap marked as dirty before each draw. forces bitmap to be updated on device cache
djsollen@google.comc2532dd2013-04-09 18:06:06 +000081 bool fIsVolatile;
82 SkBitmap::Config fConfig;
reed@android.comf523e252009-01-26 23:15:37 +000083 SkString fName;
reed@google.comef77ec22013-05-29 15:39:54 +000084 enum { BICUBIC_DUR_SCALE = 20 };
85 enum { N = SkBENCHLOOP(15 * BICUBIC_DUR_SCALE) };
djsollen@google.comc2532dd2013-04-09 18:06:06 +000086 enum { W = 128 };
87 enum { H = 128 };
reed@android.comf523e252009-01-26 23:15:37 +000088public:
reed@android.com11ec1862009-10-19 19:01:45 +000089 BitmapBench(void* param, bool isOpaque, SkBitmap::Config c,
reed@google.comef77ec22013-05-29 15:39:54 +000090 bool forceUpdate = false, bool bitmapVolatile = false)
djsollen@google.comc2532dd2013-04-09 18:06:06 +000091 : INHERITED(param)
92 , fIsOpaque(isOpaque)
93 , fForceUpdate(forceUpdate)
djsollen@google.comc2532dd2013-04-09 18:06:06 +000094 , fIsVolatile(bitmapVolatile)
95 , fConfig(c) {
reed@android.comf523e252009-01-26 23:15:37 +000096 }
97
98protected:
99 virtual const char* onGetName() {
100 fName.set("bitmap");
djsollen@google.comc2532dd2013-04-09 18:06:06 +0000101 fName.appendf("_%s%s", gConfigName[fConfig],
reed@android.com11ec1862009-10-19 19:01:45 +0000102 fIsOpaque ? "" : "_A");
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000103 if (fForceUpdate)
junov@google.com4ee7ae52011-06-30 17:30:49 +0000104 fName.append("_update");
djsollen@google.comc2532dd2013-04-09 18:06:06 +0000105 if (fIsVolatile)
junov@google.com4ee7ae52011-06-30 17:30:49 +0000106 fName.append("_volatile");
107
reed@android.comf523e252009-01-26 23:15:37 +0000108 return fName.c_str();
109 }
110
djsollen@google.comc2532dd2013-04-09 18:06:06 +0000111 virtual void onPreDraw() {
112 SkBitmap bm;
113
114 if (SkBitmap::kIndex8_Config == fConfig) {
115 bm.setConfig(SkBitmap::kARGB_8888_Config, W, H);
116 } else {
117 bm.setConfig(fConfig, W, H);
118 }
119
120 bm.allocPixels();
121 bm.eraseColor(fIsOpaque ? SK_ColorBLACK : 0);
122
123 onDrawIntoBitmap(bm);
124
125 if (SkBitmap::kIndex8_Config == fConfig) {
126 convertToIndex666(bm, &fBitmap);
127 } else {
128 fBitmap = bm;
129 }
130
131 if (fBitmap.getColorTable()) {
132 fBitmap.getColorTable()->setIsOpaque(fIsOpaque);
133 }
134 fBitmap.setIsOpaque(fIsOpaque);
135 fBitmap.setIsVolatile(fIsVolatile);
136 }
137
reed@android.comf523e252009-01-26 23:15:37 +0000138 virtual void onDraw(SkCanvas* canvas) {
139 SkIPoint dim = this->getSize();
140 SkRandom rand;
141
142 SkPaint paint(fPaint);
143 this->setupPaint(&paint);
144
145 const SkBitmap& bitmap = fBitmap;
146 const SkScalar x0 = SkIntToScalar(-bitmap.width() / 2);
147 const SkScalar y0 = SkIntToScalar(-bitmap.height() / 2);
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000148
reed@google.comef77ec22013-05-29 15:39:54 +0000149 int count = N;
150#ifdef SK_RELEASE
151 // in DEBUG, N is always 1
humper@google.comb0889472013-07-09 21:37:14 +0000152 if (paint.getFlags() & SkPaint::kHighQualityFilterBitmap_Flag) {
reed@google.comef77ec22013-05-29 15:39:54 +0000153 count /= BICUBIC_DUR_SCALE;
154 }
155#endif
156 for (int i = 0; i < count; i++) {
reed@android.comf523e252009-01-26 23:15:37 +0000157 SkScalar x = x0 + rand.nextUScalar1() * dim.fX;
158 SkScalar y = y0 + rand.nextUScalar1() * dim.fY;
junov@google.com4ee7ae52011-06-30 17:30:49 +0000159
160 if (fForceUpdate)
161 bitmap.notifyPixelsChanged();
162
reed@android.comf523e252009-01-26 23:15:37 +0000163 canvas->drawBitmap(bitmap, x, y, &paint);
164 }
165 }
166
reed@google.comef77ec22013-05-29 15:39:54 +0000167 virtual float onGetDurationScale() SK_OVERRIDE {
168 SkPaint paint;
169 this->setupPaint(&paint);
170#ifdef SK_DEBUG
171 return 1;
172#else
humper@google.comb0889472013-07-09 21:37:14 +0000173 return (paint.getFlags() & SkPaint::kHighQualityFilterBitmap_Flag) ?
reed@google.coma2be2cc2013-05-29 16:19:48 +0000174 (float)BICUBIC_DUR_SCALE : 1;
reed@google.comef77ec22013-05-29 15:39:54 +0000175#endif
176 }
177
djsollen@google.comc2532dd2013-04-09 18:06:06 +0000178 virtual void onDrawIntoBitmap(const SkBitmap& bm) {
179 const int w = bm.width();
180 const int h = bm.height();
181
182 SkCanvas canvas(bm);
183 SkPaint p;
184 p.setAntiAlias(true);
185 p.setColor(SK_ColorRED);
186 canvas.drawCircle(SkIntToScalar(w)/2, SkIntToScalar(h)/2,
187 SkIntToScalar(SkMin32(w, h))*3/8, p);
188
189 SkRect r;
190 r.set(0, 0, SkIntToScalar(w), SkIntToScalar(h));
191 p.setStyle(SkPaint::kStroke_Style);
192 p.setStrokeWidth(SkIntToScalar(4));
193 p.setColor(SK_ColorBLUE);
194 canvas.drawRect(r, p);
195 }
196
reed@android.comf523e252009-01-26 23:15:37 +0000197private:
198 typedef SkBenchmark INHERITED;
199};
200
tomhudson@google.comc3be34d2012-05-15 20:09:33 +0000201/** Explicitly invoke some filter types to improve coverage of acceleration
202 procs. */
203
reed@google.comef77ec22013-05-29 15:39:54 +0000204enum Flags {
205 kScale_Flag = 1 << 0,
206 kRotate_Flag = 1 << 1,
207 kBilerp_Flag = 1 << 2,
208 kBicubic_Flag = 1 << 3,
209};
210
211static bool isBilerp(uint32_t flags) {
212 return (flags & (kBilerp_Flag | kBicubic_Flag)) == (kBilerp_Flag);
213}
214
215static bool isBicubic(uint32_t flags) {
216 return (flags & (kBilerp_Flag | kBicubic_Flag)) == (kBilerp_Flag | kBicubic_Flag);
217}
218
tomhudson@google.comc3be34d2012-05-15 20:09:33 +0000219class FilterBitmapBench : public BitmapBench {
reed@google.comef77ec22013-05-29 15:39:54 +0000220 uint32_t fFlags;
tomhudson@google.comc3be34d2012-05-15 20:09:33 +0000221 SkString fFullName;
222 enum { N = SkBENCHLOOP(300) };
223public:
224 FilterBitmapBench(void* param, bool isOpaque, SkBitmap::Config c,
reed@google.comef77ec22013-05-29 15:39:54 +0000225 bool forceUpdate, bool isVolitile, uint32_t flags)
226 : INHERITED(param, isOpaque, c, forceUpdate, isVolitile)
227 , fFlags(flags) {
tomhudson@google.comc3be34d2012-05-15 20:09:33 +0000228 }
229
230protected:
231 virtual const char* onGetName() {
232 fFullName.set(INHERITED::onGetName());
reed@google.comef77ec22013-05-29 15:39:54 +0000233 if (fFlags & kScale_Flag) {
tomhudson@google.comc3be34d2012-05-15 20:09:33 +0000234 fFullName.append("_scale");
reed@google.comef77ec22013-05-29 15:39:54 +0000235 }
236 if (fFlags & kRotate_Flag) {
tomhudson@google.comc3be34d2012-05-15 20:09:33 +0000237 fFullName.append("_rotate");
reed@google.comef77ec22013-05-29 15:39:54 +0000238 }
239 if (isBilerp(fFlags)) {
240 fFullName.append("_bilerp");
241 } else if (isBicubic(fFlags)) {
242 fFullName.append("_bicubic");
243 }
tomhudson@google.comc3be34d2012-05-15 20:09:33 +0000244
245 return fFullName.c_str();
246 }
247
248 virtual void onDraw(SkCanvas* canvas) {
249 SkISize dim = canvas->getDeviceSize();
reed@google.comef77ec22013-05-29 15:39:54 +0000250 if (fFlags & kScale_Flag) {
tomhudson@google.comc3be34d2012-05-15 20:09:33 +0000251 const SkScalar x = SkIntToScalar(dim.fWidth) / 2;
252 const SkScalar y = SkIntToScalar(dim.fHeight) / 2;
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000253
tomhudson@google.comc3be34d2012-05-15 20:09:33 +0000254 canvas->translate(x, y);
255 // just enough so we can't take the sprite case
256 canvas->scale(SK_Scalar1 * 99/100, SK_Scalar1 * 99/100);
257 canvas->translate(-x, -y);
258 }
reed@google.comef77ec22013-05-29 15:39:54 +0000259 if (fFlags & kRotate_Flag) {
tomhudson@google.comc3be34d2012-05-15 20:09:33 +0000260 const SkScalar x = SkIntToScalar(dim.fWidth) / 2;
261 const SkScalar y = SkIntToScalar(dim.fHeight) / 2;
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000262
tomhudson@google.comc3be34d2012-05-15 20:09:33 +0000263 canvas->translate(x, y);
264 canvas->rotate(SkIntToScalar(35));
265 canvas->translate(-x, -y);
266 }
267
reed@google.comef77ec22013-05-29 15:39:54 +0000268 uint32_t orMask = 0;
humper@google.comb0889472013-07-09 21:37:14 +0000269 uint32_t clearMask = SkPaint::kFilterBitmap_Flag | SkPaint::kHighQualityFilterBitmap_Flag;
reed@google.comef77ec22013-05-29 15:39:54 +0000270 if (fFlags & kBilerp_Flag) {
271 orMask |= SkPaint::kFilterBitmap_Flag;
272 }
273 if (fFlags & kBicubic_Flag) {
humper@google.comb0889472013-07-09 21:37:14 +0000274 orMask |= SkPaint::kHighQualityFilterBitmap_Flag;
reed@google.comef77ec22013-05-29 15:39:54 +0000275 }
276 this->setPaintMasks(orMask, clearMask);
277
tomhudson@google.comc3be34d2012-05-15 20:09:33 +0000278 INHERITED::onDraw(canvas);
279 }
280
281private:
282 typedef BitmapBench INHERITED;
283};
284
djsollen@google.comc2532dd2013-04-09 18:06:06 +0000285/** Verify optimizations that test source alpha values. */
286
287class SourceAlphaBitmapBench : public BitmapBench {
288public:
289 enum SourceAlpha { kOpaque_SourceAlpha, kTransparent_SourceAlpha,
290 kTwoStripes_SourceAlpha, kThreeStripes_SourceAlpha};
291private:
292 SkString fFullName;
293 SourceAlpha fSourceAlpha;
294public:
295 SourceAlphaBitmapBench(void* param, SourceAlpha alpha, SkBitmap::Config c,
reed@google.comef77ec22013-05-29 15:39:54 +0000296 bool forceUpdate = false, bool bitmapVolatile = false)
297 : INHERITED(param, false, c, forceUpdate, bitmapVolatile)
djsollen@google.comc2532dd2013-04-09 18:06:06 +0000298 , fSourceAlpha(alpha) {
299 }
300
301protected:
302 virtual const char* onGetName() {
303 fFullName.set(INHERITED::onGetName());
304
305 if (fSourceAlpha == kOpaque_SourceAlpha) {
306 fFullName.append("_source_opaque");
307 } else if (fSourceAlpha == kTransparent_SourceAlpha) {
308 fFullName.append("_source_transparent");
309 } else if (fSourceAlpha == kTwoStripes_SourceAlpha) {
310 fFullName.append("_source_stripes_two");
311 } else if (fSourceAlpha == kThreeStripes_SourceAlpha) {
312 fFullName.append("_source_stripes_three");
313 }
314
315 return fFullName.c_str();
316 }
317
318 virtual void onDrawIntoBitmap(const SkBitmap& bm) SK_OVERRIDE {
319 const int w = bm.width();
320 const int h = bm.height();
321
322 if (kOpaque_SourceAlpha == fSourceAlpha) {
323 bm.eraseColor(SK_ColorBLACK);
324 } else if (kTransparent_SourceAlpha == fSourceAlpha) {
325 bm.eraseColor(0);
326 } else if (kTwoStripes_SourceAlpha == fSourceAlpha) {
327 bm.eraseColor(0);
328
329 SkCanvas canvas(bm);
330 SkPaint p;
331 p.setAntiAlias(false);
332 p.setStyle(SkPaint::kFill_Style);
333 p.setColor(SK_ColorRED);
334
335 // Draw red vertical stripes on transparent background
336 SkRect r;
337 for (int x = 0; x < w; x+=2)
338 {
339 r.set(SkIntToScalar(x), 0, SkIntToScalar(x+1), SkIntToScalar(h));
340 canvas.drawRect(r, p);
341 }
342
343 } else if (kThreeStripes_SourceAlpha == fSourceAlpha) {
344 bm.eraseColor(0);
345
346 SkCanvas canvas(bm);
347 SkPaint p;
348 p.setAntiAlias(false);
349 p.setStyle(SkPaint::kFill_Style);
350
351 // Draw vertical stripes on transparent background with a pattern
352 // where the first pixel is fully transparent, the next is semi-transparent
353 // and the third is fully opaque.
354 SkRect r;
355 for (int x = 0; x < w; x++)
356 {
357 if (x % 3 == 0) {
358 continue; // Keep transparent
359 } else if (x % 3 == 1) {
360 p.setColor(SkColorSetARGB(127, 127, 127, 127)); // Semi-transparent
361 } else if (x % 3 == 2) {
362 p.setColor(SK_ColorRED); // Opaque
363 }
364 r.set(SkIntToScalar(x), 0, SkIntToScalar(x+1), SkIntToScalar(h));
365 canvas.drawRect(r, p);
366 }
367 }
368 }
369
370private:
371 typedef BitmapBench INHERITED;
372};
reed@google.com2ece2872013-05-28 20:48:14 +0000373
374DEF_BENCH( return new BitmapBench(p, false, SkBitmap::kARGB_8888_Config); )
375DEF_BENCH( return new BitmapBench(p, true, SkBitmap::kARGB_8888_Config); )
376DEF_BENCH( return new BitmapBench(p, true, SkBitmap::kRGB_565_Config); )
reed@google.com2ece2872013-05-28 20:48:14 +0000377DEF_BENCH( return new BitmapBench(p, false, SkBitmap::kIndex8_Config); )
378DEF_BENCH( return new BitmapBench(p, true, SkBitmap::kIndex8_Config); )
379DEF_BENCH( return new BitmapBench(p, true, SkBitmap::kARGB_8888_Config, true, true); )
380DEF_BENCH( return new BitmapBench(p, true, SkBitmap::kARGB_8888_Config, true, false); )
reed@android.comf523e252009-01-26 23:15:37 +0000381
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000382// scale filter -> S32_opaque_D32_filter_DX_{SSE2,SSSE3} and Fact9 is also for S32_D16_filter_DX_SSE2
reed@google.comef77ec22013-05-29 15:39:54 +0000383DEF_BENCH( return new FilterBitmapBench(p, false, SkBitmap::kARGB_8888_Config, false, false, kScale_Flag | kBilerp_Flag); )
384DEF_BENCH( return new FilterBitmapBench(p, true, SkBitmap::kARGB_8888_Config, false, false, kScale_Flag | kBilerp_Flag); )
385DEF_BENCH( return new FilterBitmapBench(p, true, SkBitmap::kARGB_8888_Config, true, true, kScale_Flag | kBilerp_Flag); )
386DEF_BENCH( return new FilterBitmapBench(p, true, SkBitmap::kARGB_8888_Config, true, false, kScale_Flag | kBilerp_Flag); )
tomhudson@google.comc3be34d2012-05-15 20:09:33 +0000387
388// scale rotate filter -> S32_opaque_D32_filter_DXDY_{SSE2,SSSE3}
reed@google.comef77ec22013-05-29 15:39:54 +0000389DEF_BENCH( return new FilterBitmapBench(p, false, SkBitmap::kARGB_8888_Config, false, false, kScale_Flag | kRotate_Flag | kBilerp_Flag); )
390DEF_BENCH( return new FilterBitmapBench(p, true, SkBitmap::kARGB_8888_Config, false, false, kScale_Flag | kRotate_Flag | kBilerp_Flag); )
391DEF_BENCH( return new FilterBitmapBench(p, true, SkBitmap::kARGB_8888_Config, true, true, kScale_Flag | kRotate_Flag | kBilerp_Flag); )
392DEF_BENCH( return new FilterBitmapBench(p, true, SkBitmap::kARGB_8888_Config, true, false, kScale_Flag | kRotate_Flag | kBilerp_Flag); )
393
394DEF_BENCH( return new FilterBitmapBench(p, false, SkBitmap::kARGB_8888_Config, false, false, kScale_Flag | kBilerp_Flag | kBicubic_Flag); )
395DEF_BENCH( return new FilterBitmapBench(p, false, SkBitmap::kARGB_8888_Config, false, false, kScale_Flag | kRotate_Flag | kBilerp_Flag | kBicubic_Flag); )
tomhudson@google.comc3be34d2012-05-15 20:09:33 +0000396
djsollen@google.comc2532dd2013-04-09 18:06:06 +0000397// source alpha tests -> S32A_Opaque_BlitRow32_{arm,neon}
reed@google.com2ece2872013-05-28 20:48:14 +0000398DEF_BENCH( return new SourceAlphaBitmapBench(p, SourceAlphaBitmapBench::kOpaque_SourceAlpha, SkBitmap::kARGB_8888_Config); )
399DEF_BENCH( return new SourceAlphaBitmapBench(p, SourceAlphaBitmapBench::kTransparent_SourceAlpha, SkBitmap::kARGB_8888_Config); )
400DEF_BENCH( return new SourceAlphaBitmapBench(p, SourceAlphaBitmapBench::kTwoStripes_SourceAlpha, SkBitmap::kARGB_8888_Config); )
401DEF_BENCH( return new SourceAlphaBitmapBench(p, SourceAlphaBitmapBench::kThreeStripes_SourceAlpha, SkBitmap::kARGB_8888_Config); )