Flutter Engine
The Flutter Engine
get_images_from_skps.cpp
Go to the documentation of this file.
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
12#include "include/core/SkData.h"
17#include "src/core/SkMD5.h"
18#include "src/core/SkOSFile.h"
19#include "src/core/SkTHash.h"
21#include "src/utils/SkOSPath.h"
23
24#include <iostream>
25#include <map>
26
27using namespace skia_private;
28
29static DEFINE_string2(skps, s, "skps", "A path to a directory of skps or a single skp.");
30static DEFINE_string2(out, o, "img-out", "A path to an output directory.");
31static DEFINE_bool(testDecode, false,
32 "Indicates if we want to test that the images decode successfully.");
33static DEFINE_bool(writeImages, true,
34 "Indicates if we want to write out supported/decoded images.");
35static DEFINE_bool(writeFailedImages, false,
36 "Indicates if we want to write out unsupported/failed to decode images.");
37static DEFINE_string2(failuresJsonPath, j, "",
38 "Dump SKP and count of unknown images to the specified JSON file. Will not be "
39 "written anywhere if empty.");
40
41static int gKnown;
42static const char* gOutputDir;
43static std::map<std::string, unsigned int> gSkpToUnknownCount = {};
44static std::map<std::string, unsigned int> gSkpToUnsupportedCount;
45
47
48struct Sniffer {
49
50 std::string skpName;
51
52 Sniffer(std::string name) {
53 skpName = name;
54 }
55
56 void sniff(const void* ptr, size_t len) {
57 SkMD5 md5;
58 md5.write(ptr, len);
59 SkMD5::Digest digest = md5.finish();
60
61 if (gSeen.contains(digest)) {
62 return;
63 }
64 gSeen.add(digest);
65
67 std::unique_ptr<SkCodec> codec = SkCodec::MakeFromData(data);
68 if (!codec) {
69 // FIXME: This code is currently unreachable because we create an empty generator when
70 // we fail to create a codec.
71 SkDebugf("Codec could not be created for %s\n", skpName.c_str());
73 return;
74 }
76 switch (codec->getEncodedFormat()) {
77 case SkEncodedImageFormat::kBMP: ext = "bmp"; break;
78 case SkEncodedImageFormat::kGIF: ext = "gif"; break;
79 case SkEncodedImageFormat::kICO: ext = "ico"; break;
80 case SkEncodedImageFormat::kJPEG: ext = "jpg"; break;
81 case SkEncodedImageFormat::kPNG: ext = "png"; break;
82 case SkEncodedImageFormat::kDNG: ext = "dng"; break;
83 case SkEncodedImageFormat::kWBMP: ext = "wbmp"; break;
84 case SkEncodedImageFormat::kWEBP: ext = "webp"; break;
85 default:
86 // This should be unreachable because we cannot create a codec if we do not know
87 // the image type.
88 SkASSERT(false);
89 }
90
91 auto writeImage = [&] (const char* name, int num) {
93 path.appendf("%s/%s%d.%s", gOutputDir, name, num, ext.c_str());
94
95 SkFILEWStream file(path.c_str());
96 file.write(ptr, len);
97
98 SkDebugf("%s\n", path.c_str());
99 };
100
101 if (FLAGS_testDecode) {
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 switch (result) {
111 break;
112 default:
113 SkDebugf("Decoding failed for %s\n", skpName.c_str());
114 if (FLAGS_writeFailedImages) {
115 writeImage("unknown", gSkpToUnknownCount[skpName]);
116 }
118 return;
119 }
120 }
121
122 if (FLAGS_writeImages) {
123 writeImage("", gKnown);
124 }
125
126 gKnown++;
127 }
128};
129
130static bool get_images_from_file(const SkString& file) {
131 Sniffer sniff(file.c_str());
132 auto stream = SkStream::MakeFromFile(file.c_str());
133
134 SkDeserialProcs procs;
135 procs.fImageProc = [](const void* data, size_t size, void* ctx) -> sk_sp<SkImage> {
136 ((Sniffer*)ctx)->sniff(data, size);
137 return nullptr;
138 };
139 procs.fImageCtx = &sniff;
140 return SkPicture::MakeFromStream(stream.get(), &procs) != nullptr;
141}
142
143int main(int argc, char** argv) {
145 "Usage: get_images_from_skps -s <dir of skps> -o <dir for output images> --testDecode "
146 "-j <output JSON path> --writeImages, --writeFailedImages\n");
147
149 const char* inputs = FLAGS_skps[0];
150 gOutputDir = FLAGS_out[0];
151
152 if (!sk_isdir(gOutputDir)) {
154 return 1;
155 }
156
157 if (sk_isdir(inputs)) {
158 SkOSFile::Iter iter(inputs, "skp");
159 for (SkString file; iter.next(&file); ) {
161 return 2;
162 }
163 }
164 } else {
166 return 2;
167 }
168 }
169 /**
170 JSON results are written out in the following format:
171 {
172 "failures": {
173 "skp1": 12,
174 "skp4": 2,
175 ...
176 },
177 "unsupported": {
178 "skp9": 13,
179 "skp17": 3,
180 ...
181 }
182 "totalFailures": 32,
183 "totalUnsupported": 9,
184 "totalSuccesses": 21,
185 }
186 */
187
188 unsigned int totalFailures = 0,
189 totalUnsupported = 0;
190 SkDynamicMemoryWStream memStream;
191 SkJSONWriter writer(&memStream, SkJSONWriter::Mode::kPretty);
192 writer.beginObject();
193 {
194 writer.beginObject("failures");
195 {
196 for (const auto& failure : gSkpToUnknownCount) {
197 SkDebugf("%s %u\n", failure.first.c_str(), failure.second);
198 totalFailures += failure.second;
199 writer.appendU32(failure.first.c_str(), failure.second);
200 }
201 }
202 writer.endObject();
203 writer.appendU32("totalFailures", totalFailures);
204
205#ifdef SK_DEBUG
206 writer.beginObject("unsupported");
207 {
208 for (const auto& unsupported : gSkpToUnsupportedCount) {
209 SkDebugf("%s %u\n", unsupported.first.c_str(), unsupported.second);
210 totalUnsupported += unsupported.second;
211 writer.appendHexU32(unsupported.first.c_str(), unsupported.second);
212 }
213 }
214 writer.endObject();
215 writer.appendU32("totalUnsupported", totalUnsupported);
216#endif
217
218 writer.appendS32("totalSuccesses", gKnown);
219 SkDebugf("%d known, %u failures, %u unsupported\n",
220 gKnown, totalFailures, totalUnsupported);
221 }
222 writer.endObject();
223 writer.flush();
224
225 if (totalFailures > 0 || totalUnsupported > 0) {
226 if (!FLAGS_failuresJsonPath.isEmpty()) {
227 SkDebugf("Writing failures to %s\n", FLAGS_failuresJsonPath[0]);
228 SkFILEWStream stream(FLAGS_failuresJsonPath[0]);
229 auto jsonStream = memStream.detachAsStream();
230 stream.writeStream(jsonStream.get(), jsonStream->getLength());
231 }
232 }
233
234 return 0;
235}
static SkMD5::Digest md5(const SkBitmap &bm)
Definition: CodecTest.cpp:77
static void info(const char *fmt,...) SK_PRINTF_LIKE(1
Definition: DM.cpp:213
#define SkASSERT(cond)
Definition: SkAssert.h:116
void SK_SPI SkDebugf(const char format[],...) SK_PRINTF_LIKE(1
bool sk_isdir(const char *path)
static void PrintUsage()
static void Parse(int argc, const char *const *argv)
static void SetUsage(const char *usage)
static std::unique_ptr< SkCodec > MakeFromData(sk_sp< SkData >, SkSpan< const SkCodecs::Decoder > decoders, SkPngChunkReader *=nullptr)
Definition: SkCodec.cpp:241
Result
Definition: SkCodec.h:76
@ kIncompleteInput
Definition: SkCodec.h:84
@ kSuccess
Definition: SkCodec.h:80
@ kErrorInInput
Definition: SkCodec.h:91
static sk_sp< SkData > MakeWithoutCopy(const void *data, size_t length)
Definition: SkData.h:116
std::unique_ptr< SkStreamAsset > detachAsStream()
Definition: SkStream.cpp:876
void appendS32(int32_t value)
Definition: SkJSONWriter.h:237
void appendU32(uint32_t value)
Definition: SkJSONWriter.h:239
void beginObject(const char *name=nullptr, bool multiline=true)
Definition: SkJSONWriter.h:114
void endObject()
Definition: SkJSONWriter.h:126
void flush()
Definition: SkJSONWriter.h:78
void appendHexU32(uint32_t value)
Definition: SkJSONWriter.h:251
Definition: SkMD5.h:19
SK_SPI bool next(SkString *name, bool getDir=false)
static SkString Join(const char *rootPath, const char *relativePath)
Definition: SkOSPath.cpp:14
static sk_sp< SkPicture > MakeFromStream(SkStream *stream, const SkDeserialProcs *procs=nullptr)
Definition: SkPicture.cpp:147
static std::unique_ptr< SkStreamAsset > MakeFromFile(const char path[])
Definition: SkStream.cpp:922
struct MyStruct s
GAsyncResult * result
static bool get_images_from_file(const SkString &file)
int main(int argc, char **argv)
static std::map< std::string, unsigned int > gSkpToUnsupportedCount
static DEFINE_string2(skps, s, "skps", "A path to a directory of skps or a single skp.")
static DEFINE_bool(testDecode, false, "Indicates if we want to test that the images decode successfully.")
static std::map< std::string, unsigned int > gSkpToUnknownCount
static int gKnown
static THashSet< SkMD5::Digest > gSeen
static const char * gOutputDir
char ** argv
Definition: library.h:9
Definition: bitmap.py:1
DEF_SWITCHES_START aot vmservice shared library Name of the *so containing AOT compiled Dart assets for launching the service isolate vm snapshot The VM snapshot data that will be memory mapped as read only SnapshotAssetPath must be present isolate snapshot The isolate snapshot data that will be memory mapped as read only SnapshotAssetPath must be present cache dir path
Definition: switches.h:57
DEF_SWITCHES_START aot vmservice shared library name
Definition: switches.h:32
it will be possible to load the file into Perfetto s trace viewer disable asset Prevents usage of any non test fonts unless they were explicitly Loaded via prefetched default font Indicates whether the embedding started a prefetch of the default font manager before creating the engine run In non interactive keep the shell running after the Dart script has completed enable serial On low power devices with low core running concurrent GC tasks on threads can cause them to contend with the UI thread which could potentially lead to jank This option turns off all concurrent GC activities domain network JSON encoded network policy per domain This overrides the DisallowInsecureConnections switch Embedder can specify whether to allow or disallow insecure connections at a domain level old gen heap size
Definition: switches.h:259
SkDeserialImageProc fImageProc
Sniffer(std::string name)
std::string skpName
void sniff(const void *ptr, size_t len)
std::shared_ptr< const fml::Mapping > data
Definition: texture_gles.cc:63