From 39edf4cd94e6fbeb8c1187a588b314e9795c81e4 Mon Sep 17 00:00:00 2001 From: "scroggo@google.com" Date: Thu, 25 Apr 2013 17:33:51 +0000 Subject: Updates to skimage tool to use it for testing. skimage_main.cpp: More changes in the interest of testing our decoders. force_all_opaque before writing PNG files. Test reencoding the image to its original type (if possible), and then test redecoding it (to make sure the encoding was successful). Add an option to turn off this behavior. Merge decodeFileAndWrite with decodeFile. SkImageDecoder: Add kUnknown_Type to SkImageEncoder::Types. Add a static function to get the Format of an SkStream. In getFormatName(), remove an incorrect assert. When calling the flavor of DecodeStream that returns the Format, check the stream if the decoder returns kUnknown_Format. BUG=https://code.google.com/p/skia/issues/detail?id=1241 Review URL: https://codereview.chromium.org/14363003 git-svn-id: http://skia.googlecode.com/svn/trunk@8862 2bbb7eff-a529-9590-31e7-b0007b416f81 --- tools/skimage_main.cpp | 234 ++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 185 insertions(+), 49 deletions(-) (limited to 'tools') diff --git a/tools/skimage_main.cpp b/tools/skimage_main.cpp index af9b4adea2..188164ff06 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 invalids; -static SkTArray nocodecs; -static SkTArray failures; -static SkTArray 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 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 gInvalidStreams; +static SkTArray gMissingCodecs; +static SkTArray gDecodeFailures; +static SkTArray gEncodeFailures; +static SkTArray 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 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 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 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& strings) { +// @return bool True if strings had at least one entry. +static bool print_strings(const char* title, const SkTArray& 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(); -- cgit v1.2.3