1
2/*
3 * Copyright 2011 Google Inc.
4 *
5 * Use of this source code is governed by a BSD-style license that can be
6 * found in the LICENSE file.
7 */
8
9
10#define WIN32_LEAN_AND_MEAN
11#include <Windows.h>
12#include <wincodec.h>
13#include "SkAutoCoInitialize.h"
14#include "SkImageDecoder.h"
15#include "SkImageEncoder.h"
16#include "SkIStream.h"
17#include "SkMovie.h"
18#include "SkStream.h"
19#include "SkTScopedComPtr.h"
20
21class SkImageDecoder_WIC : public SkImageDecoder {
22protected:
23    virtual bool onDecode(SkStream* stream, SkBitmap* bm, Mode mode);
24};
25
26bool SkImageDecoder_WIC::onDecode(SkStream* stream, SkBitmap* bm, Mode mode) {
27    //Initialize COM.
28    SkAutoCoInitialize scopedCo;
29    if (!scopedCo.succeeded()) {
30        return false;
31    }
32
33    HRESULT hr = S_OK;
34
35    //Create Windows Imaging Component ImagingFactory.
36    SkTScopedComPtr<IWICImagingFactory> piImagingFactory;
37    if (SUCCEEDED(hr)) {
38        hr = CoCreateInstance(
39            CLSID_WICImagingFactory
40            , NULL
41            , CLSCTX_INPROC_SERVER
42            , IID_PPV_ARGS(&piImagingFactory)
43        );
44    }
45
46    //Convert SkStream to IStream.
47    SkTScopedComPtr<IStream> piStream;
48    if (SUCCEEDED(hr)) {
49        hr = SkIStream::CreateFromSkStream(stream, false, &piStream);
50    }
51
52    //Make sure we're at the beginning of the stream.
53    if (SUCCEEDED(hr)) {
54        LARGE_INTEGER liBeginning = { 0 };
55        hr = piStream->Seek(liBeginning, STREAM_SEEK_SET, NULL);
56    }
57
58    //Create the decoder from the stream content.
59    SkTScopedComPtr<IWICBitmapDecoder> piBitmapDecoder;
60    if (SUCCEEDED(hr)) {
61        hr = piImagingFactory->CreateDecoderFromStream(
62            piStream.get()                    //Image to be decoded
63            , NULL                            //No particular vendor
64            , WICDecodeMetadataCacheOnDemand  //Cache metadata when needed
65            , &piBitmapDecoder                //Pointer to the decoder
66        );
67    }
68
69    //Get the first frame from the decoder.
70    SkTScopedComPtr<IWICBitmapFrameDecode> piBitmapFrameDecode;
71    if (SUCCEEDED(hr)) {
72        hr = piBitmapDecoder->GetFrame(0, &piBitmapFrameDecode);
73    }
74
75    //Get the BitmapSource interface of the frame.
76    SkTScopedComPtr<IWICBitmapSource> piBitmapSourceOriginal;
77    if (SUCCEEDED(hr)) {
78        hr = piBitmapFrameDecode->QueryInterface(
79            IID_PPV_ARGS(&piBitmapSourceOriginal)
80        );
81    }
82
83    //Get the size of the bitmap.
84    UINT width;
85    UINT height;
86    if (SUCCEEDED(hr)) {
87        hr = piBitmapSourceOriginal->GetSize(&width, &height);
88    }
89
90    //Exit early if we're only looking for the bitmap bounds.
91    if (SUCCEEDED(hr)) {
92        bm->setConfig(SkBitmap::kARGB_8888_Config, width, height);
93        if (SkImageDecoder::kDecodeBounds_Mode == mode) {
94            return true;
95        }
96        if (!this->allocPixelRef(bm, NULL)) {
97            return false;
98        }
99    }
100
101    //Create a format converter.
102    SkTScopedComPtr<IWICFormatConverter> piFormatConverter;
103    if (SUCCEEDED(hr)) {
104        hr = piImagingFactory->CreateFormatConverter(&piFormatConverter);
105    }
106
107    if (SUCCEEDED(hr)) {
108        hr = piFormatConverter->Initialize(
109            piBitmapSourceOriginal.get()      //Input bitmap to convert
110            , GUID_WICPixelFormat32bppPBGRA   //Destination pixel format
111            , WICBitmapDitherTypeNone         //Specified dither patterm
112            , NULL                            //Specify a particular palette
113            , 0.f                             //Alpha threshold
114            , WICBitmapPaletteTypeCustom      //Palette translation type
115        );
116    }
117
118    //Get the BitmapSource interface of the format converter.
119    SkTScopedComPtr<IWICBitmapSource> piBitmapSourceConverted;
120    if (SUCCEEDED(hr)) {
121        hr = piFormatConverter->QueryInterface(
122            IID_PPV_ARGS(&piBitmapSourceConverted)
123        );
124    }
125
126    //Copy the pixels into the bitmap.
127    if (SUCCEEDED(hr)) {
128        SkAutoLockPixels alp(*bm);
129        bm->eraseColor(0);
130        const int stride = bm->rowBytes();
131        hr = piBitmapSourceConverted->CopyPixels(
132            NULL,                             //Get all the pixels
133            stride,
134            stride * height,
135            reinterpret_cast<BYTE *>(bm->getPixels())
136        );
137    }
138
139    return SUCCEEDED(hr);
140}
141
142/////////////////////////////////////////////////////////////////////////
143
144SkImageDecoder* SkImageDecoder::Factory(SkStream* stream) {
145    return SkNEW(SkImageDecoder_WIC);
146}
147
148/////////////////////////////////////////////////////////////////////////
149
150SkMovie* SkMovie::DecodeStream(SkStream* stream) {
151    return NULL;
152}
153
154/////////////////////////////////////////////////////////////////////////
155
156class SkImageEncoder_WIC : public SkImageEncoder {
157public:
158    SkImageEncoder_WIC(Type t) : fType(t) {}
159
160protected:
161    virtual bool onEncode(SkWStream* stream, const SkBitmap& bm, int quality);
162
163private:
164    Type fType;
165};
166
167bool SkImageEncoder_WIC::onEncode(SkWStream* stream
168                                , const SkBitmap& bitmapOrig
169                                , int quality)
170{
171    GUID type;
172    switch (fType) {
173        case kJPEG_Type:
174            type = GUID_ContainerFormatJpeg;
175            break;
176        case kPNG_Type:
177            type = GUID_ContainerFormatPng;
178            break;
179        default:
180            return false;
181    }
182
183    //Convert to 8888 if needed.
184    const SkBitmap* bitmap;
185    SkBitmap bitmapCopy;
186    if (SkBitmap::kARGB_8888_Config == bitmapOrig.config()) {
187        bitmap = &bitmapOrig;
188    } else {
189        if (!bitmapOrig.copyTo(&bitmapCopy, SkBitmap::kARGB_8888_Config)) {
190            return false;
191        }
192        bitmap = &bitmapCopy;
193    }
194
195    //Initialize COM.
196    SkAutoCoInitialize scopedCo;
197    if (!scopedCo.succeeded()) {
198        return false;
199    }
200
201    HRESULT hr = S_OK;
202
203    //Create Windows Imaging Component ImagingFactory.
204    SkTScopedComPtr<IWICImagingFactory> piImagingFactory;
205    if (SUCCEEDED(hr)) {
206        hr = CoCreateInstance(
207            CLSID_WICImagingFactory
208            , NULL
209            , CLSCTX_INPROC_SERVER
210            , IID_PPV_ARGS(&piImagingFactory)
211        );
212    }
213
214    //Convert the SkWStream to an IStream.
215    SkTScopedComPtr<IStream> piStream;
216    if (SUCCEEDED(hr)) {
217        hr = SkWIStream::CreateFromSkWStream(stream, &piStream);
218    }
219
220    //Create an encode of the appropriate type.
221    SkTScopedComPtr<IWICBitmapEncoder> piEncoder;
222    if (SUCCEEDED(hr)) {
223        hr = piImagingFactory->CreateEncoder(type, NULL, &piEncoder);
224    }
225
226    if (SUCCEEDED(hr)) {
227        hr = piEncoder->Initialize(piStream.get(), WICBitmapEncoderNoCache);
228    }
229
230    //Create a the frame.
231    SkTScopedComPtr<IWICBitmapFrameEncode> piBitmapFrameEncode;
232    SkTScopedComPtr<IPropertyBag2> piPropertybag;
233    if (SUCCEEDED(hr)) {
234        hr = piEncoder->CreateNewFrame(&piBitmapFrameEncode, &piPropertybag);
235    }
236
237    if (SUCCEEDED(hr)) {
238        PROPBAG2 name = { 0 };
239        name.dwType = PROPBAG2_TYPE_DATA;
240        name.vt = VT_R4;
241        name.pstrName = L"ImageQuality";
242
243        VARIANT value;
244        VariantInit(&value);
245        value.vt = VT_R4;
246        value.fltVal = (FLOAT)(quality / 100.0);
247
248        //Ignore result code.
249        //  This returns E_FAIL if the named property is not in the bag.
250        //TODO(bungeman) enumerate the properties,
251        //  write and set hr iff property exists.
252        piPropertybag->Write(1, &name, &value);
253    }
254    if (SUCCEEDED(hr)) {
255        hr = piBitmapFrameEncode->Initialize(piPropertybag.get());
256    }
257
258    //Set the size of the frame.
259    const UINT width = bitmap->width();
260    const UINT height = bitmap->height();
261    if (SUCCEEDED(hr)) {
262        hr = piBitmapFrameEncode->SetSize(width, height);
263    }
264
265    //Set the pixel format of the frame.
266    const WICPixelFormatGUID formatDesired = GUID_WICPixelFormat32bppBGRA;
267    WICPixelFormatGUID formatGUID = formatDesired;
268    if (SUCCEEDED(hr)) {
269        hr = piBitmapFrameEncode->SetPixelFormat(&formatGUID);
270    }
271    if (SUCCEEDED(hr)) {
272        //Be sure the image format is the one requested.
273        hr = IsEqualGUID(formatGUID, formatDesired) ? S_OK : E_FAIL;
274    }
275
276    //Write the pixels into the frame.
277    if (SUCCEEDED(hr)) {
278        SkAutoLockPixels alp(*bitmap);
279        hr = piBitmapFrameEncode->WritePixels(
280            height
281            , bitmap->rowBytes()
282            , bitmap->rowBytes()*height
283            , reinterpret_cast<BYTE*>(bitmap->getPixels()));
284    }
285
286    if (SUCCEEDED(hr)) {
287        hr = piBitmapFrameEncode->Commit();
288    }
289
290    if (SUCCEEDED(hr)) {
291        hr = piEncoder->Commit();
292    }
293
294    return SUCCEEDED(hr);
295}
296
297SkImageEncoder* SkImageEncoder::Create(Type t) {
298    switch (t) {
299        case kJPEG_Type:
300        case kPNG_Type:
301            break;
302        default:
303            return NULL;
304    }
305    return SkNEW_ARGS(SkImageEncoder_WIC, (t));
306}
307
308