|
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 |
|
49 class SkImageDecoder_WIC : public SkImageDecoder { |
|
50 public: |
|
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 |
|
69 protected: |
|
70 virtual bool onDecode(SkStream* stream, SkBitmap* bm, Mode mode) SK_OVERRIDE; |
|
71 }; |
|
72 |
|
73 struct FormatConversion { |
|
74 GUID fGuidFormat; |
|
75 SkImageDecoder::Format fFormat; |
|
76 }; |
|
77 |
|
78 static 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 |
|
86 static 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 |
|
95 bool 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); |
|
106 } |
|
107 |
|
108 bool 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->setConfig(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 |
|
253 extern SkImageDecoder* image_decoder_from_stream(SkStreamRewindable*); |
|
254 |
|
255 SkImageDecoder* 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 SkNEW(SkImageDecoder_WIC); |
|
260 } else { |
|
261 return decoder; |
|
262 } |
|
263 } |
|
264 |
|
265 ///////////////////////////////////////////////////////////////////////// |
|
266 |
|
267 SkMovie* SkMovie::DecodeStream(SkStreamRewindable* stream) { |
|
268 return NULL; |
|
269 } |
|
270 |
|
271 ///////////////////////////////////////////////////////////////////////// |
|
272 |
|
273 class SkImageEncoder_WIC : public SkImageEncoder { |
|
274 public: |
|
275 SkImageEncoder_WIC(Type t) : fType(t) {} |
|
276 |
|
277 protected: |
|
278 virtual bool onEncode(SkWStream* stream, const SkBitmap& bm, int quality); |
|
279 |
|
280 private: |
|
281 Type fType; |
|
282 }; |
|
283 |
|
284 bool 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 (kPMColor_SkColorType == bitmapOrig.colorType() && bitmapOrig.isOpaque()) { |
|
310 bitmap = &bitmapOrig; |
|
311 } else { |
|
312 if (!bitmapOrig.copyTo(&bitmapCopy, kPMColor_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 |
|
440 static 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 SkNEW_ARGS(SkImageEncoder_WIC, (t)); |
|
451 } |
|
452 |
|
453 static SkImageEncoder_EncodeReg gEReg(sk_imageencoder_wic_factory); |
|
454 |
|
455 static 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 |
|
464 static SkImageDecoder_FormatReg gFormatReg(get_format_wic); |