1 // Copyright 2013 Google Inc. All Rights Reserved.
2 //
3 // Use of this source code is governed by a BSD-style license
4 // that can be found in the COPYING file in the root of the source
5 // tree. An additional intellectual property rights grant can be found
6 // in the file PATENTS. All contributing project authors may
7 // be found in the AUTHORS file in the root of the source tree.
8 // -----------------------------------------------------------------------------
9 //
10 // Windows Imaging Component (WIC) decode.
11
12 #include "./wicdec.h"
13
14 #ifdef HAVE_CONFIG_H
15 #include "webp/config.h"
16 #endif
17
18 #include <assert.h>
19 #include <stdio.h>
20 #include <string.h>
21
22 #ifdef HAVE_WINCODEC_H
23 #ifdef __MINGW32__
24 #define INITGUID // Without this GUIDs are declared extern and fail to link
25 #endif
26 #define CINTERFACE
27 #define COBJMACROS
28 #define _WIN32_IE 0x500 // Workaround bug in shlwapi.h when compiling C++
29 // code with COBJMACROS.
30 #include <ole2.h> // CreateStreamOnHGlobal()
31 #include <shlwapi.h>
32 #include <tchar.h>
33 #include <windows.h>
34 #include <wincodec.h>
35
36 #include "../examples/unicode.h"
37 #include "./imageio_util.h"
38 #include "./metadata.h"
39 #include "webp/encode.h"
40
41 #define IFS(fn) \
42 do { \
43 if (SUCCEEDED(hr)) { \
44 hr = (fn); \
45 if (FAILED(hr)) fprintf(stderr, #fn " failed %08lx\n", hr); \
46 } \
47 } while (0)
48
49 // modified version of DEFINE_GUID from guiddef.h.
50 #define WEBP_DEFINE_GUID(name, l, w1, w2, b1, b2, b3, b4, b5, b6, b7, b8) \
51 static const GUID name = \
52 { l, w1, w2, { b1, b2, b3, b4, b5, b6, b7, b8 } }
53
54 #ifdef __cplusplus
55 #define MAKE_REFGUID(x) (x)
56 #else
57 #define MAKE_REFGUID(x) &(x)
58 #endif
59
60 typedef struct WICFormatImporter {
61 const GUID* pixel_format;
62 int bytes_per_pixel;
63 int (*import)(WebPPicture* const, const uint8_t* const, int);
64 } WICFormatImporter;
65
66 // From Microsoft SDK 7.0a -- wincodec.h
67 // Create local copies for compatibility when building against earlier
68 // versions of the SDK.
69 WEBP_DEFINE_GUID(GUID_WICPixelFormat24bppBGR_,
70 0x6fddc324, 0x4e03, 0x4bfe,
71 0xb1, 0x85, 0x3d, 0x77, 0x76, 0x8d, 0xc9, 0x0c);
72 WEBP_DEFINE_GUID(GUID_WICPixelFormat24bppRGB_,
73 0x6fddc324, 0x4e03, 0x4bfe,
74 0xb1, 0x85, 0x3d, 0x77, 0x76, 0x8d, 0xc9, 0x0d);
75 WEBP_DEFINE_GUID(GUID_WICPixelFormat32bppBGRA_,
76 0x6fddc324, 0x4e03, 0x4bfe,
77 0xb1, 0x85, 0x3d, 0x77, 0x76, 0x8d, 0xc9, 0x0f);
78 WEBP_DEFINE_GUID(GUID_WICPixelFormat32bppRGBA_,
79 0xf5c7ad2d, 0x6a8d, 0x43dd,
80 0xa7, 0xa8, 0xa2, 0x99, 0x35, 0x26, 0x1a, 0xe9);
81 WEBP_DEFINE_GUID(GUID_WICPixelFormat64bppBGRA_,
82 0x1562ff7c, 0xd352, 0x46f9,
83 0x97, 0x9e, 0x42, 0x97, 0x6b, 0x79, 0x22, 0x46);
84 WEBP_DEFINE_GUID(GUID_WICPixelFormat64bppRGBA_,
85 0x6fddc324, 0x4e03, 0x4bfe,
86 0xb1, 0x85, 0x3d, 0x77, 0x76, 0x8d, 0xc9, 0x16);
87
OpenInputStream(const char * filename,IStream ** stream)88 static HRESULT OpenInputStream(const char* filename, IStream** stream) {
89 HRESULT hr = S_OK;
90 if (!WSTRCMP(filename, "-")) {
91 const uint8_t* data = NULL;
92 size_t data_size = 0;
93 const int ok = ImgIoUtilReadFile(filename, &data, &data_size);
94 if (ok) {
95 HGLOBAL image = GlobalAlloc(GMEM_MOVEABLE, data_size);
96 if (image != NULL) {
97 void* const image_mem = GlobalLock(image);
98 if (image_mem != NULL) {
99 memcpy(image_mem, data, data_size);
100 GlobalUnlock(image);
101 IFS(CreateStreamOnHGlobal(image, TRUE, stream));
102 } else {
103 hr = E_FAIL;
104 }
105 } else {
106 hr = E_OUTOFMEMORY;
107 }
108 free((void*)data);
109 } else {
110 hr = E_FAIL;
111 }
112 } else {
113 IFS(SHCreateStreamOnFile((const LPTSTR)filename, STGM_READ, stream));
114 }
115
116 if (FAILED(hr)) {
117 _ftprintf(stderr, _T("Error opening input file %s (%08lx)\n"),
118 (const LPTSTR)filename, hr);
119 }
120 return hr;
121 }
122
123 // -----------------------------------------------------------------------------
124 // Metadata processing
125
126 // Stores the first non-zero sized color profile from 'frame' to 'iccp'.
127 // Returns an HRESULT to indicate success or failure. The caller is responsible
128 // for freeing 'iccp->bytes' in either case.
ExtractICCP(IWICImagingFactory * const factory,IWICBitmapFrameDecode * const frame,MetadataPayload * const iccp)129 static HRESULT ExtractICCP(IWICImagingFactory* const factory,
130 IWICBitmapFrameDecode* const frame,
131 MetadataPayload* const iccp) {
132 HRESULT hr = S_OK;
133 UINT i, count;
134 IWICColorContext** color_contexts;
135
136 IFS(IWICBitmapFrameDecode_GetColorContexts(frame, 0, NULL, &count));
137 if (FAILED(hr) || count == 0) return hr;
138
139 color_contexts = (IWICColorContext**)calloc(count, sizeof(*color_contexts));
140 if (color_contexts == NULL) return E_OUTOFMEMORY;
141 for (i = 0; SUCCEEDED(hr) && i < count; ++i) {
142 IFS(IWICImagingFactory_CreateColorContext(factory, &color_contexts[i]));
143 }
144
145 if (SUCCEEDED(hr)) {
146 UINT num_color_contexts;
147 IFS(IWICBitmapFrameDecode_GetColorContexts(frame,
148 count, color_contexts,
149 &num_color_contexts));
150 assert(FAILED(hr) || num_color_contexts <= count);
151 for (i = 0; SUCCEEDED(hr) && i < num_color_contexts; ++i) {
152 WICColorContextType type;
153 IFS(IWICColorContext_GetType(color_contexts[i], &type));
154 if (SUCCEEDED(hr) && type == WICColorContextProfile) {
155 UINT size;
156 IFS(IWICColorContext_GetProfileBytes(color_contexts[i],
157 0, NULL, &size));
158 if (SUCCEEDED(hr) && size > 0) {
159 iccp->bytes = (uint8_t*)malloc(size);
160 if (iccp->bytes == NULL) {
161 hr = E_OUTOFMEMORY;
162 break;
163 }
164 iccp->size = size;
165 IFS(IWICColorContext_GetProfileBytes(color_contexts[i],
166 (UINT)iccp->size, iccp->bytes,
167 &size));
168 if (SUCCEEDED(hr) && size != iccp->size) {
169 fprintf(stderr, "Warning! ICC profile size (%u) != expected (%u)\n",
170 size, (uint32_t)iccp->size);
171 iccp->size = size;
172 }
173 break;
174 }
175 }
176 }
177 }
178 for (i = 0; i < count; ++i) {
179 if (color_contexts[i] != NULL) IUnknown_Release(color_contexts[i]);
180 }
181 free(color_contexts);
182 return hr;
183 }
184
ExtractMetadata(IWICImagingFactory * const factory,IWICBitmapFrameDecode * const frame,Metadata * const metadata)185 static HRESULT ExtractMetadata(IWICImagingFactory* const factory,
186 IWICBitmapFrameDecode* const frame,
187 Metadata* const metadata) {
188 // TODO(jzern): add XMP/EXIF extraction.
189 const HRESULT hr = ExtractICCP(factory, frame, &metadata->iccp);
190 if (FAILED(hr)) MetadataFree(metadata);
191 return hr;
192 }
193
194 // -----------------------------------------------------------------------------
195
HasPalette(GUID pixel_format)196 static int HasPalette(GUID pixel_format) {
197 return (IsEqualGUID(MAKE_REFGUID(pixel_format),
198 MAKE_REFGUID(GUID_WICPixelFormat1bppIndexed)) ||
199 IsEqualGUID(MAKE_REFGUID(pixel_format),
200 MAKE_REFGUID(GUID_WICPixelFormat2bppIndexed)) ||
201 IsEqualGUID(MAKE_REFGUID(pixel_format),
202 MAKE_REFGUID(GUID_WICPixelFormat4bppIndexed)) ||
203 IsEqualGUID(MAKE_REFGUID(pixel_format),
204 MAKE_REFGUID(GUID_WICPixelFormat8bppIndexed)));
205 }
206
HasAlpha(IWICImagingFactory * const factory,IWICBitmapDecoder * const decoder,IWICBitmapFrameDecode * const frame,GUID pixel_format)207 static int HasAlpha(IWICImagingFactory* const factory,
208 IWICBitmapDecoder* const decoder,
209 IWICBitmapFrameDecode* const frame,
210 GUID pixel_format) {
211 int has_alpha;
212 if (HasPalette(pixel_format)) {
213 IWICPalette* frame_palette = NULL;
214 IWICPalette* global_palette = NULL;
215 BOOL frame_palette_has_alpha = FALSE;
216 BOOL global_palette_has_alpha = FALSE;
217
218 // A palette may exist at the frame or container level,
219 // check IWICPalette::HasAlpha() for both if present.
220 if (SUCCEEDED(IWICImagingFactory_CreatePalette(factory, &frame_palette)) &&
221 SUCCEEDED(IWICBitmapFrameDecode_CopyPalette(frame, frame_palette))) {
222 IWICPalette_HasAlpha(frame_palette, &frame_palette_has_alpha);
223 }
224 if (SUCCEEDED(IWICImagingFactory_CreatePalette(factory, &global_palette)) &&
225 SUCCEEDED(IWICBitmapDecoder_CopyPalette(decoder, global_palette))) {
226 IWICPalette_HasAlpha(global_palette, &global_palette_has_alpha);
227 }
228 has_alpha = frame_palette_has_alpha || global_palette_has_alpha;
229
230 if (frame_palette != NULL) IUnknown_Release(frame_palette);
231 if (global_palette != NULL) IUnknown_Release(global_palette);
232 } else {
233 has_alpha = IsEqualGUID(MAKE_REFGUID(pixel_format),
234 MAKE_REFGUID(GUID_WICPixelFormat32bppRGBA_)) ||
235 IsEqualGUID(MAKE_REFGUID(pixel_format),
236 MAKE_REFGUID(GUID_WICPixelFormat32bppBGRA_)) ||
237 IsEqualGUID(MAKE_REFGUID(pixel_format),
238 MAKE_REFGUID(GUID_WICPixelFormat64bppRGBA_)) ||
239 IsEqualGUID(MAKE_REFGUID(pixel_format),
240 MAKE_REFGUID(GUID_WICPixelFormat64bppBGRA_));
241 }
242 return has_alpha;
243 }
244
ReadPictureWithWIC(const char * const filename,WebPPicture * const pic,int keep_alpha,Metadata * const metadata)245 int ReadPictureWithWIC(const char* const filename,
246 WebPPicture* const pic, int keep_alpha,
247 Metadata* const metadata) {
248 // From Microsoft SDK 6.0a -- ks.h
249 // Define a local copy to avoid link errors under mingw.
250 WEBP_DEFINE_GUID(GUID_NULL_, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
251 static const WICFormatImporter kAlphaFormatImporters[] = {
252 { &GUID_WICPixelFormat32bppBGRA_, 4, WebPPictureImportBGRA },
253 { &GUID_WICPixelFormat32bppRGBA_, 4, WebPPictureImportRGBA },
254 { NULL, 0, NULL },
255 };
256 static const WICFormatImporter kNonAlphaFormatImporters[] = {
257 { &GUID_WICPixelFormat24bppBGR_, 3, WebPPictureImportBGR },
258 { &GUID_WICPixelFormat24bppRGB_, 3, WebPPictureImportRGB },
259 { NULL, 0, NULL },
260 };
261 HRESULT hr = S_OK;
262 IWICBitmapFrameDecode* frame = NULL;
263 IWICFormatConverter* converter = NULL;
264 IWICImagingFactory* factory = NULL;
265 IWICBitmapDecoder* decoder = NULL;
266 IStream* stream = NULL;
267 UINT frame_count = 0;
268 UINT width = 0, height = 0;
269 BYTE* rgb = NULL;
270 WICPixelFormatGUID src_pixel_format = GUID_WICPixelFormatUndefined;
271 const WICFormatImporter* importer = NULL;
272 GUID src_container_format = GUID_NULL_;
273 static const GUID* kAlphaContainers[] = {
274 &GUID_ContainerFormatBmp,
275 &GUID_ContainerFormatPng,
276 &GUID_ContainerFormatTiff,
277 NULL
278 };
279 int has_alpha = 0;
280 int64_t stride;
281
282 if (filename == NULL || pic == NULL) return 0;
283
284 IFS(CoInitialize(NULL));
285 IFS(CoCreateInstance(MAKE_REFGUID(CLSID_WICImagingFactory), NULL,
286 CLSCTX_INPROC_SERVER,
287 MAKE_REFGUID(IID_IWICImagingFactory),
288 (LPVOID*)&factory));
289 if (hr == REGDB_E_CLASSNOTREG) {
290 fprintf(stderr,
291 "Couldn't access Windows Imaging Component (are you running "
292 "Windows XP SP3 or newer?). Most formats not available. "
293 "Use -s for the available YUV input.\n");
294 }
295 // Prepare for image decoding.
296 IFS(OpenInputStream(filename, &stream));
297 IFS(IWICImagingFactory_CreateDecoderFromStream(
298 factory, stream, NULL,
299 WICDecodeMetadataCacheOnDemand, &decoder));
300 IFS(IWICBitmapDecoder_GetFrameCount(decoder, &frame_count));
301 if (SUCCEEDED(hr) && frame_count == 0) {
302 fprintf(stderr, "No frame found in input file.\n");
303 hr = E_FAIL;
304 }
305 IFS(IWICBitmapDecoder_GetFrame(decoder, 0, &frame));
306 IFS(IWICBitmapFrameDecode_GetPixelFormat(frame, &src_pixel_format));
307 IFS(IWICBitmapDecoder_GetContainerFormat(decoder, &src_container_format));
308
309 if (SUCCEEDED(hr) && keep_alpha) {
310 const GUID** guid;
311 for (guid = kAlphaContainers; *guid != NULL; ++guid) {
312 if (IsEqualGUID(MAKE_REFGUID(src_container_format),
313 MAKE_REFGUID(**guid))) {
314 has_alpha = HasAlpha(factory, decoder, frame, src_pixel_format);
315 break;
316 }
317 }
318 }
319
320 // Prepare for pixel format conversion (if necessary).
321 IFS(IWICImagingFactory_CreateFormatConverter(factory, &converter));
322
323 for (importer = has_alpha ? kAlphaFormatImporters : kNonAlphaFormatImporters;
324 hr == S_OK && importer->import != NULL; ++importer) {
325 BOOL can_convert;
326 const HRESULT cchr = IWICFormatConverter_CanConvert(
327 converter,
328 MAKE_REFGUID(src_pixel_format),
329 MAKE_REFGUID(*importer->pixel_format),
330 &can_convert);
331 if (SUCCEEDED(cchr) && can_convert) break;
332 }
333 if (importer->import == NULL) hr = E_FAIL;
334
335 IFS(IWICFormatConverter_Initialize(converter, (IWICBitmapSource*)frame,
336 importer->pixel_format,
337 WICBitmapDitherTypeNone,
338 NULL, 0.0, WICBitmapPaletteTypeCustom));
339
340 // Decode.
341 IFS(IWICFormatConverter_GetSize(converter, &width, &height));
342 stride = (int64_t)importer->bytes_per_pixel * width * sizeof(*rgb);
343 if (stride != (int)stride ||
344 !ImgIoUtilCheckSizeArgumentsOverflow(stride, height)) {
345 hr = E_FAIL;
346 }
347
348 if (SUCCEEDED(hr)) {
349 rgb = (BYTE*)malloc((size_t)stride * height);
350 if (rgb == NULL)
351 hr = E_OUTOFMEMORY;
352 }
353 IFS(IWICFormatConverter_CopyPixels(converter, NULL,
354 (UINT)stride, (UINT)stride * height, rgb));
355
356 // WebP conversion.
357 if (SUCCEEDED(hr)) {
358 int ok;
359 pic->width = width;
360 pic->height = height;
361 pic->use_argb = 1; // For WIC, we always force to argb
362 ok = importer->import(pic, rgb, (int)stride);
363 if (!ok) hr = E_FAIL;
364 }
365 if (SUCCEEDED(hr)) {
366 if (metadata != NULL) {
367 hr = ExtractMetadata(factory, frame, metadata);
368 if (FAILED(hr)) {
369 fprintf(stderr, "Error extracting image metadata using WIC!\n");
370 }
371 }
372 }
373
374 // Cleanup.
375 if (converter != NULL) IUnknown_Release(converter);
376 if (frame != NULL) IUnknown_Release(frame);
377 if (decoder != NULL) IUnknown_Release(decoder);
378 if (factory != NULL) IUnknown_Release(factory);
379 if (stream != NULL) IUnknown_Release(stream);
380 free(rgb);
381 return SUCCEEDED(hr);
382 }
383 #else // !HAVE_WINCODEC_H
ReadPictureWithWIC(const char * const filename,struct WebPPicture * const pic,int keep_alpha,struct Metadata * const metadata)384 int ReadPictureWithWIC(const char* const filename,
385 struct WebPPicture* const pic, int keep_alpha,
386 struct Metadata* const metadata) {
387 (void)filename;
388 (void)pic;
389 (void)keep_alpha;
390 (void)metadata;
391 fprintf(stderr, "Windows Imaging Component (WIC) support not compiled. "
392 "Visual Studio and mingw-w64 builds support WIC. Make sure "
393 "wincodec.h detection is working correctly if using autoconf "
394 "and HAVE_WINCODEC_H is defined before building.\n");
395 return 0;
396 }
397 #endif // HAVE_WINCODEC_H
398
399 // -----------------------------------------------------------------------------
400