Chromium Code Reviews| Index: tools/skimage_main.cpp |
| diff --git a/tools/skimage_main.cpp b/tools/skimage_main.cpp |
| index af9b4adea27b9dc1534ab5e654d535e836a2fcc3..0e0417c6917a4a1a38889c815d39ffddfc8cabfd 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::GetFormat(&stream); |
| + } |
| + if (SkImageDecoder::kUnknown_Format == format) { |
| + const char* dot = strrchr(srcPath, '.'); |
| + if (NULL != dot) { |
| + format = guess_format_from_suffix(dot); |
|
epoger
2013/04/25 14:38:43
Since there are two possible ways skimage can figu
|
| + } |
| + if (SkImageDecoder::kUnknown_Format == format) { |
| + SkDebugf("Could not determine type for '%s'\n", srcPath); |
| + format = SkImageDecoder::kPNG_Format; |
| + } |
| + |
| + } |
| + } else { |
| + SkASSERT(!stream.rewind() || SkImageDecoder::GetFormat(&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(); |