1 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 */ 8 #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 16 static const char* gConfigName[] = { 17 "ERROR", "a1", "a8", "index8", "565", "4444", "8888" 18 }; 19 20 static int conv6ToByte(int x) { 21 return x * 0xFF / 5; 22 } 23 24 static int convByteTo6(int x) { 25 return x * 5 / 255; 26 } 27 28 static uint8_t compute666Index(SkPMColor c) { 29 int r = SkGetPackedR32(c); 30 int g = SkGetPackedG32(c); 31 int b = SkGetPackedB32(c); 32 33 return convByteTo6(r) * 36 + convByteTo6(g) * 6 + convByteTo6(b); 34 } 35 36 static 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(); 54 55 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 68 69 - 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 73 74 */ 75 76 class BitmapBench : public SkBenchmark { 77 SkBitmap fBitmap; 78 SkPaint fPaint; 79 bool fIsOpaque; 80 bool fForceUpdate; //bitmap marked as dirty before each draw. forces bitmap to be updated on device cache 81 bool fIsVolatile; 82 SkBitmap::Config fConfig; 83 SkString fName; 84 enum { BICUBIC_DUR_SCALE = 20 }; 85 enum { N = SkBENCHLOOP(15 * BICUBIC_DUR_SCALE) }; 86 enum { W = 128 }; 87 enum { H = 128 }; 88 public: 89 BitmapBench(void* param, bool isOpaque, SkBitmap::Config c, 90 bool forceUpdate = false, bool bitmapVolatile = false) 91 : INHERITED(param) 92 , fIsOpaque(isOpaque) 93 , fForceUpdate(forceUpdate) 94 , fIsVolatile(bitmapVolatile) 95 , fConfig(c) { 96 } 97 98 protected: 99 virtual const char* onGetName() { 100 fName.set("bitmap"); 101 fName.appendf("_%s%s", gConfigName[fConfig], 102 fIsOpaque ? "" : "_A"); 103 if (fForceUpdate) 104 fName.append("_update"); 105 if (fIsVolatile) 106 fName.append("_volatile"); 107 108 return fName.c_str(); 109 } 110 111 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 138 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); 148 149 int count = N; 150 #ifdef SK_RELEASE 151 // in DEBUG, N is always 1 152 if (SkPaint::kHigh_FilterLevel == paint.getFilterLevel()) { 153 count /= BICUBIC_DUR_SCALE; 154 } 155 #endif 156 for (int i = 0; i < count; i++) { 157 SkScalar x = x0 + rand.nextUScalar1() * dim.fX; 158 SkScalar y = y0 + rand.nextUScalar1() * dim.fY; 159 160 if (fForceUpdate) 161 bitmap.notifyPixelsChanged(); 162 163 canvas->drawBitmap(bitmap, x, y, &paint); 164 } 165 } 166 167 virtual float onGetDurationScale() SK_OVERRIDE { 168 SkPaint paint; 169 this->setupPaint(&paint); 170 #ifdef SK_DEBUG 171 return 1; 172 #else 173 return SkPaint::kHigh_FilterLevel == paint.getFilterLevel() ? 174 (float)BICUBIC_DUR_SCALE : 1; 175 #endif 176 } 177 178 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 197 private: 198 typedef SkBenchmark INHERITED; 199 }; 200 201 /** Explicitly invoke some filter types to improve coverage of acceleration 202 procs. */ 203 204 enum Flags { 205 kScale_Flag = 1 << 0, 206 kRotate_Flag = 1 << 1, 207 kBilerp_Flag = 1 << 2, 208 kBicubic_Flag = 1 << 3, 209 }; 210 211 static bool isBilerp(uint32_t flags) { 212 return (flags & (kBilerp_Flag | kBicubic_Flag)) == (kBilerp_Flag); 213 } 214 215 static bool isBicubic(uint32_t flags) { 216 return (flags & (kBilerp_Flag | kBicubic_Flag)) == (kBilerp_Flag | kBicubic_Flag); 217 } 218 219 class FilterBitmapBench : public BitmapBench { 220 uint32_t fFlags; 221 SkString fFullName; 222 enum { N = SkBENCHLOOP(300) }; 223 public: 224 FilterBitmapBench(void* param, bool isOpaque, SkBitmap::Config c, 225 bool forceUpdate, bool isVolitile, uint32_t flags) 226 : INHERITED(param, isOpaque, c, forceUpdate, isVolitile) 227 , fFlags(flags) { 228 } 229 230 protected: 231 virtual const char* onGetName() { 232 fFullName.set(INHERITED::onGetName()); 233 if (fFlags & kScale_Flag) { 234 fFullName.append("_scale"); 235 } 236 if (fFlags & kRotate_Flag) { 237 fFullName.append("_rotate"); 238 } 239 if (isBilerp(fFlags)) { 240 fFullName.append("_bilerp"); 241 } else if (isBicubic(fFlags)) { 242 fFullName.append("_bicubic"); 243 } 244 245 return fFullName.c_str(); 246 } 247 248 virtual void onDraw(SkCanvas* canvas) { 249 SkISize dim = canvas->getDeviceSize(); 250 if (fFlags & kScale_Flag) { 251 const SkScalar x = SkIntToScalar(dim.fWidth) / 2; 252 const SkScalar y = SkIntToScalar(dim.fHeight) / 2; 253 254 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 } 259 if (fFlags & kRotate_Flag) { 260 const SkScalar x = SkIntToScalar(dim.fWidth) / 2; 261 const SkScalar y = SkIntToScalar(dim.fHeight) / 2; 262 263 canvas->translate(x, y); 264 canvas->rotate(SkIntToScalar(35)); 265 canvas->translate(-x, -y); 266 } 267 INHERITED::onDraw(canvas); 268 } 269 270 virtual void setupPaint(SkPaint* paint) SK_OVERRIDE { 271 this->INHERITED::setupPaint(paint); 272 273 int index = 0; 274 if (fFlags & kBilerp_Flag) { 275 index |= 1; 276 } 277 if (fFlags & kBicubic_Flag) { 278 index |= 2; 279 } 280 static const SkPaint::FilterLevel gLevels[] = { 281 SkPaint::kNone_FilterLevel, 282 SkPaint::kLow_FilterLevel, 283 SkPaint::kMedium_FilterLevel, 284 SkPaint::kHigh_FilterLevel 285 }; 286 paint->setFilterLevel(gLevels[index]); 287 } 288 289 private: 290 typedef BitmapBench INHERITED; 291 }; 292 293 /** Verify optimizations that test source alpha values. */ 294 295 class SourceAlphaBitmapBench : public BitmapBench { 296 public: 297 enum SourceAlpha { kOpaque_SourceAlpha, kTransparent_SourceAlpha, 298 kTwoStripes_SourceAlpha, kThreeStripes_SourceAlpha}; 299 private: 300 SkString fFullName; 301 SourceAlpha fSourceAlpha; 302 public: 303 SourceAlphaBitmapBench(void* param, SourceAlpha alpha, SkBitmap::Config c, 304 bool forceUpdate = false, bool bitmapVolatile = false) 305 : INHERITED(param, false, c, forceUpdate, bitmapVolatile) 306 , fSourceAlpha(alpha) { 307 } 308 309 protected: 310 virtual const char* onGetName() { 311 fFullName.set(INHERITED::onGetName()); 312 313 if (fSourceAlpha == kOpaque_SourceAlpha) { 314 fFullName.append("_source_opaque"); 315 } else if (fSourceAlpha == kTransparent_SourceAlpha) { 316 fFullName.append("_source_transparent"); 317 } else if (fSourceAlpha == kTwoStripes_SourceAlpha) { 318 fFullName.append("_source_stripes_two"); 319 } else if (fSourceAlpha == kThreeStripes_SourceAlpha) { 320 fFullName.append("_source_stripes_three"); 321 } 322 323 return fFullName.c_str(); 324 } 325 326 virtual void onDrawIntoBitmap(const SkBitmap& bm) SK_OVERRIDE { 327 const int w = bm.width(); 328 const int h = bm.height(); 329 330 if (kOpaque_SourceAlpha == fSourceAlpha) { 331 bm.eraseColor(SK_ColorBLACK); 332 } else if (kTransparent_SourceAlpha == fSourceAlpha) { 333 bm.eraseColor(0); 334 } else if (kTwoStripes_SourceAlpha == fSourceAlpha) { 335 bm.eraseColor(0); 336 337 SkCanvas canvas(bm); 338 SkPaint p; 339 p.setAntiAlias(false); 340 p.setStyle(SkPaint::kFill_Style); 341 p.setColor(SK_ColorRED); 342 343 // Draw red vertical stripes on transparent background 344 SkRect r; 345 for (int x = 0; x < w; x+=2) 346 { 347 r.set(SkIntToScalar(x), 0, SkIntToScalar(x+1), SkIntToScalar(h)); 348 canvas.drawRect(r, p); 349 } 350 351 } else if (kThreeStripes_SourceAlpha == fSourceAlpha) { 352 bm.eraseColor(0); 353 354 SkCanvas canvas(bm); 355 SkPaint p; 356 p.setAntiAlias(false); 357 p.setStyle(SkPaint::kFill_Style); 358 359 // Draw vertical stripes on transparent background with a pattern 360 // where the first pixel is fully transparent, the next is semi-transparent 361 // and the third is fully opaque. 362 SkRect r; 363 for (int x = 0; x < w; x++) 364 { 365 if (x % 3 == 0) { 366 continue; // Keep transparent 367 } else if (x % 3 == 1) { 368 p.setColor(SkColorSetARGB(127, 127, 127, 127)); // Semi-transparent 369 } else if (x % 3 == 2) { 370 p.setColor(SK_ColorRED); // Opaque 371 } 372 r.set(SkIntToScalar(x), 0, SkIntToScalar(x+1), SkIntToScalar(h)); 373 canvas.drawRect(r, p); 374 } 375 } 376 } 377 378 private: 379 typedef BitmapBench INHERITED; 380 }; 381 382 DEF_BENCH( return new BitmapBench(p, false, SkBitmap::kARGB_8888_Config); ) 383 DEF_BENCH( return new BitmapBench(p, true, SkBitmap::kARGB_8888_Config); ) 384 DEF_BENCH( return new BitmapBench(p, true, SkBitmap::kRGB_565_Config); ) 385 DEF_BENCH( return new BitmapBench(p, false, SkBitmap::kIndex8_Config); ) 386 DEF_BENCH( return new BitmapBench(p, true, SkBitmap::kIndex8_Config); ) 387 DEF_BENCH( return new BitmapBench(p, true, SkBitmap::kARGB_8888_Config, true, true); ) 388 DEF_BENCH( return new BitmapBench(p, true, SkBitmap::kARGB_8888_Config, true, false); ) 389 390 // scale filter -> S32_opaque_D32_filter_DX_{SSE2,SSSE3} and Fact9 is also for S32_D16_filter_DX_SSE2 391 DEF_BENCH( return new FilterBitmapBench(p, false, SkBitmap::kARGB_8888_Config, false, false, kScale_Flag | kBilerp_Flag); ) 392 DEF_BENCH( return new FilterBitmapBench(p, true, SkBitmap::kARGB_8888_Config, false, false, kScale_Flag | kBilerp_Flag); ) 393 DEF_BENCH( return new FilterBitmapBench(p, true, SkBitmap::kARGB_8888_Config, true, true, kScale_Flag | kBilerp_Flag); ) 394 DEF_BENCH( return new FilterBitmapBench(p, true, SkBitmap::kARGB_8888_Config, true, false, kScale_Flag | kBilerp_Flag); ) 395 396 // scale rotate filter -> S32_opaque_D32_filter_DXDY_{SSE2,SSSE3} 397 DEF_BENCH( return new FilterBitmapBench(p, false, SkBitmap::kARGB_8888_Config, false, false, kScale_Flag | kRotate_Flag | kBilerp_Flag); ) 398 DEF_BENCH( return new FilterBitmapBench(p, true, SkBitmap::kARGB_8888_Config, false, false, kScale_Flag | kRotate_Flag | kBilerp_Flag); ) 399 DEF_BENCH( return new FilterBitmapBench(p, true, SkBitmap::kARGB_8888_Config, true, true, kScale_Flag | kRotate_Flag | kBilerp_Flag); ) 400 DEF_BENCH( return new FilterBitmapBench(p, true, SkBitmap::kARGB_8888_Config, true, false, kScale_Flag | kRotate_Flag | kBilerp_Flag); ) 401 402 DEF_BENCH( return new FilterBitmapBench(p, false, SkBitmap::kARGB_8888_Config, false, false, kScale_Flag | kBilerp_Flag | kBicubic_Flag); ) 403 DEF_BENCH( return new FilterBitmapBench(p, false, SkBitmap::kARGB_8888_Config, false, false, kScale_Flag | kRotate_Flag | kBilerp_Flag | kBicubic_Flag); ) 404 405 // source alpha tests -> S32A_Opaque_BlitRow32_{arm,neon} 406 DEF_BENCH( return new SourceAlphaBitmapBench(p, SourceAlphaBitmapBench::kOpaque_SourceAlpha, SkBitmap::kARGB_8888_Config); ) 407 DEF_BENCH( return new SourceAlphaBitmapBench(p, SourceAlphaBitmapBench::kTransparent_SourceAlpha, SkBitmap::kARGB_8888_Config); ) 408 DEF_BENCH( return new SourceAlphaBitmapBench(p, SourceAlphaBitmapBench::kTwoStripes_SourceAlpha, SkBitmap::kARGB_8888_Config); ) 409 DEF_BENCH( return new SourceAlphaBitmapBench(p, SourceAlphaBitmapBench::kThreeStripes_SourceAlpha, SkBitmap::kARGB_8888_Config); ) 410