|  | /* | 
|  | * Copyright 2016 Google Inc. | 
|  | * | 
|  | * Use of this source code is governed by a BSD-style license that can be | 
|  | * found in the LICENSE file. | 
|  | */ | 
|  |  | 
|  | #include "include/codec/SkCodec.h" | 
|  | #include "include/codec/SkEncodedImageFormat.h" | 
|  | #include "include/core/SkBitmap.h" | 
|  | #include "include/core/SkColorSpace.h" | 
|  | #include "include/core/SkData.h" | 
|  | #include "include/core/SkImage.h" | 
|  | #include "include/core/SkPicture.h" | 
|  | #include "include/core/SkSerialProcs.h" | 
|  | #include "include/core/SkStream.h" | 
|  | #include "src/core/SkMD5.h" | 
|  | #include "src/core/SkOSFile.h" | 
|  | #include "src/core/SkTHash.h" | 
|  | #include "src/utils/SkJSONWriter.h" | 
|  | #include "src/utils/SkOSPath.h" | 
|  | #include "tools/flags/CommandLineFlags.h" | 
|  |  | 
|  | #include <iostream> | 
|  | #include <map> | 
|  |  | 
|  | using namespace skia_private; | 
|  |  | 
|  | static DEFINE_string2(skps, s, "skps", "A path to a directory of skps or a single skp."); | 
|  | static DEFINE_string2(out, o, "img-out", "A path to an output directory."); | 
|  | static DEFINE_bool(testDecode, false, | 
|  | "Indicates if we want to test that the images decode successfully."); | 
|  | static DEFINE_bool(writeImages, true, | 
|  | "Indicates if we want to write out supported/decoded images."); | 
|  | static DEFINE_bool(writeFailedImages, false, | 
|  | "Indicates if we want to write out unsupported/failed to decode images."); | 
|  | static 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 const char* gOutputDir; | 
|  | static std::map<std::string, unsigned int> gSkpToUnknownCount = {}; | 
|  | static std::map<std::string, unsigned int> gSkpToUnsupportedCount; | 
|  |  | 
|  | static THashSet<SkMD5::Digest> gSeen; | 
|  |  | 
|  | struct Sniffer { | 
|  |  | 
|  | std::string skpName; | 
|  |  | 
|  | Sniffer(std::string name) { | 
|  | skpName = name; | 
|  | } | 
|  |  | 
|  | void sniff(const void* ptr, size_t len) { | 
|  | SkMD5 md5; | 
|  | md5.write(ptr, len); | 
|  | SkMD5::Digest digest = md5.finish(); | 
|  |  | 
|  | if (gSeen.contains(digest)) { | 
|  | return; | 
|  | } | 
|  | gSeen.add(digest); | 
|  |  | 
|  | sk_sp<SkData> data(SkData::MakeWithoutCopy(ptr, len)); | 
|  | std::unique_ptr<SkCodec> codec = SkCodec::MakeFromData(data); | 
|  | if (!codec) { | 
|  | // 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; | 
|  | switch (codec->getEncodedFormat()) { | 
|  | case SkEncodedImageFormat::kBMP:  ext =  "bmp"; break; | 
|  | case SkEncodedImageFormat::kGIF:  ext =  "gif"; break; | 
|  | case SkEncodedImageFormat::kICO:  ext =  "ico"; break; | 
|  | case SkEncodedImageFormat::kJPEG: ext =  "jpg"; break; | 
|  | case SkEncodedImageFormat::kPNG:  ext =  "png"; break; | 
|  | case SkEncodedImageFormat::kDNG:  ext =  "dng"; break; | 
|  | case SkEncodedImageFormat::kWBMP: ext = "wbmp"; break; | 
|  | case SkEncodedImageFormat::kWEBP: ext = "webp"; break; | 
|  | default: | 
|  | // This should be unreachable because we cannot create a codec if we do not know | 
|  | // the image type. | 
|  | SkASSERT(false); | 
|  | } | 
|  |  | 
|  | auto writeImage = [&] (const char* name, int num) { | 
|  | SkString path; | 
|  | path.appendf("%s/%s%d.%s", gOutputDir, name, num, ext.c_str()); | 
|  |  | 
|  | SkFILEWStream file(path.c_str()); | 
|  | file.write(ptr, len); | 
|  |  | 
|  | SkDebugf("%s\n", path.c_str()); | 
|  | }; | 
|  |  | 
|  | if (FLAGS_testDecode) { | 
|  | SkBitmap bitmap; | 
|  | SkImageInfo info = codec->getInfo().makeColorType(kN32_SkColorType); | 
|  | bitmap.allocPixels(info); | 
|  | const SkCodec::Result result = codec->getPixels( | 
|  | info, bitmap.getPixels(),  bitmap.rowBytes()); | 
|  | switch (result) { | 
|  | case SkCodec::kSuccess: | 
|  | case SkCodec::kIncompleteInput: | 
|  | case SkCodec::kErrorInInput: | 
|  | break; | 
|  | default: | 
|  | SkDebugf("Decoding failed for %s\n", skpName.c_str()); | 
|  | if (FLAGS_writeFailedImages) { | 
|  | writeImage("unknown", gSkpToUnknownCount[skpName]); | 
|  | } | 
|  | gSkpToUnknownCount[skpName]++; | 
|  | return; | 
|  | } | 
|  | } | 
|  |  | 
|  | if (FLAGS_writeImages) { | 
|  | writeImage("", gKnown); | 
|  | } | 
|  |  | 
|  | gKnown++; | 
|  | } | 
|  | }; | 
|  |  | 
|  | static bool get_images_from_file(const SkString& file) { | 
|  | Sniffer sniff(file.c_str()); | 
|  | auto stream = SkStream::MakeFromFile(file.c_str()); | 
|  |  | 
|  | SkDeserialProcs procs; | 
|  | procs.fImageProc = [](const void* data, size_t size, void* ctx) -> sk_sp<SkImage> { | 
|  | ((Sniffer*)ctx)->sniff(data, size); | 
|  | return nullptr; | 
|  | }; | 
|  | procs.fImageCtx = &sniff; | 
|  | return SkPicture::MakeFromStream(stream.get(), &procs) != nullptr; | 
|  | } | 
|  |  | 
|  | int main(int argc, char** argv) { | 
|  | CommandLineFlags::SetUsage( | 
|  | "Usage: get_images_from_skps -s <dir of skps> -o <dir for output images> --testDecode " | 
|  | "-j <output JSON path> --writeImages, --writeFailedImages\n"); | 
|  |  | 
|  | CommandLineFlags::Parse(argc, argv); | 
|  | const char* inputs = FLAGS_skps[0]; | 
|  | gOutputDir = FLAGS_out[0]; | 
|  |  | 
|  | if (!sk_isdir(gOutputDir)) { | 
|  | CommandLineFlags::PrintUsage(); | 
|  | return 1; | 
|  | } | 
|  |  | 
|  | if (sk_isdir(inputs)) { | 
|  | SkOSFile::Iter iter(inputs, "skp"); | 
|  | for (SkString file; iter.next(&file); ) { | 
|  | if (!get_images_from_file(SkOSPath::Join(inputs, file.c_str()))) { | 
|  | return 2; | 
|  | } | 
|  | } | 
|  | } else { | 
|  | if (!get_images_from_file(SkString(inputs))) { | 
|  | return 2; | 
|  | } | 
|  | } | 
|  | /** | 
|  | JSON results are written out in the following format: | 
|  | { | 
|  | "failures": { | 
|  | "skp1": 12, | 
|  | "skp4": 2, | 
|  | ... | 
|  | }, | 
|  | "unsupported": { | 
|  | "skp9": 13, | 
|  | "skp17": 3, | 
|  | ... | 
|  | } | 
|  | "totalFailures": 32, | 
|  | "totalUnsupported": 9, | 
|  | "totalSuccesses": 21, | 
|  | } | 
|  | */ | 
|  |  | 
|  | unsigned int totalFailures = 0, | 
|  | totalUnsupported = 0; | 
|  | SkDynamicMemoryWStream memStream; | 
|  | SkJSONWriter writer(&memStream, SkJSONWriter::Mode::kPretty); | 
|  | writer.beginObject(); | 
|  | { | 
|  | writer.beginObject("failures"); | 
|  | { | 
|  | for (const auto& failure : gSkpToUnknownCount) { | 
|  | SkDebugf("%s %u\n", failure.first.c_str(), failure.second); | 
|  | totalFailures += failure.second; | 
|  | writer.appendU32(failure.first.c_str(), failure.second); | 
|  | } | 
|  | } | 
|  | writer.endObject(); | 
|  | writer.appendU32("totalFailures", totalFailures); | 
|  |  | 
|  | #ifdef SK_DEBUG | 
|  | writer.beginObject("unsupported"); | 
|  | { | 
|  | for (const auto& unsupported : gSkpToUnsupportedCount) { | 
|  | SkDebugf("%s %u\n", unsupported.first.c_str(), unsupported.second); | 
|  | totalUnsupported += unsupported.second; | 
|  | writer.appendHexU32(unsupported.first.c_str(), unsupported.second); | 
|  | } | 
|  | } | 
|  | writer.endObject(); | 
|  | writer.appendU32("totalUnsupported", totalUnsupported); | 
|  | #endif | 
|  |  | 
|  | writer.appendS32("totalSuccesses", gKnown); | 
|  | SkDebugf("%d known, %u failures, %u unsupported\n", | 
|  | gKnown, totalFailures, totalUnsupported); | 
|  | } | 
|  | writer.endObject(); | 
|  | writer.flush(); | 
|  |  | 
|  | if (totalFailures > 0 || totalUnsupported > 0) { | 
|  | if (!FLAGS_failuresJsonPath.isEmpty()) { | 
|  | SkDebugf("Writing failures to %s\n", FLAGS_failuresJsonPath[0]); | 
|  | SkFILEWStream stream(FLAGS_failuresJsonPath[0]); | 
|  | auto jsonStream = memStream.detachAsStream(); | 
|  | stream.writeStream(jsonStream.get(), jsonStream->getLength()); | 
|  | } | 
|  | } | 
|  |  | 
|  | return 0; | 
|  | } |