diff options
Diffstat (limited to 'chromium/third_party/skia/tools/render_pictures_main.cpp')
-rw-r--r-- | chromium/third_party/skia/tools/render_pictures_main.cpp | 503 |
1 files changed, 503 insertions, 0 deletions
diff --git a/chromium/third_party/skia/tools/render_pictures_main.cpp b/chromium/third_party/skia/tools/render_pictures_main.cpp new file mode 100644 index 00000000000..850053cfc6f --- /dev/null +++ b/chromium/third_party/skia/tools/render_pictures_main.cpp @@ -0,0 +1,503 @@ +/* + * Copyright 2012 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "LazyDecodeBitmap.h" +#include "CopyTilesRenderer.h" +#include "SkBitmap.h" +#include "SkDevice.h" +#include "SkCommandLineFlags.h" +#include "SkGraphics.h" +#include "SkImageDecoder.h" +#include "SkImageEncoder.h" +#include "SkMath.h" +#include "SkOSFile.h" +#include "SkPicture.h" +#include "SkPictureRecorder.h" +#include "SkStream.h" +#include "SkString.h" + +#include "image_expectations.h" +#include "PictureRenderer.h" +#include "PictureRenderingFlags.h" +#include "picture_utils.h" + +// Flags used by this file, alphabetically: +DEFINE_int32(clone, 0, "Clone the picture n times before rendering."); +DECLARE_bool(deferImageDecoding); +DEFINE_int32(maxComponentDiff, 256, "Maximum diff on a component, 0 - 256. Components that differ " + "by more than this amount are considered errors, though all diffs are reported. " + "Requires --validate."); +DEFINE_string(mismatchPath, "", "Write images for tests that failed due to " + "pixel mismatches into this directory."); +DEFINE_string(readJsonSummaryPath, "", "JSON file to read image expectations from."); +DECLARE_string(readPath); +DEFINE_bool(writeChecksumBasedFilenames, false, + "When writing out images, use checksum-based filenames."); +DEFINE_bool(writeEncodedImages, false, "Any time the skp contains an encoded image, write it to a " + "file rather than decoding it. Requires writePath to be set. Skips drawing the full " + "skp to a file. Not compatible with deferImageDecoding."); +DEFINE_string(writeJsonSummaryPath, "", "File to write a JSON summary of image results to."); +DEFINE_string2(writePath, w, "", "Directory to write the rendered images into."); +DEFINE_bool(writeWholeImage, false, "In tile mode, write the entire rendered image to a " + "file, instead of an image for each tile."); +DEFINE_bool(validate, false, "Verify that the rendered image contains the same pixels as " + "the picture rendered in simple mode. When used in conjunction with --bbh, results " + "are validated against the picture rendered in the same mode, but without the bbh."); + +DEFINE_bool(bench_record, false, "If true, drop into an infinite loop of recording the picture."); + +DEFINE_bool(preprocess, false, "If true, perform device specific preprocessing before rendering."); + +//////////////////////////////////////////////////////////////////////////////////////////////////// + +/** + * Table for translating from format of data to a suffix. + */ +struct Format { + SkImageDecoder::Format fFormat; + const char* fSuffix; +}; +static const Format gFormats[] = { + { SkImageDecoder::kBMP_Format, ".bmp" }, + { SkImageDecoder::kGIF_Format, ".gif" }, + { SkImageDecoder::kICO_Format, ".ico" }, + { SkImageDecoder::kJPEG_Format, ".jpg" }, + { SkImageDecoder::kPNG_Format, ".png" }, + { SkImageDecoder::kWBMP_Format, ".wbmp" }, + { SkImageDecoder::kWEBP_Format, ".webp" }, + { SkImageDecoder::kUnknown_Format, "" }, +}; + +/** + * Get an appropriate suffix for an image format. + */ +static const char* get_suffix_from_format(SkImageDecoder::Format format) { + for (size_t i = 0; i < SK_ARRAY_COUNT(gFormats); i++) { + if (gFormats[i].fFormat == format) { + return gFormats[i].fSuffix; + } + } + return ""; +} + +/** + * Base name for an image file created from the encoded data in an skp. + */ +static SkString gInputFileName; + +/** + * Number to be appended to the image file name so that it is unique. + */ +static uint32_t gImageNo; + +/** + * Set up the name for writing encoded data to a file. + * Sets gInputFileName to name, minus any extension ".*" + * Sets gImageNo to 0, so images from file "X.skp" will + * look like "X_<gImageNo>.<suffix>", beginning with 0 + * for each new skp. + */ +static void reset_image_file_base_name(const SkString& name) { + gImageNo = 0; + // Remove ".skp" + const char* cName = name.c_str(); + const char* dot = strrchr(cName, '.'); + if (dot != NULL) { + gInputFileName.set(cName, dot - cName); + } else { + gInputFileName.set(name); + } +} + +/** + * Write the raw encoded bitmap data to a file. + */ +static bool write_image_to_file(const void* buffer, size_t size, SkBitmap* bitmap) { + SkASSERT(!FLAGS_writePath.isEmpty()); + SkMemoryStream memStream(buffer, size); + SkString outPath; + SkImageDecoder::Format format = SkImageDecoder::GetStreamFormat(&memStream); + SkString name = SkStringPrintf("%s_%d%s", gInputFileName.c_str(), gImageNo++, + get_suffix_from_format(format)); + SkString dir(FLAGS_writePath[0]); + outPath = SkOSPath::SkPathJoin(dir.c_str(), name.c_str()); + SkFILEWStream fileStream(outPath.c_str()); + if (!(fileStream.isValid() && fileStream.write(buffer, size))) { + SkDebugf("Failed to write encoded data to \"%s\"\n", outPath.c_str()); + } + // Put in a dummy bitmap. + return SkImageDecoder::DecodeStream(&memStream, bitmap, kUnknown_SkColorType, + SkImageDecoder::kDecodeBounds_Mode); +} + +//////////////////////////////////////////////////////////////////////////////////////////////////// + +/** + * Called only by render_picture(). + */ +static bool render_picture_internal(const SkString& inputPath, const SkString* writePath, + const SkString* mismatchPath, + sk_tools::PictureRenderer& renderer, + SkBitmap** out) { + SkString inputFilename = SkOSPath::SkBasename(inputPath.c_str()); + SkString writePathString; + if (NULL != writePath && writePath->size() > 0 && !FLAGS_writeEncodedImages) { + writePathString.set(*writePath); + } + SkString mismatchPathString; + if (NULL != mismatchPath && mismatchPath->size() > 0) { + mismatchPathString.set(*mismatchPath); + } + + SkFILEStream inputStream; + inputStream.setPath(inputPath.c_str()); + if (!inputStream.isValid()) { + SkDebugf("Could not open file %s\n", inputPath.c_str()); + return false; + } + + SkPicture::InstallPixelRefProc proc; + if (FLAGS_deferImageDecoding) { + proc = &sk_tools::LazyDecodeBitmap; + } else if (FLAGS_writeEncodedImages) { + SkASSERT(!FLAGS_writePath.isEmpty()); + reset_image_file_base_name(inputFilename); + proc = &write_image_to_file; + } else { + proc = &SkImageDecoder::DecodeMemory; + } + + SkDebugf("deserializing... %s\n", inputPath.c_str()); + + SkPicture* picture = SkPicture::CreateFromStream(&inputStream, proc); + + if (NULL == picture) { + SkDebugf("Could not read an SkPicture from %s\n", inputPath.c_str()); + return false; + } + + while (FLAGS_bench_record) { + SkPictureRecorder recorder; + picture->draw(recorder.beginRecording(picture->width(), picture->height(), NULL, 0)); + SkAutoTUnref<SkPicture> other(recorder.endRecording()); + } + + for (int i = 0; i < FLAGS_clone; ++i) { + SkPicture* clone = picture->clone(); + SkDELETE(picture); + picture = clone; + } + + SkDebugf("drawing... [%i %i] %s\n", picture->width(), picture->height(), + inputPath.c_str()); + + renderer.init(picture, &writePathString, &mismatchPathString, &inputFilename, + FLAGS_writeChecksumBasedFilenames); + + if (FLAGS_preprocess) { + if (NULL != renderer.getCanvas()) { + renderer.getCanvas()->EXPERIMENTAL_optimize(renderer.getPicture()); + } + } + + renderer.setup(); + renderer.enableWrites(); + + bool success = renderer.render(out); + if (!success) { + SkDebugf("Failed to render %s\n", inputFilename.c_str()); + } + + renderer.end(); + + SkDELETE(picture); + return success; +} + +static inline int getByte(uint32_t value, int index) { + SkASSERT(0 <= index && index < 4); + return (value >> (index * 8)) & 0xFF; +} + +static int MaxByteDiff(uint32_t v1, uint32_t v2) { + return SkMax32(SkMax32(abs(getByte(v1, 0) - getByte(v2, 0)), abs(getByte(v1, 1) - getByte(v2, 1))), + SkMax32(abs(getByte(v1, 2) - getByte(v2, 2)), abs(getByte(v1, 3) - getByte(v2, 3)))); +} + +class AutoRestoreBbhType { +public: + AutoRestoreBbhType() { + fRenderer = NULL; + fSavedBbhType = sk_tools::PictureRenderer::kNone_BBoxHierarchyType; + } + + void set(sk_tools::PictureRenderer* renderer, + sk_tools::PictureRenderer::BBoxHierarchyType bbhType) { + fRenderer = renderer; + fSavedBbhType = renderer->getBBoxHierarchyType(); + renderer->setBBoxHierarchyType(bbhType); + } + + ~AutoRestoreBbhType() { + if (NULL != fRenderer) { + fRenderer->setBBoxHierarchyType(fSavedBbhType); + } + } + +private: + sk_tools::PictureRenderer* fRenderer; + sk_tools::PictureRenderer::BBoxHierarchyType fSavedBbhType; +}; + +/** + * Render the SKP file(s) within inputPath. + * + * @param inputPath path to an individual SKP file, or a directory of SKP files + * @param writePath if not NULL, write all image(s) generated into this directory + * @param mismatchPath if not NULL, write any image(s) not matching expectations into this directory + * @param renderer PictureRenderer to use to render the SKPs + * @param jsonSummaryPtr if not NULL, add the image(s) generated to this summary + */ +static bool render_picture(const SkString& inputPath, const SkString* writePath, + const SkString* mismatchPath, sk_tools::PictureRenderer& renderer, + sk_tools::ImageResultsAndExpectations *jsonSummaryPtr) { + int diffs[256] = {0}; + SkBitmap* bitmap = NULL; + renderer.setJsonSummaryPtr(jsonSummaryPtr); + bool success = render_picture_internal(inputPath, + FLAGS_writeWholeImage ? NULL : writePath, + FLAGS_writeWholeImage ? NULL : mismatchPath, + renderer, + FLAGS_validate || FLAGS_writeWholeImage ? &bitmap : NULL); + + if (!success || ((FLAGS_validate || FLAGS_writeWholeImage) && bitmap == NULL)) { + SkDebugf("Failed to draw the picture.\n"); + SkDELETE(bitmap); + return false; + } + + if (FLAGS_validate) { + SkBitmap* referenceBitmap = NULL; + sk_tools::PictureRenderer* referenceRenderer; + // If the renderer uses a BBoxHierarchy, then the reference renderer + // will be the same renderer, without the bbh. + AutoRestoreBbhType arbbh; + if (sk_tools::PictureRenderer::kNone_BBoxHierarchyType != + renderer.getBBoxHierarchyType()) { + referenceRenderer = &renderer; + referenceRenderer->ref(); // to match auto unref below + arbbh.set(referenceRenderer, sk_tools::PictureRenderer::kNone_BBoxHierarchyType); + } else { + referenceRenderer = SkNEW(sk_tools::SimplePictureRenderer); + } + SkAutoTUnref<sk_tools::PictureRenderer> aurReferenceRenderer(referenceRenderer); + + success = render_picture_internal(inputPath, NULL, NULL, *referenceRenderer, + &referenceBitmap); + + if (!success || NULL == referenceBitmap || NULL == referenceBitmap->getPixels()) { + SkDebugf("Failed to draw the reference picture.\n"); + SkDELETE(bitmap); + SkDELETE(referenceBitmap); + return false; + } + + if (success && (bitmap->width() != referenceBitmap->width())) { + SkDebugf("Expected image width: %i, actual image width %i.\n", + referenceBitmap->width(), bitmap->width()); + SkDELETE(bitmap); + SkDELETE(referenceBitmap); + return false; + } + if (success && (bitmap->height() != referenceBitmap->height())) { + SkDebugf("Expected image height: %i, actual image height %i", + referenceBitmap->height(), bitmap->height()); + SkDELETE(bitmap); + SkDELETE(referenceBitmap); + return false; + } + + for (int y = 0; success && y < bitmap->height(); y++) { + for (int x = 0; success && x < bitmap->width(); x++) { + int diff = MaxByteDiff(*referenceBitmap->getAddr32(x, y), + *bitmap->getAddr32(x, y)); + SkASSERT(diff >= 0 && diff <= 255); + diffs[diff]++; + + if (diff > FLAGS_maxComponentDiff) { + SkDebugf("Expected pixel at (%i %i) exceedds maximum " + "component diff of %i: 0x%x, actual 0x%x\n", + x, y, FLAGS_maxComponentDiff, + *referenceBitmap->getAddr32(x, y), + *bitmap->getAddr32(x, y)); + SkDELETE(bitmap); + SkDELETE(referenceBitmap); + return false; + } + } + } + SkDELETE(referenceBitmap); + + for (int i = 1; i <= 255; ++i) { + if(diffs[i] > 0) { + SkDebugf("Number of pixels with max diff of %i is %i\n", i, diffs[i]); + } + } + } + + if (FLAGS_writeWholeImage) { + sk_tools::force_all_opaque(*bitmap); + + SkString inputFilename = SkOSPath::SkBasename(inputPath.c_str()); + SkString outputFilename(inputFilename); + sk_tools::replace_char(&outputFilename, '.', '_'); + outputFilename.append(".png"); + + if (NULL != jsonSummaryPtr) { + sk_tools::ImageDigest imageDigest(*bitmap); + jsonSummaryPtr->add(inputFilename.c_str(), outputFilename.c_str(), imageDigest); + if ((NULL != mismatchPath) && !mismatchPath->isEmpty() && + !jsonSummaryPtr->matchesExpectation(inputFilename.c_str(), imageDigest)) { + success &= sk_tools::write_bitmap_to_disk(*bitmap, *mismatchPath, NULL, + outputFilename); + } + } + + if ((NULL != writePath) && !writePath->isEmpty()) { + success &= sk_tools::write_bitmap_to_disk(*bitmap, *writePath, NULL, outputFilename); + } + } + SkDELETE(bitmap); + + return success; +} + + +static int process_input(const char* input, const SkString* writePath, + const SkString* mismatchPath, sk_tools::PictureRenderer& renderer, + sk_tools::ImageResultsAndExpectations *jsonSummaryPtr) { + SkOSFile::Iter iter(input, "skp"); + SkString inputFilename; + int failures = 0; + SkDebugf("process_input, %s\n", input); + if (iter.next(&inputFilename)) { + do { + SkString inputPath = SkOSPath::SkPathJoin(input, inputFilename.c_str()); + if (!render_picture(inputPath, writePath, mismatchPath, renderer, jsonSummaryPtr)) { + ++failures; + } + } while(iter.next(&inputFilename)); + } else if (SkStrEndsWith(input, ".skp")) { + SkString inputPath(input); + if (!render_picture(inputPath, writePath, mismatchPath, renderer, jsonSummaryPtr)) { + ++failures; + } + } else { + SkString warning; + warning.printf("Warning: skipping %s\n", input); + SkDebugf(warning.c_str()); + } + return failures; +} + +int tool_main(int argc, char** argv); +int tool_main(int argc, char** argv) { + SkCommandLineFlags::SetUsage("Render .skp files."); + SkCommandLineFlags::Parse(argc, argv); + + if (FLAGS_readPath.isEmpty()) { + SkDebugf(".skp files or directories are required.\n"); + exit(-1); + } + + if (FLAGS_maxComponentDiff < 0 || FLAGS_maxComponentDiff > 256) { + SkDebugf("--maxComponentDiff must be between 0 and 256\n"); + exit(-1); + } + + if (FLAGS_maxComponentDiff != 256 && !FLAGS_validate) { + SkDebugf("--maxComponentDiff requires --validate\n"); + exit(-1); + } + + if (FLAGS_clone < 0) { + SkDebugf("--clone must be >= 0. Was %i\n", FLAGS_clone); + exit(-1); + } + + if (FLAGS_writeEncodedImages) { + if (FLAGS_writePath.isEmpty()) { + SkDebugf("--writeEncodedImages requires --writePath\n"); + exit(-1); + } + if (FLAGS_deferImageDecoding) { + SkDebugf("--writeEncodedImages is not compatible with --deferImageDecoding\n"); + exit(-1); + } + } + + SkString errorString; + SkAutoTUnref<sk_tools::PictureRenderer> renderer(parseRenderer(errorString, + kRender_PictureTool)); + if (errorString.size() > 0) { + SkDebugf("%s\n", errorString.c_str()); + } + + if (renderer.get() == NULL) { + exit(-1); + } + + SkAutoGraphics ag; + + SkString writePath; + if (FLAGS_writePath.count() == 1) { + writePath.set(FLAGS_writePath[0]); + } + SkString mismatchPath; + if (FLAGS_mismatchPath.count() == 1) { + mismatchPath.set(FLAGS_mismatchPath[0]); + } + sk_tools::ImageResultsAndExpectations jsonSummary; + sk_tools::ImageResultsAndExpectations* jsonSummaryPtr = NULL; + if (FLAGS_writeJsonSummaryPath.count() == 1) { + jsonSummaryPtr = &jsonSummary; + if (FLAGS_readJsonSummaryPath.count() == 1) { + SkASSERT(jsonSummary.readExpectationsFile(FLAGS_readJsonSummaryPath[0])); + } + } + + int failures = 0; + for (int i = 0; i < FLAGS_readPath.count(); i ++) { + failures += process_input(FLAGS_readPath[i], &writePath, &mismatchPath, *renderer.get(), + jsonSummaryPtr); + } + if (failures != 0) { + SkDebugf("Failed to render %i pictures.\n", failures); + return 1; + } +#if SK_SUPPORT_GPU +#if GR_CACHE_STATS + if (renderer->isUsingGpuDevice()) { + GrContext* ctx = renderer->getGrContext(); + ctx->printCacheStats(); +#ifdef SK_DEVELOPER + ctx->dumpFontCache(); +#endif + } +#endif +#endif + if (FLAGS_writeJsonSummaryPath.count() == 1) { + jsonSummary.writeToFile(FLAGS_writeJsonSummaryPath[0]); + } + return 0; +} + +#if !defined SK_BUILD_FOR_IOS +int main(int argc, char * const argv[]) { + return tool_main(argc, (char**) argv); +} +#endif |