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