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
diff --git a/src/images/SkImageDecoder.cpp b/src/images/SkImageDecoder.cpp
index c16efcf..16cba64 100644
--- a/src/images/SkImageDecoder.cpp
+++ b/src/images/SkImageDecoder.cpp
@@ -18,17 +18,6 @@
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 @@
}
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 @@
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;
}
diff --git a/src/images/SkImageDecoder_FactoryRegistrar.cpp b/src/images/SkImageDecoder_FactoryRegistrar.cpp
index 6cc417a..f1eca3d 100644
--- a/src/images/SkImageDecoder_FactoryRegistrar.cpp
+++ b/src/images/SkImageDecoder_FactoryRegistrar.cpp
@@ -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 @@
}
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;
+}
diff --git a/src/images/SkImageDecoder_libbmp.cpp b/src/images/SkImageDecoder_libbmp.cpp
index 488eddc..5c2299b 100644
--- a/src/images/SkImageDecoder_libbmp.cpp
+++ b/src/images/SkImageDecoder_libbmp.cpp
@@ -34,14 +34,18 @@
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 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 {
diff --git a/src/images/SkImageDecoder_libgif.cpp b/src/images/SkImageDecoder_libgif.cpp
index 3e4cda8..f6c54c2 100644
--- a/src/images/SkImageDecoder_libgif.cpp
+++ b/src/images/SkImageDecoder_libgif.cpp
@@ -91,9 +91,6 @@
///////////////////////////////////////////////////////////////////////////////
-//#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 @@
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);
diff --git a/src/images/SkImageDecoder_libico.cpp b/src/images/SkImageDecoder_libico.cpp
index ffc59e0..195b6ff 100644
--- a/src/images/SkImageDecoder_libico.cpp
+++ b/src/images/SkImageDecoder_libico.cpp
@@ -373,9 +373,7 @@
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 @@
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);
diff --git a/src/images/SkImageDecoder_libjpeg.cpp b/src/images/SkImageDecoder_libjpeg.cpp
index 9920bac..c55f4c4 100644
--- a/src/images/SkImageDecoder_libjpeg.cpp
+++ b/src/images/SkImageDecoder_libjpeg.cpp
@@ -998,9 +998,7 @@
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 @@
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 SkNEW(SkJPEGImageDecoder);
+ 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 SkImageDecoder::kUnknown_Format;
}
static SkImageEncoder* sk_libjpeg_efactory(SkImageEncoder::Type t) {
@@ -1022,4 +1036,5 @@
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);
diff --git a/src/images/SkImageDecoder_libpng.cpp b/src/images/SkImageDecoder_libpng.cpp
index efa4595..aec8948 100644
--- a/src/images/SkImageDecoder_libpng.cpp
+++ b/src/images/SkImageDecoder_libpng.cpp
@@ -47,6 +47,7 @@
virtual Format getFormat() const SK_OVERRIDE {
return kPNG_Format;
}
+
virtual ~SkPNGImageDecoder() {
SkDELETE(fImageIndex);
}
@@ -1086,18 +1087,33 @@
#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);
diff --git a/src/images/SkImageDecoder_libwebp.cpp b/src/images/SkImageDecoder_libwebp.cpp
index d9434c2..a33d0f9 100644
--- a/src/images/SkImageDecoder_libwebp.cpp
+++ b/src/images/SkImageDecoder_libwebp.cpp
@@ -587,9 +587,18 @@
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);
diff --git a/src/images/SkImageDecoder_wbmp.cpp b/src/images/SkImageDecoder_wbmp.cpp
index 96677cc..db40f59 100644
--- a/src/images/SkImageDecoder_wbmp.cpp
+++ b/src/images/SkImageDecoder_wbmp.cpp
@@ -170,4 +170,13 @@
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);
diff --git a/src/ports/SkImageDecoder_CG.cpp b/src/ports/SkImageDecoder_CG.cpp
index 9fc49a4..5fe6fdd 100644
--- a/src/ports/SkImageDecoder_CG.cpp
+++ b/src/ports/SkImageDecoder_CG.cpp
@@ -245,3 +245,45 @@
}
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);
diff --git a/src/ports/SkImageDecoder_WIC.cpp b/src/ports/SkImageDecoder_WIC.cpp
index 17d75cc..cd7f29f 100644
--- a/src/ports/SkImageDecoder_WIC.cpp
+++ b/src/ports/SkImageDecoder_WIC.cpp
@@ -29,11 +29,66 @@
#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 @@
);
}
+ 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 @@
//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 @@
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 @@
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 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);