render_pictures_main.cpp revision 7def5e1630d47cdbfa4b58a9c86bc060693c4d79
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 "SkDevice.h" 11#include "SkCommandLineFlags.h" 12#include "SkForceLinking.h" 13#include "SkGraphics.h" 14#include "SkImageDecoder.h" 15#include "SkImageEncoder.h" 16#include "SkMath.h" 17#include "SkOSFile.h" 18#include "SkPicture.h" 19#include "SkStream.h" 20#include "SkString.h" 21#include "PictureRenderer.h" 22#include "PictureRenderingFlags.h" 23#include "picture_utils.h" 24 25// Required to ensure that image decoders get linked correctly. 26__SK_FORCE_IMAGE_DECODER_LINKING; 27 28// Flags used by this file, alphabetically: 29DEFINE_int32(clone, 0, "Clone the picture n times before rendering."); 30DECLARE_bool(deferImageDecoding); 31DEFINE_int32(maxComponentDiff, 256, "Maximum diff on a component, 0 - 256. Components that differ " 32 "by more than this amount are considered errors, though all diffs are reported. " 33 "Requires --validate."); 34DECLARE_string(readPath); 35DEFINE_bool(writeEncodedImages, false, "Any time the skp contains an encoded image, write it to a " 36 "file rather than decoding it. Requires writePath to be set. Skips drawing the full " 37 "skp to a file. Not compatible with deferImageDecoding."); 38DEFINE_string2(writePath, w, "", "Directory to write the rendered images."); 39DEFINE_bool(writeWholeImage, false, "In tile mode, write the entire rendered image to a " 40 "file, instead of an image for each tile."); 41DEFINE_bool(validate, false, "Verify that the rendered image contains the same pixels as " 42 "the picture rendered in simple mode. When used in conjunction with --bbh, results " 43 "are validated against the picture rendered in the same mode, but without the bbh."); 44 45static void make_output_filepath(SkString* path, const SkString& dir, 46 const SkString& name) { 47 sk_tools::make_filepath(path, dir, name); 48 // Remove ".skp" 49 path->remove(path->size() - 4, 4); 50} 51 52// Defined in PictureRenderingFlags.cpp 53extern bool lazy_decode_bitmap(const void* buffer, size_t size, SkBitmap* bitmap); 54 55//////////////////////////////////////////////////////////////////////////////////////////////////// 56 57/** 58 * Table for translating from format of data to a suffix. 59 */ 60struct Format { 61 SkImageDecoder::Format fFormat; 62 const char* fSuffix; 63}; 64static const Format gFormats[] = { 65 { SkImageDecoder::kBMP_Format, ".bmp" }, 66 { SkImageDecoder::kGIF_Format, ".gif" }, 67 { SkImageDecoder::kICO_Format, ".ico" }, 68 { SkImageDecoder::kJPEG_Format, ".jpg" }, 69 { SkImageDecoder::kPNG_Format, ".png" }, 70 { SkImageDecoder::kWBMP_Format, ".wbmp" }, 71 { SkImageDecoder::kWEBP_Format, ".webp" }, 72 { SkImageDecoder::kUnknown_Format, "" }, 73}; 74 75/** 76 * Get an appropriate suffix for an image format. 77 */ 78static const char* get_suffix_from_format(SkImageDecoder::Format format) { 79 for (size_t i = 0; i < SK_ARRAY_COUNT(gFormats); i++) { 80 if (gFormats[i].fFormat == format) { 81 return gFormats[i].fSuffix; 82 } 83 } 84 return ""; 85} 86 87/** 88 * Base name for an image file created from the encoded data in an skp. 89 */ 90static SkString gInputFileName; 91 92/** 93 * Number to be appended to the image file name so that it is unique. 94 */ 95static uint32_t gImageNo; 96 97/** 98 * Set up the name for writing encoded data to a file. 99 * Sets gInputFileName to name, minus any extension ".*" 100 * Sets gImageNo to 0, so images from file "X.skp" will 101 * look like "X_<gImageNo>.<suffix>", beginning with 0 102 * for each new skp. 103 */ 104static void reset_image_file_base_name(const SkString& name) { 105 gImageNo = 0; 106 // Remove ".skp" 107 const char* cName = name.c_str(); 108 const char* dot = strrchr(cName, '.'); 109 if (dot != NULL) { 110 gInputFileName.set(cName, dot - cName); 111 } else { 112 gInputFileName.set(name); 113 } 114} 115 116/** 117 * Write the raw encoded bitmap data to a file. 118 */ 119static bool write_image_to_file(const void* buffer, size_t size, SkBitmap* bitmap) { 120 SkASSERT(!FLAGS_writePath.isEmpty()); 121 SkMemoryStream memStream(buffer, size); 122 SkString outPath; 123 SkImageDecoder::Format format = SkImageDecoder::GetStreamFormat(&memStream); 124 SkString name = SkStringPrintf("%s_%d%s", gInputFileName.c_str(), gImageNo++, 125 get_suffix_from_format(format)); 126 SkString dir(FLAGS_writePath[0]); 127 sk_tools::make_filepath(&outPath, dir, name); 128 SkFILEWStream fileStream(outPath.c_str()); 129 if (!(fileStream.isValid() && fileStream.write(buffer, size))) { 130 SkDebugf("Failed to write encoded data to \"%s\"\n", outPath.c_str()); 131 } 132 // Put in a dummy bitmap. 133 return SkImageDecoder::DecodeStream(&memStream, bitmap, SkBitmap::kNo_Config, 134 SkImageDecoder::kDecodeBounds_Mode); 135} 136 137//////////////////////////////////////////////////////////////////////////////////////////////////// 138 139static bool render_picture(const SkString& inputPath, const SkString* outputDir, 140 sk_tools::PictureRenderer& renderer, 141 SkBitmap** out) { 142 SkString inputFilename; 143 sk_tools::get_basename(&inputFilename, inputPath); 144 145 SkFILEStream inputStream; 146 inputStream.setPath(inputPath.c_str()); 147 if (!inputStream.isValid()) { 148 SkDebugf("Could not open file %s\n", inputPath.c_str()); 149 return false; 150 } 151 152 bool success = false; 153 SkPicture* picture; 154 if (FLAGS_deferImageDecoding) { 155 picture = SkNEW_ARGS(SkPicture, (&inputStream, &success, &lazy_decode_bitmap)); 156 } else if (FLAGS_writeEncodedImages) { 157 SkASSERT(!FLAGS_writePath.isEmpty()); 158 reset_image_file_base_name(inputFilename); 159 picture = SkNEW_ARGS(SkPicture, (&inputStream, &success, &write_image_to_file)); 160 } else { 161 picture = SkNEW_ARGS(SkPicture, (&inputStream, &success, &SkImageDecoder::DecodeMemory)); 162 } 163 164 if (!success) { 165 SkDebugf("Could not read an SkPicture from %s\n", inputPath.c_str()); 166 return false; 167 } 168 169 for (int i = 0; i < FLAGS_clone; ++i) { 170 SkPicture* clone = picture->clone(); 171 SkDELETE(picture); 172 picture = clone; 173 } 174 175 SkDebugf("drawing... [%i %i] %s\n", picture->width(), picture->height(), 176 inputPath.c_str()); 177 178 renderer.init(picture); 179 renderer.setup(); 180 181 SkString* outputPath = NULL; 182 if (NULL != outputDir && outputDir->size() > 0 && !FLAGS_writeEncodedImages) { 183 outputPath = SkNEW(SkString); 184 make_output_filepath(outputPath, *outputDir, inputFilename); 185 } 186 187 success = renderer.render(outputPath, out); 188 if (outputPath) { 189 if (!success) { 190 SkDebugf("Could not write to file %s\n", outputPath->c_str()); 191 } 192 SkDELETE(outputPath); 193 } 194 195 renderer.end(); 196 197 SkDELETE(picture); 198 return success; 199} 200 201static inline int getByte(uint32_t value, int index) { 202 SkASSERT(0 <= index && index < 4); 203 return (value >> (index * 8)) & 0xFF; 204} 205 206static int MaxByteDiff(uint32_t v1, uint32_t v2) { 207 return SkMax32(SkMax32(abs(getByte(v1, 0) - getByte(v2, 0)), abs(getByte(v1, 1) - getByte(v2, 1))), 208 SkMax32(abs(getByte(v1, 2) - getByte(v2, 2)), abs(getByte(v1, 3) - getByte(v2, 3)))); 209} 210 211namespace { 212class AutoRestoreBbhType { 213public: 214 AutoRestoreBbhType() { 215 fRenderer = NULL; 216 fSavedBbhType = sk_tools::PictureRenderer::kNone_BBoxHierarchyType; 217 } 218 219 void set(sk_tools::PictureRenderer* renderer, 220 sk_tools::PictureRenderer::BBoxHierarchyType bbhType) { 221 fRenderer = renderer; 222 fSavedBbhType = renderer->getBBoxHierarchyType(); 223 renderer->setBBoxHierarchyType(bbhType); 224 } 225 226 ~AutoRestoreBbhType() { 227 if (NULL != fRenderer) { 228 fRenderer->setBBoxHierarchyType(fSavedBbhType); 229 } 230 } 231 232private: 233 sk_tools::PictureRenderer* fRenderer; 234 sk_tools::PictureRenderer::BBoxHierarchyType fSavedBbhType; 235}; 236} 237 238static bool render_picture(const SkString& inputPath, const SkString* outputDir, 239 sk_tools::PictureRenderer& renderer) { 240 int diffs[256] = {0}; 241 SkBitmap* bitmap = NULL; 242 bool success = render_picture(inputPath, 243 FLAGS_writeWholeImage ? NULL : outputDir, 244 renderer, 245 FLAGS_validate || FLAGS_writeWholeImage ? &bitmap : NULL); 246 247 if (!success || ((FLAGS_validate || FLAGS_writeWholeImage) && bitmap == NULL)) { 248 SkDebugf("Failed to draw the picture.\n"); 249 SkDELETE(bitmap); 250 return false; 251 } 252 253 if (FLAGS_validate) { 254 SkBitmap* referenceBitmap = NULL; 255 sk_tools::PictureRenderer* referenceRenderer; 256 // If the renderer uses a BBoxHierarchy, then the reference renderer 257 // will be the same renderer, without the bbh. 258 AutoRestoreBbhType arbbh; 259 if (sk_tools::PictureRenderer::kNone_BBoxHierarchyType != 260 renderer.getBBoxHierarchyType()) { 261 referenceRenderer = &renderer; 262 referenceRenderer->ref(); // to match auto unref below 263 arbbh.set(referenceRenderer, sk_tools::PictureRenderer::kNone_BBoxHierarchyType); 264 } else { 265 referenceRenderer = SkNEW(sk_tools::SimplePictureRenderer); 266 } 267 SkAutoTUnref<sk_tools::PictureRenderer> aurReferenceRenderer(referenceRenderer); 268 269 success = render_picture(inputPath, NULL, *referenceRenderer, 270 &referenceBitmap); 271 272 if (!success || NULL == referenceBitmap || NULL == referenceBitmap->getPixels()) { 273 SkDebugf("Failed to draw the reference picture.\n"); 274 SkDELETE(bitmap); 275 SkDELETE(referenceBitmap); 276 return false; 277 } 278 279 if (success && (bitmap->width() != referenceBitmap->width())) { 280 SkDebugf("Expected image width: %i, actual image width %i.\n", 281 referenceBitmap->width(), bitmap->width()); 282 SkDELETE(bitmap); 283 SkDELETE(referenceBitmap); 284 return false; 285 } 286 if (success && (bitmap->height() != referenceBitmap->height())) { 287 SkDebugf("Expected image height: %i, actual image height %i", 288 referenceBitmap->height(), bitmap->height()); 289 SkDELETE(bitmap); 290 SkDELETE(referenceBitmap); 291 return false; 292 } 293 294 for (int y = 0; success && y < bitmap->height(); y++) { 295 for (int x = 0; success && x < bitmap->width(); x++) { 296 int diff = MaxByteDiff(*referenceBitmap->getAddr32(x, y), 297 *bitmap->getAddr32(x, y)); 298 SkASSERT(diff >= 0 && diff <= 255); 299 diffs[diff]++; 300 301 if (diff > FLAGS_maxComponentDiff) { 302 SkDebugf("Expected pixel at (%i %i) exceedds maximum " 303 "component diff of %i: 0x%x, actual 0x%x\n", 304 x, y, FLAGS_maxComponentDiff, 305 *referenceBitmap->getAddr32(x, y), 306 *bitmap->getAddr32(x, y)); 307 SkDELETE(bitmap); 308 SkDELETE(referenceBitmap); 309 return false; 310 } 311 } 312 } 313 SkDELETE(referenceBitmap); 314 315 for (int i = 1; i <= 255; ++i) { 316 if(diffs[i] > 0) { 317 SkDebugf("Number of pixels with max diff of %i is %i\n", i, diffs[i]); 318 } 319 } 320 } 321 322 if (FLAGS_writeWholeImage) { 323 sk_tools::force_all_opaque(*bitmap); 324 if (NULL != outputDir && FLAGS_writeWholeImage) { 325 SkString inputFilename; 326 sk_tools::get_basename(&inputFilename, inputPath); 327 SkString outputPath; 328 make_output_filepath(&outputPath, *outputDir, inputFilename); 329 outputPath.append(".png"); 330 if (!SkImageEncoder::EncodeFile(outputPath.c_str(), *bitmap, 331 SkImageEncoder::kPNG_Type, 100)) { 332 SkDebugf("Failed to draw the picture.\n"); 333 success = false; 334 } 335 } 336 } 337 SkDELETE(bitmap); 338 339 return success; 340} 341 342 343static int process_input(const char* input, const SkString* outputDir, 344 sk_tools::PictureRenderer& renderer) { 345 SkOSFile::Iter iter(input, "skp"); 346 SkString inputFilename; 347 int failures = 0; 348 SkDebugf("process_input, %s\n", input); 349 if (iter.next(&inputFilename)) { 350 do { 351 SkString inputPath; 352 SkString inputAsSkString(input); 353 sk_tools::make_filepath(&inputPath, inputAsSkString, inputFilename); 354 if (!render_picture(inputPath, outputDir, renderer)) { 355 ++failures; 356 } 357 } while(iter.next(&inputFilename)); 358 } else if (SkStrEndsWith(input, ".skp")) { 359 SkString inputPath(input); 360 if (!render_picture(inputPath, outputDir, renderer)) { 361 ++failures; 362 } 363 } else { 364 SkString warning; 365 warning.printf("Warning: skipping %s\n", input); 366 SkDebugf(warning.c_str()); 367 } 368 return failures; 369} 370 371int tool_main(int argc, char** argv); 372int tool_main(int argc, char** argv) { 373 SkCommandLineFlags::SetUsage("Render .skp files."); 374 SkCommandLineFlags::Parse(argc, argv); 375 376 if (FLAGS_readPath.isEmpty()) { 377 SkDebugf(".skp files or directories are required.\n"); 378 exit(-1); 379 } 380 381 if (FLAGS_maxComponentDiff < 0 || FLAGS_maxComponentDiff > 256) { 382 SkDebugf("--maxComponentDiff must be between 0 and 256\n"); 383 exit(-1); 384 } 385 386 if (FLAGS_maxComponentDiff != 256 && !FLAGS_validate) { 387 SkDebugf("--maxComponentDiff requires --validate\n"); 388 exit(-1); 389 } 390 391 if (FLAGS_clone < 0) { 392 SkDebugf("--clone must be >= 0. Was %i\n", FLAGS_clone); 393 exit(-1); 394 } 395 396 if (FLAGS_writeEncodedImages) { 397 if (FLAGS_writePath.isEmpty()) { 398 SkDebugf("--writeEncodedImages requires --writePath\n"); 399 exit(-1); 400 } 401 if (FLAGS_deferImageDecoding) { 402 SkDebugf("--writeEncodedImages is not compatible with --deferImageDecoding\n"); 403 exit(-1); 404 } 405 } 406 407 SkString errorString; 408 SkAutoTUnref<sk_tools::PictureRenderer> renderer(parseRenderer(errorString, 409 kRender_PictureTool)); 410 if (errorString.size() > 0) { 411 SkDebugf("%s\n", errorString.c_str()); 412 } 413 414 if (renderer.get() == NULL) { 415 exit(-1); 416 } 417 418 SkAutoGraphics ag; 419 420 SkString outputDir; 421 if (FLAGS_writePath.count() == 1) { 422 outputDir.set(FLAGS_writePath[0]); 423 } 424 425 int failures = 0; 426 for (int i = 0; i < FLAGS_readPath.count(); i ++) { 427 failures += process_input(FLAGS_readPath[i], &outputDir, *renderer.get()); 428 } 429 if (failures != 0) { 430 SkDebugf("Failed to render %i pictures.\n", failures); 431 return 1; 432 } 433#if SK_SUPPORT_GPU 434#if GR_CACHE_STATS 435 if (renderer->isUsingGpuDevice()) { 436 GrContext* ctx = renderer->getGrContext(); 437 438 ctx->printCacheStats(); 439 } 440#endif 441#endif 442 return 0; 443} 444 445#if !defined SK_BUILD_FOR_IOS 446int main(int argc, char * const argv[]) { 447 return tool_main(argc, (char**) argv); 448} 449#endif 450