render_pictures_main.cpp revision e04e92b19f050892c2770da955e4c931e0ef698b
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 "SkBitmap.h" 9#include "SkCanvas.h" 10#include "SkDevice.h" 11#include "SkGraphics.h" 12#include "SkMath.h" 13#include "SkOSFile.h" 14#include "SkPicture.h" 15#include "SkStream.h" 16#include "SkString.h" 17#include "SkTArray.h" 18#include "PictureRenderer.h" 19#include "picture_utils.h" 20 21static void usage(const char* argv0) { 22 SkDebugf("SkPicture rendering tool\n"); 23 SkDebugf("\n" 24"Usage: \n" 25" %s <input>... <outputDir> \n" 26" [--mode pipe | pow2tile minWidth height[%] | simple\n" 27" | tile width[%] height[%]]\n" 28" [--device bitmap" 29#if SK_SUPPORT_GPU 30" | gpu" 31#endif 32"]" 33, argv0); 34 SkDebugf("\n\n"); 35 SkDebugf( 36" input: A list of directories and files to use as input. Files are\n" 37" expected to have the .skp extension.\n\n"); 38 SkDebugf( 39" outputDir: directory to write the rendered images.\n\n"); 40 SkDebugf( 41" --mode pipe | pow2tile minWidth height[%] | simple\n" 42" | tile width[%] height[%]: Run in the corresponding mode.\n" 43" Default is simple.\n"); 44 SkDebugf( 45" pipe, Render using a SkGPipe.\n"); 46 SkDebugf( 47" pow2tile minWidth height[%], Creates tiles with widths\n" 48" that are all a power of two\n" 49" such that they minimize the\n" 50" amount of wasted tile space.\n" 51" minWidth is the minimum width\n" 52" of these tiles and must be a\n" 53" power of two. A simple render\n" 54" is done with these tiles.\n"); 55 SkDebugf( 56" simple, Render using the default rendering method.\n"); 57 SkDebugf( 58" tile width[%] height[%], Do a simple render using tiles\n" 59" with the given dimensions.\n"); 60 SkDebugf("\n"); 61 SkDebugf( 62" --device bitmap" 63#if SK_SUPPORT_GPU 64" | gpu" 65#endif 66": Use the corresponding device. Default is bitmap.\n"); 67 SkDebugf( 68" bitmap, Render to a bitmap.\n"); 69#if SK_SUPPORT_GPU 70 SkDebugf( 71" gpu, Render to the GPU.\n"); 72#endif 73} 74 75static void make_output_filepath(SkString* path, const SkString& dir, 76 const SkString& name) { 77 sk_tools::make_filepath(path, dir, name); 78 path->remove(path->size() - 3, 3); 79 path->append("png"); 80} 81 82static void write_output(const SkString& outputDir, const SkString& inputFilename, 83 const sk_tools::PictureRenderer& renderer) { 84 SkString outputPath; 85 make_output_filepath(&outputPath, outputDir, inputFilename); 86 bool isWritten = renderer.write(outputPath); 87 if (!isWritten) { 88 SkDebugf("Could not write to file %s\n", outputPath.c_str()); 89 } 90} 91 92static bool area_too_big(int w, int h, SkISize* newSize) { 93 // just a guess, based on what seems to fail on smaller android devices 94 static const int64_t kMaxAreaForMemory = 16 * 1024 * 1024; 95 96 if ((int64_t)w * h > kMaxAreaForMemory) { 97 do { 98 w >>= 1; 99 h >>= 1; 100 } while ((int64_t)w * h > kMaxAreaForMemory); 101 if (0 == w) { 102 w = 1; 103 } 104 if (0 == h) { 105 h = 1; 106 } 107 newSize->set(w, h); 108 return true; 109 } 110 return false; 111} 112 113static void render_picture(const SkString& inputPath, const SkString& outputDir, 114 sk_tools::PictureRenderer& renderer) { 115 SkString inputFilename; 116 sk_tools::get_basename(&inputFilename, inputPath); 117 118 SkFILEStream inputStream; 119 inputStream.setPath(inputPath.c_str()); 120 if (!inputStream.isValid()) { 121 SkDebugf("Could not open file %s\n", inputPath.c_str()); 122 return; 123 } 124 125 SkPicture picture(&inputStream); 126 127 SkDebugf("drawing... [%i %i] %s\n", picture.width(), picture.height(), 128 inputPath.c_str()); 129 130 131 // rescale to avoid memory issues allcoating a very large offscreen 132 SkPicture* pic = &picture; 133 SkISize newSize; 134 SkAutoUnref aur(NULL); 135 136 if (area_too_big(picture.width(), picture.height(), &newSize)) { 137 pic = new SkPicture; 138 aur.reset(pic); 139 140 SkCanvas* canvas = pic->beginRecording(newSize.width(), newSize.height()); 141 SkScalar scale = SkIntToScalar(newSize.width()) / picture.width(); 142 canvas->scale(scale, scale); 143 canvas->drawPicture(picture); 144 pic->endRecording(); 145 146 SkDebugf("... rescaling to [%d %d] to avoid overly large allocations\n", 147 newSize.width(), newSize.height()); 148 } 149 150 renderer.init(pic); 151 152 renderer.render(true); 153 154 renderer.resetState(); 155 156 write_output(outputDir, inputFilename, renderer); 157 158 renderer.end(); 159} 160 161static void process_input(const SkString& input, const SkString& outputDir, 162 sk_tools::PictureRenderer& renderer) { 163 SkOSFile::Iter iter(input.c_str(), "skp"); 164 SkString inputFilename; 165 166 if (iter.next(&inputFilename)) { 167 do { 168 SkString inputPath; 169 sk_tools::make_filepath(&inputPath, input, inputFilename); 170 render_picture(inputPath, outputDir, renderer); 171 } while(iter.next(&inputFilename)); 172 } else { 173 SkString inputPath(input); 174 render_picture(inputPath, outputDir, renderer); 175 } 176} 177 178static void parse_commandline(int argc, char* const argv[], SkTArray<SkString>* inputs, 179 sk_tools::PictureRenderer*& renderer){ 180 const char* argv0 = argv[0]; 181 char* const* stop = argv + argc; 182 183 sk_tools::PictureRenderer::SkDeviceTypes deviceType = 184 sk_tools::PictureRenderer::kBitmap_DeviceType; 185 186 for (++argv; argv < stop; ++argv) { 187 if (0 == strcmp(*argv, "--mode")) { 188 SkDELETE(renderer); 189 190 ++argv; 191 if (argv >= stop) { 192 SkDebugf("Missing mode for --mode\n"); 193 usage(argv0); 194 exit(-1); 195 } 196 197 if (0 == strcmp(*argv, "pipe")) { 198 renderer = SkNEW(sk_tools::PipePictureRenderer); 199 } else if (0 == strcmp(*argv, "simple")) { 200 renderer = SkNEW(sk_tools::SimplePictureRenderer); 201 } else if ((0 == strcmp(*argv, "tile")) || (0 == strcmp(*argv, "pow2tile"))) { 202 char* mode = *argv; 203 bool isPowerOf2Mode = false; 204 205 if (0 == strcmp(*argv, "pow2tile")) { 206 isPowerOf2Mode = true; 207 } 208 209 sk_tools::TiledPictureRenderer* tileRenderer = 210 SkNEW(sk_tools::TiledPictureRenderer); 211 ++argv; 212 if (argv >= stop) { 213 SkDELETE(tileRenderer); 214 SkDebugf("Missing width for --mode %s\n", mode); 215 usage(argv0); 216 exit(-1); 217 } 218 219 if (isPowerOf2Mode) { 220 int minWidth = atoi(*argv); 221 222 if (!SkIsPow2(minWidth) || minWidth <= 0) { 223 SkDELETE(tileRenderer); 224 SkDebugf("--mode %s must be given a width" 225 " value that is a power of two\n", mode); 226 exit(-1); 227 } 228 229 tileRenderer->setTileMinPowerOf2Width(minWidth); 230 } else if (sk_tools::is_percentage(*argv)) { 231 tileRenderer->setTileWidthPercentage(atof(*argv)); 232 if (!(tileRenderer->getTileWidthPercentage() > 0)) { 233 SkDELETE(tileRenderer); 234 SkDebugf("--mode %s must be given a width percentage > 0\n", mode); 235 exit(-1); 236 } 237 } else { 238 tileRenderer->setTileWidth(atoi(*argv)); 239 if (!(tileRenderer->getTileWidth() > 0)) { 240 SkDELETE(tileRenderer); 241 SkDebugf("--mode %s must be given a width > 0\n", mode); 242 exit(-1); 243 } 244 } 245 246 ++argv; 247 if (argv >= stop) { 248 SkDELETE(tileRenderer); 249 SkDebugf("Missing height for --mode %s\n", mode); 250 usage(argv0); 251 exit(-1); 252 } 253 254 if (sk_tools::is_percentage(*argv)) { 255 tileRenderer->setTileHeightPercentage(atof(*argv)); 256 if (!(tileRenderer->getTileHeightPercentage() > 0)) { 257 SkDELETE(tileRenderer); 258 SkDebugf( 259 "--mode %s must be given a height percentage > 0\n", mode); 260 exit(-1); 261 } 262 } else { 263 tileRenderer->setTileHeight(atoi(*argv)); 264 if (!(tileRenderer->getTileHeight() > 0)) { 265 SkDELETE(tileRenderer); 266 SkDebugf("--mode %s must be given a height > 0\n", mode); 267 exit(-1); 268 } 269 } 270 271 renderer = tileRenderer; 272 } else { 273 SkDebugf("%s is not a valid mode for --mode\n", *argv); 274 usage(argv0); 275 exit(-1); 276 } 277 } else if (0 == strcmp(*argv, "--device")) { 278 ++argv; 279 if (argv >= stop) { 280 SkDebugf("Missing mode for --deivce\n"); 281 usage(argv0); 282 exit(-1); 283 } 284 285 if (0 == strcmp(*argv, "bitmap")) { 286 deviceType = sk_tools::PictureRenderer::kBitmap_DeviceType; 287 } 288#if SK_SUPPORT_GPU 289 else if (0 == strcmp(*argv, "gpu")) { 290 deviceType = sk_tools::PictureRenderer::kGPU_DeviceType; 291 } 292#endif 293 else { 294 SkDebugf("%s is not a valid mode for --device\n", *argv); 295 usage(argv0); 296 exit(-1); 297 } 298 299 } else if ((0 == strcmp(*argv, "-h")) || (0 == strcmp(*argv, "--help"))) { 300 SkDELETE(renderer); 301 usage(argv0); 302 exit(-1); 303 } else { 304 inputs->push_back(SkString(*argv)); 305 } 306 } 307 308 if (inputs->count() < 2) { 309 SkDELETE(renderer); 310 usage(argv0); 311 exit(-1); 312 } 313 314 if (NULL == renderer) { 315 renderer = SkNEW(sk_tools::SimplePictureRenderer); 316 } 317 318 renderer->setDeviceType(deviceType); 319} 320 321int main(int argc, char* const argv[]) { 322 SkGraphics::Init(); 323 SkTArray<SkString> inputs; 324 sk_tools::PictureRenderer* renderer = NULL; 325 326 parse_commandline(argc, argv, &inputs, renderer); 327 SkString outputDir = inputs[inputs.count() - 1]; 328 SkASSERT(renderer); 329 330 for (int i = 0; i < inputs.count() - 1; i ++) { 331 process_input(inputs[i], outputDir, *renderer); 332 } 333 334#if SK_SUPPORT_GPU 335#if GR_CACHE_STATS 336 if (renderer->isUsingGpuDevice()) { 337 GrContext* ctx = renderer->getGrContext(); 338 339 ctx->printCacheStats(); 340 } 341#endif 342#endif 343 344 SkDELETE(renderer); 345 SkGraphics::Term(); 346} 347