blob: c4f1748a6f896014baf741881a57e82cbca81072 [file] [log] [blame]
/*
Copyright 2011 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 "SkAutoCoInitialize.h"
#include "SkImageDecoder.h"
#include "SkImageEncoder.h"
#include "SkIStream.h"
#include "SkMovie.h"
#include "SkStream.h"
#include "SkTScopedComPtr.h"
class SkImageDecoder_WIC : public SkImageDecoder {
protected:
virtual bool onDecode(SkStream* stream, SkBitmap* bm, Mode mode);
};
bool SkImageDecoder_WIC::onDecode(SkStream* stream, SkBitmap* bm, Mode mode) {
//Initialize COM.
AutoCoInitialize scopedCo;
HRESULT hr = scopedCo.getHR();
//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
);
}
//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 (SkImageDecoder::kDecodeBounds_Mode == mode) {
return true;
}
if (!this->allocPixelRef(bm, NULL)) {
return false;
}
}
//Create a format converter.
SkTScopedComPtr<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.
SkTScopedComPtr<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.
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 = 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();
}
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));
}