1#include "SkCanvas.h" 2#include "SkColorPriv.h" 3#include "SkGraphics.h" 4#include "SkImageEncoder.h" 5#include "SkNWayCanvas.h" 6#include "SkPicture.h" 7#include "SkString.h" 8#include "SkTime.h" 9 10#include "SkBenchmark.h" 11 12#ifdef ANDROID 13static void log_error(const char msg[]) { SkDebugf("%s", msg); } 14static void log_progress(const char msg[]) { SkDebugf("%s", msg); } 15#else 16static void log_error(const char msg[]) { fprintf(stderr, "%s", msg); } 17static void log_progress(const char msg[]) { printf("%s", msg); } 18#endif 19 20static void log_error(const SkString& str) { log_error(str.c_str()); } 21static void log_progress(const SkString& str) { log_progress(str.c_str()); } 22 23/////////////////////////////////////////////////////////////////////////////// 24 25static void erase(SkBitmap& bm) { 26 if (bm.config() == SkBitmap::kA8_Config) { 27 bm.eraseColor(0); 28 } else { 29 bm.eraseColor(SK_ColorWHITE); 30 } 31} 32 33static bool equal(const SkBitmap& bm1, const SkBitmap& bm2) { 34 if (bm1.width() != bm2.width() || 35 bm1.height() != bm2.height() || 36 bm1.config() != bm2.config()) { 37 return false; 38 } 39 40 size_t pixelBytes = bm1.width() * bm1.bytesPerPixel(); 41 for (int y = 0; y < bm1.height(); y++) { 42 if (memcmp(bm1.getAddr(0, y), bm2.getAddr(0, y), pixelBytes)) { 43 return false; 44 } 45 } 46 47 return true; 48} 49 50class Iter { 51public: 52 Iter(void* param) { 53 fBench = BenchRegistry::Head(); 54 fParam = param; 55 } 56 57 SkBenchmark* next() { 58 if (fBench) { 59 BenchRegistry::Factory f = fBench->factory(); 60 fBench = fBench->next(); 61 return f(fParam); 62 } 63 return NULL; 64 } 65 66private: 67 const BenchRegistry* fBench; 68 void* fParam; 69}; 70 71static void make_filename(const char name[], SkString* path) { 72 path->set(name); 73 for (int i = 0; name[i]; i++) { 74 switch (name[i]) { 75 case '/': 76 case '\\': 77 case ' ': 78 case ':': 79 path->writable_str()[i] = '-'; 80 break; 81 default: 82 break; 83 } 84 } 85} 86 87static void saveFile(const char name[], const char config[], const char dir[], 88 const SkBitmap& bm) { 89 SkBitmap copy; 90 if (!bm.copyTo(©, SkBitmap::kARGB_8888_Config)) { 91 return; 92 } 93 94 if (bm.config() == SkBitmap::kA8_Config) { 95 // turn alpha into gray-scale 96 size_t size = copy.getSize() >> 2; 97 SkPMColor* p = copy.getAddr32(0, 0); 98 for (size_t i = 0; i < size; i++) { 99 int c = (*p >> SK_A32_SHIFT) & 0xFF; 100 c = 255 - c; 101 c |= (c << 24) | (c << 16) | (c << 8); 102 *p++ = c | (SK_A32_MASK << SK_A32_SHIFT); 103 } 104 } 105 106 SkString str; 107 make_filename(name, &str); 108 str.appendf("_%s.png", config); 109 str.prepend(dir); 110 ::remove(str.c_str()); 111 SkImageEncoder::EncodeFile(str.c_str(), copy, SkImageEncoder::kPNG_Type, 112 100); 113} 114 115static void performClip(SkCanvas* canvas, int w, int h) { 116 SkRect r; 117 118 r.set(SkIntToScalar(10), SkIntToScalar(10), 119 SkIntToScalar(w*2/3), SkIntToScalar(h*2/3)); 120 canvas->clipRect(r, SkRegion::kIntersect_Op); 121 122 r.set(SkIntToScalar(w/3), SkIntToScalar(h/3), 123 SkIntToScalar(w-10), SkIntToScalar(h-10)); 124 canvas->clipRect(r, SkRegion::kXOR_Op); 125} 126 127static void performRotate(SkCanvas* canvas, int w, int h) { 128 const SkScalar x = SkIntToScalar(w) / 2; 129 const SkScalar y = SkIntToScalar(h) / 2; 130 131 canvas->translate(x, y); 132 canvas->rotate(SkIntToScalar(35)); 133 canvas->translate(-x, -y); 134} 135 136static void performScale(SkCanvas* canvas, int w, int h) { 137 const SkScalar x = SkIntToScalar(w) / 2; 138 const SkScalar y = SkIntToScalar(h) / 2; 139 140 canvas->translate(x, y); 141 // just enough so we can't take the sprite case 142 canvas->scale(SK_Scalar1 * 99/100, SK_Scalar1 * 99/100); 143 canvas->translate(-x, -y); 144} 145 146static void compare_pict_to_bitmap(SkPicture* pict, const SkBitmap& bm) { 147 SkBitmap bm2; 148 149 bm2.setConfig(bm.config(), bm.width(), bm.height()); 150 bm2.allocPixels(); 151 erase(bm2); 152 153 SkCanvas canvas(bm2); 154 canvas.drawPicture(*pict); 155 156 if (!equal(bm, bm2)) { 157 SkDebugf("----- compare_pict_to_bitmap failed\n"); 158 } 159} 160 161static bool parse_bool_arg(char * const* argv, char* const* stop, bool* var) { 162 if (argv < stop) { 163 *var = atoi(*argv) != 0; 164 return true; 165 } 166 return false; 167} 168 169static const struct { 170 SkBitmap::Config fConfig; 171 const char* fName; 172} gConfigs[] = { 173 { SkBitmap::kARGB_8888_Config, "8888" }, 174 { SkBitmap::kRGB_565_Config, "565", }, 175#if 0 176 { SkBitmap::kARGB_4444_Config, "4444", }, 177 { SkBitmap::kA8_Config, "A8", } 178#endif 179}; 180 181static int findConfig(const char config[]) { 182 for (size_t i = 0; i < SK_ARRAY_COUNT(gConfigs); i++) { 183 if (!strcmp(config, gConfigs[i].fName)) { 184 return i; 185 } 186 } 187 return -1; 188} 189 190int main (int argc, char * const argv[]) { 191 SkAutoGraphics ag; 192 193 SkTDict<const char*> defineDict(1024); 194 int repeatDraw = 1; 195 int forceAlpha = 0xFF; 196 bool forceAA = true; 197 bool forceFilter = false; 198 SkTriState::State forceDither = SkTriState::kDefault; 199 bool doScale = false; 200 bool doRotate = false; 201 bool doClip = false; 202 bool doPict = false; 203 const char* matchStr = NULL; 204 205 SkString outDir; 206 SkBitmap::Config outConfig = SkBitmap::kNo_Config; 207 const char* configName = ""; 208 int configCount = SK_ARRAY_COUNT(gConfigs); 209 210 char* const* stop = argv + argc; 211 for (++argv; argv < stop; ++argv) { 212 if (strcmp(*argv, "-o") == 0) { 213 argv++; 214 if (argv < stop && **argv) { 215 outDir.set(*argv); 216 if (outDir.c_str()[outDir.size() - 1] != '/') { 217 outDir.append("/"); 218 } 219 } 220 } else if (strcmp(*argv, "-pict") == 0) { 221 doPict = true; 222 } else if (strcmp(*argv, "-repeat") == 0) { 223 argv++; 224 if (argv < stop) { 225 repeatDraw = atoi(*argv); 226 if (repeatDraw < 1) { 227 repeatDraw = 1; 228 } 229 } else { 230 log_error("missing arg for -repeat\n"); 231 return -1; 232 } 233 } else if (!strcmp(*argv, "-rotate")) { 234 doRotate = true; 235 } else if (!strcmp(*argv, "-scale")) { 236 doScale = true; 237 } else if (!strcmp(*argv, "-clip")) { 238 doClip = true; 239 } else if (strcmp(*argv, "-forceAA") == 0) { 240 if (!parse_bool_arg(++argv, stop, &forceAA)) { 241 log_error("missing arg for -forceAA\n"); 242 return -1; 243 } 244 } else if (strcmp(*argv, "-forceFilter") == 0) { 245 if (!parse_bool_arg(++argv, stop, &forceFilter)) { 246 log_error("missing arg for -forceFilter\n"); 247 return -1; 248 } 249 } else if (strcmp(*argv, "-forceDither") == 0) { 250 bool tmp; 251 if (!parse_bool_arg(++argv, stop, &tmp)) { 252 log_error("missing arg for -forceDither\n"); 253 return -1; 254 } 255 forceDither = tmp ? SkTriState::kTrue : SkTriState::kFalse; 256 } else if (strcmp(*argv, "-forceBlend") == 0) { 257 bool wantAlpha = false; 258 if (!parse_bool_arg(++argv, stop, &wantAlpha)) { 259 log_error("missing arg for -forceBlend\n"); 260 return -1; 261 } 262 forceAlpha = wantAlpha ? 0x80 : 0xFF; 263 } else if (strcmp(*argv, "-match") == 0) { 264 argv++; 265 if (argv < stop) { 266 matchStr = *argv; 267 } else { 268 log_error("missing arg for -match\n"); 269 return -1; 270 } 271 } else if (strcmp(*argv, "-config") == 0) { 272 argv++; 273 if (argv < stop) { 274 int index = findConfig(*argv); 275 if (index >= 0) { 276 outConfig = gConfigs[index].fConfig; 277 configName = gConfigs[index].fName; 278 configCount = 1; 279 } else { 280 SkString str; 281 str.printf("unrecognized config %s\n", *argv); 282 log_error(str); 283 return -1; 284 } 285 } else { 286 log_error("missing arg for -config\n"); 287 return -1; 288 } 289 } else if (strlen(*argv) > 2 && strncmp(*argv, "-D", 2) == 0) { 290 argv++; 291 if (argv < stop) { 292 defineDict.set(argv[-1] + 2, *argv); 293 } else { 294 log_error("incomplete '-Dfoo bar' definition\n"); 295 return -1; 296 } 297 } else { 298 SkString str; 299 str.printf("unrecognized arg %s\n", *argv); 300 log_error(str); 301 return -1; 302 } 303 } 304 305 Iter iter(&defineDict); 306 SkBenchmark* bench; 307 while ((bench = iter.next()) != NULL) { 308 SkIPoint dim = bench->getSize(); 309 if (dim.fX <= 0 || dim.fY <= 0) { 310 continue; 311 } 312 313 bench->setForceAlpha(forceAlpha); 314 bench->setForceAA(forceAA); 315 bench->setForceFilter(forceFilter); 316 bench->setDither(forceDither); 317 318 // only run benchmarks if their name contains matchStr 319 if (matchStr && strstr(bench->getName(), matchStr) == NULL) { 320 continue; 321 } 322 323 { 324 SkString str; 325 str.printf("running bench [%d %d] %16s", dim.fX, dim.fY, 326 bench->getName()); 327 log_progress(str); 328 } 329 330 for (int configIndex = 0; configIndex < configCount; configIndex++) { 331 if (configCount > 1) { 332 outConfig = gConfigs[configIndex].fConfig; 333 configName = gConfigs[configIndex].fName; 334 } 335 336 SkBitmap bm; 337 bm.setConfig(outConfig, dim.fX, dim.fY); 338 bm.allocPixels(); 339 erase(bm); 340 341 SkCanvas canvas(bm); 342 343 if (doClip) { 344 performClip(&canvas, dim.fX, dim.fY); 345 } 346 if (doScale) { 347 performScale(&canvas, dim.fX, dim.fY); 348 } 349 if (doRotate) { 350 performRotate(&canvas, dim.fX, dim.fY); 351 } 352 353 SkMSec now = SkTime::GetMSecs(); 354 for (int i = 0; i < repeatDraw; i++) { 355 SkCanvas* c = &canvas; 356 357 SkNWayCanvas nway; 358 SkPicture* pict = NULL; 359 if (doPict) { 360 pict = new SkPicture; 361 nway.addCanvas(pict->beginRecording(bm.width(), bm.height())); 362 nway.addCanvas(&canvas); 363 c = &nway; 364 } 365 366 SkAutoCanvasRestore acr(c, true); 367 bench->draw(c); 368 369 if (pict) { 370 compare_pict_to_bitmap(pict, bm); 371 pict->unref(); 372 } 373 } 374 if (repeatDraw > 1) { 375 double duration = SkTime::GetMSecs() - now; 376 SkString str; 377 str.printf(" %4s: msecs = %7.2f, fps = %7.2f", configName, 378 duration / repeatDraw, repeatDraw * 1000.0 / duration); 379 log_progress(str); 380 } 381 if (outDir.size() > 0) { 382 saveFile(bench->getName(), configName, outDir.c_str(), bm); 383 } 384 } 385 log_progress("\n"); 386 } 387 388 return 0; 389} 390