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:
bungeman@google.com 2011-06-22 20:42:34 +00:00
parent 5d2e4cc165
commit 242bb89c0d
2 changed files with 450 additions and 5 deletions

View File

@ -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!': [

View 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));
}