Add image encoder/decoder for Windows.
http://codereview.appspot.com/4634078/ git-svn-id: http://skia.googlecode.com/svn/trunk@1676 2bbb7eff-a529-9590-31e7-b0007b416f81
This commit is contained in:
parent
5d2e4cc165
commit
242bb89c0d
@ -1,4 +1,4 @@
|
||||
{
|
||||
{
|
||||
'includes': [
|
||||
'target_defaults.gypi',
|
||||
],
|
||||
@ -47,6 +47,9 @@
|
||||
'../src/images/SkPageFlipper.cpp',
|
||||
'../src/images/SkScaledBitmapSampler.cpp',
|
||||
'../src/images/SkScaledBitmapSampler.h',
|
||||
|
||||
'../src/ports/SkImageDecoder_CG.cpp',
|
||||
'../src/ports/SkImageDecoder_WIC.cpp',
|
||||
],
|
||||
'conditions': [
|
||||
[ 'OS == "win"', {
|
||||
@ -54,33 +57,40 @@
|
||||
'../include/images/SkJpegUtility.h',
|
||||
|
||||
'../src/images/SkFDStream.cpp',
|
||||
'../src/images/SkImageDecoder_Factory.cpp',
|
||||
'../src/images/SkImageDecoder_libgif.cpp',
|
||||
'../src/images/SkImageDecoder_libjpeg.cpp',
|
||||
'../src/images/SkImageDecoder_libpng.cpp',
|
||||
'../src/images/SkImageDecoder_libpvjpeg.c',
|
||||
'../src/images/SkImageEncoder_Factory.cpp',
|
||||
'../src/images/SkJpegUtility.cpp',
|
||||
'../src/images/SkMovie_gif.cpp',
|
||||
],
|
||||
},{ #else if OS != win
|
||||
'sources!': [
|
||||
'../src/ports/SkImageDecoder_WIC.cpp',
|
||||
],
|
||||
}],
|
||||
[ 'OS == "mac"', {
|
||||
'include_dirs': [
|
||||
'../include/utils/mac',
|
||||
],
|
||||
'sources': [
|
||||
'../src/ports/SkImageDecoder_CG.cpp'
|
||||
],
|
||||
'sources!': [
|
||||
'../include/images/SkJpegUtility.h',
|
||||
|
||||
'../src/images/SkImageEncoder_Factory.cpp',
|
||||
'../src/images/SkImageDecoder_Factory.cpp',
|
||||
'../src/images/SkImageDecoder_libpng.cpp',
|
||||
'../src/images/SkImageDecoder_libgif.cpp',
|
||||
'../src/images/SkImageDecoder_libjpeg.cpp',
|
||||
'../src/images/SkImageDecoder_libpvjpeg.c',
|
||||
'../src/images/SkImageEncoder_Factory.cpp',
|
||||
'../src/images/SkJpegUtility.cpp',
|
||||
'../src/images/SkMovie_gif.cpp',
|
||||
],
|
||||
},{ #else if OS != mac
|
||||
'sources!': [
|
||||
'../src/ports/SkImageDecoder_CG.cpp',
|
||||
],
|
||||
}],
|
||||
[ 'OS == "linux" or OS == "freebsd" or OS == "openbsd" or OS == "solaris"', {
|
||||
'sources!': [
|
||||
|
435
src/ports/SkImageDecoder_WIC.cpp
Normal file
435
src/ports/SkImageDecoder_WIC.cpp
Normal file
@ -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));
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user