render_pictures_main.cpp revision f1754ec69131801c1a6ed3c704501a9400bbf324
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 SkPicture::InstallPixelRefProc proc; 153 if (FLAGS_deferImageDecoding) { 154 proc = &lazy_decode_bitmap; 155 } else if (FLAGS_writeEncodedImages) { 156 SkASSERT(!FLAGS_writePath.isEmpty()); 157 reset_image_file_base_name(inputFilename); 158 proc = &write_image_to_file; 159 } else { 160 proc = &SkImageDecoder::DecodeMemory; 161 } 162 163 SkDebugf("deserializing... %s\n", inputPath.c_str()); 164 165 SkPicture* picture = SkPicture::CreateFromStream(&inputStream, proc); 166 167 if (NULL == picture) { 168 SkDebugf("Could not read an SkPicture from %s\n", inputPath.c_str()); 169 return false; 170 } 171 172 for (int i = 0; i < FLAGS_clone; ++i) { 173 SkPicture* clone = picture->clone(); 174 SkDELETE(picture); 175 picture = clone; 176 } 177 178 SkDebugf("drawing... [%i %i] %s\n", picture->width(), picture->height(), 179 inputPath.c_str()); 180 181 renderer.init(picture); 182 renderer.setup(); 183 184 SkString* outputPath = NULL; 185 if (NULL != outputDir && outputDir->size() > 0 && !FLAGS_writeEncodedImages) { 186 outputPath = SkNEW(SkString); 187 make_output_filepath(outputPath, *outputDir, inputFilename); 188 } 189 190 bool success = renderer.render(outputPath, out); 191 if (outputPath) { 192 if (!success) { 193 SkDebugf("Could not write to file %s\n", outputPath->c_str()); 194 } 195 SkDELETE(outputPath); 196 } 197 198 renderer.end(); 199 200 SkDELETE(picture); 201 return success; 202} 203 204static inline int getByte(uint32_t value, int index) { 205 SkASSERT(0 <= index && index < 4); 206 return (value >> (index * 8)) & 0xFF; 207} 208 209static int MaxByteDiff(uint32_t v1, uint32_t v2) { 210 return SkMax32(SkMax32(abs(getByte(v1, 0) - getByte(v2, 0)), abs(getByte(v1, 1) - getByte(v2, 1))), 211 SkMax32(abs(getByte(v1, 2) - getByte(v2, 2)), abs(getByte(v1, 3) - getByte(v2, 3)))); 212} 213 214namespace { 215class AutoRestoreBbhType { 216public: 217 AutoRestoreBbhType() { 218 fRenderer = NULL; 219 fSavedBbhType = sk_tools::PictureRenderer::kNone_BBoxHierarchyType; 220 } 221 222 void set(sk_tools::PictureRenderer* renderer, 223 sk_tools::PictureRenderer::BBoxHierarchyType bbhType) { 224 fRenderer = renderer; 225 fSavedBbhType = renderer->getBBoxHierarchyType(); 226 renderer->setBBoxHierarchyType(bbhType); 227 } 228 229 ~AutoRestoreBbhType() { 230 if (NULL != fRenderer) { 231 fRenderer->setBBoxHierarchyType(fSavedBbhType); 232 } 233 } 234 235private: 236 sk_tools::PictureRenderer* fRenderer; 237 sk_tools::PictureRenderer::BBoxHierarchyType fSavedBbhType; 238}; 239} 240 241static bool render_picture(const SkString& inputPath, const SkString* outputDir, 242 sk_tools::PictureRenderer& renderer) { 243 int diffs[256] = {0}; 244 SkBitmap* bitmap = NULL; 245 bool success = render_picture(inputPath, 246 FLAGS_writeWholeImage ? NULL : outputDir, 247 renderer, 248 FLAGS_validate || FLAGS_writeWholeImage ? &bitmap : NULL); 249 250 if (!success || ((FLAGS_validate || FLAGS_writeWholeImage) && bitmap == NULL)) { 251 SkDebugf("Failed to draw the picture.\n"); 252 SkDELETE(bitmap); 253 return false; 254 } 255 256 if (FLAGS_validate) { 257 SkBitmap* referenceBitmap = NULL; 258 sk_tools::PictureRenderer* referenceRenderer; 259 // If the renderer uses a BBoxHierarchy, then the reference renderer 260 // will be the same renderer, without the bbh. 261 AutoRestoreBbhType arbbh; 262 if (sk_tools::PictureRenderer::kNone_BBoxHierarchyType != 263 renderer.getBBoxHierarchyType()) { 264 referenceRenderer = &renderer; 265 referenceRenderer->ref(); // to match auto unref below 266 arbbh.set(referenceRenderer, sk_tools::PictureRenderer::kNone_BBoxHierarchyType); 267 } else { 268 referenceRenderer = SkNEW(sk_tools::SimplePictureRenderer); 269 } 270 SkAutoTUnref<sk_tools::PictureRenderer> aurReferenceRenderer(referenceRenderer); 271 272 success = render_picture(inputPath, NULL, *referenceRenderer, 273 &referenceBitmap); 274 275 if (!success || NULL == referenceBitmap || NULL == referenceBitmap->getPixels()) { 276 SkDebugf("Failed to draw the reference picture.\n"); 277 SkDELETE(bitmap); 278 SkDELETE(referenceBitmap); 279 return false; 280 } 281 282 if (success && (bitmap->width() != referenceBitmap->width())) { 283 SkDebugf("Expected image width: %i, actual image width %i.\n", 284 referenceBitmap->width(), bitmap->width()); 285 SkDELETE(bitmap); 286 SkDELETE(referenceBitmap); 287 return false; 288 } 289 if (success && (bitmap->height() != referenceBitmap->height())) { 290 SkDebugf("Expected image height: %i, actual image height %i", 291 referenceBitmap->height(), bitmap->height()); 292 SkDELETE(bitmap); 293 SkDELETE(referenceBitmap); 294 return false; 295 } 296 297 for (int y = 0; success && y < bitmap->height(); y++) { 298 for (int x = 0; success && x < bitmap->width(); x++) { 299 int diff = MaxByteDiff(*referenceBitmap->getAddr32(x, y), 300 *bitmap->getAddr32(x, y)); 301 SkASSERT(diff >= 0 && diff <= 255); 302 diffs[diff]++; 303 304 if (diff > FLAGS_maxComponentDiff) { 305 SkDebugf("Expected pixel at (%i %i) exceedds maximum " 306 "component diff of %i: 0x%x, actual 0x%x\n", 307 x, y, FLAGS_maxComponentDiff, 308 *referenceBitmap->getAddr32(x, y), 309 *bitmap->getAddr32(x, y)); 310 SkDELETE(bitmap); 311 SkDELETE(referenceBitmap); 312 return false; 313 } 314 } 315 } 316 SkDELETE(referenceBitmap); 317 318 for (int i = 1; i <= 255; ++i) { 319 if(diffs[i] > 0) { 320 SkDebugf("Number of pixels with max diff of %i is %i\n", i, diffs[i]); 321 } 322 } 323 } 324 325 if (FLAGS_writeWholeImage) { 326 sk_tools::force_all_opaque(*bitmap); 327 if (NULL != outputDir && FLAGS_writeWholeImage) { 328 SkString inputFilename; 329 sk_tools::get_basename(&inputFilename, inputPath); 330 SkString outputPath; 331 make_output_filepath(&outputPath, *outputDir, inputFilename); 332 outputPath.append(".png"); 333 if (!SkImageEncoder::EncodeFile(outputPath.c_str(), *bitmap, 334 SkImageEncoder::kPNG_Type, 100)) { 335 SkDebugf("Failed to draw the picture.\n"); 336 success = false; 337 } 338 } 339 } 340 SkDELETE(bitmap); 341 342 return success; 343} 344 345 346static int process_input(const char* input, const SkString* outputDir, 347 sk_tools::PictureRenderer& renderer) { 348 SkOSFile::Iter iter(input, "skp"); 349 SkString inputFilename; 350 int failures = 0; 351 SkDebugf("process_input, %s\n", input); 352 if (iter.next(&inputFilename)) { 353 do { 354 SkString inputPath; 355 SkString inputAsSkString(input); 356 sk_tools::make_filepath(&inputPath, inputAsSkString, inputFilename); 357 if (!render_picture(inputPath, outputDir, renderer)) { 358 ++failures; 359 } 360 } while(iter.next(&inputFilename)); 361 } else if (SkStrEndsWith(input, ".skp")) { 362 SkString inputPath(input); 363 if (!render_picture(inputPath, outputDir, renderer)) { 364 ++failures; 365 } 366 } else { 367 SkString warning; 368 warning.printf("Warning: skipping %s\n", input); 369 SkDebugf(warning.c_str()); 370 } 371 return failures; 372} 373 374int tool_main(int argc, char** argv); 375int tool_main(int argc, char** argv) { 376 SkCommandLineFlags::SetUsage("Render .skp files."); 377 SkCommandLineFlags::Parse(argc, argv); 378 379 if (FLAGS_readPath.isEmpty()) { 380 SkDebugf(".skp files or directories are required.\n"); 381 exit(-1); 382 } 383 384 if (FLAGS_maxComponentDiff < 0 || FLAGS_maxComponentDiff > 256) { 385 SkDebugf("--maxComponentDiff must be between 0 and 256\n"); 386 exit(-1); 387 } 388 389 if (FLAGS_maxComponentDiff != 256 && !FLAGS_validate) { 390 SkDebugf("--maxComponentDiff requires --validate\n"); 391 exit(-1); 392 } 393 394 if (FLAGS_clone < 0) { 395 SkDebugf("--clone must be >= 0. Was %i\n", FLAGS_clone); 396 exit(-1); 397 } 398 399 if (FLAGS_writeEncodedImages) { 400 if (FLAGS_writePath.isEmpty()) { 401 SkDebugf("--writeEncodedImages requires --writePath\n"); 402 exit(-1); 403 } 404 if (FLAGS_deferImageDecoding) { 405 SkDebugf("--writeEncodedImages is not compatible with --deferImageDecoding\n"); 406 exit(-1); 407 } 408 } 409 410 SkString errorString; 411 SkAutoTUnref<sk_tools::PictureRenderer> renderer(parseRenderer(errorString, 412 kRender_PictureTool)); 413 if (errorString.size() > 0) { 414 SkDebugf("%s\n", errorString.c_str()); 415 } 416 417 if (renderer.get() == NULL) { 418 exit(-1); 419 } 420 421 SkAutoGraphics ag; 422 423 SkString outputDir; 424 if (FLAGS_writePath.count() == 1) { 425 outputDir.set(FLAGS_writePath[0]); 426 } 427 428 int failures = 0; 429 for (int i = 0; i < FLAGS_readPath.count(); i ++) { 430 failures += process_input(FLAGS_readPath[i], &outputDir, *renderer.get()); 431 } 432 if (failures != 0) { 433 SkDebugf("Failed to render %i pictures.\n", failures); 434 return 1; 435 } 436#if SK_SUPPORT_GPU 437#if GR_CACHE_STATS 438 if (renderer->isUsingGpuDevice()) { 439 GrContext* ctx = renderer->getGrContext(); 440 441 ctx->printCacheStats(); 442 } 443#endif 444#endif 445 return 0; 446} 447 448#if !defined SK_BUILD_FOR_IOS 449int main(int argc, char * const argv[]) { 450 return tool_main(argc, (char**) argv); 451} 452#endif 453