summaryrefslogtreecommitdiff
path: root/ports/SkImageDecoder_WIC.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'ports/SkImageDecoder_WIC.cpp')
-rw-r--r--ports/SkImageDecoder_WIC.cpp446
1 files changed, 446 insertions, 0 deletions
diff --git a/ports/SkImageDecoder_WIC.cpp b/ports/SkImageDecoder_WIC.cpp
new file mode 100644
index 00000000..77f4b952
--- /dev/null
+++ b/ports/SkImageDecoder_WIC.cpp
@@ -0,0 +1,446 @@
+
+/*
+ * Copyright 2011 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+
+#define WIN32_LEAN_AND_MEAN
+#include <Windows.h>
+#include <wincodec.h>
+#include "SkAutoCoInitialize.h"
+#include "SkImageDecoder.h"
+#include "SkImageEncoder.h"
+#include "SkIStream.h"
+#include "SkMovie.h"
+#include "SkStream.h"
+#include "SkTScopedComPtr.h"
+#include "SkUnPreMultiply.h"
+
+//All Windows SDKs back to XPSP2 export the CLSID_WICImagingFactory symbol.
+//In the Windows8 SDK the CLSID_WICImagingFactory symbol is still exported
+//but CLSID_WICImagingFactory is then #defined to CLSID_WICImagingFactory2.
+//Undo this #define if it has been done so that we link against the symbols
+//we intended to link against on all SDKs.
+#if defined(CLSID_WICImagingFactory)
+#undef CLSID_WICImagingFactory
+#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) 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()) {
+ return false;
+ }
+
+ HRESULT hr = S_OK;
+
+ //Create Windows Imaging Component ImagingFactory.
+ SkTScopedComPtr<IWICImagingFactory> piImagingFactory;
+ if (SUCCEEDED(hr)) {
+ hr = CoCreateInstance(
+ CLSID_WICImagingFactory
+ , NULL
+ , CLSCTX_INPROC_SERVER
+ , IID_PPV_ARGS(&piImagingFactory)
+ );
+ }
+
+ //Convert SkStream to IStream.
+ SkTScopedComPtr<IStream> piStream;
+ if (SUCCEEDED(hr)) {
+ hr = SkIStream::CreateFromSkStream(stream, false, &piStream);
+ }
+
+ //Make sure we're at the beginning of the stream.
+ if (SUCCEEDED(hr)) {
+ LARGE_INTEGER liBeginning = { 0 };
+ hr = piStream->Seek(liBeginning, STREAM_SEEK_SET, NULL);
+ }
+
+ //Create the decoder from the stream content.
+ SkTScopedComPtr<IWICBitmapDecoder> piBitmapDecoder;
+ if (SUCCEEDED(hr)) {
+ hr = piImagingFactory->CreateDecoderFromStream(
+ piStream.get() //Image to be decoded
+ , NULL //No particular vendor
+ , WICDecodeMetadataCacheOnDemand //Cache metadata when needed
+ , &piBitmapDecoder //Pointer to the decoder
+ );
+ }
+
+ 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)) {
+ hr = piBitmapDecoder->GetFrame(0, &piBitmapFrameDecode);
+ }
+
+ //Get the BitmapSource interface of the frame.
+ SkTScopedComPtr<IWICBitmapSource> piBitmapSourceOriginal;
+ if (SUCCEEDED(hr)) {
+ hr = piBitmapFrameDecode->QueryInterface(
+ IID_PPV_ARGS(&piBitmapSourceOriginal)
+ );
+ }
+
+ //Get the size of the bitmap.
+ UINT width;
+ UINT height;
+ if (SUCCEEDED(hr)) {
+ hr = piBitmapSourceOriginal->GetSize(&width, &height);
+ }
+
+ //Exit early if we're only looking for the bitmap bounds.
+ if (SUCCEEDED(hr)) {
+ bm->setConfig(SkBitmap::kARGB_8888_Config, width, height);
+ if (kDecodeBounds_WICMode == wicMode) {
+ return true;
+ }
+ if (!this->allocPixelRef(bm, NULL)) {
+ return false;
+ }
+ }
+
+ //Create a format converter.
+ SkTScopedComPtr<IWICFormatConverter> piFormatConverter;
+ if (SUCCEEDED(hr)) {
+ hr = piImagingFactory->CreateFormatConverter(&piFormatConverter);
+ }
+
+ GUID destinationPixelFormat;
+ if (this->getRequireUnpremultipliedColors()) {
+ destinationPixelFormat = GUID_WICPixelFormat32bppBGRA;
+ } else {
+ destinationPixelFormat = GUID_WICPixelFormat32bppPBGRA;
+ }
+
+ if (SUCCEEDED(hr)) {
+ hr = piFormatConverter->Initialize(
+ piBitmapSourceOriginal.get() //Input bitmap to convert
+ , destinationPixelFormat //Destination pixel format
+ , WICBitmapDitherTypeNone //Specified dither patterm
+ , NULL //Specify a particular palette
+ , 0.f //Alpha threshold
+ , WICBitmapPaletteTypeCustom //Palette translation type
+ );
+ }
+
+ //Get the BitmapSource interface of the format converter.
+ SkTScopedComPtr<IWICBitmapSource> piBitmapSourceConverted;
+ if (SUCCEEDED(hr)) {
+ hr = piFormatConverter->QueryInterface(
+ IID_PPV_ARGS(&piBitmapSourceConverted)
+ );
+ }
+
+ //Copy the pixels into the bitmap.
+ if (SUCCEEDED(hr)) {
+ SkAutoLockPixels alp(*bm);
+ bm->eraseColor(SK_ColorTRANSPARENT);
+ const UINT stride = bm->rowBytes();
+ hr = piBitmapSourceConverted->CopyPixels(
+ NULL, //Get all the pixels
+ stride,
+ stride * height,
+ reinterpret_cast<BYTE *>(bm->getPixels())
+ );
+
+ // Note: we don't need to premultiply here since we specified PBGRA
+ bm->computeAndSetOpaquePredicate();
+ }
+
+ return SUCCEEDED(hr);
+}
+
+/////////////////////////////////////////////////////////////////////////
+
+extern SkImageDecoder* image_decoder_from_stream(SkStream*);
+
+SkImageDecoder* SkImageDecoder::Factory(SkStream* stream) {
+ SkImageDecoder* decoder = image_decoder_from_stream(stream);
+ if (NULL == decoder) {
+ // If no image decoder specific to the stream exists, use SkImageDecoder_WIC.
+ return SkNEW(SkImageDecoder_WIC);
+ } else {
+ return decoder;
+ }
+}
+
+/////////////////////////////////////////////////////////////////////////
+
+SkMovie* SkMovie::DecodeStream(SkStream* stream) {
+ return NULL;
+}
+
+/////////////////////////////////////////////////////////////////////////
+
+class SkImageEncoder_WIC : public SkImageEncoder {
+public:
+ SkImageEncoder_WIC(Type t) : fType(t) {}
+
+protected:
+ virtual bool onEncode(SkWStream* stream, const SkBitmap& bm, int quality);
+
+private:
+ Type fType;
+};
+
+bool SkImageEncoder_WIC::onEncode(SkWStream* stream
+ , const SkBitmap& bitmapOrig
+ , int quality)
+{
+ GUID type;
+ switch (fType) {
+ case kBMP_Type:
+ type = GUID_ContainerFormatBmp;
+ break;
+ case kICO_Type:
+ type = GUID_ContainerFormatIco;
+ break;
+ case kJPEG_Type:
+ type = GUID_ContainerFormatJpeg;
+ break;
+ case kPNG_Type:
+ type = GUID_ContainerFormatPng;
+ break;
+ default:
+ return false;
+ }
+
+ //Convert to 8888 if needed.
+ const SkBitmap* bitmap;
+ SkBitmap bitmapCopy;
+ if (SkBitmap::kARGB_8888_Config == bitmapOrig.config() && bitmapOrig.isOpaque()) {
+ bitmap = &bitmapOrig;
+ } else {
+ if (!bitmapOrig.copyTo(&bitmapCopy, SkBitmap::kARGB_8888_Config)) {
+ return false;
+ }
+ bitmap = &bitmapCopy;
+ }
+
+ // We cannot use PBGRA so we need to unpremultiply ourselves
+ if (!bitmap->isOpaque()) {
+ SkAutoLockPixels alp(*bitmap);
+
+ uint8_t* pixels = reinterpret_cast<uint8_t*>(bitmap->getPixels());
+ for (int y = 0; y < bitmap->height(); ++y) {
+ for (int x = 0; x < bitmap->width(); ++x) {
+ uint8_t* bytes = pixels + y * bitmap->rowBytes() + x * bitmap->bytesPerPixel();
+
+ SkPMColor* src = reinterpret_cast<SkPMColor*>(bytes);
+ SkColor* dst = reinterpret_cast<SkColor*>(bytes);
+
+ *dst = SkUnPreMultiply::PMColorToColor(*src);
+ }
+ }
+ }
+
+ //Initialize COM.
+ SkAutoCoInitialize scopedCo;
+ if (!scopedCo.succeeded()) {
+ return false;
+ }
+
+ HRESULT hr = S_OK;
+
+ //Create Windows Imaging Component ImagingFactory.
+ SkTScopedComPtr<IWICImagingFactory> piImagingFactory;
+ if (SUCCEEDED(hr)) {
+ hr = CoCreateInstance(
+ CLSID_WICImagingFactory
+ , NULL
+ , CLSCTX_INPROC_SERVER
+ , IID_PPV_ARGS(&piImagingFactory)
+ );
+ }
+
+ //Convert the SkWStream to an IStream.
+ SkTScopedComPtr<IStream> piStream;
+ if (SUCCEEDED(hr)) {
+ hr = SkWIStream::CreateFromSkWStream(stream, &piStream);
+ }
+
+ //Create an encode of the appropriate type.
+ SkTScopedComPtr<IWICBitmapEncoder> piEncoder;
+ if (SUCCEEDED(hr)) {
+ hr = piImagingFactory->CreateEncoder(type, NULL, &piEncoder);
+ }
+
+ if (SUCCEEDED(hr)) {
+ hr = piEncoder->Initialize(piStream.get(), WICBitmapEncoderNoCache);
+ }
+
+ //Create a the frame.
+ SkTScopedComPtr<IWICBitmapFrameEncode> piBitmapFrameEncode;
+ SkTScopedComPtr<IPropertyBag2> piPropertybag;
+ if (SUCCEEDED(hr)) {
+ hr = piEncoder->CreateNewFrame(&piBitmapFrameEncode, &piPropertybag);
+ }
+
+ if (SUCCEEDED(hr)) {
+ PROPBAG2 name = { 0 };
+ name.dwType = PROPBAG2_TYPE_DATA;
+ name.vt = VT_R4;
+ name.pstrName = L"ImageQuality";
+
+ VARIANT value;
+ VariantInit(&value);
+ value.vt = VT_R4;
+ value.fltVal = (FLOAT)(quality / 100.0);
+
+ //Ignore result code.
+ // This returns E_FAIL if the named property is not in the bag.
+ //TODO(bungeman) enumerate the properties,
+ // write and set hr iff property exists.
+ piPropertybag->Write(1, &name, &value);
+ }
+ if (SUCCEEDED(hr)) {
+ hr = piBitmapFrameEncode->Initialize(piPropertybag.get());
+ }
+
+ //Set the size of the frame.
+ const UINT width = bitmap->width();
+ const UINT height = bitmap->height();
+ if (SUCCEEDED(hr)) {
+ hr = piBitmapFrameEncode->SetSize(width, height);
+ }
+
+ //Set the pixel format of the frame.
+ const WICPixelFormatGUID formatDesired = GUID_WICPixelFormat32bppBGRA;
+ WICPixelFormatGUID formatGUID = formatDesired;
+ if (SUCCEEDED(hr)) {
+ hr = piBitmapFrameEncode->SetPixelFormat(&formatGUID);
+ }
+ if (SUCCEEDED(hr)) {
+ //Be sure the image format is the one requested.
+ hr = IsEqualGUID(formatGUID, formatDesired) ? S_OK : E_FAIL;
+ }
+
+ //Write the pixels into the frame.
+ if (SUCCEEDED(hr)) {
+ SkAutoLockPixels alp(*bitmap);
+ const UINT stride = bitmap->rowBytes();
+ hr = piBitmapFrameEncode->WritePixels(
+ height
+ , stride
+ , stride * height
+ , reinterpret_cast<BYTE*>(bitmap->getPixels()));
+ }
+
+ if (SUCCEEDED(hr)) {
+ hr = piBitmapFrameEncode->Commit();
+ }
+
+ if (SUCCEEDED(hr)) {
+ hr = piEncoder->Commit();
+ }
+
+ return SUCCEEDED(hr);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+#include "SkTRegistry.h"
+
+static SkImageEncoder* sk_imageencoder_wic_factory(SkImageEncoder::Type t) {
+ switch (t) {
+ case SkImageEncoder::kBMP_Type:
+ case SkImageEncoder::kICO_Type:
+ case SkImageEncoder::kJPEG_Type:
+ case SkImageEncoder::kPNG_Type:
+ break;
+ default:
+ return NULL;
+ }
+ return SkNEW_ARGS(SkImageEncoder_WIC, (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);