Home | History | Annotate | Download | only in tools
      1 /*
      2  * Copyright 2016 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 "SkBitmap.h"
      9 #include "SkCodec.h"
     10 #include "SkColorSpace.h"
     11 #include "SkCommandLineFlags.h"
     12 #include "SkData.h"
     13 #include "SkJSONCPP.h"
     14 #include "SkMD5.h"
     15 #include "SkOSFile.h"
     16 #include "SkOSPath.h"
     17 #include "SkPicture.h"
     18 #include "SkPixelSerializer.h"
     19 #include "SkStream.h"
     20 #include "SkTHash.h"
     21 
     22 
     23 #include <iostream>
     24 #include <map>
     25 
     26 DEFINE_string2(skps, s, "skps", "A path to a directory of skps.");
     27 DEFINE_string2(out, o, "img-out", "A path to an output directory.");
     28 DEFINE_bool(testDecode, false, "Indicates if we want to test that the images decode successfully.");
     29 DEFINE_bool(writeImages, true,
     30             "Indicates if we want to write out supported/decoded images.");
     31 DEFINE_bool(writeFailedImages, false,
     32             "Indicates if we want to write out unsupported/failed to decode images.");
     33 DEFINE_bool(testICCSupport, false,
     34             "Indicates if we want to test that the images with ICC profiles are supported");
     35 DEFINE_string2(failuresJsonPath, j, "",
     36                "Dump SKP and count of unknown images to the specified JSON file. Will not be "
     37                "written anywhere if empty.");
     38 
     39 static int gKnown;
     40 static const char* gOutputDir;
     41 static std::map<std::string, unsigned int> gSkpToUnknownCount = {};
     42 static std::map<std::string, unsigned int> gSkpToUnsupportedCount;
     43 
     44 static SkTHashSet<SkMD5::Digest> gSeen;
     45 
     46 struct Sniffer : public SkPixelSerializer {
     47 
     48     std::string skpName;
     49 
     50     Sniffer(std::string name) {
     51         skpName = name;
     52     }
     53 
     54     void sniff(const void* ptr, size_t len) {
     55         SkMD5 md5;
     56         md5.write(ptr, len);
     57         SkMD5::Digest digest;
     58         md5.finish(digest);
     59 
     60         if (gSeen.contains(digest)) {
     61             return;
     62         }
     63         gSeen.add(digest);
     64 
     65         sk_sp<SkData> data(SkData::MakeWithoutCopy(ptr, len));
     66         std::unique_ptr<SkCodec> codec(SkCodec::NewFromData(data));
     67         if (!codec) {
     68             // FIXME: This code is currently unreachable because we create an empty generator when
     69             //        we fail to create a codec.
     70             SkDebugf("Codec could not be created for %s\n", skpName.c_str());
     71             gSkpToUnknownCount[skpName]++;
     72             return;
     73         }
     74         SkString ext;
     75         switch (codec->getEncodedFormat()) {
     76             case SkEncodedImageFormat::kBMP:  ext =  "bmp"; break;
     77             case SkEncodedImageFormat::kGIF:  ext =  "gif"; break;
     78             case SkEncodedImageFormat::kICO:  ext =  "ico"; break;
     79             case SkEncodedImageFormat::kJPEG: ext =  "jpg"; break;
     80             case SkEncodedImageFormat::kPNG:  ext =  "png"; break;
     81             case SkEncodedImageFormat::kDNG:  ext =  "dng"; break;
     82             case SkEncodedImageFormat::kWBMP: ext = "wbmp"; break;
     83             case SkEncodedImageFormat::kWEBP: ext = "webp"; break;
     84             default:
     85                 // This should be unreachable because we cannot create a codec if we do not know
     86                 // the image type.
     87                 SkASSERT(false);
     88         }
     89 
     90         auto writeImage = [&] {
     91             SkString path;
     92             path.appendf("%s/%d.%s", gOutputDir, gKnown, ext.c_str());
     93 
     94             SkFILEWStream file(path.c_str());
     95             file.write(ptr, len);
     96 
     97             SkDebugf("%s\n", path.c_str());
     98         };
     99 
    100 
    101         if (FLAGS_testDecode) {
    102             SkBitmap bitmap;
    103             SkImageInfo info = codec->getInfo().makeColorType(kN32_SkColorType);
    104             bitmap.allocPixels(info);
    105             const SkCodec::Result result = codec->getPixels(
    106                 info, bitmap.getPixels(),  bitmap.rowBytes());
    107             if (SkCodec::kIncompleteInput != result && SkCodec::kSuccess != result) {
    108                 SkDebugf("Decoding failed for %s\n", skpName.c_str());
    109                 gSkpToUnknownCount[skpName]++;
    110                 if (FLAGS_writeFailedImages) {
    111                     writeImage();
    112                 }
    113                 return;
    114             }
    115         }
    116 
    117 #ifdef SK_DEBUG
    118         if (FLAGS_testICCSupport) {
    119             if (codec->fUnsupportedICC) {
    120                 SkDebugf("Color correction failed for %s\n", skpName.c_str());
    121                 gSkpToUnsupportedCount[skpName]++;
    122                 if (FLAGS_writeFailedImages) {
    123                     writeImage();
    124                 }
    125                 return;
    126             }
    127         }
    128 #endif
    129 
    130         if (FLAGS_writeImages) {
    131             writeImage();
    132         }
    133 
    134 
    135         gKnown++;
    136     }
    137 
    138     bool onUseEncodedData(const void* ptr, size_t len) override {
    139         this->sniff(ptr, len);
    140         return true;
    141     }
    142     SkData* onEncode(const SkPixmap&) override { return nullptr; }
    143 };
    144 
    145 
    146 int main(int argc, char** argv) {
    147     SkCommandLineFlags::SetUsage(
    148             "Usage: get_images_from_skps -s <dir of skps> -o <dir for output images> --testDecode "
    149             "-j <output JSON path> --testICCSupport --writeImages, --writeFailedImages\n");
    150 
    151     SkCommandLineFlags::Parse(argc, argv);
    152     const char* inputs = FLAGS_skps[0];
    153     gOutputDir = FLAGS_out[0];
    154 
    155     if (!sk_isdir(inputs) || !sk_isdir(gOutputDir)) {
    156         SkCommandLineFlags::PrintUsage();
    157         return 1;
    158     }
    159 #ifndef SK_DEBUG
    160     if (FLAGS_testICCSupport) {
    161         std::cerr << "--testICCSupport unavailable outside of SK_DEBUG builds" << std::endl;
    162         return 1;
    163     }
    164 #endif
    165 
    166     SkOSFile::Iter iter(inputs, "skp");
    167     for (SkString file; iter.next(&file); ) {
    168         std::unique_ptr<SkStream> stream =
    169                 SkStream::MakeFromFile(SkOSPath::Join(inputs, file.c_str()).c_str());
    170         sk_sp<SkPicture> picture(SkPicture::MakeFromStream(stream.get()));
    171 
    172         SkDynamicMemoryWStream scratch;
    173         Sniffer sniff(file.c_str());
    174         picture->serialize(&scratch, &sniff);
    175     }
    176     /**
    177      JSON results are written out in the following format:
    178      {
    179        "failures": {
    180          "skp1": 12,
    181          "skp4": 2,
    182          ...
    183        },
    184        "unsupported": {
    185         "skp9": 13,
    186         "skp17": 3,
    187         ...
    188        }
    189        "totalFailures": 32,
    190        "totalUnsupported": 9,
    191        "totalSuccesses": 21,
    192      }
    193      */
    194     Json::Value fRoot;
    195     int totalFailures = 0;
    196     for(auto it = gSkpToUnknownCount.cbegin(); it != gSkpToUnknownCount.cend(); ++it)
    197     {
    198         SkDebugf("%s %d\n", it->first.c_str(), it->second);
    199         totalFailures += it->second;
    200         fRoot["failures"][it->first.c_str()] = it->second;
    201     }
    202     fRoot["totalFailures"] = totalFailures;
    203     int totalUnsupported = 0;
    204 #ifdef SK_DEBUG
    205     for (const auto& unsupported : gSkpToUnsupportedCount) {
    206         SkDebugf("%s %d\n", unsupported.first.c_str(), unsupported.second);
    207         totalUnsupported += unsupported.second;
    208         fRoot["unsupported"][unsupported.first] = unsupported.second;
    209     }
    210     fRoot["totalUnsupported"] = totalUnsupported;
    211 #endif
    212     fRoot["totalSuccesses"] = gKnown;
    213     SkDebugf("%d known, %d failures, %d unsupported\n", gKnown, totalFailures, totalUnsupported);
    214     if (totalFailures > 0 || totalUnsupported > 0) {
    215         if (!FLAGS_failuresJsonPath.isEmpty()) {
    216             SkDebugf("Writing failures to %s\n", FLAGS_failuresJsonPath[0]);
    217             SkFILEWStream stream(FLAGS_failuresJsonPath[0]);
    218             stream.writeText(Json::StyledWriter().write(fRoot).c_str());
    219             stream.flush();
    220         }
    221         return -1;
    222     }
    223     return 0;
    224 }
    225