| Index: tools/get_images_from_skps.cpp
 | 
| diff --git a/tools/get_images_from_skps.cpp b/tools/get_images_from_skps.cpp
 | 
| index e6bb6e2012190698ebda8359826f7d36be275861..f963b9a86b59bd08d8d6a04afe27e2de824df30f 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 <map>
 | 
| +
 | 
|  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<std::string, unsigned int> gSkpToUnknownCount = {};
 | 
|  
 | 
|  static SkTHashSet<SkMD5::Digest> 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<SkData> data(SkData::NewWithoutCopy(ptr, len));
 | 
|          SkAutoTDelete<SkCodec> 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;
 | 
| +            }
 | 
| +        }
 | 
| +
 | 
| +        if (FLAGS_writeImages) {
 | 
| +            SkString path;
 | 
| +            path.appendf("%s/%d.%s", gOutputDir, gKnown, ext.c_str());
 | 
|  
 | 
| -        SkFILEWStream file(path.c_str());
 | 
| -        file.write(ptr, len);
 | 
| +            SkFILEWStream file(path.c_str());
 | 
| +            file.write(ptr, len);
 | 
|  
 | 
| -        SkDebugf("%s\n", path.c_str());
 | 
| +            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 <dir of skps> -o <dir for output images>\n");
 | 
| +            "Usage: get_images_from_skps -s <dir of skps> -o <dir for output images> --testDecode "
 | 
| +            "-j <output JSON path>\n");
 | 
|  
 | 
|      SkCommandLineFlags::Parse(argc, argv);
 | 
|      const char* inputs = FLAGS_skps[0];
 | 
| @@ -93,10 +131,38 @@ int main(int argc, char** argv) {
 | 
|          sk_sp<SkPicture> 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;
 | 
|  }
 | 
| 
 |