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