diff --git a/gyp/tools.gyp b/gyp/tools.gyp index cd9223335f..1f38ef8632 100644 --- a/gyp/tools.gyp +++ b/gyp/tools.gyp @@ -278,6 +278,7 @@ ], 'dependencies': [ 'flags.gyp:flags', + 'jsoncpp.gyp:jsoncpp', 'skia_lib.gyp:skia_lib', ], }, diff --git a/tools/get_images_from_skps.cpp b/tools/get_images_from_skps.cpp index e6bb6e2012..f963b9a86b 100644 --- a/tools/get_images_from_skps.cpp +++ b/tools/get_images_from_skps.cpp @@ -5,9 +5,11 @@ * found in the LICENSE file. */ +#include "SkBitmap.h" #include "SkCodec.h" #include "SkCommandLineFlags.h" #include "SkData.h" +#include "SkJSONCPP.h" #include "SkMD5.h" #include "SkOSFile.h" #include "SkPicture.h" @@ -15,17 +17,31 @@ #include "SkStream.h" #include "SkTHash.h" + +#include + DEFINE_string2(skps, s, "skps", "A path to a directory of skps."); DEFINE_string2(out, o, "img-out", "A path to an output directory."); +DEFINE_bool(testDecode, false, "Indicates if we want to test that the images decode successfully."); +DEFINE_bool(writeImages, true, "Indicates if we want to write out images."); +DEFINE_string2(failuresJsonPath, j, "", + "Dump SKP and count of unknown images to the specified JSON file. Will not be " + "written anywhere if empty."); static int gKnown; -static int gUnknown; static const char* gOutputDir; +static std::map gSkpToUnknownCount = {}; static SkTHashSet gSeen; struct Sniffer : public SkPixelSerializer { + std::string skpName; + + Sniffer(std::string name) { + skpName = name; + } + void sniff(const void* ptr, size_t len) { SkMD5 md5; md5.write(ptr, len); @@ -40,7 +56,10 @@ struct Sniffer : public SkPixelSerializer { SkAutoTUnref data(SkData::NewWithoutCopy(ptr, len)); SkAutoTDelete codec(SkCodec::NewFromData(data)); if (!codec) { - gUnknown++; + // FIXME: This code is currently unreachable because we create an empty generator when + // we fail to create a codec. + SkDebugf("Codec could not be created for %s\n", skpName.c_str()); + gSkpToUnknownCount[skpName]++; return; } SkString ext; @@ -53,16 +72,34 @@ struct Sniffer : public SkPixelSerializer { case SkEncodedFormat::kDNG_SkEncodedFormat: ext = "dng"; break; case SkEncodedFormat::kWBMP_SkEncodedFormat: ext = "wbmp"; break; case SkEncodedFormat::kWEBP_SkEncodedFormat: ext = "webp"; break; - default: gUnknown++; return; + default: + // This should be unreachable because we cannot create a codec if we do not know + // the image type. + SkASSERT(false); } - SkString path; - path.appendf("%s/%d.%s", gOutputDir, gKnown++, ext.c_str()); + if (FLAGS_testDecode) { + SkBitmap bitmap; + SkImageInfo info = codec->getInfo().makeColorType(kN32_SkColorType); + bitmap.allocPixels(info); + if (SkCodec::kSuccess != codec->getPixels(info, bitmap.getPixels(), bitmap.rowBytes())) + { + SkDebugf("Decoding failed for %s\n", skpName.c_str()); + gSkpToUnknownCount[skpName]++; + return; + } + } - SkFILEWStream file(path.c_str()); - file.write(ptr, len); + if (FLAGS_writeImages) { + SkString path; + path.appendf("%s/%d.%s", gOutputDir, gKnown, ext.c_str()); - SkDebugf("%s\n", path.c_str()); + SkFILEWStream file(path.c_str()); + file.write(ptr, len); + + SkDebugf("%s\n", path.c_str()); + } + gKnown++; } bool onUseEncodedData(const void* ptr, size_t len) override { @@ -75,7 +112,8 @@ struct Sniffer : public SkPixelSerializer { int main(int argc, char** argv) { SkCommandLineFlags::SetUsage( - "Usage: get_images_from_skps -s -o \n"); + "Usage: get_images_from_skps -s -o --testDecode " + "-j \n"); SkCommandLineFlags::Parse(argc, argv); const char* inputs = FLAGS_skps[0]; @@ -93,10 +131,38 @@ int main(int argc, char** argv) { sk_sp picture(SkPicture::MakeFromStream(stream)); SkDynamicMemoryWStream scratch; - Sniffer sniff; + Sniffer sniff(file.c_str()); picture->serialize(&scratch, &sniff); } - SkDebugf("%d known, %d unknown\n", gKnown, gUnknown); + int totalUnknowns = 0; + /** + JSON results are written out in the following format: + { + "failures": { + "skp1": 12, + "skp4": 2, + ... + }, + "totalFailures": 32, + "totalSuccesses": 21, + } + */ + Json::Value fRoot; + for(auto it = gSkpToUnknownCount.cbegin(); it != gSkpToUnknownCount.cend(); ++it) + { + SkDebugf("%s %d\n", it->first.c_str(), it->second); + totalUnknowns += it->second; + fRoot["failures"][it->first.c_str()] = it->second; + } + SkDebugf("%d known, %d unknown\n", gKnown, totalUnknowns); + fRoot["totalFailures"] = totalUnknowns; + fRoot["totalSuccesses"] = gKnown; + if (totalUnknowns > 0 && !FLAGS_failuresJsonPath.isEmpty()) { + SkDebugf("Writing failures to %s\n", FLAGS_failuresJsonPath[0]); + SkFILEWStream stream(FLAGS_failuresJsonPath[0]); + stream.writeText(Json::StyledWriter().write(fRoot).c_str()); + stream.flush(); + } return 0; }