SkImageDecoder_WIC.cpp revision 385fe4d4b62d7d1dd76116dd570df3290a2f487b
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#include "SkTypes.h"
10
11// Workaround for:
12// http://connect.microsoft.com/VisualStudio/feedback/details/621653/
13// http://crbug.com/225822
14// In VS2010 both intsafe.h and stdint.h define the following without guards.
15// SkTypes brought in windows.h and stdint.h and the following defines are
16// not used by this file. However, they may be re-introduced by wincodec.h.
17#undef INT8_MIN
18#undef INT16_MIN
19#undef INT32_MIN
20#undef INT64_MIN
21#undef INT8_MAX
22#undef UINT8_MAX
23#undef INT16_MAX
24#undef UINT16_MAX
25#undef INT32_MAX
26#undef UINT32_MAX
27#undef INT64_MAX
28#undef UINT64_MAX
29
30#include <wincodec.h>
31#include "SkAutoCoInitialize.h"
32#include "SkImageDecoder.h"
33#include "SkImageEncoder.h"
34#include "SkIStream.h"
35#include "SkMovie.h"
36#include "SkStream.h"
37#include "SkTScopedComPtr.h"
38#include "SkUnPreMultiply.h"
39
40//All Windows SDKs back to XPSP2 export the CLSID_WICImagingFactory symbol.
41//In the Windows8 SDK the CLSID_WICImagingFactory symbol is still exported
42//but CLSID_WICImagingFactory is then #defined to CLSID_WICImagingFactory2.
43//Undo this #define if it has been done so that we link against the symbols
44//we intended to link against on all SDKs.
45#if defined(CLSID_WICImagingFactory)
46#undef CLSID_WICImagingFactory
47#endif
48
49class SkImageDecoder_WIC : public SkImageDecoder {
50public:
51    // Decoding modes corresponding to SkImageDecoder::Mode, plus an extra mode for decoding
52    // only the format.
53    enum WICModes {
54        kDecodeFormat_WICMode,
55        kDecodeBounds_WICMode,
56        kDecodePixels_WICMode,
57    };
58
59    /**
60     *  Helper function to decode an SkStream.
61     *  @param stream SkStream to decode. Must be at the beginning.
62     *  @param bm   SkBitmap to decode into. Only used if wicMode is kDecodeBounds_WICMode or
63     *      kDecodePixels_WICMode, in which case it must not be NULL.
64     *  @param format Out parameter for the SkImageDecoder::Format of the SkStream. Only used if
65     *      wicMode is kDecodeFormat_WICMode.
66     */
67    bool decodeStream(SkStream* stream, SkBitmap* bm, WICModes wicMode, Format* format) const;
68
69protected:
70    Result onDecode(SkStream* stream, SkBitmap* bm, Mode mode) override;
71};
72
73struct FormatConversion {
74    GUID                    fGuidFormat;
75    SkImageDecoder::Format  fFormat;
76};
77
78static const FormatConversion gFormatConversions[] = {
79    { GUID_ContainerFormatBmp, SkImageDecoder::kBMP_Format },
80    { GUID_ContainerFormatGif, SkImageDecoder::kGIF_Format },
81    { GUID_ContainerFormatIco, SkImageDecoder::kICO_Format },
82    { GUID_ContainerFormatJpeg, SkImageDecoder::kJPEG_Format },
83    { GUID_ContainerFormatPng, SkImageDecoder::kPNG_Format },
84};
85
86static SkImageDecoder::Format GuidContainerFormat_to_Format(REFGUID guid) {
87    for (size_t i = 0; i < SK_ARRAY_COUNT(gFormatConversions); i++) {
88        if (IsEqualGUID(guid, gFormatConversions[i].fGuidFormat)) {
89            return gFormatConversions[i].fFormat;
90        }
91    }
92    return SkImageDecoder::kUnknown_Format;
93}
94
95SkImageDecoder::Result SkImageDecoder_WIC::onDecode(SkStream* stream, SkBitmap* bm, Mode mode) {
96    WICModes wicMode;
97    switch (mode) {
98        case SkImageDecoder::kDecodeBounds_Mode:
99            wicMode = kDecodeBounds_WICMode;
100            break;
101        case SkImageDecoder::kDecodePixels_Mode:
102            wicMode = kDecodePixels_WICMode;
103            break;
104    }
105    return this->decodeStream(stream, bm, wicMode, NULL) ? kSuccess : kFailure;
106}
107
108bool SkImageDecoder_WIC::decodeStream(SkStream* stream, SkBitmap* bm, WICModes wicMode,
109                                      Format* format) const {
110    //Initialize COM.
111    SkAutoCoInitialize scopedCo;
112    if (!scopedCo.succeeded()) {
113        return false;
114    }
115
116    HRESULT hr = S_OK;
117
118    //Create Windows Imaging Component ImagingFactory.
119    SkTScopedComPtr<IWICImagingFactory> piImagingFactory;
120    if (SUCCEEDED(hr)) {
121        hr = CoCreateInstance(
122            CLSID_WICImagingFactory
123            , NULL
124            , CLSCTX_INPROC_SERVER
125            , IID_PPV_ARGS(&piImagingFactory)
126        );
127    }
128
129    //Convert SkStream to IStream.
130    SkTScopedComPtr<IStream> piStream;
131    if (SUCCEEDED(hr)) {
132        hr = SkIStream::CreateFromSkStream(stream, false, &piStream);
133    }
134
135    //Make sure we're at the beginning of the stream.
136    if (SUCCEEDED(hr)) {
137        LARGE_INTEGER liBeginning = { 0 };
138        hr = piStream->Seek(liBeginning, STREAM_SEEK_SET, NULL);
139    }
140
141    //Create the decoder from the stream content.
142    SkTScopedComPtr<IWICBitmapDecoder> piBitmapDecoder;
143    if (SUCCEEDED(hr)) {
144        hr = piImagingFactory->CreateDecoderFromStream(
145            piStream.get()                    //Image to be decoded
146            , NULL                            //No particular vendor
147            , WICDecodeMetadataCacheOnDemand  //Cache metadata when needed
148            , &piBitmapDecoder                //Pointer to the decoder
149        );
150    }
151
152    if (kDecodeFormat_WICMode == wicMode) {
153        SkASSERT(format != NULL);
154        //Get the format
155        if (SUCCEEDED(hr)) {
156            GUID guidFormat;
157            hr = piBitmapDecoder->GetContainerFormat(&guidFormat);
158            if (SUCCEEDED(hr)) {
159                *format = GuidContainerFormat_to_Format(guidFormat);
160                return true;
161            }
162        }
163        return false;
164    }
165
166    //Get the first frame from the decoder.
167    SkTScopedComPtr<IWICBitmapFrameDecode> piBitmapFrameDecode;
168    if (SUCCEEDED(hr)) {
169        hr = piBitmapDecoder->GetFrame(0, &piBitmapFrameDecode);
170    }
171
172    //Get the BitmapSource interface of the frame.
173    SkTScopedComPtr<IWICBitmapSource> piBitmapSourceOriginal;
174    if (SUCCEEDED(hr)) {
175        hr = piBitmapFrameDecode->QueryInterface(
176            IID_PPV_ARGS(&piBitmapSourceOriginal)
177        );
178    }
179
180    //Get the size of the bitmap.
181    UINT width;
182    UINT height;
183    if (SUCCEEDED(hr)) {
184        hr = piBitmapSourceOriginal->GetSize(&width, &height);
185    }
186
187    //Exit early if we're only looking for the bitmap bounds.
188    if (SUCCEEDED(hr)) {
189        bm->setInfo(SkImageInfo::MakeN32Premul(width, height));
190        if (kDecodeBounds_WICMode == wicMode) {
191            return true;
192        }
193        if (!this->allocPixelRef(bm, NULL)) {
194            return false;
195        }
196    }
197
198    //Create a format converter.
199    SkTScopedComPtr<IWICFormatConverter> piFormatConverter;
200    if (SUCCEEDED(hr)) {
201        hr = piImagingFactory->CreateFormatConverter(&piFormatConverter);
202    }
203
204    GUID destinationPixelFormat;
205    if (this->getRequireUnpremultipliedColors()) {
206        destinationPixelFormat = GUID_WICPixelFormat32bppBGRA;
207    } else {
208        destinationPixelFormat = GUID_WICPixelFormat32bppPBGRA;
209    }
210
211    if (SUCCEEDED(hr)) {
212        hr = piFormatConverter->Initialize(
213            piBitmapSourceOriginal.get()      //Input bitmap to convert
214            , destinationPixelFormat          //Destination pixel format
215            , WICBitmapDitherTypeNone         //Specified dither patterm
216            , NULL                            //Specify a particular palette
217            , 0.f                             //Alpha threshold
218            , WICBitmapPaletteTypeCustom      //Palette translation type
219        );
220    }
221
222    //Get the BitmapSource interface of the format converter.
223    SkTScopedComPtr<IWICBitmapSource> piBitmapSourceConverted;
224    if (SUCCEEDED(hr)) {
225        hr = piFormatConverter->QueryInterface(
226            IID_PPV_ARGS(&piBitmapSourceConverted)
227        );
228    }
229
230    //Copy the pixels into the bitmap.
231    if (SUCCEEDED(hr)) {
232        SkAutoLockPixels alp(*bm);
233        bm->eraseColor(SK_ColorTRANSPARENT);
234        const UINT stride = (UINT) bm->rowBytes();
235        hr = piBitmapSourceConverted->CopyPixels(
236            NULL,                             //Get all the pixels
237            stride,
238            stride * height,
239            reinterpret_cast<BYTE *>(bm->getPixels())
240        );
241
242        // Note: we don't need to premultiply here since we specified PBGRA
243        if (SkBitmap::ComputeIsOpaque(*bm)) {
244            bm->setAlphaType(kOpaque_SkAlphaType);
245        }
246    }
247
248    return SUCCEEDED(hr);
249}
250
251/////////////////////////////////////////////////////////////////////////
252
253extern SkImageDecoder* image_decoder_from_stream(SkStreamRewindable*);
254
255SkImageDecoder* SkImageDecoder::Factory(SkStreamRewindable* stream) {
256    SkImageDecoder* decoder = image_decoder_from_stream(stream);
257    if (NULL == decoder) {
258        // If no image decoder specific to the stream exists, use SkImageDecoder_WIC.
259        return new SkImageDecoder_WIC;
260    } else {
261        return decoder;
262    }
263}
264
265/////////////////////////////////////////////////////////////////////////
266
267SkMovie* SkMovie::DecodeStream(SkStreamRewindable* stream) {
268    return NULL;
269}
270
271/////////////////////////////////////////////////////////////////////////
272
273class SkImageEncoder_WIC : public SkImageEncoder {
274public:
275    SkImageEncoder_WIC(Type t) : fType(t) {}
276
277protected:
278    virtual bool onEncode(SkWStream* stream, const SkBitmap& bm, int quality);
279
280private:
281    Type fType;
282};
283
284bool SkImageEncoder_WIC::onEncode(SkWStream* stream
285                                , const SkBitmap& bitmapOrig
286                                , int quality)
287{
288    GUID type;
289    switch (fType) {
290        case kBMP_Type:
291            type = GUID_ContainerFormatBmp;
292            break;
293        case kICO_Type:
294            type = GUID_ContainerFormatIco;
295            break;
296        case kJPEG_Type:
297            type = GUID_ContainerFormatJpeg;
298            break;
299        case kPNG_Type:
300            type = GUID_ContainerFormatPng;
301            break;
302        default:
303            return false;
304    }
305
306    //Convert to 8888 if needed.
307    const SkBitmap* bitmap;
308    SkBitmap bitmapCopy;
309    if (kN32_SkColorType == bitmapOrig.colorType() && bitmapOrig.isOpaque()) {
310        bitmap = &bitmapOrig;
311    } else {
312        if (!bitmapOrig.copyTo(&bitmapCopy, kN32_SkColorType)) {
313            return false;
314        }
315        bitmap = &bitmapCopy;
316    }
317
318    // We cannot use PBGRA so we need to unpremultiply ourselves
319    if (!bitmap->isOpaque()) {
320        SkAutoLockPixels alp(*bitmap);
321
322        uint8_t* pixels = reinterpret_cast<uint8_t*>(bitmap->getPixels());
323        for (int y = 0; y < bitmap->height(); ++y) {
324            for (int x = 0; x < bitmap->width(); ++x) {
325                uint8_t* bytes = pixels + y * bitmap->rowBytes() + x * bitmap->bytesPerPixel();
326
327                SkPMColor* src = reinterpret_cast<SkPMColor*>(bytes);
328                SkColor* dst = reinterpret_cast<SkColor*>(bytes);
329
330                *dst = SkUnPreMultiply::PMColorToColor(*src);
331            }
332        }
333    }
334
335    //Initialize COM.
336    SkAutoCoInitialize scopedCo;
337    if (!scopedCo.succeeded()) {
338        return false;
339    }
340
341    HRESULT hr = S_OK;
342
343    //Create Windows Imaging Component ImagingFactory.
344    SkTScopedComPtr<IWICImagingFactory> piImagingFactory;
345    if (SUCCEEDED(hr)) {
346        hr = CoCreateInstance(
347            CLSID_WICImagingFactory
348            , NULL
349            , CLSCTX_INPROC_SERVER
350            , IID_PPV_ARGS(&piImagingFactory)
351        );
352    }
353
354    //Convert the SkWStream to an IStream.
355    SkTScopedComPtr<IStream> piStream;
356    if (SUCCEEDED(hr)) {
357        hr = SkWIStream::CreateFromSkWStream(stream, &piStream);
358    }
359
360    //Create an encode of the appropriate type.
361    SkTScopedComPtr<IWICBitmapEncoder> piEncoder;
362    if (SUCCEEDED(hr)) {
363        hr = piImagingFactory->CreateEncoder(type, NULL, &piEncoder);
364    }
365
366    if (SUCCEEDED(hr)) {
367        hr = piEncoder->Initialize(piStream.get(), WICBitmapEncoderNoCache);
368    }
369
370    //Create a the frame.
371    SkTScopedComPtr<IWICBitmapFrameEncode> piBitmapFrameEncode;
372    SkTScopedComPtr<IPropertyBag2> piPropertybag;
373    if (SUCCEEDED(hr)) {
374        hr = piEncoder->CreateNewFrame(&piBitmapFrameEncode, &piPropertybag);
375    }
376
377    if (SUCCEEDED(hr)) {
378        PROPBAG2 name = { 0 };
379        name.dwType = PROPBAG2_TYPE_DATA;
380        name.vt = VT_R4;
381        name.pstrName = L"ImageQuality";
382
383        VARIANT value;
384        VariantInit(&value);
385        value.vt = VT_R4;
386        value.fltVal = (FLOAT)(quality / 100.0);
387
388        //Ignore result code.
389        //  This returns E_FAIL if the named property is not in the bag.
390        //TODO(bungeman) enumerate the properties,
391        //  write and set hr iff property exists.
392        piPropertybag->Write(1, &name, &value);
393    }
394    if (SUCCEEDED(hr)) {
395        hr = piBitmapFrameEncode->Initialize(piPropertybag.get());
396    }
397
398    //Set the size of the frame.
399    const UINT width = bitmap->width();
400    const UINT height = bitmap->height();
401    if (SUCCEEDED(hr)) {
402        hr = piBitmapFrameEncode->SetSize(width, height);
403    }
404
405    //Set the pixel format of the frame.
406    const WICPixelFormatGUID formatDesired = GUID_WICPixelFormat32bppBGRA;
407    WICPixelFormatGUID formatGUID = formatDesired;
408    if (SUCCEEDED(hr)) {
409        hr = piBitmapFrameEncode->SetPixelFormat(&formatGUID);
410    }
411    if (SUCCEEDED(hr)) {
412        //Be sure the image format is the one requested.
413        hr = IsEqualGUID(formatGUID, formatDesired) ? S_OK : E_FAIL;
414    }
415
416    //Write the pixels into the frame.
417    if (SUCCEEDED(hr)) {
418        SkAutoLockPixels alp(*bitmap);
419        const UINT stride = (UINT) bitmap->rowBytes();
420        hr = piBitmapFrameEncode->WritePixels(
421            height
422            , stride
423            , stride * height
424            , reinterpret_cast<BYTE*>(bitmap->getPixels()));
425    }
426
427    if (SUCCEEDED(hr)) {
428        hr = piBitmapFrameEncode->Commit();
429    }
430
431    if (SUCCEEDED(hr)) {
432        hr = piEncoder->Commit();
433    }
434
435    return SUCCEEDED(hr);
436}
437
438///////////////////////////////////////////////////////////////////////////////
439
440static SkImageEncoder* sk_imageencoder_wic_factory(SkImageEncoder::Type t) {
441    switch (t) {
442        case SkImageEncoder::kBMP_Type:
443        case SkImageEncoder::kICO_Type:
444        case SkImageEncoder::kJPEG_Type:
445        case SkImageEncoder::kPNG_Type:
446            break;
447        default:
448            return NULL;
449    }
450    return new SkImageEncoder_WIC(t);
451}
452
453static SkImageEncoder_EncodeReg gEReg(sk_imageencoder_wic_factory);
454
455static SkImageDecoder::Format get_format_wic(SkStreamRewindable* stream) {
456    SkImageDecoder::Format format;
457    SkImageDecoder_WIC codec;
458    if (!codec.decodeStream(stream, NULL, SkImageDecoder_WIC::kDecodeFormat_WICMode, &format)) {
459        format = SkImageDecoder::kUnknown_Format;
460    }
461    return format;
462}
463
464static SkImageDecoder_FormatReg gFormatReg(get_format_wic);
465