render_pictures_main.cpp revision 03682beb8c1c5dfe714933e9419e1412b33c932d
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 "SkBitmapFactory.h" 11#include "SkCanvas.h" 12#include "SkDevice.h" 13#include "SkFlags.h" 14#include "SkGraphics.h" 15#include "SkImageDecoder.h" 16#include "SkImageEncoder.h" 17#include "SkMath.h" 18#include "SkOSFile.h" 19#include "SkPicture.h" 20#include "SkStream.h" 21#include "SkString.h" 22#include "SkTArray.h" 23#include "PictureRenderer.h" 24#include "PictureRenderingFlags.h" 25#include "picture_utils.h" 26 27// Flags used by this file, alphabetically: 28DEFINE_int32(clone, 0, "Clone the picture n times before rendering."); 29DECLARE_bool(deferImageDecoding); 30DEFINE_int32(maxComponentDiff, 256, "Maximum diff on a component, 0 - 256. Components that differ " 31 "by more than this amount are considered errors, though all diffs are reported. " 32 "Requires --validate."); 33DECLARE_string(r); 34DEFINE_string(w, "", "Directory to write the rendered images."); 35DEFINE_bool(writeWholeImage, false, "In tile mode, write the entire rendered image to a " 36 "file, instead of an image for each tile."); 37DEFINE_bool(validate, false, "Verify that the rendered image contains the same pixels as " 38 "the picture rendered in simple mode. When used in conjunction with --bbh, results " 39 "are validated against the picture rendered in the same mode, but without the bbh."); 40 41static void make_output_filepath(SkString* path, const SkString& dir, 42 const SkString& name) { 43 sk_tools::make_filepath(path, dir, name); 44 // Remove ".skp" 45 path->remove(path->size() - 4, 4); 46} 47 48#include "SkData.h" 49#include "SkLruImageCache.h" 50 51static SkLruImageCache gLruImageCache(1024*1024); 52 53#ifdef SK_BUILD_FOR_ANDROID 54#include "SkAshmemImageCache.h" 55#include "SkImage.h" 56 57static SkImageCache* cache_selector(const SkImage::Info& info) { 58 if (info.fWidth * info.fHeight > 32 * 1024) { 59 return SkAshmemImageCache::GetAshmemImageCache(); 60 } 61 return &gLruImageCache; 62} 63 64#endif 65 66static bool lazy_decode_bitmap(const void* buffer, size_t size, SkBitmap* bitmap) { 67 void* copiedBuffer = sk_malloc_throw(size); 68 memcpy(copiedBuffer, buffer, size); 69 SkAutoDataUnref data(SkData::NewFromMalloc(copiedBuffer, size)); 70 SkBitmapFactory factory(&SkImageDecoder::DecodeMemoryToTarget); 71#ifdef SK_BUILD_FOR_ANDROID 72 factory.setCacheSelector(&cache_selector); 73#else 74 factory.setImageCache(&gLruImageCache); 75#endif 76 return factory.installPixelRef(data, bitmap); 77} 78 79static bool render_picture(const SkString& inputPath, const SkString* outputDir, 80 sk_tools::PictureRenderer& renderer, 81 SkBitmap** out) { 82 SkString inputFilename; 83 sk_tools::get_basename(&inputFilename, inputPath); 84 85 SkFILEStream inputStream; 86 inputStream.setPath(inputPath.c_str()); 87 if (!inputStream.isValid()) { 88 SkDebugf("Could not open file %s\n", inputPath.c_str()); 89 return false; 90 } 91 92 bool success = false; 93 SkPicture* picture; 94 if (FLAGS_deferImageDecoding) { 95 picture = SkNEW_ARGS(SkPicture, (&inputStream, &success, &lazy_decode_bitmap)); 96 } else { 97 picture = SkNEW_ARGS(SkPicture, (&inputStream, &success, &SkImageDecoder::DecodeMemory)); 98 } 99 if (!success) { 100 SkDebugf("Could not read an SkPicture from %s\n", inputPath.c_str()); 101 return false; 102 } 103 104 for (int i = 0; i < FLAGS_clone; ++i) { 105 SkPicture* clone = picture->clone(); 106 SkDELETE(picture); 107 picture = clone; 108 } 109 110 SkDebugf("drawing... [%i %i] %s\n", picture->width(), picture->height(), 111 inputPath.c_str()); 112 113 renderer.init(picture); 114 renderer.setup(); 115 116 SkString* outputPath = NULL; 117 if (NULL != outputDir && outputDir->size() > 0) { 118 outputPath = SkNEW(SkString); 119 make_output_filepath(outputPath, *outputDir, inputFilename); 120 } 121 122 success = renderer.render(outputPath, out); 123 if (outputPath) { 124 if (!success) { 125 SkDebugf("Could not write to file %s\n", outputPath->c_str()); 126 } 127 SkDELETE(outputPath); 128 } 129 130 renderer.end(); 131 132 SkDELETE(picture); 133 return success; 134} 135 136static inline int getByte(uint32_t value, int index) { 137 SkASSERT(0 <= index && index < 4); 138 return (value >> (index * 8)) & 0xFF; 139} 140 141static int MaxByteDiff(uint32_t v1, uint32_t v2) { 142 return SkMax32(SkMax32(abs(getByte(v1, 0) - getByte(v2, 0)), abs(getByte(v1, 1) - getByte(v2, 1))), 143 SkMax32(abs(getByte(v1, 2) - getByte(v2, 2)), abs(getByte(v1, 3) - getByte(v2, 3)))); 144} 145 146namespace { 147class AutoRestoreBbhType { 148public: 149 AutoRestoreBbhType() { 150 fRenderer = NULL; 151 fSavedBbhType = sk_tools::PictureRenderer::kNone_BBoxHierarchyType; 152 } 153 154 void set(sk_tools::PictureRenderer* renderer, 155 sk_tools::PictureRenderer::BBoxHierarchyType bbhType) { 156 fRenderer = renderer; 157 fSavedBbhType = renderer->getBBoxHierarchyType(); 158 renderer->setBBoxHierarchyType(bbhType); 159 } 160 161 ~AutoRestoreBbhType() { 162 if (NULL != fRenderer) { 163 fRenderer->setBBoxHierarchyType(fSavedBbhType); 164 } 165 } 166 167private: 168 sk_tools::PictureRenderer* fRenderer; 169 sk_tools::PictureRenderer::BBoxHierarchyType fSavedBbhType; 170}; 171} 172 173static bool render_picture(const SkString& inputPath, const SkString* outputDir, 174 sk_tools::PictureRenderer& renderer) { 175 int diffs[256] = {0}; 176 SkBitmap* bitmap = NULL; 177 bool success = render_picture(inputPath, 178 FLAGS_writeWholeImage ? NULL : outputDir, 179 renderer, 180 FLAGS_validate || FLAGS_writeWholeImage ? &bitmap : NULL); 181 182 if (!success || ((FLAGS_validate || FLAGS_writeWholeImage) && bitmap == NULL)) { 183 SkDebugf("Failed to draw the picture.\n"); 184 SkDELETE(bitmap); 185 return false; 186 } 187 188 if (FLAGS_validate) { 189 SkBitmap* referenceBitmap = NULL; 190 sk_tools::PictureRenderer* referenceRenderer; 191 // If the renderer uses a BBoxHierarchy, then the reference renderer 192 // will be the same renderer, without the bbh. 193 AutoRestoreBbhType arbbh; 194 if (sk_tools::PictureRenderer::kNone_BBoxHierarchyType != 195 renderer.getBBoxHierarchyType()) { 196 referenceRenderer = &renderer; 197 referenceRenderer->ref(); // to match auto unref below 198 arbbh.set(referenceRenderer, sk_tools::PictureRenderer::kNone_BBoxHierarchyType); 199 } else { 200 referenceRenderer = SkNEW(sk_tools::SimplePictureRenderer); 201 } 202 SkAutoTUnref<sk_tools::PictureRenderer> aurReferenceRenderer(referenceRenderer); 203 204 success = render_picture(inputPath, NULL, *referenceRenderer, 205 &referenceBitmap); 206 207 if (!success || NULL == referenceBitmap || NULL == referenceBitmap->getPixels()) { 208 SkDebugf("Failed to draw the reference picture.\n"); 209 SkDELETE(bitmap); 210 SkDELETE(referenceBitmap); 211 return false; 212 } 213 214 if (success && (bitmap->width() != referenceBitmap->width())) { 215 SkDebugf("Expected image width: %i, actual image width %i.\n", 216 referenceBitmap->width(), bitmap->width()); 217 SkDELETE(bitmap); 218 SkDELETE(referenceBitmap); 219 return false; 220 } 221 if (success && (bitmap->height() != referenceBitmap->height())) { 222 SkDebugf("Expected image height: %i, actual image height %i", 223 referenceBitmap->height(), bitmap->height()); 224 SkDELETE(bitmap); 225 SkDELETE(referenceBitmap); 226 return false; 227 } 228 229 for (int y = 0; success && y < bitmap->height(); y++) { 230 for (int x = 0; success && x < bitmap->width(); x++) { 231 int diff = MaxByteDiff(*referenceBitmap->getAddr32(x, y), 232 *bitmap->getAddr32(x, y)); 233 SkASSERT(diff >= 0 && diff <= 255); 234 diffs[diff]++; 235 236 if (diff > FLAGS_maxComponentDiff) { 237 SkDebugf("Expected pixel at (%i %i) exceedds maximum " 238 "component diff of %i: 0x%x, actual 0x%x\n", 239 x, y, FLAGS_maxComponentDiff, 240 *referenceBitmap->getAddr32(x, y), 241 *bitmap->getAddr32(x, y)); 242 SkDELETE(bitmap); 243 SkDELETE(referenceBitmap); 244 return false; 245 } 246 } 247 } 248 SkDELETE(referenceBitmap); 249 250 for (int i = 1; i <= 255; ++i) { 251 if(diffs[i] > 0) { 252 SkDebugf("Number of pixels with max diff of %i is %i\n", i, diffs[i]); 253 } 254 } 255 } 256 257 if (FLAGS_writeWholeImage) { 258 sk_tools::force_all_opaque(*bitmap); 259 if (NULL != outputDir && FLAGS_writeWholeImage) { 260 SkString inputFilename; 261 sk_tools::get_basename(&inputFilename, inputPath); 262 SkString outputPath; 263 make_output_filepath(&outputPath, *outputDir, inputFilename); 264 outputPath.append(".png"); 265 if (!SkImageEncoder::EncodeFile(outputPath.c_str(), *bitmap, 266 SkImageEncoder::kPNG_Type, 100)) { 267 SkDebugf("Failed to draw the picture.\n"); 268 success = false; 269 } 270 } 271 } 272 SkDELETE(bitmap); 273 274 return success; 275} 276 277 278static int process_input(const char* input, const SkString* outputDir, 279 sk_tools::PictureRenderer& renderer) { 280 SkOSFile::Iter iter(input, "skp"); 281 SkString inputFilename; 282 int failures = 0; 283 SkDebugf("process_input, %s\n", input); 284 if (iter.next(&inputFilename)) { 285 do { 286 SkString inputPath; 287 SkString inputAsSkString(input); 288 sk_tools::make_filepath(&inputPath, inputAsSkString, inputFilename); 289 if (!render_picture(inputPath, outputDir, renderer)) { 290 ++failures; 291 } 292 } while(iter.next(&inputFilename)); 293 } else if (SkStrEndsWith(input, ".skp")) { 294 SkString inputPath(input); 295 if (!render_picture(inputPath, outputDir, renderer)) { 296 ++failures; 297 } 298 } else { 299 SkString warning; 300 warning.printf("Warning: skipping %s\n", input); 301 SkDebugf(warning.c_str()); 302 } 303 return failures; 304} 305 306int tool_main(int argc, char** argv); 307int tool_main(int argc, char** argv) { 308 SkFlags::SetUsage("Render .skp files."); 309 SkFlags::ParseCommandLine(argc, argv); 310 311 if (FLAGS_r.isEmpty()) { 312 SkDebugf(".skp files or directories are required.\n"); 313 exit(-1); 314 } 315 316 if (FLAGS_maxComponentDiff < 0 || FLAGS_maxComponentDiff > 256) { 317 SkDebugf("--maxComponentDiff must be between 0 and 256\n"); 318 exit(-1); 319 } 320 321 if (FLAGS_maxComponentDiff != 256 && !FLAGS_validate) { 322 SkDebugf("--maxComponentDiff requires --validate\n"); 323 exit(-1); 324 } 325 326 if (FLAGS_clone < 0) { 327 SkDebugf("--clone must be >= 0. Was %i\n", FLAGS_clone); 328 exit(-1); 329 } 330 331 SkString errorString; 332 SkAutoTUnref<sk_tools::PictureRenderer> renderer(parseRenderer(errorString, 333 kRender_PictureTool)); 334 if (errorString.size() > 0) { 335 SkDebugf("%s\n", errorString.c_str()); 336 } 337 338 if (renderer.get() == NULL) { 339 exit(-1); 340 } 341 342 SkAutoGraphics ag; 343 344 SkString outputDir; 345 if (FLAGS_w.count() == 1) { 346 outputDir.set(FLAGS_w[0]); 347 } 348 349 int failures = 0; 350 for (int i = 0; i < FLAGS_r.count(); i ++) { 351 failures += process_input(FLAGS_r[i], &outputDir, *renderer.get()); 352 } 353 if (failures != 0) { 354 SkDebugf("Failed to render %i pictures.\n", failures); 355 return 1; 356 } 357#if SK_SUPPORT_GPU 358#if GR_CACHE_STATS 359 if (renderer->isUsingGpuDevice()) { 360 GrContext* ctx = renderer->getGrContext(); 361 362 ctx->printCacheStats(); 363 } 364#endif 365#endif 366 return 0; 367} 368 369#if !defined SK_BUILD_FOR_IOS 370int main(int argc, char * const argv[]) { 371 return tool_main(argc, (char**) argv); 372} 373#endif 374