blob: a75d47f5b4782f71b603780873f06b7e02d8992c [file] [log] [blame]
msarett3478f752016-02-12 14:47:09 -08001/*
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
Mike Kleinc0bd9f92019-04-23 12:05:21 -05008#include "include/codec/SkCodec.h"
9#include "include/core/SkBitmap.h"
10#include "include/core/SkColorSpace.h"
11#include "include/core/SkData.h"
12#include "include/core/SkPicture.h"
13#include "include/core/SkSerialProcs.h"
14#include "include/core/SkStream.h"
15#include "include/private/SkTHash.h"
16#include "src/core/SkMD5.h"
17#include "src/core/SkOSFile.h"
18#include "src/utils/SkJSONWriter.h"
19#include "src/utils/SkOSPath.h"
20#include "tools/flags/CommandLineFlags.h"
msarett3478f752016-02-12 14:47:09 -080021
raftiasd737bee2016-12-08 10:53:24 -050022#include <iostream>
rmistry8d965a62016-04-25 10:35:03 -070023#include <map>
24
Mike Klein84836b72019-03-21 11:31:36 -050025static DEFINE_string2(skps, s, "skps", "A path to a directory of skps or a single skp.");
26static DEFINE_string2(out, o, "img-out", "A path to an output directory.");
27static DEFINE_bool(testDecode, false,
28 "Indicates if we want to test that the images decode successfully.");
29static DEFINE_bool(writeImages, true,
30 "Indicates if we want to write out supported/decoded images.");
31static DEFINE_bool(writeFailedImages, false,
32 "Indicates if we want to write out unsupported/failed to decode images.");
33static DEFINE_string2(failuresJsonPath, j, "",
rmistry8d965a62016-04-25 10:35:03 -070034 "Dump SKP and count of unknown images to the specified JSON file. Will not be "
35 "written anywhere if empty.");
msarett3478f752016-02-12 14:47:09 -080036
mtklein2d225e32016-02-29 09:05:32 -080037static int gKnown;
msarett3478f752016-02-12 14:47:09 -080038static const char* gOutputDir;
rmistry8d965a62016-04-25 10:35:03 -070039static std::map<std::string, unsigned int> gSkpToUnknownCount = {};
raftiasd737bee2016-12-08 10:53:24 -050040static std::map<std::string, unsigned int> gSkpToUnsupportedCount;
msarett3478f752016-02-12 14:47:09 -080041
mtklein2d225e32016-02-29 09:05:32 -080042static SkTHashSet<SkMD5::Digest> gSeen;
msarett3478f752016-02-12 14:47:09 -080043
Mike Reedef038482017-12-16 08:41:28 -050044struct Sniffer {
msarett3478f752016-02-12 14:47:09 -080045
rmistry8d965a62016-04-25 10:35:03 -070046 std::string skpName;
47
48 Sniffer(std::string name) {
49 skpName = name;
50 }
51
mtklein2d225e32016-02-29 09:05:32 -080052 void sniff(const void* ptr, size_t len) {
53 SkMD5 md5;
54 md5.write(ptr, len);
Hal Canary0f2f5222019-04-03 10:13:45 -040055 SkMD5::Digest digest = md5.finish();
mtklein2d225e32016-02-29 09:05:32 -080056
57 if (gSeen.contains(digest)) {
58 return;
msarett3478f752016-02-12 14:47:09 -080059 }
mtklein2d225e32016-02-29 09:05:32 -080060 gSeen.add(digest);
61
bungeman38d909e2016-08-02 14:40:46 -070062 sk_sp<SkData> data(SkData::MakeWithoutCopy(ptr, len));
Mike Reedede7bac2017-07-23 15:30:02 -040063 std::unique_ptr<SkCodec> codec = SkCodec::MakeFromData(data);
mtklein2d225e32016-02-29 09:05:32 -080064 if (!codec) {
rmistry8d965a62016-04-25 10:35:03 -070065 // FIXME: This code is currently unreachable because we create an empty generator when
66 // we fail to create a codec.
67 SkDebugf("Codec could not be created for %s\n", skpName.c_str());
68 gSkpToUnknownCount[skpName]++;
mtklein2d225e32016-02-29 09:05:32 -080069 return;
70 }
71 SkString ext;
72 switch (codec->getEncodedFormat()) {
Hal Canarydb683012016-11-23 08:55:18 -070073 case SkEncodedImageFormat::kBMP: ext = "bmp"; break;
74 case SkEncodedImageFormat::kGIF: ext = "gif"; break;
75 case SkEncodedImageFormat::kICO: ext = "ico"; break;
76 case SkEncodedImageFormat::kJPEG: ext = "jpg"; break;
77 case SkEncodedImageFormat::kPNG: ext = "png"; break;
78 case SkEncodedImageFormat::kDNG: ext = "dng"; break;
79 case SkEncodedImageFormat::kWBMP: ext = "wbmp"; break;
80 case SkEncodedImageFormat::kWEBP: ext = "webp"; break;
rmistry8d965a62016-04-25 10:35:03 -070081 default:
82 // This should be unreachable because we cannot create a codec if we do not know
83 // the image type.
84 SkASSERT(false);
mtklein2d225e32016-02-29 09:05:32 -080085 }
86
Leon Scroggins IIIda1893f2017-07-10 11:23:07 -040087 auto writeImage = [&] (const char* name, int num) {
rmistry8d965a62016-04-25 10:35:03 -070088 SkString path;
Leon Scroggins IIIda1893f2017-07-10 11:23:07 -040089 path.appendf("%s/%s%d.%s", gOutputDir, name, num, ext.c_str());
mtklein2d225e32016-02-29 09:05:32 -080090
rmistry8d965a62016-04-25 10:35:03 -070091 SkFILEWStream file(path.c_str());
92 file.write(ptr, len);
93
94 SkDebugf("%s\n", path.c_str());
raftiasd737bee2016-12-08 10:53:24 -050095 };
96
raftiasd737bee2016-12-08 10:53:24 -050097 if (FLAGS_testDecode) {
98 SkBitmap bitmap;
99 SkImageInfo info = codec->getInfo().makeColorType(kN32_SkColorType);
100 bitmap.allocPixels(info);
101 const SkCodec::Result result = codec->getPixels(
102 info, bitmap.getPixels(), bitmap.rowBytes());
Leon Scroggins IIIda1893f2017-07-10 11:23:07 -0400103 switch (result) {
104 case SkCodec::kSuccess:
105 case SkCodec::kIncompleteInput:
106 case SkCodec::kErrorInInput:
107 break;
108 default:
109 SkDebugf("Decoding failed for %s\n", skpName.c_str());
110 if (FLAGS_writeFailedImages) {
111 writeImage("unknown", gSkpToUnknownCount[skpName]);
112 }
113 gSkpToUnknownCount[skpName]++;
114 return;
raftiasd737bee2016-12-08 10:53:24 -0500115 }
rmistry8d965a62016-04-25 10:35:03 -0700116 }
raftiasd737bee2016-12-08 10:53:24 -0500117
raftiasd737bee2016-12-08 10:53:24 -0500118 if (FLAGS_writeImages) {
Leon Scroggins IIIda1893f2017-07-10 11:23:07 -0400119 writeImage("", gKnown);
raftiasd737bee2016-12-08 10:53:24 -0500120 }
121
rmistry8d965a62016-04-25 10:35:03 -0700122 gKnown++;
msarett3478f752016-02-12 14:47:09 -0800123 }
mtklein2d225e32016-02-29 09:05:32 -0800124};
msarett3478f752016-02-12 14:47:09 -0800125
Leon Scroggins IIIda1893f2017-07-10 11:23:07 -0400126static bool get_images_from_file(const SkString& file) {
Leon Scroggins III4b0b00e2017-06-05 10:22:53 -0400127 Sniffer sniff(file.c_str());
Mike Reed43f0ba02018-03-09 13:08:38 -0500128 auto stream = SkStream::MakeFromFile(file.c_str());
129
130 SkDeserialProcs procs;
131 procs.fImageProc = [](const void* data, size_t size, void* ctx) -> sk_sp<SkImage> {
132 ((Sniffer*)ctx)->sniff(data, size);
133 return nullptr;
Mike Reedef038482017-12-16 08:41:28 -0500134 };
135 procs.fImageCtx = &sniff;
Mike Reed43f0ba02018-03-09 13:08:38 -0500136 return SkPicture::MakeFromStream(stream.get(), &procs) != nullptr;
Leon Scroggins III4b0b00e2017-06-05 10:22:53 -0400137}
msarett3478f752016-02-12 14:47:09 -0800138
139int main(int argc, char** argv) {
Mike Klein88544fb2019-03-20 10:50:33 -0500140 CommandLineFlags::SetUsage(
rmistry8d965a62016-04-25 10:35:03 -0700141 "Usage: get_images_from_skps -s <dir of skps> -o <dir for output images> --testDecode "
Matt Sarettee7c8202017-06-09 10:18:34 -0400142 "-j <output JSON path> --writeImages, --writeFailedImages\n");
msarett3478f752016-02-12 14:47:09 -0800143
Mike Klein88544fb2019-03-20 10:50:33 -0500144 CommandLineFlags::Parse(argc, argv);
msarett3478f752016-02-12 14:47:09 -0800145 const char* inputs = FLAGS_skps[0];
146 gOutputDir = FLAGS_out[0];
mtklein2d225e32016-02-29 09:05:32 -0800147
Leon Scroggins III4b0b00e2017-06-05 10:22:53 -0400148 if (!sk_isdir(gOutputDir)) {
Mike Klein88544fb2019-03-20 10:50:33 -0500149 CommandLineFlags::PrintUsage();
msarett3478f752016-02-12 14:47:09 -0800150 return 1;
151 }
152
Leon Scroggins III4b0b00e2017-06-05 10:22:53 -0400153 if (sk_isdir(inputs)) {
154 SkOSFile::Iter iter(inputs, "skp");
155 for (SkString file; iter.next(&file); ) {
Leon Scroggins IIIda1893f2017-07-10 11:23:07 -0400156 if (!get_images_from_file(SkOSPath::Join(inputs, file.c_str()))) {
157 return 2;
158 }
Leon Scroggins III4b0b00e2017-06-05 10:22:53 -0400159 }
160 } else {
Leon Scroggins IIIda1893f2017-07-10 11:23:07 -0400161 if (!get_images_from_file(SkString(inputs))) {
162 return 2;
163 }
msarett3478f752016-02-12 14:47:09 -0800164 }
rmistry8d965a62016-04-25 10:35:03 -0700165 /**
166 JSON results are written out in the following format:
167 {
168 "failures": {
169 "skp1": 12,
170 "skp4": 2,
171 ...
172 },
raftiasd737bee2016-12-08 10:53:24 -0500173 "unsupported": {
174 "skp9": 13,
175 "skp17": 3,
176 ...
177 }
rmistry8d965a62016-04-25 10:35:03 -0700178 "totalFailures": 32,
raftiasd737bee2016-12-08 10:53:24 -0500179 "totalUnsupported": 9,
rmistry8d965a62016-04-25 10:35:03 -0700180 "totalSuccesses": 21,
181 }
182 */
Florin Malita3425e222018-06-19 10:52:20 -0400183
184 unsigned int totalFailures = 0,
185 totalUnsupported = 0;
186 SkDynamicMemoryWStream memStream;
187 SkJSONWriter writer(&memStream, SkJSONWriter::Mode::kPretty);
188 writer.beginObject();
rmistry8d965a62016-04-25 10:35:03 -0700189 {
Florin Malita3425e222018-06-19 10:52:20 -0400190 writer.beginObject("failures");
191 {
192 for(const auto& failure : gSkpToUnknownCount) {
193 SkDebugf("%s %d\n", failure.first.c_str(), failure.second);
194 totalFailures += failure.second;
195 writer.appendU32(failure.first.c_str(), failure.second);
196 }
197 }
198 writer.endObject();
199 writer.appendU32("totalFailures", totalFailures);
200
raftiasd737bee2016-12-08 10:53:24 -0500201#ifdef SK_DEBUG
Florin Malita3425e222018-06-19 10:52:20 -0400202 writer.beginObject("unsupported");
203 {
204 for (const auto& unsupported : gSkpToUnsupportedCount) {
205 SkDebugf("%s %d\n", unsupported.first.c_str(), unsupported.second);
206 totalUnsupported += unsupported.second;
207 writer.appendHexU32(unsupported.first.c_str(), unsupported.second);
208 }
209
210 }
211 writer.endObject();
212 writer.appendU32("totalUnsupported", totalUnsupported);
raftiasd737bee2016-12-08 10:53:24 -0500213#endif
Florin Malita3425e222018-06-19 10:52:20 -0400214
215 writer.appendS32("totalSuccesses", gKnown);
216 SkDebugf("%d known, %d failures, %d unsupported\n",
217 gKnown, totalFailures, totalUnsupported);
218 }
219 writer.endObject();
220 writer.flush();
221
raftiasd737bee2016-12-08 10:53:24 -0500222 if (totalFailures > 0 || totalUnsupported > 0) {
rmistrycda08f82016-04-26 08:27:49 -0700223 if (!FLAGS_failuresJsonPath.isEmpty()) {
224 SkDebugf("Writing failures to %s\n", FLAGS_failuresJsonPath[0]);
225 SkFILEWStream stream(FLAGS_failuresJsonPath[0]);
Florin Malita3425e222018-06-19 10:52:20 -0400226 auto jsonStream = memStream.detachAsStream();
227 stream.writeStream(jsonStream.get(), jsonStream->getLength());
rmistrycda08f82016-04-26 08:27:49 -0700228 }
rmistry8d965a62016-04-25 10:35:03 -0700229 }
Florin Malita3425e222018-06-19 10:52:20 -0400230
msarett3478f752016-02-12 14:47:09 -0800231 return 0;
232}