Updates to skimage tool to use it for testing.
authorscroggo@google.com <scroggo@google.com@2bbb7eff-a529-9590-31e7-b0007b416f81>
Thu, 25 Apr 2013 17:33:51 +0000 (17:33 +0000)
committerscroggo@google.com <scroggo@google.com@2bbb7eff-a529-9590-31e7-b0007b416f81>
Thu, 25 Apr 2013 17:33:51 +0000 (17:33 +0000)
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

15 files changed:
gyp/images.gyp
include/images/SkImageDecoder.h
include/images/SkImageEncoder.h
src/images/SkImageDecoder.cpp
src/images/SkImageDecoder_FactoryRegistrar.cpp
src/images/SkImageDecoder_libbmp.cpp
src/images/SkImageDecoder_libgif.cpp
src/images/SkImageDecoder_libico.cpp
src/images/SkImageDecoder_libjpeg.cpp
src/images/SkImageDecoder_libpng.cpp
src/images/SkImageDecoder_libwebp.cpp
src/images/SkImageDecoder_wbmp.cpp
src/ports/SkImageDecoder_CG.cpp
src/ports/SkImageDecoder_WIC.cpp
tools/skimage_main.cpp

index c96ff4d..bb1b92c 100644 (file)
@@ -18,6 +18,8 @@
         '../include/core',
         '../include/images',
         '../include/lazy',
+        # for access to SkErrorInternals.h
+        '../src/core/',
         # for access to SkImagePriv.h
         '../src/image/',
       ],
index 38e77f4..54176bd 100644 (file)
@@ -26,7 +26,6 @@ class SkImageDecoder {
 public:
     virtual ~SkImageDecoder();
 
-    // Should be consistent with sFormatName
     enum Format {
         kUnknown_Format,
         kBMP_Format,
@@ -37,7 +36,7 @@ public:
         kWBMP_Format,
         kWEBP_Format,
 
-        kLastKnownFormat = kWEBP_Format
+        kLastKnownFormat = kWEBP_Format,
     };
 
     /** Return the format of image this decoder can decode. If this decoder can decode multiple
@@ -45,6 +44,11 @@ public:
     */
     virtual Format getFormat() const;
 
+    /** Return the format of the SkStream or kUnknown_Format if it cannot be determined. Rewinds the
+        stream before returning.
+    */
+    static Format GetStreamFormat(SkStream*);
+
     /** Return a readable string of the value returned by getFormat().
     */
     const char* getFormatName() const;
@@ -416,13 +420,6 @@ private:
     mutable bool            fShouldCancelDecode;
     bool                    fPreferQualityOverSpeed;
 
-    /** Contains the image format name.
-     *  This should be consistent with Format.
-     *
-     *  The format name gives a more meaningful error message than enum.
-     */
-    static const char* sFormatName[];
-
     // illegal
     SkImageDecoder(const SkImageDecoder&);
     SkImageDecoder& operator=(const SkImageDecoder&);
index 0f7d18a..2965c88 100644 (file)
@@ -16,6 +16,7 @@ class SkWStream;
 class SkImageEncoder {
 public:
     enum Type {
+        kUnknown_Type,
         kBMP_Type,
         kGIF_Type,
         kICO_Type,
index c16efcf..16cba64 100644 (file)
@@ -18,17 +18,6 @@ SK_DEFINE_INST_COUNT(SkImageDecoder::Peeker)
 SK_DEFINE_INST_COUNT(SkImageDecoder::Chooser)
 SK_DEFINE_INST_COUNT(SkImageDecoderFactory)
 
-const char *SkImageDecoder::sFormatName[] = {
-    "Unknown Format",
-    "BMP",
-    "GIF",
-    "ICO",
-    "JPEG",
-    "PNG",
-    "WBMP",
-    "WEBP",
-};
-
 static SkBitmap::Config gDeviceConfig = SkBitmap::kNo_Config;
 
 SkBitmap::Config SkImageDecoder::GetDeviceConfig()
@@ -60,8 +49,27 @@ SkImageDecoder::Format SkImageDecoder::getFormat() const {
 }
 
 const char* SkImageDecoder::getFormatName() const {
-    SkASSERT(SK_ARRAY_COUNT(sFormatName) == kLastKnownFormat);
-    return sFormatName[this->getFormat()];
+    switch (this->getFormat()) {
+        case kUnknown_Format:
+            return "Unknown Format";
+        case kBMP_Format:
+            return "BMP";
+        case kGIF_Format:
+            return "GIF";
+        case kICO_Format:
+            return "ICO";
+        case kJPEG_Format:
+            return "JPEG";
+        case kPNG_Format:
+            return "PNG";
+        case kWBMP_Format:
+            return "WBMP";
+        case kWEBP_Format:
+            return "WEBP";
+        default:
+            SkASSERT(!"Invalid format type!");
+    }
+    return "Unknown Format";
 }
 
 SkImageDecoder::Peeker* SkImageDecoder::setPeeker(Peeker* peeker) {
@@ -321,6 +329,11 @@ bool SkImageDecoder::DecodeStream(SkStream* stream, SkBitmap* bm,
         success = codec->decode(stream, bm, pref, mode);
         if (success && format) {
             *format = codec->getFormat();
+            if (kUnknown_Format == *format) {
+                if (stream->rewind()) {
+                    *format = GetStreamFormat(stream);
+                }
+            }
         }
         delete codec;
     }
index 6cc417a..f1eca3d 100644 (file)
@@ -5,6 +5,7 @@
  * found in the LICENSE file.
  */
 
+#include "SkErrorInternals.h"
 #include "SkImageDecoder.h"
 #include "SkStream.h"
 #include "SkTRegistry.h"
@@ -45,3 +46,24 @@ SkImageDecoder* image_decoder_from_stream(SkStream* stream) {
     }
     return NULL;
 }
+
+typedef SkTRegistry<SkImageDecoder::Format, SkStream*> FormatReg;
+
+template FormatReg* SkTRegistry<SkImageDecoder::Format, SkStream*>::gHead;
+
+SkImageDecoder::Format SkImageDecoder::GetStreamFormat(SkStream* stream) {
+    const FormatReg* curr = FormatReg::Head();
+    while (curr != NULL) {
+        Format format = curr->factory()(stream);
+        if (!stream->rewind()) {
+            SkErrorInternals::SetError(kInvalidOperation_SkError,
+                                       "Unable to rewind the image stream\n");
+            return kUnknown_Format;
+        }
+        if (format != kUnknown_Format) {
+            return format;
+        }
+        curr = curr->next();
+    }
+    return kUnknown_Format;
+}
index 488eddc..5c2299b 100644 (file)
@@ -34,14 +34,18 @@ private:
 DEFINE_DECODER_CREATOR(BMPImageDecoder);
 ///////////////////////////////////////////////////////////////////////////////
 
-static SkImageDecoder* sk_libbmp_dfactory(SkStream* stream) {
+static bool is_bmp(SkStream* stream) {
     static const char kBmpMagic[] = { 'B', 'M' };
 
 
     char buffer[sizeof(kBmpMagic)];
 
-    if (stream->read(buffer, sizeof(kBmpMagic)) == sizeof(kBmpMagic) &&
-        !memcmp(buffer, kBmpMagic, sizeof(kBmpMagic))) {
+    return stream->read(buffer, sizeof(kBmpMagic)) == sizeof(kBmpMagic) &&
+        !memcmp(buffer, kBmpMagic, sizeof(kBmpMagic));
+}
+
+static SkImageDecoder* sk_libbmp_dfactory(SkStream* stream) {
+    if (is_bmp(stream)) {
         return SkNEW(SkBMPImageDecoder);
     }
     return NULL;
@@ -49,6 +53,15 @@ static SkImageDecoder* sk_libbmp_dfactory(SkStream* stream) {
 
 static SkTRegistry<SkImageDecoder*, SkStream*> gReg(sk_libbmp_dfactory);
 
+static SkImageDecoder::Format get_format_bmp(SkStream* stream) {
+    if (is_bmp(stream)) {
+        return SkImageDecoder::kBMP_Format;
+    }
+    return SkImageDecoder::kUnknown_Format;
+}
+
+static SkTRegistry<SkImageDecoder::Format, SkStream*> gFormatReg(get_format_bmp);
+
 ///////////////////////////////////////////////////////////////////////////////
 
 class SkBmpDecoderCallback : public image_codec::BmpDecoderCallback {
index 3e4cda8..f6c54c2 100644 (file)
@@ -91,9 +91,6 @@ private:
 
 ///////////////////////////////////////////////////////////////////////////////
 
-//#define GIF_STAMP       "GIF"    /* First chars in file - GIF stamp. */
-//#define GIF_STAMP_LEN   (sizeof(GIF_STAMP) - 1)
-
 static int DecodeCallBackProc(GifFileType* fileType, GifByteType* out,
                               int size) {
     SkStream* stream = (SkStream*) fileType->UserData;
@@ -365,18 +362,34 @@ DONE:
 DEFINE_DECODER_CREATOR(GIFImageDecoder);
 ///////////////////////////////////////////////////////////////////////////////
 
-#include "SkTRegistry.h"
-
-static SkImageDecoder* sk_libgif_dfactory(SkStream* stream) {
+static bool is_gif(SkStream* stream) {
     char buf[GIF_STAMP_LEN];
     if (stream->read(buf, GIF_STAMP_LEN) == GIF_STAMP_LEN) {
         if (memcmp(GIF_STAMP,   buf, GIF_STAMP_LEN) == 0 ||
                 memcmp(GIF87_STAMP, buf, GIF_STAMP_LEN) == 0 ||
                 memcmp(GIF89_STAMP, buf, GIF_STAMP_LEN) == 0) {
-            return SkNEW(SkGIFImageDecoder);
+            return true;
         }
     }
+    return false;
+}
+
+#include "SkTRegistry.h"
+
+static SkImageDecoder* sk_libgif_dfactory(SkStream* stream) {
+    if (is_gif(stream)) {
+        return SkNEW(SkGIFImageDecoder);
+    }
     return NULL;
 }
 
 static SkTRegistry<SkImageDecoder*, SkStream*> gReg(sk_libgif_dfactory);
+
+static SkImageDecoder::Format get_format_gif(SkStream* stream) {
+    if (is_gif(stream)) {
+        return SkImageDecoder::kGIF_Format;
+    }
+    return SkImageDecoder::kUnknown_Format;
+}
+
+static SkTRegistry<SkImageDecoder::Format, SkStream*> gFormatReg(get_format_gif);
index ffc59e0..195b6ff 100644 (file)
@@ -373,9 +373,7 @@ static void editPixelBit32(const int pixelNo, const unsigned char* buf,
 DEFINE_DECODER_CREATOR(ICOImageDecoder);
 /////////////////////////////////////////////////////////////////////////////////////////
 
-#include "SkTRegistry.h"
-
-static SkImageDecoder* sk_libico_dfactory(SkStream* stream) {
+static bool is_ico(SkStream* stream) {
     // Check to see if the first four bytes are 0,0,1,0
     // FIXME: Is that required and sufficient?
     SkAutoMalloc autoMal(4);
@@ -385,9 +383,27 @@ static SkImageDecoder* sk_libico_dfactory(SkStream* stream) {
     int type = read2Bytes(buf, 2);
     if (reserved != 0 || type != 1) {
         // This stream does not represent an ICO image.
-        return NULL;
+        return false;
     }
-    return SkNEW(SkICOImageDecoder);
+    return true;
+}
+
+#include "SkTRegistry.h"
+
+static SkImageDecoder* sk_libico_dfactory(SkStream* stream) {
+    if (is_ico(stream)) {
+        return SkNEW(SkICOImageDecoder);
+    }
+    return NULL;
 }
 
 static SkTRegistry<SkImageDecoder*, SkStream*> gReg(sk_libico_dfactory);
+
+static SkImageDecoder::Format get_format_ico(SkStream* stream) {
+    if (is_ico(stream)) {
+        return SkImageDecoder::kICO_Format;
+    }
+    return SkImageDecoder::kUnknown_Format;
+}
+
+static SkTRegistry<SkImageDecoder::Format, SkStream*> gFormatReg(get_format_ico);
index 9920bac..c55f4c4 100644 (file)
@@ -998,9 +998,7 @@ DEFINE_DECODER_CREATOR(JPEGImageDecoder);
 DEFINE_ENCODER_CREATOR(JPEGImageEncoder);
 ///////////////////////////////////////////////////////////////////////////////
 
-#include "SkTRegistry.h"
-
-static SkImageDecoder* sk_libjpeg_dfactory(SkStream* stream) {
+static bool is_jpeg(SkStream* stream) {
     static const unsigned char gHeader[] = { 0xFF, 0xD8, 0xFF };
     static const size_t HEADER_SIZE = sizeof(gHeader);
 
@@ -1008,12 +1006,28 @@ static SkImageDecoder* sk_libjpeg_dfactory(SkStream* stream) {
     size_t len = stream->read(buffer, HEADER_SIZE);
 
     if (len != HEADER_SIZE) {
-        return NULL;   // can't read enough
+        return false;   // can't read enough
     }
     if (memcmp(buffer, gHeader, HEADER_SIZE)) {
-        return NULL;
+        return false;
+    }
+    return true;
+}
+
+#include "SkTRegistry.h"
+
+static SkImageDecoder* sk_libjpeg_dfactory(SkStream* stream) {
+    if (is_jpeg(stream)) {
+        return SkNEW(SkJPEGImageDecoder);
+    }
+    return NULL;
+}
+
+static SkImageDecoder::Format get_format_jpeg(SkStream* stream) {
+    if (is_jpeg(stream)) {
+        return SkImageDecoder::kJPEG_Format;
     }
-    return SkNEW(SkJPEGImageDecoder);
+    return SkImageDecoder::kUnknown_Format;
 }
 
 static SkImageEncoder* sk_libjpeg_efactory(SkImageEncoder::Type t) {
@@ -1022,4 +1036,5 @@ static SkImageEncoder* sk_libjpeg_efactory(SkImageEncoder::Type t) {
 
 
 static SkTRegistry<SkImageDecoder*, SkStream*> gDReg(sk_libjpeg_dfactory);
+static SkTRegistry<SkImageDecoder::Format, SkStream*> gFormatReg(get_format_jpeg);
 static SkTRegistry<SkImageEncoder*, SkImageEncoder::Type> gEReg(sk_libjpeg_efactory);
index efa4595..aec8948 100644 (file)
@@ -47,6 +47,7 @@ public:
     virtual Format getFormat() const SK_OVERRIDE {
         return kPNG_Format;
     }
+
     virtual ~SkPNGImageDecoder() {
         SkDELETE(fImageIndex);
     }
@@ -1086,18 +1087,33 @@ DEFINE_ENCODER_CREATOR(PNGImageEncoder);
 
 #include "SkTRegistry.h"
 
-SkImageDecoder* sk_libpng_dfactory(SkStream* stream) {
+static bool is_png(SkStream* stream) {
     char buf[PNG_BYTES_TO_CHECK];
     if (stream->read(buf, PNG_BYTES_TO_CHECK) == PNG_BYTES_TO_CHECK &&
         !png_sig_cmp((png_bytep) buf, (png_size_t)0, PNG_BYTES_TO_CHECK)) {
+        return true;
+    }
+    return false;
+}
+
+SkImageDecoder* sk_libpng_dfactory(SkStream* stream) {
+    if (is_png(stream)) {
         return SkNEW(SkPNGImageDecoder);
     }
     return NULL;
 }
 
+static SkImageDecoder::Format get_format_png(SkStream* stream) {
+    if (is_png(stream)) {
+        return SkImageDecoder::kPNG_Format;
+    }
+    return SkImageDecoder::kUnknown_Format;
+}
+
 SkImageEncoder* sk_libpng_efactory(SkImageEncoder::Type t) {
     return (SkImageEncoder::kPNG_Type == t) ? SkNEW(SkPNGImageEncoder) : NULL;
 }
 
 static SkTRegistry<SkImageEncoder*, SkImageEncoder::Type> gEReg(sk_libpng_efactory);
+static SkTRegistry<SkImageDecoder::Format, SkStream*> gFormatReg(get_format_png);
 static SkTRegistry<SkImageDecoder*, SkStream*> gDReg(sk_libpng_dfactory);
index d9434c2..a33d0f9 100644 (file)
@@ -587,9 +587,18 @@ static SkImageDecoder* sk_libwebp_dfactory(SkStream* stream) {
     return SkNEW(SkWEBPImageDecoder);
 }
 
+static SkImageDecoder::Format get_format_webp(SkStream* stream) {
+    int width, height, hasAlpha;
+    if (webp_parse_header(stream, &width, &height, &hasAlpha)) {
+        return SkImageDecoder::kWEBP_Format;
+    }
+    return SkImageDecoder::kUnknown_Format;
+}
+
 static SkImageEncoder* sk_libwebp_efactory(SkImageEncoder::Type t) {
       return (SkImageEncoder::kWEBP_Type == t) ? SkNEW(SkWEBPImageEncoder) : NULL;
 }
 
 static SkTRegistry<SkImageDecoder*, SkStream*> gDReg(sk_libwebp_dfactory);
+static SkTRegistry<SkImageDecoder::Format, SkStream*> gFormatReg(get_format_webp);
 static SkTRegistry<SkImageEncoder*, SkImageEncoder::Type> gEReg(sk_libwebp_efactory);
index 96677cc..db40f59 100644 (file)
@@ -170,4 +170,13 @@ static SkImageDecoder* sk_wbmp_dfactory(SkStream* stream) {
     return NULL;
 }
 
+static SkImageDecoder::Format get_format_wbmp(SkStream* stream) {
+    wbmp_head head;
+    if (head.init(stream)) {
+        return SkImageDecoder::kWBMP_Format;
+    }
+    return SkImageDecoder::kUnknown_Format;
+}
+
 static SkTRegistry<SkImageDecoder*, SkStream*> gReg(sk_wbmp_dfactory);
+static SkTRegistry<SkImageDecoder::Format, SkStream*> gFormatReg(get_format_wbmp);
index 9fc49a4..5fe6fdd 100644 (file)
@@ -245,3 +245,45 @@ static SkImageEncoder* sk_imageencoder_cg_factory(SkImageEncoder::Type t) {
 }
 
 static SkTRegistry<SkImageEncoder*, SkImageEncoder::Type> gEReg(sk_imageencoder_cg_factory);
+
+struct FormatConversion {
+    CFStringRef             fUTType;
+    SkImageDecoder::Format  fFormat;
+};
+
+// Array of the types supported by the decoder.
+static const FormatConversion gFormatConversions[] = {
+    { kUTTypeBMP, SkImageDecoder::kBMP_Format },
+    { kUTTypeGIF, SkImageDecoder::kGIF_Format },
+    { kUTTypeICO, SkImageDecoder::kICO_Format },
+    { kUTTypeJPEG, SkImageDecoder::kJPEG_Format },
+    // Also include JPEG2000
+    { kUTTypeJPEG2000, SkImageDecoder::kJPEG_Format },
+    { kUTTypePNG, SkImageDecoder::kPNG_Format },
+};
+
+static SkImageDecoder::Format UTType_to_Format(const CFStringRef uttype) {
+    for (size_t i = 0; i < SK_ARRAY_COUNT(gFormatConversions); i++) {
+        if (CFStringCompare(uttype, gFormatConversions[i].fUTType, 0) == kCFCompareEqualTo) {
+            return gFormatConversions[i].fFormat;
+        }
+    }
+    return SkImageDecoder::kUnknown_Format;
+}
+
+static SkImageDecoder::Format get_format_cg(SkStream *stream) {
+    CGImageSourceRef imageSrc = SkStreamToCGImageSource(stream);
+
+    if (NULL == imageSrc) {
+        return SkImageDecoder::kUnknown_Format;
+    }
+
+    SkAutoTCallVProc<const void, CFRelease> arsrc(imageSrc);
+    const CFStringRef name = CGImageSourceGetType(imageSrc);
+    if (NULL == name) {
+        return SkImageDecoder::kUnknown_Format;
+    }
+    return UTType_to_Format(name);
+}
+
+static SkTRegistry<SkImageDecoder::Format, SkStream*> gFormatReg(get_format_cg);
index 17d75cc..cd7f29f 100644 (file)
 #endif
 
 class SkImageDecoder_WIC : public SkImageDecoder {
+public:
+    // Decoding modes corresponding to SkImageDecoder::Mode, plus an extra mode for decoding
+    // only the format.
+    enum WICModes {
+        kDecodeFormat_WICMode,
+        kDecodeBounds_WICMode,
+        kDecodePixels_WICMode,
+    };
+
+    /**
+     *  Helper function to decode an SkStream.
+     *  @param stream SkStream to decode. Must be at the beginning.
+     *  @param bm   SkBitmap to decode into. Only used if wicMode is kDecodeBounds_WICMode or
+     *      kDecodePixels_WICMode, in which case it must not be NULL.
+     *  @param format Out parameter for the SkImageDecoder::Format of the SkStream. Only used if
+     *      wicMode is kDecodeFormat_WICMode.
+     */
+    bool decodeStream(SkStream* stream, SkBitmap* bm, WICModes wicMode, Format* format) const;
+
 protected:
-    virtual bool onDecode(SkStream* stream, SkBitmap* bm, Mode mode);
+    virtual bool onDecode(SkStream* stream, SkBitmap* bm, Mode mode) SK_OVERRIDE;
+};
+
+struct FormatConversion {
+    GUID                    fGuidFormat;
+    SkImageDecoder::Format  fFormat;
 };
 
+static const FormatConversion gFormatConversions[] = {
+    { GUID_ContainerFormatBmp, SkImageDecoder::kBMP_Format },
+    { GUID_ContainerFormatGif, SkImageDecoder::kGIF_Format },
+    { GUID_ContainerFormatIco, SkImageDecoder::kICO_Format },
+    { GUID_ContainerFormatJpeg, SkImageDecoder::kJPEG_Format },
+    { GUID_ContainerFormatPng, SkImageDecoder::kPNG_Format },
+};
+
+static SkImageDecoder::Format GuidContainerFormat_to_Format(REFGUID guid) {
+    for (size_t i = 0; i < SK_ARRAY_COUNT(gFormatConversions); i++) {
+        if (IsEqualGUID(guid, gFormatConversions[i].fGuidFormat)) {
+            return gFormatConversions[i].fFormat;
+        }
+    }
+    return SkImageDecoder::kUnknown_Format;
+}
+
 bool SkImageDecoder_WIC::onDecode(SkStream* stream, SkBitmap* bm, Mode mode) {
+    WICModes wicMode;
+    switch (mode) {
+        case SkImageDecoder::kDecodeBounds_Mode:
+            wicMode = kDecodeBounds_WICMode;
+            break;
+        case SkImageDecoder::kDecodePixels_Mode:
+            wicMode = kDecodePixels_WICMode;
+            break;
+    }
+    return this->decodeStream(stream, bm, wicMode, NULL);
+}
+
+bool SkImageDecoder_WIC::decodeStream(SkStream* stream, SkBitmap* bm, WICModes wicMode,
+                                      Format* format) const {
     //Initialize COM.
     SkAutoCoInitialize scopedCo;
     if (!scopedCo.succeeded()) {
@@ -76,6 +131,20 @@ bool SkImageDecoder_WIC::onDecode(SkStream* stream, SkBitmap* bm, Mode mode) {
         );
     }
 
+    if (kDecodeFormat_WICMode == wicMode) {
+        SkASSERT(format != NULL);
+        //Get the format
+        if (SUCCEEDED(hr)) {
+            GUID guidFormat;
+            hr = piBitmapDecoder->GetContainerFormat(&guidFormat);
+            if (SUCCEEDED(hr)) {
+                *format = GuidContainerFormat_to_Format(guidFormat);
+                return true;
+            }
+        }
+        return false;
+    }
+
     //Get the first frame from the decoder.
     SkTScopedComPtr<IWICBitmapFrameDecode> piBitmapFrameDecode;
     if (SUCCEEDED(hr)) {
@@ -100,7 +169,7 @@ bool SkImageDecoder_WIC::onDecode(SkStream* stream, SkBitmap* bm, Mode mode) {
     //Exit early if we're only looking for the bitmap bounds.
     if (SUCCEEDED(hr)) {
         bm->setConfig(SkBitmap::kARGB_8888_Config, width, height);
-        if (SkImageDecoder::kDecodeBounds_Mode == mode) {
+        if (kDecodeBounds_WICMode == wicMode) {
             return true;
         }
         if (!this->allocPixelRef(bm, NULL)) {
@@ -194,9 +263,6 @@ bool SkImageEncoder_WIC::onEncode(SkWStream* stream
         case kBMP_Type:
             type = GUID_ContainerFormatBmp;
             break;
-        case kGIF_Type:
-            type = GUID_ContainerFormatGif;
-            break;
         case kICO_Type:
             type = GUID_ContainerFormatIco;
             break;
@@ -348,7 +414,6 @@ bool SkImageEncoder_WIC::onEncode(SkWStream* stream
 static SkImageEncoder* sk_imageencoder_wic_factory(SkImageEncoder::Type t) {
     switch (t) {
         case SkImageEncoder::kBMP_Type:
-        case SkImageEncoder::kGIF_Type:
         case SkImageEncoder::kICO_Type:
         case SkImageEncoder::kJPEG_Type:
         case SkImageEncoder::kPNG_Type:
@@ -360,3 +425,14 @@ static SkImageEncoder* sk_imageencoder_wic_factory(SkImageEncoder::Type t) {
 }
 
 static SkTRegistry<SkImageEncoder*, SkImageEncoder::Type> gEReg(sk_imageencoder_wic_factory);
+
+static SkImageDecoder::Format get_format_wic(SkStream* stream) {
+    SkImageDecoder::Format format;
+    SkImageDecoder_WIC codec;
+    if (!codec.decodeStream(stream, NULL, SkImageDecoder_WIC::kDecodeFormat_WICMode, &format)) {
+        format = SkImageDecoder::kUnknown_Format;
+    }
+    return format;
+}
+
+static SkTRegistry<SkImageDecoder::Format, SkStream*> gFormatReg(get_format_wic);
index af9b4ad..188164f 100644 (file)
@@ -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"
 #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();