• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
SnifferSniffer50     Sniffer(std::string name) {
51         skpName = name;
52     }
53 
sniffSniffer54     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 
onUseEncodedDataSniffer138     bool onUseEncodedData(const void* ptr, size_t len) override {
139         this->sniff(ptr, len);
140         return true;
141     }
onEncodeSniffer142     SkData* onEncode(const SkPixmap&) override { return nullptr; }
143 };
144 
145 
main(int argc,char ** argv)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