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_WIN)
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
SkEncodeImageWithWIC(SkWStream * stream,const SkPixmap & pixmap,SkEncodedImageFormat format,int quality)52 bool 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;
160 memset(&name, 0, sizeof(name));
161 name.dwType = PROPBAG2_TYPE_DATA;
162 name.vt = VT_R4;
163 name.pstrName = const_cast<LPOLESTR>(L"ImageQuality");
164
165 VARIANT value;
166 VariantInit(&value);
167 value.vt = VT_R4;
168 value.fltVal = (FLOAT)(quality / 100.0);
169
170 //Ignore result code.
171 // This returns E_FAIL if the named property is not in the bag.
172 //TODO(bungeman) enumerate the properties,
173 // write and set hr iff property exists.
174 piPropertybag->Write(1, &name, &value);
175 }
176 if (SUCCEEDED(hr)) {
177 hr = piBitmapFrameEncode->Initialize(piPropertybag.get());
178 }
179
180 //Set the size of the frame.
181 const UINT width = bitmap.width();
182 const UINT height = bitmap.height();
183 if (SUCCEEDED(hr)) {
184 hr = piBitmapFrameEncode->SetSize(width, height);
185 }
186
187 //Set the pixel format of the frame. If native encoded format cannot match BGRA,
188 //it will choose the closest pixel format that it supports.
189 WICPixelFormatGUID formatGUID = formatDesired;
190 if (SUCCEEDED(hr)) {
191 hr = piBitmapFrameEncode->SetPixelFormat(&formatGUID);
192 }
193 if (SUCCEEDED(hr)) {
194 //Be sure the image format is the one requested.
195 hr = IsEqualGUID(formatGUID, formatDesired) ? S_OK : E_FAIL;
196 }
197
198 //Write the pixels into the frame.
199 if (SUCCEEDED(hr)) {
200 hr = piBitmapFrameEncode->WritePixels(height,
201 (UINT) rowBytes,
202 (UINT) rowBytes * height,
203 reinterpret_cast<BYTE*>(pixels));
204 }
205
206 if (SUCCEEDED(hr)) {
207 hr = piBitmapFrameEncode->Commit();
208 }
209
210 if (SUCCEEDED(hr)) {
211 hr = piEncoder->Commit();
212 }
213
214 return SUCCEEDED(hr);
215 }
216
217 #endif // defined(SK_BUILD_FOR_WIN)
218