1 /* 2 * Copyright 2012 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 "CopyTilesRenderer.h" 9 #include "SkBitmap.h" 10 #include "SkCanvas.h" 11 #include "SkDevice.h" 12 #include "SkGraphics.h" 13 #include "SkImageDecoder.h" 14 #include "SkImageEncoder.h" 15 #include "SkMath.h" 16 #include "SkOSFile.h" 17 #include "SkPicture.h" 18 #include "SkStream.h" 19 #include "SkString.h" 20 #include "SkTArray.h" 21 #include "PictureRenderer.h" 22 #include "picture_utils.h" 23 24 static void usage(const char* argv0) { 25 SkDebugf("SkPicture rendering tool\n"); 26 SkDebugf("\n" 27 "Usage: \n" 28 " %s <input>... \n" 29 " [-w <outputDir>]\n" 30 " [--mode pow2tile minWidth height | copyTile width height | simple\n" 31 " | tile width height]\n" 32 " [--pipe]\n" 33 " [--bbh bbhType]\n" 34 " [--multi count]\n" 35 " [--validate [--maxComponentDiff n]]\n" 36 " [--writeWholeImage]\n" 37 " [--clone n]\n" 38 " [--viewport width height][--scale sf]\n" 39 " [--device bitmap" 40 #if SK_SUPPORT_GPU 41 " | gpu" 42 #endif 43 "]" 44 , argv0); 45 SkDebugf("\n\n"); 46 SkDebugf( 47 " input: A list of directories and files to use as input. Files are\n" 48 " expected to have the .skp extension.\n\n"); 49 SkDebugf( 50 " outputDir: directory to write the rendered images.\n\n"); 51 SkDebugf( 52 " --mode pow2tile minWidth height | copyTile width height | simple\n" 53 " | tile width height | rerecord: Run in the corresponding mode.\n" 54 " Default is simple.\n"); 55 SkDebugf( 56 " pow2tile minWidth height, Creates tiles with widths\n" 57 " that are all a power of two\n" 58 " such that they minimize the\n" 59 " amount of wasted tile space.\n" 60 " minWidth is the minimum width\n" 61 " of these tiles and must be a\n" 62 " power of two. A simple render\n" 63 " is done with these tiles.\n"); 64 SkDebugf( 65 " simple, Render using the default rendering method.\n" 66 " rerecord, Record the picture as a new skp, with the bitmaps PNG encoded.\n" 67 ); 68 SkDebugf( 69 " tile width height, Do a simple render using tiles\n" 70 " with the given dimensions.\n" 71 " copyTile width height, Draw the picture, then copy it into tiles.\n" 72 " Does not support percentages.\n" 73 " If the picture is large enough, breaks it into\n" 74 " larger tiles (and draws the picture once per\n" 75 " larger tile) to avoid creating a large canvas.\n" 76 " Add --tiles x y to specify the number of tiles\n" 77 " per larger tile in the x and y direction.\n" 78 ); 79 SkDebugf("\n"); 80 SkDebugf( 81 " --multi count : Set the number of threads for multi threaded drawing. Must be greater\n" 82 " than 1. Only works with tiled rendering.\n" 83 " --viewport width height : Set the viewport.\n" 84 " --scale sf : Scale drawing by sf.\n" 85 " --pipe: Benchmark SkGPipe rendering. Currently incompatible with \"mode\".\n"); 86 SkDebugf( 87 " --validate: Verify that the rendered image contains the same pixels as " 88 "the picture rendered in simple mode.\n" 89 " --maxComponentDiff: maximum diff on a component. Default is 256, " 90 "which means we report but we do not generate an error.\n" 91 " --writeWholeImage: In tile mode, write the entire rendered image to a " 92 "file, instead of an image for each tile.\n"); 93 SkDebugf( 94 " --clone n: Clone the picture n times before rendering.\n"); 95 SkDebugf( 96 " --bbh bbhType [width height]: Set the bounding box hierarchy type to\n" 97 " be used. Accepted values are: none, rtree, grid. Default\n" 98 " value is none. Not compatible with --pipe. With value\n" 99 " 'grid', width and height must be specified. 'grid' can\n" 100 " only be used with modes tile, record, and\n" 101 " playbackCreation."); 102 SkDebugf( 103 " --device bitmap" 104 #if SK_SUPPORT_GPU 105 " | gpu" 106 #endif 107 ": Use the corresponding device. Default is bitmap.\n"); 108 SkDebugf( 109 " bitmap, Render to a bitmap.\n"); 110 #if SK_SUPPORT_GPU 111 SkDebugf( 112 " gpu, Render to the GPU.\n"); 113 #endif 114 } 115 116 static void make_output_filepath(SkString* path, const SkString& dir, 117 const SkString& name) { 118 sk_tools::make_filepath(path, dir, name); 119 // Remove ".skp" 120 path->remove(path->size() - 4, 4); 121 } 122 123 static bool render_picture(const SkString& inputPath, const SkString* outputDir, 124 sk_tools::PictureRenderer& renderer, 125 SkBitmap** out, 126 int clones) { 127 SkString inputFilename; 128 sk_tools::get_basename(&inputFilename, inputPath); 129 130 SkFILEStream inputStream; 131 inputStream.setPath(inputPath.c_str()); 132 if (!inputStream.isValid()) { 133 SkDebugf("Could not open file %s\n", inputPath.c_str()); 134 return false; 135 } 136 137 bool success = false; 138 SkPicture* picture = SkNEW_ARGS(SkPicture, 139 (&inputStream, &success, &SkImageDecoder::DecodeStream)); 140 if (!success) { 141 SkDebugf("Could not read an SkPicture from %s\n", inputPath.c_str()); 142 return false; 143 } 144 145 for (int i = 0; i < clones; ++i) { 146 SkPicture* clone = picture->clone(); 147 SkDELETE(picture); 148 picture = clone; 149 } 150 151 SkDebugf("drawing... [%i %i] %s\n", picture->width(), picture->height(), 152 inputPath.c_str()); 153 154 renderer.init(picture); 155 renderer.setup(); 156 157 SkString* outputPath = NULL; 158 if (NULL != outputDir) { 159 outputPath = SkNEW(SkString); 160 make_output_filepath(outputPath, *outputDir, inputFilename); 161 } 162 163 success = renderer.render(outputPath, out); 164 if (outputPath) { 165 if (!success) { 166 SkDebugf("Could not write to file %s\n", outputPath->c_str()); 167 } 168 SkDELETE(outputPath); 169 } 170 171 renderer.end(); 172 173 SkDELETE(picture); 174 return success; 175 } 176 177 static inline int getByte(uint32_t value, int index) { 178 SkASSERT(0 <= index && index < 4); 179 return (value >> (index * 8)) & 0xFF; 180 } 181 182 static int MaxByteDiff(uint32_t v1, uint32_t v2) { 183 return SkMax32(SkMax32(abs(getByte(v1, 0) - getByte(v2, 0)), abs(getByte(v1, 1) - getByte(v2, 1))), 184 SkMax32(abs(getByte(v1, 2) - getByte(v2, 2)), abs(getByte(v1, 3) - getByte(v2, 3)))); 185 } 186 187 static bool render_picture(const SkString& inputPath, const SkString* outputDir, 188 sk_tools::PictureRenderer& renderer, 189 bool validate, int maxComponentDiff, 190 bool writeWholeImage, 191 int clones) { 192 int diffs[256] = {0}; 193 SkBitmap* bitmap = NULL; 194 bool success = render_picture(inputPath, 195 writeWholeImage ? NULL : outputDir, 196 renderer, 197 validate || writeWholeImage ? &bitmap : NULL, clones); 198 199 if (!success || ((validate || writeWholeImage) && bitmap == NULL)) { 200 SkDebugf("Failed to draw the picture.\n"); 201 SkDELETE(bitmap); 202 return false; 203 } 204 205 if (validate) { 206 SkBitmap* referenceBitmap = NULL; 207 sk_tools::SimplePictureRenderer referenceRenderer; 208 success = render_picture(inputPath, NULL, referenceRenderer, 209 &referenceBitmap, 0); 210 211 if (!success || !referenceBitmap) { 212 SkDebugf("Failed to draw the reference picture.\n"); 213 SkDELETE(bitmap); 214 SkDELETE(referenceBitmap); 215 return false; 216 } 217 218 if (success && (bitmap->width() != referenceBitmap->width())) { 219 SkDebugf("Expected image width: %i, actual image width %i.\n", 220 referenceBitmap->width(), bitmap->width()); 221 SkDELETE(bitmap); 222 SkDELETE(referenceBitmap); 223 return false; 224 } 225 if (success && (bitmap->height() != referenceBitmap->height())) { 226 SkDebugf("Expected image height: %i, actual image height %i", 227 referenceBitmap->height(), bitmap->height()); 228 SkDELETE(bitmap); 229 SkDELETE(referenceBitmap); 230 return false; 231 } 232 233 for (int y = 0; success && y < bitmap->height(); y++) { 234 for (int x = 0; success && x < bitmap->width(); x++) { 235 int diff = MaxByteDiff(*referenceBitmap->getAddr32(x, y), 236 *bitmap->getAddr32(x, y)); 237 SkASSERT(diff >= 0 && diff <= 255); 238 diffs[diff]++; 239 240 if (diff > maxComponentDiff) { 241 SkDebugf("Expected pixel at (%i %i) exceedds maximum " 242 "component diff of %i: 0x%x, actual 0x%x\n", 243 x, y, maxComponentDiff, 244 *referenceBitmap->getAddr32(x, y), 245 *bitmap->getAddr32(x, y)); 246 SkDELETE(bitmap); 247 SkDELETE(referenceBitmap); 248 return false; 249 } 250 } 251 } 252 SkDELETE(referenceBitmap); 253 254 for (int i = 1; i <= 255; ++i) { 255 if(diffs[i] > 0) { 256 SkDebugf("Number of pixels with max diff of %i is %i\n", i, diffs[i]); 257 } 258 } 259 } 260 261 if (writeWholeImage) { 262 sk_tools::force_all_opaque(*bitmap); 263 if (NULL != outputDir && writeWholeImage) { 264 SkString inputFilename; 265 sk_tools::get_basename(&inputFilename, inputPath); 266 SkString outputPath; 267 make_output_filepath(&outputPath, *outputDir, inputFilename); 268 outputPath.append(".png"); 269 if (!SkImageEncoder::EncodeFile(outputPath.c_str(), *bitmap, 270 SkImageEncoder::kPNG_Type, 100)) { 271 SkDebugf("Failed to draw the picture.\n"); 272 success = false; 273 } 274 } 275 } 276 SkDELETE(bitmap); 277 278 return success; 279 } 280 281 282 static int process_input(const SkString& input, const SkString* outputDir, 283 sk_tools::PictureRenderer& renderer, 284 bool validate, int maxComponentDiff, 285 bool writeWholeImage, int clones) { 286 SkOSFile::Iter iter(input.c_str(), "skp"); 287 SkString inputFilename; 288 int failures = 0; 289 SkDebugf("process_input, %s\n", input.c_str()); 290 if (iter.next(&inputFilename)) { 291 do { 292 SkString inputPath; 293 sk_tools::make_filepath(&inputPath, input, inputFilename); 294 if (!render_picture(inputPath, outputDir, renderer, 295 validate, maxComponentDiff, 296 writeWholeImage, clones)) { 297 ++failures; 298 } 299 } while(iter.next(&inputFilename)); 300 } else if (SkStrEndsWith(input.c_str(), ".skp")) { 301 SkString inputPath(input); 302 if (!render_picture(inputPath, outputDir, renderer, 303 validate, maxComponentDiff, 304 writeWholeImage, clones)) { 305 ++failures; 306 } 307 } else { 308 SkString warning; 309 warning.printf("Warning: skipping %s\n", input.c_str()); 310 SkDebugf(warning.c_str()); 311 } 312 return failures; 313 } 314 315 static void parse_commandline(int argc, char* const argv[], SkTArray<SkString>* inputs, 316 sk_tools::PictureRenderer*& renderer, SkString*& outputDir, 317 bool* validate, int* maxComponentDiff, 318 bool* writeWholeImage, 319 int* clones){ 320 const char* argv0 = argv[0]; 321 char* const* stop = argv + argc; 322 323 sk_tools::PictureRenderer::SkDeviceTypes deviceType = 324 sk_tools::PictureRenderer::kBitmap_DeviceType; 325 326 bool usePipe = false; 327 int numThreads = 1; 328 bool useTiles = false; 329 const char* widthString = NULL; 330 const char* heightString = NULL; 331 int gridWidth = 0; 332 int gridHeight = 0; 333 bool isPowerOf2Mode = false; 334 bool isCopyMode = false; 335 const char* xTilesString = NULL; 336 const char* yTilesString = NULL; 337 const char* mode = NULL; 338 bool gridSupported = false; 339 sk_tools::PictureRenderer::BBoxHierarchyType bbhType = 340 sk_tools::PictureRenderer::kNone_BBoxHierarchyType; 341 *validate = false; 342 *maxComponentDiff = 256; 343 *writeWholeImage = false; 344 *clones = 0; 345 SkISize viewport; 346 viewport.setEmpty(); 347 SkScalar scaleFactor = SK_Scalar1; 348 349 for (++argv; argv < stop; ++argv) { 350 if (0 == strcmp(*argv, "--mode")) { 351 if (renderer != NULL) { 352 renderer->unref(); 353 SkDebugf("Cannot combine modes.\n"); 354 usage(argv0); 355 exit(-1); 356 } 357 358 ++argv; 359 if (argv >= stop) { 360 SkDebugf("Missing mode for --mode\n"); 361 usage(argv0); 362 exit(-1); 363 } 364 365 if (0 == strcmp(*argv, "simple")) { 366 renderer = SkNEW(sk_tools::SimplePictureRenderer); 367 } else if ((0 == strcmp(*argv, "tile")) || (0 == strcmp(*argv, "pow2tile")) 368 || 0 == strcmp(*argv, "copyTile")) { 369 useTiles = true; 370 mode = *argv; 371 372 if (0 == strcmp(*argv, "pow2tile")) { 373 isPowerOf2Mode = true; 374 } else if (0 == strcmp(*argv, "copyTile")) { 375 isCopyMode = true; 376 } else { 377 gridSupported = true; 378 } 379 380 ++argv; 381 if (argv >= stop) { 382 SkDebugf("Missing width for --mode %s\n", mode); 383 usage(argv0); 384 exit(-1); 385 } 386 387 widthString = *argv; 388 ++argv; 389 if (argv >= stop) { 390 SkDebugf("Missing height for --mode %s\n", mode); 391 usage(argv0); 392 exit(-1); 393 } 394 heightString = *argv; 395 } else if (0 == strcmp(*argv, "rerecord")) { 396 renderer = SkNEW(sk_tools::RecordPictureRenderer); 397 } else { 398 SkDebugf("%s is not a valid mode for --mode\n", *argv); 399 usage(argv0); 400 exit(-1); 401 } 402 } else if (0 == strcmp(*argv, "--bbh")) { 403 ++argv; 404 if (argv >= stop) { 405 SkDebugf("Missing value for --bbh\n"); 406 usage(argv0); 407 exit(-1); 408 } 409 if (0 == strcmp(*argv, "none")) { 410 bbhType = sk_tools::PictureRenderer::kNone_BBoxHierarchyType; 411 } else if (0 == strcmp(*argv, "rtree")) { 412 bbhType = sk_tools::PictureRenderer::kRTree_BBoxHierarchyType; 413 } else if (0 == strcmp(*argv, "grid")) { 414 bbhType = sk_tools::PictureRenderer::kTileGrid_BBoxHierarchyType; 415 ++argv; 416 if (argv >= stop) { 417 SkDebugf("Missing width for --bbh grid\n"); 418 usage(argv0); 419 exit(-1); 420 } 421 gridWidth = atoi(*argv); 422 ++argv; 423 if (argv >= stop) { 424 SkDebugf("Missing height for --bbh grid\n"); 425 usage(argv0); 426 exit(-1); 427 } 428 gridHeight = atoi(*argv); 429 } else { 430 SkDebugf("%s is not a valid value for --bbhType\n", *argv); 431 usage(argv0); 432 exit(-1);; 433 } 434 } else if (0 == strcmp(*argv, "--viewport")) { 435 ++argv; 436 if (argv >= stop) { 437 SkDebugf("Missing width for --viewport\n"); 438 usage(argv0); 439 exit(-1); 440 } 441 viewport.fWidth = atoi(*argv); 442 ++argv; 443 if (argv >= stop) { 444 SkDebugf("Missing height for --viewport\n"); 445 usage(argv0); 446 exit(-1); 447 } 448 viewport.fHeight = atoi(*argv); 449 } else if (0 == strcmp(*argv, "--scale")) { 450 ++argv; 451 if (argv >= stop) { 452 SkDebugf("Missing scaleFactor for --scale\n"); 453 usage(argv0); 454 exit(-1); 455 } 456 scaleFactor = SkDoubleToScalar(atof(*argv)); 457 } else if (0 == strcmp(*argv, "--tiles")) { 458 ++argv; 459 if (argv >= stop) { 460 SkDebugf("Missing x for --tiles\n"); 461 usage(argv0); 462 exit(-1); 463 } 464 xTilesString = *argv; 465 ++argv; 466 if (argv >= stop) { 467 SkDebugf("Missing y for --tiles\n"); 468 usage(argv0); 469 exit(-1); 470 } 471 yTilesString = *argv; 472 } else if (0 == strcmp(*argv, "--pipe")) { 473 usePipe = true; 474 } else if (0 == strcmp(*argv, "--multi")) { 475 ++argv; 476 if (argv >= stop) { 477 SkSafeUnref(renderer); 478 SkDebugf("Missing arg for --multi\n"); 479 usage(argv0); 480 exit(-1); 481 } 482 numThreads = atoi(*argv); 483 if (numThreads < 2) { 484 SkSafeUnref(renderer); 485 SkDebugf("Number of threads must be at least 2.\n"); 486 usage(argv0); 487 exit(-1); 488 } 489 } else if (0 == strcmp(*argv, "--clone")) { 490 ++argv; 491 if (argv >= stop) { 492 SkSafeUnref(renderer); 493 SkDebugf("Missing arg for --clone\n"); 494 usage(argv0); 495 exit(-1); 496 } 497 *clones = atoi(*argv); 498 if (*clones < 0) { 499 SkSafeUnref(renderer); 500 SkDebugf("Number of clones must be at least 0.\n"); 501 usage(argv0); 502 exit(-1); 503 } 504 } else if (0 == strcmp(*argv, "--device")) { 505 ++argv; 506 if (argv >= stop) { 507 SkSafeUnref(renderer); 508 SkDebugf("Missing mode for --device\n"); 509 usage(argv0); 510 exit(-1); 511 } 512 513 if (0 == strcmp(*argv, "bitmap")) { 514 deviceType = sk_tools::PictureRenderer::kBitmap_DeviceType; 515 } 516 #if SK_SUPPORT_GPU 517 else if (0 == strcmp(*argv, "gpu")) { 518 deviceType = sk_tools::PictureRenderer::kGPU_DeviceType; 519 } 520 #endif 521 else { 522 SkSafeUnref(renderer); 523 SkDebugf("%s is not a valid mode for --device\n", *argv); 524 usage(argv0); 525 exit(-1); 526 } 527 528 } else if ((0 == strcmp(*argv, "-h")) || (0 == strcmp(*argv, "--help"))) { 529 SkSafeUnref(renderer); 530 usage(argv0); 531 exit(-1); 532 } else if (0 == strcmp(*argv, "-w")) { 533 ++argv; 534 if (argv >= stop) { 535 SkDebugf("Missing output directory for -w\n"); 536 usage(argv0); 537 exit(-1); 538 } 539 outputDir = SkNEW_ARGS(SkString, (*argv)); 540 } else if (0 == strcmp(*argv, "--validate")) { 541 *validate = true; 542 } else if (0 == strcmp(*argv, "--maxComponentDiff")) { 543 if (!*validate) { 544 SkDebugf("--maxComponentDiff must be used only with --validate\n"); 545 usage(argv0); 546 exit(-1); 547 } 548 ++argv; 549 if (argv >= stop) { 550 SkDebugf("Missing arg for --maxComponentDiff\n"); 551 usage(argv0); 552 exit(-1); 553 } 554 *maxComponentDiff = atoi(*argv); 555 if (*maxComponentDiff < 0 || *maxComponentDiff > 256) { 556 SkSafeUnref(renderer); 557 SkDebugf("maxComponentDiff: 0 - 256.\n"); 558 usage(argv0); 559 exit(-1); 560 } 561 } else if (0 == strcmp(*argv, "--writeWholeImage")) { 562 *writeWholeImage = true; 563 } else { 564 inputs->push_back(SkString(*argv)); 565 } 566 } 567 568 if (numThreads > 1 && !useTiles) { 569 SkSafeUnref(renderer); 570 SkDebugf("Multithreaded drawing requires tiled rendering.\n"); 571 usage(argv0); 572 exit(-1); 573 } 574 575 if (usePipe && sk_tools::PictureRenderer::kNone_BBoxHierarchyType != bbhType) { 576 SkDebugf("--pipe and --bbh cannot be used together\n"); 577 usage(argv0); 578 exit(-1); 579 } 580 581 if (sk_tools::PictureRenderer::kTileGrid_BBoxHierarchyType == bbhType && 582 !gridSupported) { 583 SkDebugf("'--bbh grid' is not compatible with specified --mode.\n"); 584 usage(argv0); 585 exit(-1); 586 } 587 588 if (useTiles) { 589 SkASSERT(NULL == renderer); 590 sk_tools::TiledPictureRenderer* tiledRenderer; 591 if (isCopyMode) { 592 int x, y; 593 if (xTilesString != NULL) { 594 SkASSERT(yTilesString != NULL); 595 x = atoi(xTilesString); 596 y = atoi(yTilesString); 597 if (x <= 0 || y <= 0) { 598 SkDebugf("--tiles must be given values > 0\n"); 599 usage(argv0); 600 exit(-1); 601 } 602 } else { 603 x = y = 4; 604 } 605 tiledRenderer = SkNEW_ARGS(sk_tools::CopyTilesRenderer, (x, y)); 606 } else if (numThreads > 1) { 607 tiledRenderer = SkNEW_ARGS(sk_tools::MultiCorePictureRenderer, (numThreads)); 608 } else { 609 tiledRenderer = SkNEW(sk_tools::TiledPictureRenderer); 610 } 611 if (isPowerOf2Mode) { 612 int minWidth = atoi(widthString); 613 if (!SkIsPow2(minWidth) || minWidth < 0) { 614 tiledRenderer->unref(); 615 SkString err; 616 err.printf("-mode %s must be given a width" 617 " value that is a power of two\n", mode); 618 SkDebugf(err.c_str()); 619 usage(argv0); 620 exit(-1); 621 } 622 tiledRenderer->setTileMinPowerOf2Width(minWidth); 623 } else if (sk_tools::is_percentage(widthString)) { 624 if (isCopyMode) { 625 tiledRenderer->unref(); 626 SkString err; 627 err.printf("--mode %s does not support percentages.\n", mode); 628 SkDebugf(err.c_str()); 629 usage(argv0); 630 exit(-1); 631 } 632 tiledRenderer->setTileWidthPercentage(atof(widthString)); 633 if (!(tiledRenderer->getTileWidthPercentage() > 0)) { 634 tiledRenderer->unref(); 635 SkDebugf("--mode %s must be given a width percentage > 0\n", mode); 636 usage(argv0); 637 exit(-1); 638 } 639 } else { 640 tiledRenderer->setTileWidth(atoi(widthString)); 641 if (!(tiledRenderer->getTileWidth() > 0)) { 642 tiledRenderer->unref(); 643 SkDebugf("--mode %s must be given a width > 0\n", mode); 644 usage(argv0); 645 exit(-1); 646 } 647 } 648 649 if (sk_tools::is_percentage(heightString)) { 650 if (isCopyMode) { 651 tiledRenderer->unref(); 652 SkString err; 653 err.printf("--mode %s does not support percentages.\n", mode); 654 SkDebugf(err.c_str()); 655 usage(argv0); 656 exit(-1); 657 } 658 tiledRenderer->setTileHeightPercentage(atof(heightString)); 659 if (!(tiledRenderer->getTileHeightPercentage() > 0)) { 660 tiledRenderer->unref(); 661 SkDebugf("--mode %s must be given a height percentage > 0\n", mode); 662 usage(argv0); 663 exit(-1); 664 } 665 } else { 666 tiledRenderer->setTileHeight(atoi(heightString)); 667 if (!(tiledRenderer->getTileHeight() > 0)) { 668 tiledRenderer->unref(); 669 SkDebugf("--mode %s must be given a height > 0\n", mode); 670 usage(argv0); 671 exit(-1); 672 } 673 } 674 if (numThreads > 1) { 675 #if SK_SUPPORT_GPU 676 if (sk_tools::PictureRenderer::kGPU_DeviceType == deviceType) { 677 tiledRenderer->unref(); 678 SkDebugf("GPU not compatible with multithreaded tiling.\n"); 679 usage(argv0); 680 exit(-1); 681 } 682 #endif 683 } 684 renderer = tiledRenderer; 685 if (usePipe) { 686 SkDebugf("Pipe rendering is currently not compatible with tiling.\n" 687 "Turning off pipe.\n"); 688 } 689 } else if (usePipe) { 690 if (renderer != NULL) { 691 renderer->unref(); 692 SkDebugf("Pipe is incompatible with other modes.\n"); 693 usage(argv0); 694 exit(-1); 695 } 696 renderer = SkNEW(sk_tools::PipePictureRenderer); 697 } 698 699 if (inputs->empty()) { 700 SkSafeUnref(renderer); 701 if (NULL != outputDir) { 702 SkDELETE(outputDir); 703 } 704 usage(argv0); 705 exit(-1); 706 } 707 708 if (NULL == renderer) { 709 renderer = SkNEW(sk_tools::SimplePictureRenderer); 710 } 711 712 renderer->setBBoxHierarchyType(bbhType); 713 renderer->setGridSize(gridWidth, gridHeight); 714 renderer->setViewport(viewport); 715 renderer->setScaleFactor(scaleFactor); 716 renderer->setDeviceType(deviceType); 717 } 718 719 int tool_main(int argc, char** argv); 720 int tool_main(int argc, char** argv) { 721 SkAutoGraphics ag; 722 SkTArray<SkString> inputs; 723 sk_tools::PictureRenderer* renderer = NULL; 724 SkString* outputDir = NULL; 725 bool validate = false; 726 int maxComponentDiff = 256; 727 bool writeWholeImage = false; 728 int clones = 0; 729 parse_commandline(argc, argv, &inputs, renderer, outputDir, 730 &validate, &maxComponentDiff, &writeWholeImage, &clones); 731 SkASSERT(renderer); 732 733 int failures = 0; 734 for (int i = 0; i < inputs.count(); i ++) { 735 failures += process_input(inputs[i], outputDir, *renderer, 736 validate, maxComponentDiff, 737 writeWholeImage, clones); 738 } 739 if (failures != 0) { 740 SkDebugf("Failed to render %i pictures.\n", failures); 741 return 1; 742 } 743 #if SK_SUPPORT_GPU 744 #if GR_CACHE_STATS 745 if (renderer->isUsingGpuDevice()) { 746 GrContext* ctx = renderer->getGrContext(); 747 748 ctx->printCacheStats(); 749 } 750 #endif 751 #endif 752 if (NULL != outputDir) { 753 SkDELETE(outputDir); 754 } 755 SkDELETE(renderer); 756 return 0; 757 } 758 759 #if !defined SK_BUILD_FOR_IOS 760 int main(int argc, char * const argv[]) { 761 return tool_main(argc, (char**) argv); 762 } 763 #endif 764