blob: 4b869dd3280b85d3a454b32b7a652009c276ea3d [file] [log] [blame]
epoger@google.comec3ed6a2011-07-28 14:26:00 +00001
bungeman@google.com242bb892011-06-22 20:42:34 +00002/*
epoger@google.comec3ed6a2011-07-28 14:26:00 +00003 * 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.
bungeman@google.com242bb892011-06-22 20:42:34 +00007 */
8
epoger@google.comec3ed6a2011-07-28 14:26:00 +00009
bungeman@google.com242bb892011-06-22 20:42:34 +000010#define WIN32_LEAN_AND_MEAN
11#include <Windows.h>
12#include <wincodec.h>
bungeman@google.com9df621d2011-06-23 21:43:52 +000013#include "SkAutoCoInitialize.h"
bungeman@google.com242bb892011-06-22 20:42:34 +000014#include "SkImageDecoder.h"
15#include "SkImageEncoder.h"
bungeman@google.com9df621d2011-06-23 21:43:52 +000016#include "SkIStream.h"
bungeman@google.com242bb892011-06-22 20:42:34 +000017#include "SkMovie.h"
18#include "SkStream.h"
bungeman@google.com9df621d2011-06-23 21:43:52 +000019#include "SkTScopedComPtr.h"
robertphillips@google.com59bfb122012-11-08 15:06:52 +000020#include "SkUnPreMultiply.h"
bungeman@google.com242bb892011-06-22 20:42:34 +000021
bungeman@google.comc18143e2013-01-11 20:02:32 +000022//All Windows SDKs back to XPSP2 export the CLSID_WICImagingFactory symbol.
23//In the Windows8 SDK the CLSID_WICImagingFactory symbol is still exported
24//but CLSID_WICImagingFactory is then #defined to CLSID_WICImagingFactory2.
25//Undo this #define if it has been done so that we link against the symbols
26//we intended to link against on all SDKs.
27#if defined(CLSID_WICImagingFactory)
28#undef CLSID_WICImagingFactory
29#endif
30
bungeman@google.com242bb892011-06-22 20:42:34 +000031class SkImageDecoder_WIC : public SkImageDecoder {
32protected:
bungeman@google.com9df621d2011-06-23 21:43:52 +000033 virtual bool onDecode(SkStream* stream, SkBitmap* bm, Mode mode);
bungeman@google.com242bb892011-06-22 20:42:34 +000034};
35
bungeman@google.com242bb892011-06-22 20:42:34 +000036bool SkImageDecoder_WIC::onDecode(SkStream* stream, SkBitmap* bm, Mode mode) {
37 //Initialize COM.
bungeman@google.com2e2f3f52011-09-16 15:37:20 +000038 SkAutoCoInitialize scopedCo;
39 if (!scopedCo.succeeded()) {
40 return false;
41 }
rmistry@google.comd6176b02012-08-23 18:14:13 +000042
bungeman@google.com2e2f3f52011-09-16 15:37:20 +000043 HRESULT hr = S_OK;
rmistry@google.comd6176b02012-08-23 18:14:13 +000044
bungeman@google.com242bb892011-06-22 20:42:34 +000045 //Create Windows Imaging Component ImagingFactory.
bungeman@google.com9df621d2011-06-23 21:43:52 +000046 SkTScopedComPtr<IWICImagingFactory> piImagingFactory;
bungeman@google.com242bb892011-06-22 20:42:34 +000047 if (SUCCEEDED(hr)) {
48 hr = CoCreateInstance(
49 CLSID_WICImagingFactory
50 , NULL
51 , CLSCTX_INPROC_SERVER
52 , IID_PPV_ARGS(&piImagingFactory)
53 );
54 }
rmistry@google.comd6176b02012-08-23 18:14:13 +000055
bungeman@google.com242bb892011-06-22 20:42:34 +000056 //Convert SkStream to IStream.
bungeman@google.com9df621d2011-06-23 21:43:52 +000057 SkTScopedComPtr<IStream> piStream;
bungeman@google.com242bb892011-06-22 20:42:34 +000058 if (SUCCEEDED(hr)) {
bungeman@google.com9df621d2011-06-23 21:43:52 +000059 hr = SkIStream::CreateFromSkStream(stream, false, &piStream);
bungeman@google.com242bb892011-06-22 20:42:34 +000060 }
rmistry@google.comd6176b02012-08-23 18:14:13 +000061
bungeman@google.com242bb892011-06-22 20:42:34 +000062 //Make sure we're at the beginning of the stream.
63 if (SUCCEEDED(hr)) {
64 LARGE_INTEGER liBeginning = { 0 };
65 hr = piStream->Seek(liBeginning, STREAM_SEEK_SET, NULL);
66 }
rmistry@google.comd6176b02012-08-23 18:14:13 +000067
bungeman@google.com242bb892011-06-22 20:42:34 +000068 //Create the decoder from the stream content.
bungeman@google.com9df621d2011-06-23 21:43:52 +000069 SkTScopedComPtr<IWICBitmapDecoder> piBitmapDecoder;
bungeman@google.com242bb892011-06-22 20:42:34 +000070 if (SUCCEEDED(hr)) {
71 hr = piImagingFactory->CreateDecoderFromStream(
72 piStream.get() //Image to be decoded
73 , NULL //No particular vendor
74 , WICDecodeMetadataCacheOnDemand //Cache metadata when needed
75 , &piBitmapDecoder //Pointer to the decoder
76 );
77 }
rmistry@google.comd6176b02012-08-23 18:14:13 +000078
bungeman@google.com242bb892011-06-22 20:42:34 +000079 //Get the first frame from the decoder.
bungeman@google.com9df621d2011-06-23 21:43:52 +000080 SkTScopedComPtr<IWICBitmapFrameDecode> piBitmapFrameDecode;
bungeman@google.com242bb892011-06-22 20:42:34 +000081 if (SUCCEEDED(hr)) {
82 hr = piBitmapDecoder->GetFrame(0, &piBitmapFrameDecode);
83 }
rmistry@google.comd6176b02012-08-23 18:14:13 +000084
bungeman@google.com242bb892011-06-22 20:42:34 +000085 //Get the BitmapSource interface of the frame.
bungeman@google.com9df621d2011-06-23 21:43:52 +000086 SkTScopedComPtr<IWICBitmapSource> piBitmapSourceOriginal;
bungeman@google.com242bb892011-06-22 20:42:34 +000087 if (SUCCEEDED(hr)) {
88 hr = piBitmapFrameDecode->QueryInterface(
89 IID_PPV_ARGS(&piBitmapSourceOriginal)
90 );
91 }
rmistry@google.comd6176b02012-08-23 18:14:13 +000092
bungeman@google.com242bb892011-06-22 20:42:34 +000093 //Get the size of the bitmap.
94 UINT width;
95 UINT height;
96 if (SUCCEEDED(hr)) {
97 hr = piBitmapSourceOriginal->GetSize(&width, &height);
98 }
rmistry@google.comd6176b02012-08-23 18:14:13 +000099
bungeman@google.com242bb892011-06-22 20:42:34 +0000100 //Exit early if we're only looking for the bitmap bounds.
101 if (SUCCEEDED(hr)) {
102 bm->setConfig(SkBitmap::kARGB_8888_Config, width, height);
103 if (SkImageDecoder::kDecodeBounds_Mode == mode) {
104 return true;
105 }
106 if (!this->allocPixelRef(bm, NULL)) {
107 return false;
108 }
109 }
rmistry@google.comd6176b02012-08-23 18:14:13 +0000110
bungeman@google.com242bb892011-06-22 20:42:34 +0000111 //Create a format converter.
bungeman@google.com9df621d2011-06-23 21:43:52 +0000112 SkTScopedComPtr<IWICFormatConverter> piFormatConverter;
bungeman@google.com242bb892011-06-22 20:42:34 +0000113 if (SUCCEEDED(hr)) {
114 hr = piImagingFactory->CreateFormatConverter(&piFormatConverter);
115 }
rmistry@google.comd6176b02012-08-23 18:14:13 +0000116
bungeman@google.com242bb892011-06-22 20:42:34 +0000117 if (SUCCEEDED(hr)) {
118 hr = piFormatConverter->Initialize(
119 piBitmapSourceOriginal.get() //Input bitmap to convert
120 , GUID_WICPixelFormat32bppPBGRA //Destination pixel format
121 , WICBitmapDitherTypeNone //Specified dither patterm
122 , NULL //Specify a particular palette
123 , 0.f //Alpha threshold
124 , WICBitmapPaletteTypeCustom //Palette translation type
125 );
126 }
rmistry@google.comd6176b02012-08-23 18:14:13 +0000127
bungeman@google.com242bb892011-06-22 20:42:34 +0000128 //Get the BitmapSource interface of the format converter.
bungeman@google.com9df621d2011-06-23 21:43:52 +0000129 SkTScopedComPtr<IWICBitmapSource> piBitmapSourceConverted;
bungeman@google.com242bb892011-06-22 20:42:34 +0000130 if (SUCCEEDED(hr)) {
131 hr = piFormatConverter->QueryInterface(
132 IID_PPV_ARGS(&piBitmapSourceConverted)
133 );
134 }
rmistry@google.comd6176b02012-08-23 18:14:13 +0000135
bungeman@google.com242bb892011-06-22 20:42:34 +0000136 //Copy the pixels into the bitmap.
137 if (SUCCEEDED(hr)) {
bungeman@google.com955bb072011-08-01 20:18:45 +0000138 SkAutoLockPixels alp(*bm);
junov@google.comdbfac8a2012-12-06 21:47:40 +0000139 bm->eraseColor(SK_ColorTRANSPARENT);
bungeman@google.com242bb892011-06-22 20:42:34 +0000140 const int stride = bm->rowBytes();
141 hr = piBitmapSourceConverted->CopyPixels(
142 NULL, //Get all the pixels
143 stride,
144 stride * height,
145 reinterpret_cast<BYTE *>(bm->getPixels())
146 );
robertphillips@google.com59bfb122012-11-08 15:06:52 +0000147
148 // Note: we don't need to premultiply here since we specified PBGRA
149 bm->computeAndSetOpaquePredicate();
bungeman@google.com242bb892011-06-22 20:42:34 +0000150 }
rmistry@google.comd6176b02012-08-23 18:14:13 +0000151
bungeman@google.com242bb892011-06-22 20:42:34 +0000152 return SUCCEEDED(hr);
153}
154
155/////////////////////////////////////////////////////////////////////////
156
157SkImageDecoder* SkImageDecoder::Factory(SkStream* stream) {
158 return SkNEW(SkImageDecoder_WIC);
159}
160
161/////////////////////////////////////////////////////////////////////////
162
163SkMovie* SkMovie::DecodeStream(SkStream* stream) {
164 return NULL;
165}
166
167/////////////////////////////////////////////////////////////////////////
168
169class SkImageEncoder_WIC : public SkImageEncoder {
170public:
171 SkImageEncoder_WIC(Type t) : fType(t) {}
172
173protected:
174 virtual bool onEncode(SkWStream* stream, const SkBitmap& bm, int quality);
175
176private:
177 Type fType;
178};
179
180bool SkImageEncoder_WIC::onEncode(SkWStream* stream
bungeman@google.com22b49502011-08-01 19:37:43 +0000181 , const SkBitmap& bitmapOrig
bungeman@google.com242bb892011-06-22 20:42:34 +0000182 , int quality)
183{
184 GUID type;
185 switch (fType) {
186 case kJPEG_Type:
187 type = GUID_ContainerFormatJpeg;
188 break;
189 case kPNG_Type:
190 type = GUID_ContainerFormatPng;
191 break;
192 default:
193 return false;
194 }
195
bungeman@google.com22b49502011-08-01 19:37:43 +0000196 //Convert to 8888 if needed.
197 const SkBitmap* bitmap;
198 SkBitmap bitmapCopy;
robertphillips@google.com59bfb122012-11-08 15:06:52 +0000199 if (SkBitmap::kARGB_8888_Config == bitmapOrig.config() && bitmapOrig.isOpaque()) {
bungeman@google.com22b49502011-08-01 19:37:43 +0000200 bitmap = &bitmapOrig;
201 } else {
202 if (!bitmapOrig.copyTo(&bitmapCopy, SkBitmap::kARGB_8888_Config)) {
203 return false;
204 }
205 bitmap = &bitmapCopy;
206 }
207
robertphillips@google.com59bfb122012-11-08 15:06:52 +0000208 // We cannot use PBGRA so we need to unpremultiply ourselves
209 if (!bitmap->isOpaque()) {
210 SkAutoLockPixels alp(*bitmap);
211
212 uint8_t* pixels = reinterpret_cast<uint8_t*>(bitmap->getPixels());
213 for (int y = 0; y < bitmap->height(); ++y) {
214 for (int x = 0; x < bitmap->width(); ++x) {
215 uint8_t* bytes = pixels + y * bitmap->rowBytes() + x * bitmap->bytesPerPixel();
216
217 SkPMColor* src = reinterpret_cast<SkPMColor*>(bytes);
218 SkColor* dst = reinterpret_cast<SkColor*>(bytes);
219
220 *dst = SkUnPreMultiply::PMColorToColor(*src);
221 }
222 }
223 }
224
bungeman@google.com242bb892011-06-22 20:42:34 +0000225 //Initialize COM.
bungeman@google.com2e2f3f52011-09-16 15:37:20 +0000226 SkAutoCoInitialize scopedCo;
227 if (!scopedCo.succeeded()) {
228 return false;
229 }
rmistry@google.comd6176b02012-08-23 18:14:13 +0000230
bungeman@google.com2e2f3f52011-09-16 15:37:20 +0000231 HRESULT hr = S_OK;
rmistry@google.comd6176b02012-08-23 18:14:13 +0000232
bungeman@google.com242bb892011-06-22 20:42:34 +0000233 //Create Windows Imaging Component ImagingFactory.
bungeman@google.com9df621d2011-06-23 21:43:52 +0000234 SkTScopedComPtr<IWICImagingFactory> piImagingFactory;
bungeman@google.com242bb892011-06-22 20:42:34 +0000235 if (SUCCEEDED(hr)) {
236 hr = CoCreateInstance(
237 CLSID_WICImagingFactory
238 , NULL
239 , CLSCTX_INPROC_SERVER
240 , IID_PPV_ARGS(&piImagingFactory)
241 );
242 }
rmistry@google.comd6176b02012-08-23 18:14:13 +0000243
bungeman@google.com9df621d2011-06-23 21:43:52 +0000244 //Convert the SkWStream to an IStream.
245 SkTScopedComPtr<IStream> piStream;
bungeman@google.com242bb892011-06-22 20:42:34 +0000246 if (SUCCEEDED(hr)) {
bungeman@google.com9df621d2011-06-23 21:43:52 +0000247 hr = SkWIStream::CreateFromSkWStream(stream, &piStream);
bungeman@google.com242bb892011-06-22 20:42:34 +0000248 }
rmistry@google.comd6176b02012-08-23 18:14:13 +0000249
bungeman@google.com242bb892011-06-22 20:42:34 +0000250 //Create an encode of the appropriate type.
bungeman@google.com9df621d2011-06-23 21:43:52 +0000251 SkTScopedComPtr<IWICBitmapEncoder> piEncoder;
bungeman@google.com242bb892011-06-22 20:42:34 +0000252 if (SUCCEEDED(hr)) {
253 hr = piImagingFactory->CreateEncoder(type, NULL, &piEncoder);
254 }
rmistry@google.comd6176b02012-08-23 18:14:13 +0000255
bungeman@google.com242bb892011-06-22 20:42:34 +0000256 if (SUCCEEDED(hr)) {
257 hr = piEncoder->Initialize(piStream.get(), WICBitmapEncoderNoCache);
258 }
rmistry@google.comd6176b02012-08-23 18:14:13 +0000259
bungeman@google.com242bb892011-06-22 20:42:34 +0000260 //Create a the frame.
bungeman@google.com9df621d2011-06-23 21:43:52 +0000261 SkTScopedComPtr<IWICBitmapFrameEncode> piBitmapFrameEncode;
262 SkTScopedComPtr<IPropertyBag2> piPropertybag;
bungeman@google.com242bb892011-06-22 20:42:34 +0000263 if (SUCCEEDED(hr)) {
264 hr = piEncoder->CreateNewFrame(&piBitmapFrameEncode, &piPropertybag);
265 }
rmistry@google.comd6176b02012-08-23 18:14:13 +0000266
bungeman@google.com242bb892011-06-22 20:42:34 +0000267 if (SUCCEEDED(hr)) {
268 PROPBAG2 name = { 0 };
269 name.dwType = PROPBAG2_TYPE_DATA;
270 name.vt = VT_R4;
271 name.pstrName = L"ImageQuality";
rmistry@google.comd6176b02012-08-23 18:14:13 +0000272
bungeman@google.com242bb892011-06-22 20:42:34 +0000273 VARIANT value;
274 VariantInit(&value);
275 value.vt = VT_R4;
276 value.fltVal = (FLOAT)(quality / 100.0);
rmistry@google.comd6176b02012-08-23 18:14:13 +0000277
bungeman@google.com242bb892011-06-22 20:42:34 +0000278 //Ignore result code.
279 // This returns E_FAIL if the named property is not in the bag.
280 //TODO(bungeman) enumerate the properties,
281 // write and set hr iff property exists.
282 piPropertybag->Write(1, &name, &value);
283 }
284 if (SUCCEEDED(hr)) {
285 hr = piBitmapFrameEncode->Initialize(piPropertybag.get());
286 }
rmistry@google.comd6176b02012-08-23 18:14:13 +0000287
bungeman@google.com242bb892011-06-22 20:42:34 +0000288 //Set the size of the frame.
bungeman@google.com22b49502011-08-01 19:37:43 +0000289 const UINT width = bitmap->width();
290 const UINT height = bitmap->height();
bungeman@google.com242bb892011-06-22 20:42:34 +0000291 if (SUCCEEDED(hr)) {
292 hr = piBitmapFrameEncode->SetSize(width, height);
293 }
rmistry@google.comd6176b02012-08-23 18:14:13 +0000294
bungeman@google.com242bb892011-06-22 20:42:34 +0000295 //Set the pixel format of the frame.
296 const WICPixelFormatGUID formatDesired = GUID_WICPixelFormat32bppBGRA;
297 WICPixelFormatGUID formatGUID = formatDesired;
298 if (SUCCEEDED(hr)) {
299 hr = piBitmapFrameEncode->SetPixelFormat(&formatGUID);
300 }
301 if (SUCCEEDED(hr)) {
302 //Be sure the image format is the one requested.
303 hr = IsEqualGUID(formatGUID, formatDesired) ? S_OK : E_FAIL;
304 }
rmistry@google.comd6176b02012-08-23 18:14:13 +0000305
bungeman@google.com242bb892011-06-22 20:42:34 +0000306 //Write the pixels into the frame.
307 if (SUCCEEDED(hr)) {
bungeman@google.com955bb072011-08-01 20:18:45 +0000308 SkAutoLockPixels alp(*bitmap);
bungeman@google.com242bb892011-06-22 20:42:34 +0000309 hr = piBitmapFrameEncode->WritePixels(
310 height
bungeman@google.com22b49502011-08-01 19:37:43 +0000311 , bitmap->rowBytes()
312 , bitmap->rowBytes()*height
313 , reinterpret_cast<BYTE*>(bitmap->getPixels()));
bungeman@google.com242bb892011-06-22 20:42:34 +0000314 }
rmistry@google.comd6176b02012-08-23 18:14:13 +0000315
bungeman@google.com242bb892011-06-22 20:42:34 +0000316 if (SUCCEEDED(hr)) {
317 hr = piBitmapFrameEncode->Commit();
318 }
rmistry@google.comd6176b02012-08-23 18:14:13 +0000319
bungeman@google.com242bb892011-06-22 20:42:34 +0000320 if (SUCCEEDED(hr)) {
321 hr = piEncoder->Commit();
322 }
rmistry@google.comd6176b02012-08-23 18:14:13 +0000323
bungeman@google.com242bb892011-06-22 20:42:34 +0000324 return SUCCEEDED(hr);
325}
326
327SkImageEncoder* SkImageEncoder::Create(Type t) {
328 switch (t) {
329 case kJPEG_Type:
330 case kPNG_Type:
331 break;
332 default:
333 return NULL;
334 }
335 return SkNEW_ARGS(SkImageEncoder_WIC, (t));
336}