1/*
2 * Copyright 2011 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
8#include "SkTypes.h"
9
10#if defined(SK_BUILD_FOR_WIN32)
11
12// Workaround for:
13// http://connect.microsoft.com/VisualStudio/feedback/details/621653/
14// http://crbug.com/225822
15// In VS2010 both intsafe.h and stdint.h define the following without guards.
16// SkTypes brought in windows.h and stdint.h and the following defines are
17// not used by this file. However, they may be re-introduced by wincodec.h.
18#undef INT8_MIN
19#undef INT16_MIN
20#undef INT32_MIN
21#undef INT64_MIN
22#undef INT8_MAX
23#undef UINT8_MAX
24#undef INT16_MAX
25#undef UINT16_MAX
26#undef INT32_MAX
27#undef UINT32_MAX
28#undef INT64_MAX
29#undef UINT64_MAX
30
31#include "SkAutoCoInitialize.h"
32#include "SkAutoMalloc.h"
33#include "SkBitmap.h"
34#include "SkImageEncoderPriv.h"
35#include "SkIStream.h"
36#include "SkImageEncoder.h"
37#include "SkStream.h"
38#include "SkTScopedComPtr.h"
39#include "SkTemplates.h"
40#include "SkUnPreMultiply.h"
41#include <wincodec.h>
42
43//All Windows SDKs back to XPSP2 export the CLSID_WICImagingFactory symbol.
44//In the Windows8 SDK the CLSID_WICImagingFactory symbol is still exported
45//but CLSID_WICImagingFactory is then #defined to CLSID_WICImagingFactory2.
46//Undo this #define if it has been done so that we link against the symbols
47//we intended to link against on all SDKs.
48#if defined(CLSID_WICImagingFactory)
49#undef CLSID_WICImagingFactory
50#endif
51
52bool SkEncodeImageWithWIC(SkWStream* stream, const SkPixmap& pixmap,
53                          SkEncodedImageFormat format, int quality) {
54    GUID type;
55    switch (format) {
56        case SkEncodedImageFormat::kJPEG:
57            type = GUID_ContainerFormatJpeg;
58            break;
59        case SkEncodedImageFormat::kPNG:
60            type = GUID_ContainerFormatPng;
61            break;
62        default:
63            return false;
64    }
65    SkBitmap bitmapOrig;
66    if (!bitmapOrig.installPixels(pixmap)) {
67        return false;
68    }
69    bitmapOrig.setImmutable();
70
71    // First convert to BGRA if necessary.
72    SkBitmap bitmap;
73    if (!bitmap.tryAllocPixels(bitmapOrig.info().makeColorType(kBGRA_8888_SkColorType)) ||
74        !bitmapOrig.readPixels(bitmap.info(), bitmap.getPixels(), bitmap.rowBytes(), 0, 0))
75    {
76        return false;
77    }
78
79    // WIC expects unpremultiplied pixels.  Unpremultiply if necessary.
80    if (kPremul_SkAlphaType == bitmap.alphaType()) {
81        uint8_t* pixels = reinterpret_cast<uint8_t*>(bitmap.getPixels());
82        for (int y = 0; y < bitmap.height(); ++y) {
83            for (int x = 0; x < bitmap.width(); ++x) {
84                uint8_t* bytes = pixels + y * bitmap.rowBytes() + x * bitmap.bytesPerPixel();
85                SkPMColor* src = reinterpret_cast<SkPMColor*>(bytes);
86                SkColor* dst = reinterpret_cast<SkColor*>(bytes);
87                *dst = SkUnPreMultiply::PMColorToColor(*src);
88            }
89        }
90    }
91
92    // Finally, if we are performing a jpeg encode, we must convert to BGR.
93    void* pixels = bitmap.getPixels();
94    size_t rowBytes = bitmap.rowBytes();
95    SkAutoMalloc pixelStorage;
96    WICPixelFormatGUID formatDesired = GUID_WICPixelFormat32bppBGRA;
97    if (SkEncodedImageFormat::kJPEG == format) {
98        formatDesired = GUID_WICPixelFormat24bppBGR;
99        rowBytes = SkAlign4(bitmap.width() * 3);
100        pixelStorage.reset(rowBytes * bitmap.height());
101        for (int y = 0; y < bitmap.height(); y++) {
102            uint8_t* dstRow = SkTAddOffset<uint8_t>(pixelStorage.get(), y * rowBytes);
103            for (int x = 0; x < bitmap.width(); x++) {
104                uint32_t bgra = *bitmap.getAddr32(x, y);
105                dstRow[0] = (uint8_t) (bgra >>  0);
106                dstRow[1] = (uint8_t) (bgra >>  8);
107                dstRow[2] = (uint8_t) (bgra >> 16);
108                dstRow += 3;
109            }
110        }
111
112        pixels = pixelStorage.get();
113    }
114
115
116    //Initialize COM.
117    SkAutoCoInitialize scopedCo;
118    if (!scopedCo.succeeded()) {
119        return false;
120    }
121
122    HRESULT hr = S_OK;
123
124    //Create Windows Imaging Component ImagingFactory.
125    SkTScopedComPtr<IWICImagingFactory> piImagingFactory;
126    if (SUCCEEDED(hr)) {
127        hr = CoCreateInstance(
128            CLSID_WICImagingFactory
129            , nullptr
130            , CLSCTX_INPROC_SERVER
131            , IID_PPV_ARGS(&piImagingFactory)
132        );
133    }
134
135    //Convert the SkWStream to an IStream.
136    SkTScopedComPtr<IStream> piStream;
137    if (SUCCEEDED(hr)) {
138        hr = SkWIStream::CreateFromSkWStream(stream, &piStream);
139    }
140
141    //Create an encode of the appropriate type.
142    SkTScopedComPtr<IWICBitmapEncoder> piEncoder;
143    if (SUCCEEDED(hr)) {
144        hr = piImagingFactory->CreateEncoder(type, nullptr, &piEncoder);
145    }
146
147    if (SUCCEEDED(hr)) {
148        hr = piEncoder->Initialize(piStream.get(), WICBitmapEncoderNoCache);
149    }
150
151    //Create a the frame.
152    SkTScopedComPtr<IWICBitmapFrameEncode> piBitmapFrameEncode;
153    SkTScopedComPtr<IPropertyBag2> piPropertybag;
154    if (SUCCEEDED(hr)) {
155        hr = piEncoder->CreateNewFrame(&piBitmapFrameEncode, &piPropertybag);
156    }
157
158    if (SUCCEEDED(hr)) {
159        PROPBAG2 name = { 0 };
160        name.dwType = PROPBAG2_TYPE_DATA;
161        name.vt = VT_R4;
162        name.pstrName = L"ImageQuality";
163
164        VARIANT value;
165        VariantInit(&value);
166        value.vt = VT_R4;
167        value.fltVal = (FLOAT)(quality / 100.0);
168
169        //Ignore result code.
170        //  This returns E_FAIL if the named property is not in the bag.
171        //TODO(bungeman) enumerate the properties,
172        //  write and set hr iff property exists.
173        piPropertybag->Write(1, &name, &value);
174    }
175    if (SUCCEEDED(hr)) {
176        hr = piBitmapFrameEncode->Initialize(piPropertybag.get());
177    }
178
179    //Set the size of the frame.
180    const UINT width = bitmap.width();
181    const UINT height = bitmap.height();
182    if (SUCCEEDED(hr)) {
183        hr = piBitmapFrameEncode->SetSize(width, height);
184    }
185
186    //Set the pixel format of the frame.  If native encoded format cannot match BGRA,
187    //it will choose the closest pixel format that it supports.
188    WICPixelFormatGUID formatGUID = formatDesired;
189    if (SUCCEEDED(hr)) {
190        hr = piBitmapFrameEncode->SetPixelFormat(&formatGUID);
191    }
192    if (SUCCEEDED(hr)) {
193        //Be sure the image format is the one requested.
194        hr = IsEqualGUID(formatGUID, formatDesired) ? S_OK : E_FAIL;
195    }
196
197    //Write the pixels into the frame.
198    if (SUCCEEDED(hr)) {
199        hr = piBitmapFrameEncode->WritePixels(height,
200                                              (UINT) rowBytes,
201                                              (UINT) rowBytes * height,
202                                              reinterpret_cast<BYTE*>(pixels));
203    }
204
205    if (SUCCEEDED(hr)) {
206        hr = piBitmapFrameEncode->Commit();
207    }
208
209    if (SUCCEEDED(hr)) {
210        hr = piEncoder->Commit();
211    }
212
213    return SUCCEEDED(hr);
214}
215
216#endif // defined(SK_BUILD_FOR_WIN32)
217