aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/ports/SkImageDecoder_WIC.cpp
diff options
context:
space:
mode:
authorGravatar bungeman@google.com <bungeman@google.com@2bbb7eff-a529-9590-31e7-b0007b416f81>2011-06-22 20:42:34 +0000
committerGravatar bungeman@google.com <bungeman@google.com@2bbb7eff-a529-9590-31e7-b0007b416f81>2011-06-22 20:42:34 +0000
commit242bb89c0d239662b05f5a396d45ad4f28b1596e (patch)
tree1346568a526f5d4d08731ca634fde035a477b280 /src/ports/SkImageDecoder_WIC.cpp
parent5d2e4cc165ede8cc5e08bf493839c0c4ab2974e7 (diff)
Add image encoder/decoder for Windows.
Diffstat (limited to 'src/ports/SkImageDecoder_WIC.cpp')
-rw-r--r--src/ports/SkImageDecoder_WIC.cpp435
1 files changed, 435 insertions, 0 deletions
diff --git a/src/ports/SkImageDecoder_WIC.cpp b/src/ports/SkImageDecoder_WIC.cpp
new file mode 100644
index 0000000000..b53db3bcae
--- /dev/null
+++ b/src/ports/SkImageDecoder_WIC.cpp
@@ -0,0 +1,435 @@
+/*
+ Copyright 2010 Google Inc.
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ */
+
+#define WIN32_LEAN_AND_MEAN
+#include <Windows.h>
+#include <wincodec.h>
+#include "SkImageDecoder.h"
+#include "SkImageEncoder.h"
+#include "SkMovie.h"
+#include "SkStream.h"
+#include "SkTemplates.h"
+
+template<typename T>
+class scoped_com_ptr {
+private:
+ T *fPtr;
+
+ scoped_com_ptr(scoped_com_ptr const &);
+ scoped_com_ptr & operator=(scoped_com_ptr const &);
+
+public:
+ explicit scoped_com_ptr(T *ptr = NULL) : fPtr(ptr) { }
+ ~scoped_com_ptr() {
+ if (NULL != fPtr) {
+ fPtr->Release();
+ fPtr = NULL;
+ }
+ }
+ T &operator*() const { return *fPtr; }
+ T *operator->() const { return fPtr; }
+ /**
+ * Returns the address of the underlying pointer.
+ * This is dangerous -- it breaks encapsulation and the reference escapes.
+ * Must only be used on instances currently pointing to NULL,
+ * and only to initialize the instance.
+ */
+ T **operator&() { SkASSERT(fPtr == NULL); return &fPtr; }
+ T *get() const { return fPtr; }
+};
+
+/**
+ * An instance of this class initializes COM on creation
+ * and closes the COM library on destruction.
+ */
+class AutoCoInitialize : SkNoncopyable {
+private:
+ HRESULT hr;
+public:
+ AutoCoInitialize() :
+ hr(
+ CoInitializeEx(
+ NULL
+ , COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE
+ )
+ )
+ { }
+ ~AutoCoInitialize() {
+ if (SUCCEEDED(this->hr)) {
+ CoUninitialize();
+ }
+ }
+ HRESULT getHR() { return this->hr; }
+};
+
+class SkImageDecoder_WIC : public SkImageDecoder {
+protected:
+ virtual bool onDecode(SkStream* stream, SkBitmap* bm, Mode);
+};
+
+/**
+ * Converts a SkStream to an IStream.
+ * The caller must call Release() on the returned IStream.
+ */
+static HRESULT SkStreamToIStream(SkStream* stream, IStream** ppStream) {
+ //TODO(bungeman): use a real IStream wrapper
+ HRESULT hr = S_OK;
+
+ size_t len = stream->getLength();
+
+ //Reserve memory for content of IStream.
+ HGLOBAL hdata = NULL;
+ if (SUCCEEDED(hr)) {
+ hdata = GlobalAlloc(GMEM_MOVEABLE | GMEM_NODISCARD, len);
+ if (NULL == hdata) {
+ hr = HRESULT_FROM_WIN32(GetLastError());
+ }
+ }
+
+ //Lock memory.
+ void* data = NULL;
+ if (SUCCEEDED(hr)) {
+ data = GlobalLock(hdata);
+ if (NULL == data) {
+ hr = HRESULT_FROM_WIN32(GetLastError());
+ }
+ }
+
+ //Write SkStream data to memory.
+ if (SUCCEEDED(hr)) {
+ size_t read = stream->read(data, len);
+ if (read != len) {
+ hr = E_FAIL;
+ }
+ }
+
+ //Unlock memory.
+ if (NULL != data) {
+ data = NULL;
+ SetLastError(NO_ERROR);
+ GlobalUnlock(hdata);
+ DWORD lastError = GetLastError();
+ if (SUCCEEDED(hr) && NO_ERROR != lastError) {
+ hr = HRESULT_FROM_WIN32(lastError);
+ }
+ }
+
+ //Create IStream from memory.
+ if (SUCCEEDED(hr)) {
+ hr = CreateStreamOnHGlobal(hdata, TRUE, ppStream);
+ }
+ //If we failed for any reason, free the memory.
+ if (FAILED(hr)) {
+ if (NULL != hdata) {
+ GlobalFree(hdata);
+ }
+ }
+
+ return hr;
+}
+
+bool SkImageDecoder_WIC::onDecode(SkStream* stream, SkBitmap* bm, Mode mode) {
+ //Initialize COM.
+ AutoCoInitialize scopedCo;
+ HRESULT hr = scopedCo.getHR();
+
+ //Create Windows Imaging Component ImagingFactory.
+ scoped_com_ptr<IWICImagingFactory> piImagingFactory;
+ if (SUCCEEDED(hr)) {
+ hr = CoCreateInstance(
+ CLSID_WICImagingFactory
+ , NULL
+ , CLSCTX_INPROC_SERVER
+ , IID_PPV_ARGS(&piImagingFactory)
+ );
+ }
+
+ //Convert SkStream to IStream.
+ scoped_com_ptr<IStream> piStream;
+ if (SUCCEEDED(hr)) {
+ hr = SkStreamToIStream(stream, &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.
+ scoped_com_ptr<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
+ );
+ }
+
+ //Get the first frame from the decoder.
+ scoped_com_ptr<IWICBitmapFrameDecode> piBitmapFrameDecode;
+ if (SUCCEEDED(hr)) {
+ hr = piBitmapDecoder->GetFrame(0, &piBitmapFrameDecode);
+ }
+
+ //Get the BitmapSource interface of the frame.
+ scoped_com_ptr<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 (SkImageDecoder::kDecodeBounds_Mode == mode) {
+ return true;
+ }
+ if (!this->allocPixelRef(bm, NULL)) {
+ return false;
+ }
+ }
+
+ //Create a format converter.
+ scoped_com_ptr<IWICFormatConverter> piFormatConverter;
+ if (SUCCEEDED(hr)) {
+ hr = piImagingFactory->CreateFormatConverter(&piFormatConverter);
+ }
+
+ if (SUCCEEDED(hr)) {
+ hr = piFormatConverter->Initialize(
+ piBitmapSourceOriginal.get() //Input bitmap to convert
+ , GUID_WICPixelFormat32bppPBGRA //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.
+ scoped_com_ptr<IWICBitmapSource> piBitmapSourceConverted;
+ if (SUCCEEDED(hr)) {
+ hr = piFormatConverter->QueryInterface(
+ IID_PPV_ARGS(&piBitmapSourceConverted)
+ );
+ }
+
+ //Copy the pixels into the bitmap.
+ if (SUCCEEDED(hr)) {
+ bm->lockPixels();
+ bm->eraseColor(0);
+ const int stride = bm->rowBytes();
+ hr = piBitmapSourceConverted->CopyPixels(
+ NULL, //Get all the pixels
+ stride,
+ stride * height,
+ reinterpret_cast<BYTE *>(bm->getPixels())
+ );
+ bm->unlockPixels();
+ }
+
+ return SUCCEEDED(hr);
+}
+
+/////////////////////////////////////////////////////////////////////////
+
+SkImageDecoder* SkImageDecoder::Factory(SkStream* stream) {
+ return SkNEW(SkImageDecoder_WIC);
+}
+
+/////////////////////////////////////////////////////////////////////////
+
+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& bm
+ , int quality)
+{
+ GUID type;
+ switch (fType) {
+ case kJPEG_Type:
+ type = GUID_ContainerFormatJpeg;
+ break;
+ case kPNG_Type:
+ type = GUID_ContainerFormatPng;
+ break;
+ default:
+ return false;
+ }
+
+ //Initialize COM.
+ AutoCoInitialize scopedCo;
+ HRESULT hr = scopedCo.getHR();
+
+ //Create Windows Imaging Component ImagingFactory.
+ scoped_com_ptr<IWICImagingFactory> piImagingFactory;
+ if (SUCCEEDED(hr)) {
+ hr = CoCreateInstance(
+ CLSID_WICImagingFactory
+ , NULL
+ , CLSCTX_INPROC_SERVER
+ , IID_PPV_ARGS(&piImagingFactory)
+ );
+ }
+
+ //Create the stream to hold the output of the encoder.
+ scoped_com_ptr<IStream> piStream;
+ if (SUCCEEDED(hr)) {
+ hr = CreateStreamOnHGlobal(NULL, TRUE, &piStream);
+ }
+
+ //Create an encode of the appropriate type.
+ scoped_com_ptr<IWICBitmapEncoder> piEncoder;
+ if (SUCCEEDED(hr)) {
+ hr = piImagingFactory->CreateEncoder(type, NULL, &piEncoder);
+ }
+
+ if (SUCCEEDED(hr)) {
+ hr = piEncoder->Initialize(piStream.get(), WICBitmapEncoderNoCache);
+ }
+
+ //Create a the frame.
+ scoped_com_ptr<IWICBitmapFrameEncode> piBitmapFrameEncode;
+ scoped_com_ptr<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 = bm.width();
+ const UINT height = bm.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)) {
+ hr = piBitmapFrameEncode->WritePixels(
+ height
+ , bm.rowBytes()
+ , bm.rowBytes()*height
+ , reinterpret_cast<BYTE*>(bm.getPixels()));
+ }
+
+ if (SUCCEEDED(hr)) {
+ hr = piBitmapFrameEncode->Commit();
+ }
+
+ if (SUCCEEDED(hr)) {
+ hr = piEncoder->Commit();
+ }
+
+ //Rewind the IStream with the output of the encoder.
+ if (SUCCEEDED(hr)) {
+ LARGE_INTEGER liBeginning = { 0 };
+ hr = piStream->Seek(liBeginning, STREAM_SEEK_SET, NULL);
+ }
+
+ //Write the content of the IStream to the SkWStream.
+ if (SUCCEEDED(hr)) {
+ //TODO(bungeman): use a real IStream(SkWStream) wrapper
+ const unsigned int BUFFER_SIZE = 1024;
+ void* buffer = new BYTE[BUFFER_SIZE];
+ ULONG bytesRead = 0;
+ while (true) {
+ hr = piStream->Read(buffer, BUFFER_SIZE, &bytesRead);
+ if (FAILED(hr)) {
+ break;
+ }
+ bool wrote = stream->write(buffer, bytesRead);
+ if (!wrote) {
+ hr = E_FAIL;
+ break;
+ }
+ if (BUFFER_SIZE != bytesRead) {
+ break;
+ }
+ }
+ stream->flush();
+ delete[] buffer;
+ }
+
+ return SUCCEEDED(hr);
+}
+
+SkImageEncoder* SkImageEncoder::Create(Type t) {
+ switch (t) {
+ case kJPEG_Type:
+ case kPNG_Type:
+ break;
+ default:
+ return NULL;
+ }
+ return SkNEW_ARGS(SkImageEncoder_WIC, (t));
+}
+