render_pictures_main.cpp revision c0d5e549ab8d594a5da8db417db39622e9491fff
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 "SkMath.h" 15#include "SkOSFile.h" 16#include "SkPicture.h" 17#include "SkStream.h" 18#include "SkString.h" 19#include "SkTArray.h" 20#include "PictureRenderer.h" 21#include "picture_utils.h" 22 23static void usage(const char* argv0) { 24 SkDebugf("SkPicture rendering tool\n"); 25 SkDebugf("\n" 26"Usage: \n" 27" %s <input>... \n" 28" [-w <outputDir>]\n" 29" [--mode pow2tile minWidth height | copyTile width height | simple\n" 30" | tile width height]\n" 31" [--pipe]\n" 32" [--multi count]\n" 33" [--viewport width height]\n" 34" [--device bitmap" 35#if SK_SUPPORT_GPU 36" | gpu" 37#endif 38"]" 39, argv0); 40 SkDebugf("\n\n"); 41 SkDebugf( 42" input: A list of directories and files to use as input. Files are\n" 43" expected to have the .skp extension.\n\n"); 44 SkDebugf( 45" outputDir: directory to write the rendered images.\n\n"); 46 SkDebugf( 47" --mode pow2tile minWidth height | copyTile width height | simple\n" 48" | tile width height | rerecord: Run in the corresponding mode.\n" 49" Default is simple.\n"); 50 SkDebugf( 51" pow2tile minWidth height, Creates tiles with widths\n" 52" that are all a power of two\n" 53" such that they minimize the\n" 54" amount of wasted tile space.\n" 55" minWidth is the minimum width\n" 56" of these tiles and must be a\n" 57" power of two. A simple render\n" 58" is done with these tiles.\n"); 59 SkDebugf( 60" simple, Render using the default rendering method.\n" 61" rerecord, Record the picture as a new skp, with the bitmaps PNG encoded.\n" 62 ); 63 SkDebugf( 64" tile width height, Do a simple render using tiles\n" 65" with the given dimensions.\n" 66" copyTile width height, Draw the picture, then copy it into tiles.\n" 67" Does not support percentages.\n" 68" If the picture is large enough, breaks it into\n" 69" larger tiles (and draws the picture once per\n" 70" larger tile) to avoid creating a large canvas.\n" 71" Add --tiles x y to specify the number of tiles\n" 72" per larger tile in the x and y direction.\n" 73 ); 74 SkDebugf("\n"); 75 SkDebugf( 76" --multi count : Set the number of threads for multi threaded drawing. Must be greater\n" 77" than 1. Only works with tiled rendering.\n" 78" --viewport width height : Set the viewport.\n" 79" --pipe: Benchmark SkGPipe rendering. Currently incompatible with \"mode\".\n"); 80 SkDebugf( 81" --device bitmap" 82#if SK_SUPPORT_GPU 83" | gpu" 84#endif 85": Use the corresponding device. Default is bitmap.\n"); 86 SkDebugf( 87" bitmap, Render to a bitmap.\n"); 88#if SK_SUPPORT_GPU 89 SkDebugf( 90" gpu, Render to the GPU.\n"); 91#endif 92} 93 94static void make_output_filepath(SkString* path, const SkString& dir, 95 const SkString& name) { 96 sk_tools::make_filepath(path, dir, name); 97 // Remove ".skp" 98 path->remove(path->size() - 4, 4); 99} 100 101static bool render_picture(const SkString& inputPath, const SkString* outputDir, 102 sk_tools::PictureRenderer& renderer) { 103 SkString inputFilename; 104 sk_tools::get_basename(&inputFilename, inputPath); 105 106 SkFILEStream inputStream; 107 inputStream.setPath(inputPath.c_str()); 108 if (!inputStream.isValid()) { 109 SkDebugf("Could not open file %s\n", inputPath.c_str()); 110 return false; 111 } 112 113 bool success = false; 114 SkPicture picture(&inputStream, &success, &SkImageDecoder::DecodeStream); 115 if (!success) { 116 SkDebugf("Could not read an SkPicture from %s\n", inputPath.c_str()); 117 return false; 118 } 119 120 SkDebugf("drawing... [%i %i] %s\n", picture.width(), picture.height(), 121 inputPath.c_str()); 122 123 renderer.init(&picture); 124 renderer.setup(); 125 126 SkString* outputPath = NULL; 127 if (NULL != outputDir) { 128 outputPath = SkNEW(SkString); 129 make_output_filepath(outputPath, *outputDir, inputFilename); 130 } 131 success = renderer.render(outputPath); 132 if (outputPath) { 133 if (!success) { 134 SkDebugf("Could not write to file %s\n", outputPath->c_str()); 135 } 136 SkDELETE(outputPath); 137 } 138 139 renderer.resetState(); 140 141 renderer.end(); 142 return success; 143} 144 145static int process_input(const SkString& input, const SkString* outputDir, 146 sk_tools::PictureRenderer& renderer) { 147 SkOSFile::Iter iter(input.c_str(), "skp"); 148 SkString inputFilename; 149 int failures = 0; 150 SkDebugf("process_input, %s\n", input.c_str()); 151 if (iter.next(&inputFilename)) { 152 do { 153 SkString inputPath; 154 sk_tools::make_filepath(&inputPath, input, inputFilename); 155 if (!render_picture(inputPath, outputDir, renderer)) { 156 ++failures; 157 } 158 } while(iter.next(&inputFilename)); 159 } else if (SkStrEndsWith(input.c_str(), ".skp")) { 160 SkString inputPath(input); 161 if (!render_picture(inputPath, outputDir, renderer)) { 162 ++failures; 163 } 164 } else { 165 SkString warning; 166 warning.printf("Warning: skipping %s\n", input.c_str()); 167 SkDebugf(warning.c_str()); 168 } 169 return failures; 170} 171 172static void parse_commandline(int argc, char* const argv[], SkTArray<SkString>* inputs, 173 sk_tools::PictureRenderer*& renderer, SkString*& outputDir){ 174 const char* argv0 = argv[0]; 175 char* const* stop = argv + argc; 176 177 sk_tools::PictureRenderer::SkDeviceTypes deviceType = 178 sk_tools::PictureRenderer::kBitmap_DeviceType; 179 180 bool usePipe = false; 181 int numThreads = 1; 182 bool useTiles = false; 183 const char* widthString = NULL; 184 const char* heightString = NULL; 185 bool isPowerOf2Mode = false; 186 bool isCopyMode = false; 187 const char* xTilesString = NULL; 188 const char* yTilesString = NULL; 189 const char* mode = NULL; 190 SkISize viewport; 191 viewport.setEmpty(); 192 for (++argv; argv < stop; ++argv) { 193 if (0 == strcmp(*argv, "--mode")) { 194 if (renderer != NULL) { 195 renderer->unref(); 196 SkDebugf("Cannot combine modes.\n"); 197 usage(argv0); 198 exit(-1); 199 } 200 201 ++argv; 202 if (argv >= stop) { 203 SkDebugf("Missing mode for --mode\n"); 204 usage(argv0); 205 exit(-1); 206 } 207 208 if (0 == strcmp(*argv, "simple")) { 209 renderer = SkNEW(sk_tools::SimplePictureRenderer); 210 } else if ((0 == strcmp(*argv, "tile")) || (0 == strcmp(*argv, "pow2tile")) 211 || 0 == strcmp(*argv, "copyTile")) { 212 useTiles = true; 213 mode = *argv; 214 215 if (0 == strcmp(*argv, "pow2tile")) { 216 isPowerOf2Mode = true; 217 } else if (0 == strcmp(*argv, "copyTile")) { 218 isCopyMode = true; 219 } 220 221 ++argv; 222 if (argv >= stop) { 223 SkDebugf("Missing width for --mode %s\n", mode); 224 usage(argv0); 225 exit(-1); 226 } 227 228 widthString = *argv; 229 ++argv; 230 if (argv >= stop) { 231 SkDebugf("Missing height for --mode %s\n", mode); 232 usage(argv0); 233 exit(-1); 234 } 235 heightString = *argv; 236 } else if (0 == strcmp(*argv, "rerecord")) { 237 renderer = SkNEW(sk_tools::RecordPictureRenderer); 238 } else { 239 SkDebugf("%s is not a valid mode for --mode\n", *argv); 240 usage(argv0); 241 exit(-1); 242 } 243 } else if (0 == strcmp(*argv, "--viewport")) { 244 ++argv; 245 if (argv >= stop) { 246 SkDebugf("Missing width for --viewport\n"); 247 usage(argv0); 248 exit(-1); 249 } 250 viewport.fWidth = atoi(*argv); 251 ++argv; 252 if (argv >= stop) { 253 SkDebugf("Missing height for --viewport\n"); 254 usage(argv0); 255 exit(-1); 256 } 257 viewport.fHeight = atoi(*argv); 258 } else if (0 == strcmp(*argv, "--tiles")) { 259 ++argv; 260 if (argv >= stop) { 261 SkDebugf("Missing x for --tiles\n"); 262 usage(argv0); 263 exit(-1); 264 } 265 xTilesString = *argv; 266 ++argv; 267 if (argv >= stop) { 268 SkDebugf("Missing y for --tiles\n"); 269 usage(argv0); 270 exit(-1); 271 } 272 yTilesString = *argv; 273 } else if (0 == strcmp(*argv, "--pipe")) { 274 usePipe = true; 275 } else if (0 == strcmp(*argv, "--multi")) { 276 ++argv; 277 if (argv >= stop) { 278 SkSafeUnref(renderer); 279 SkDebugf("Missing arg for --multi\n"); 280 usage(argv0); 281 exit(-1); 282 } 283 numThreads = atoi(*argv); 284 if (numThreads < 2) { 285 SkSafeUnref(renderer); 286 SkDebugf("Number of threads must be at least 2.\n"); 287 usage(argv0); 288 exit(-1); 289 } 290 } else if (0 == strcmp(*argv, "--device")) { 291 ++argv; 292 if (argv >= stop) { 293 SkSafeUnref(renderer); 294 SkDebugf("Missing mode for --device\n"); 295 usage(argv0); 296 exit(-1); 297 } 298 299 if (0 == strcmp(*argv, "bitmap")) { 300 deviceType = sk_tools::PictureRenderer::kBitmap_DeviceType; 301 } 302#if SK_SUPPORT_GPU 303 else if (0 == strcmp(*argv, "gpu")) { 304 deviceType = sk_tools::PictureRenderer::kGPU_DeviceType; 305 } 306#endif 307 else { 308 SkSafeUnref(renderer); 309 SkDebugf("%s is not a valid mode for --device\n", *argv); 310 usage(argv0); 311 exit(-1); 312 } 313 314 } else if ((0 == strcmp(*argv, "-h")) || (0 == strcmp(*argv, "--help"))) { 315 SkSafeUnref(renderer); 316 usage(argv0); 317 exit(-1); 318 } else if (0 == strcmp(*argv, "-w")) { 319 ++argv; 320 if (argv >= stop) { 321 SkDebugf("Missing output directory for -w\n"); 322 usage(argv0); 323 exit(-1); 324 } 325 outputDir = SkNEW_ARGS(SkString, (*argv)); 326 } else { 327 inputs->push_back(SkString(*argv)); 328 } 329 } 330 331 if (numThreads > 1 && !useTiles) { 332 SkSafeUnref(renderer); 333 SkDebugf("Multithreaded drawing requires tiled rendering.\n"); 334 usage(argv0); 335 exit(-1); 336 } 337 338 if (useTiles) { 339 SkASSERT(NULL == renderer); 340 sk_tools::TiledPictureRenderer* tiledRenderer; 341 if (isCopyMode) { 342 int x, y; 343 if (xTilesString != NULL) { 344 SkASSERT(yTilesString != NULL); 345 x = atoi(xTilesString); 346 y = atoi(yTilesString); 347 if (x <= 0 || y <= 0) { 348 SkDebugf("--tiles must be given values > 0\n"); 349 usage(argv0); 350 exit(-1); 351 } 352 } else { 353 x = y = 4; 354 } 355 tiledRenderer = SkNEW_ARGS(sk_tools::CopyTilesRenderer, (x, y)); 356 } else if (numThreads > 1) { 357 tiledRenderer = SkNEW_ARGS(sk_tools::MultiCorePictureRenderer, (numThreads)); 358 } else { 359 tiledRenderer = SkNEW(sk_tools::TiledPictureRenderer); 360 } 361 if (isPowerOf2Mode) { 362 int minWidth = atoi(widthString); 363 if (!SkIsPow2(minWidth) || minWidth < 0) { 364 tiledRenderer->unref(); 365 SkString err; 366 err.printf("-mode %s must be given a width" 367 " value that is a power of two\n", mode); 368 SkDebugf(err.c_str()); 369 usage(argv0); 370 exit(-1); 371 } 372 tiledRenderer->setTileMinPowerOf2Width(minWidth); 373 } else if (sk_tools::is_percentage(widthString)) { 374 if (isCopyMode) { 375 tiledRenderer->unref(); 376 SkString err; 377 err.printf("--mode %s does not support percentages.\n", mode); 378 SkDebugf(err.c_str()); 379 usage(argv0); 380 exit(-1); 381 } 382 tiledRenderer->setTileWidthPercentage(atof(widthString)); 383 if (!(tiledRenderer->getTileWidthPercentage() > 0)) { 384 tiledRenderer->unref(); 385 SkDebugf("--mode %s must be given a width percentage > 0\n", mode); 386 usage(argv0); 387 exit(-1); 388 } 389 } else { 390 tiledRenderer->setTileWidth(atoi(widthString)); 391 if (!(tiledRenderer->getTileWidth() > 0)) { 392 tiledRenderer->unref(); 393 SkDebugf("--mode %s must be given a width > 0\n", mode); 394 usage(argv0); 395 exit(-1); 396 } 397 } 398 399 if (sk_tools::is_percentage(heightString)) { 400 if (isCopyMode) { 401 tiledRenderer->unref(); 402 SkString err; 403 err.printf("--mode %s does not support percentages.\n", mode); 404 SkDebugf(err.c_str()); 405 usage(argv0); 406 exit(-1); 407 } 408 tiledRenderer->setTileHeightPercentage(atof(heightString)); 409 if (!(tiledRenderer->getTileHeightPercentage() > 0)) { 410 tiledRenderer->unref(); 411 SkDebugf("--mode %s must be given a height percentage > 0\n", mode); 412 usage(argv0); 413 exit(-1); 414 } 415 } else { 416 tiledRenderer->setTileHeight(atoi(heightString)); 417 if (!(tiledRenderer->getTileHeight() > 0)) { 418 tiledRenderer->unref(); 419 SkDebugf("--mode %s must be given a height > 0\n", mode); 420 usage(argv0); 421 exit(-1); 422 } 423 } 424 if (numThreads > 1) { 425#if SK_SUPPORT_GPU 426 if (sk_tools::PictureRenderer::kGPU_DeviceType == deviceType) { 427 tiledRenderer->unref(); 428 SkDebugf("GPU not compatible with multithreaded tiling.\n"); 429 usage(argv0); 430 exit(-1); 431 } 432#endif 433 } 434 renderer = tiledRenderer; 435 if (usePipe) { 436 SkDebugf("Pipe rendering is currently not compatible with tiling.\n" 437 "Turning off pipe.\n"); 438 } 439 } else if (usePipe) { 440 if (renderer != NULL) { 441 renderer->unref(); 442 SkDebugf("Pipe is incompatible with other modes.\n"); 443 usage(argv0); 444 exit(-1); 445 } 446 renderer = SkNEW(sk_tools::PipePictureRenderer); 447 } 448 449 if (inputs->empty()) { 450 SkSafeUnref(renderer); 451 if (NULL != outputDir) { 452 SkDELETE(outputDir); 453 } 454 usage(argv0); 455 exit(-1); 456 } 457 458 if (NULL == renderer) { 459 renderer = SkNEW(sk_tools::SimplePictureRenderer); 460 } 461 462 renderer->setViewport(viewport); 463 renderer->setDeviceType(deviceType); 464} 465 466int tool_main(int argc, char** argv); 467int tool_main(int argc, char** argv) { 468 SkAutoGraphics ag; 469 SkTArray<SkString> inputs; 470 sk_tools::PictureRenderer* renderer = NULL; 471 SkString* outputDir = NULL; 472 parse_commandline(argc, argv, &inputs, renderer, outputDir); 473 SkASSERT(renderer); 474 475 int failures = 0; 476 for (int i = 0; i < inputs.count(); i ++) { 477 failures += process_input(inputs[i], outputDir, *renderer); 478 } 479 if (failures != 0) { 480 SkDebugf("Failed to render %i pictures.\n", failures); 481 return 1; 482 } 483#if SK_SUPPORT_GPU 484#if GR_CACHE_STATS 485 if (renderer->isUsingGpuDevice()) { 486 GrContext* ctx = renderer->getGrContext(); 487 488 ctx->printCacheStats(); 489 } 490#endif 491#endif 492 if (NULL != outputDir) { 493 SkDELETE(outputDir); 494 } 495 SkDELETE(renderer); 496 return 0; 497} 498 499#if !defined SK_BUILD_FOR_IOS 500int main(int argc, char * const argv[]) { 501 return tool_main(argc, (char**) argv); 502} 503#endif 504