Index: tools/skimage_main.cpp |
diff --git a/tools/skimage_main.cpp b/tools/skimage_main.cpp |
index af9b4adea27b9dc1534ab5e654d535e836a2fcc3..188164ff06ac6d43f1ac337779510f07fef4e560 100644 |
--- a/tools/skimage_main.cpp |
+++ b/tools/skimage_main.cpp |
@@ -6,7 +6,9 @@ |
*/ |
#include "SkBitmap.h" |
+#include "SkColorPriv.h" |
#include "SkCommandLineFlags.h" |
+#include "SkData.h" |
#include "SkGraphics.h" |
#include "SkImageDecoder.h" |
#include "SkImageEncoder.h" |
@@ -15,46 +17,55 @@ |
#include "SkTArray.h" |
#include "SkTemplates.h" |
- |
DEFINE_string2(readPath, r, "", "Folder(s) and files to decode images. Required."); |
DEFINE_string2(writePath, w, "", "Write rendered images into this directory."); |
+DEFINE_bool(reencode, true, "Reencode the images to test encoding."); |
-// Store the names of the filenames to report later which ones failed, succeeded, and were |
-// invalid. |
-static SkTArray<SkString, false> invalids; |
-static SkTArray<SkString, false> nocodecs; |
-static SkTArray<SkString, false> failures; |
-static SkTArray<SkString, false> successes; |
+struct Format { |
+ SkImageEncoder::Type fType; |
+ SkImageDecoder::Format fFormat; |
+ const char* fSuffix; |
+}; |
-static bool decodeFile(SkBitmap* bitmap, const char srcPath[]) { |
- SkFILEStream stream(srcPath); |
- if (!stream.isValid()) { |
- invalids.push_back().set(srcPath); |
- return false; |
- } |
+static const Format gFormats[] = { |
+ { SkImageEncoder::kBMP_Type, SkImageDecoder::kBMP_Format, ".bmp" }, |
+ { SkImageEncoder::kGIF_Type, SkImageDecoder::kGIF_Format, ".gif" }, |
+ { SkImageEncoder::kICO_Type, SkImageDecoder::kICO_Format, ".ico" }, |
+ { SkImageEncoder::kJPEG_Type, SkImageDecoder::kJPEG_Format, ".jpg" }, |
+ { SkImageEncoder::kPNG_Type, SkImageDecoder::kPNG_Format, ".png" }, |
+ { SkImageEncoder::kWBMP_Type, SkImageDecoder::kWBMP_Format, ".wbmp" }, |
+ { SkImageEncoder::kWEBP_Type, SkImageDecoder::kWEBP_Format, ".webp" } |
+}; |
- SkImageDecoder* codec = SkImageDecoder::Factory(&stream); |
- if (NULL == codec) { |
- nocodecs.push_back().set(srcPath); |
- return false; |
+static SkImageEncoder::Type format_to_type(SkImageDecoder::Format format) { |
+ for (size_t i = 0; i < SK_ARRAY_COUNT(gFormats); i++) { |
+ if (gFormats[i].fFormat == format) { |
+ return gFormats[i].fType; |
+ } |
} |
+ return SkImageEncoder::kUnknown_Type; |
+} |
- SkAutoTDelete<SkImageDecoder> ad(codec); |
- |
- stream.rewind(); |
- if (!codec->decode(&stream, bitmap, SkBitmap::kARGB_8888_Config, |
- SkImageDecoder::kDecodePixels_Mode)) { |
- failures.push_back().set(srcPath); |
- return false; |
+static const char* suffix_for_type(SkImageEncoder::Type type) { |
+ for (size_t i = 0; i < SK_ARRAY_COUNT(gFormats); i++) { |
+ if (gFormats[i].fType == type) { |
+ return gFormats[i].fSuffix; |
+ } |
} |
- |
- successes.push_back().printf("%s [%d %d]", srcPath, bitmap->width(), bitmap->height()); |
- return true; |
+ return ""; |
} |
-/////////////////////////////////////////////////////////////////////////////// |
+static SkImageDecoder::Format guess_format_from_suffix(const char suffix[]) { |
+ for (size_t i = 0; i < SK_ARRAY_COUNT(gFormats); i++) { |
+ if (strcmp(suffix, gFormats[i].fSuffix) == 0) { |
+ return gFormats[i].fFormat; |
+ } |
+ } |
+ return SkImageDecoder::kUnknown_Format; |
+} |
-static void make_outname(SkString* dst, const char outDir[], const char src[]) { |
+static void make_outname(SkString* dst, const char outDir[], const char src[], |
+ const char suffix[]) { |
dst->set(outDir); |
const char* start = strrchr(src, '/'); |
if (start) { |
@@ -63,39 +74,163 @@ static void make_outname(SkString* dst, const char outDir[], const char src[]) { |
start = src; |
} |
dst->append(start); |
- if (!dst->endsWith(".png")) { |
+ if (!dst->endsWith(suffix)) { |
const char* cstyleDst = dst->c_str(); |
const char* dot = strrchr(cstyleDst, '.'); |
if (dot != NULL) { |
int32_t index = SkToS32(dot - cstyleDst); |
dst->remove(index, dst->size() - index); |
} |
- dst->append(".png"); |
+ dst->append(suffix); |
+ } |
+} |
+ |
+// Store the names of the filenames to report later which ones failed, succeeded, and were |
+// invalid. |
+static SkTArray<SkString, false> gInvalidStreams; |
+static SkTArray<SkString, false> gMissingCodecs; |
+static SkTArray<SkString, false> gDecodeFailures; |
+static SkTArray<SkString, false> gEncodeFailures; |
+static SkTArray<SkString, false> gSuccessfulDecodes; |
+ |
+static bool write_bitmap(const char outName[], SkBitmap* bm) { |
+ SkBitmap bitmap8888; |
+ if (SkBitmap::kARGB_8888_Config != bm->config()) { |
+ if (!bm->copyTo(&bitmap8888, SkBitmap::kARGB_8888_Config)) { |
+ return false; |
+ } |
+ bm = &bitmap8888; |
+ } |
+ // FIXME: This forces all pixels to be opaque, like the many implementations |
+ // of force_all_opaque. These should be unified if they cannot be eliminated. |
+ SkAutoLockPixels lock(*bm); |
+ for (int y = 0; y < bm->height(); y++) { |
+ for (int x = 0; x < bm->width(); x++) { |
+ *bm->getAddr32(x, y) |= (SK_A32_MASK << SK_A32_SHIFT); |
+ } |
+ } |
+ return SkImageEncoder::EncodeFile(outName, *bm, SkImageEncoder::kPNG_Type, 100); |
+} |
+ |
+static void decodeFileAndWrite(const char srcPath[], const SkString* writePath) { |
+ SkBitmap bitmap; |
+ SkFILEStream stream(srcPath); |
+ if (!stream.isValid()) { |
+ gInvalidStreams.push_back().set(srcPath); |
+ return; |
+ } |
+ |
+ SkImageDecoder* codec = SkImageDecoder::Factory(&stream); |
+ if (NULL == codec) { |
+ gMissingCodecs.push_back().set(srcPath); |
+ return; |
+ } |
+ |
+ SkAutoTDelete<SkImageDecoder> ad(codec); |
+ |
+ stream.rewind(); |
+ if (!codec->decode(&stream, &bitmap, SkBitmap::kARGB_8888_Config, |
+ SkImageDecoder::kDecodePixels_Mode)) { |
+ gDecodeFailures.push_back().set(srcPath); |
+ return; |
+ } |
+ |
+ gSuccessfulDecodes.push_back().printf("%s [%d %d]", srcPath, bitmap.width(), bitmap.height()); |
+ |
+ if (FLAGS_reencode) { |
+ // Encode to the format the file was originally in, or PNG if the encoder for the same |
+ // format is unavailable. |
+ SkImageDecoder::Format format = codec->getFormat(); |
+ if (SkImageDecoder::kUnknown_Format == format) { |
+ if (stream.rewind()) { |
+ format = SkImageDecoder::GetStreamFormat(&stream); |
+ } |
+ if (SkImageDecoder::kUnknown_Format == format) { |
+ const char* dot = strrchr(srcPath, '.'); |
+ if (NULL != dot) { |
+ format = guess_format_from_suffix(dot); |
+ } |
+ if (SkImageDecoder::kUnknown_Format == format) { |
+ SkDebugf("Could not determine type for '%s'\n", srcPath); |
+ format = SkImageDecoder::kPNG_Format; |
+ } |
+ |
+ } |
+ } else { |
+ SkASSERT(!stream.rewind() || SkImageDecoder::GetStreamFormat(&stream) == format); |
+ } |
+ SkImageEncoder::Type type = format_to_type(format); |
+ // format should never be kUnknown_Format, so type should never be kUnknown_Type. |
+ SkASSERT(type != SkImageEncoder::kUnknown_Type); |
+ |
+ SkImageEncoder* encoder = SkImageEncoder::Create(type); |
+ if (NULL == encoder) { |
+ type = SkImageEncoder::kPNG_Type; |
+ encoder = SkImageEncoder::Create(type); |
+ SkASSERT(encoder); |
+ } |
+ SkAutoTDelete<SkImageEncoder> ade(encoder); |
+ // Encode to a stream. |
+ SkDynamicMemoryWStream wStream; |
+ if (!encoder->encodeStream(&wStream, bitmap, 100)) { |
+ gEncodeFailures.push_back().printf("Failed to reencode %s to type '%s'", srcPath, |
+ suffix_for_type(type)); |
+ return; |
+ } |
+ |
+ SkAutoTUnref<SkData> data(wStream.copyToData()); |
+ if (writePath != NULL && type != SkImageEncoder::kPNG_Type) { |
+ // Write the encoded data to a file. Do not write to PNG, which will be written later, |
+ // regardless of the input format. |
+ SkString outPath; |
+ make_outname(&outPath, writePath->c_str(), srcPath, suffix_for_type(type)); |
+ SkFILEWStream file(outPath.c_str()); |
+ if(file.write(data->data(), data->size())) { |
+ gSuccessfulDecodes.push_back().appendf("\twrote %s", outPath.c_str()); |
+ } else { |
+ gEncodeFailures.push_back().printf("Failed to write %s", outPath.c_str()); |
+ } |
+ } |
+ // Ensure that the reencoded data can still be decoded. |
+ SkMemoryStream memStream(data); |
+ SkBitmap redecodedBitmap; |
+ SkImageDecoder::Format formatOnSecondDecode; |
+ if (SkImageDecoder::DecodeStream(&memStream, &redecodedBitmap, SkBitmap::kNo_Config, |
+ SkImageDecoder::kDecodePixels_Mode, |
+ &formatOnSecondDecode)) { |
+ SkASSERT(format_to_type(formatOnSecondDecode) == type); |
+ } else { |
+ gDecodeFailures.push_back().printf("Failed to redecode %s after reencoding to '%s'", |
+ srcPath, suffix_for_type(type)); |
+ } |
+ } |
+ |
+ if (writePath != NULL) { |
+ SkString outPath; |
+ make_outname(&outPath, writePath->c_str(), srcPath, ".png"); |
+ if (write_bitmap(outPath.c_str(), &bitmap)) { |
+ gSuccessfulDecodes.push_back().appendf("\twrote %s", outPath.c_str()); |
+ } else { |
+ gEncodeFailures.push_back().set(outPath); |
+ } |
} |
} |
+/////////////////////////////////////////////////////////////////////////////// |
+ |
// If strings is not empty, print title, followed by each string on its own line starting |
// with a tab. |
-static void print_strings(const char* title, const SkTArray<SkString, false>& strings) { |
+// @return bool True if strings had at least one entry. |
+static bool print_strings(const char* title, const SkTArray<SkString, false>& strings) { |
if (strings.count() > 0) { |
SkDebugf("%s:\n", title); |
for (int i = 0; i < strings.count(); i++) { |
SkDebugf("\t%s\n", strings[i].c_str()); |
} |
SkDebugf("\n"); |
+ return true; |
} |
-} |
- |
-static void decodeFileAndWrite(const char filePath[], const SkString* writePath) { |
- SkBitmap bitmap; |
- if (decodeFile(&bitmap, filePath)) { |
- if (writePath != NULL) { |
- SkString outPath; |
- make_outname(&outPath, writePath->c_str(), filePath); |
- successes.push_back().appendf("\twrote %s", outPath.c_str()); |
- SkImageEncoder::EncodeFile(outPath.c_str(), bitmap, SkImageEncoder::kPNG_Type, 100); |
- } |
- } |
+ return false; |
} |
int tool_main(int argc, char** argv); |
@@ -148,12 +283,13 @@ int tool_main(int argc, char** argv) { |
// Add some space, since codecs may print warnings without newline. |
SkDebugf("\n\n"); |
- print_strings("Invalid files", invalids); |
- print_strings("Missing codec", nocodecs); |
- print_strings("Failed to decode", failures); |
- print_strings("Decoded", successes); |
+ bool failed = print_strings("Invalid files", gInvalidStreams); |
+ failed |= print_strings("Missing codec", gMissingCodecs); |
+ failed |= print_strings("Failed to decode", gDecodeFailures); |
+ failed |= print_strings("Failed to encode", gEncodeFailures); |
+ print_strings("Decoded", gSuccessfulDecodes); |
- return 0; |
+ return failed ? -1 : 0; |
} |
void forceLinking(); |