xref: /aosp_15_r20/external/webp/imageio/wicdec.c (revision b2055c353e87c8814eb2b6b1b11112a1562253bd)
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) {
138     // Treat unsupported operation as a non-fatal error. See crbug.com/webp/506.
139     return (hr == WINCODEC_ERR_UNSUPPORTEDOPERATION) ? S_OK : hr;
140   }
141 
142   color_contexts = (IWICColorContext**)calloc(count, sizeof(*color_contexts));
143   if (color_contexts == NULL) return E_OUTOFMEMORY;
144   for (i = 0; SUCCEEDED(hr) && i < count; ++i) {
145     IFS(IWICImagingFactory_CreateColorContext(factory, &color_contexts[i]));
146   }
147 
148   if (SUCCEEDED(hr)) {
149     UINT num_color_contexts;
150     IFS(IWICBitmapFrameDecode_GetColorContexts(frame,
151                                                count, color_contexts,
152                                                &num_color_contexts));
153     assert(FAILED(hr) || num_color_contexts <= count);
154     for (i = 0; SUCCEEDED(hr) && i < num_color_contexts; ++i) {
155       WICColorContextType type;
156       IFS(IWICColorContext_GetType(color_contexts[i], &type));
157       if (SUCCEEDED(hr) && type == WICColorContextProfile) {
158         UINT size;
159         IFS(IWICColorContext_GetProfileBytes(color_contexts[i],
160                                              0, NULL, &size));
161         if (SUCCEEDED(hr) && size > 0) {
162           iccp->bytes = (uint8_t*)malloc(size);
163           if (iccp->bytes == NULL) {
164             hr = E_OUTOFMEMORY;
165             break;
166           }
167           iccp->size = size;
168           IFS(IWICColorContext_GetProfileBytes(color_contexts[i],
169                                                (UINT)iccp->size, iccp->bytes,
170                                                &size));
171           if (SUCCEEDED(hr) && size != iccp->size) {
172             fprintf(stderr, "Warning! ICC profile size (%u) != expected (%u)\n",
173                     size, (uint32_t)iccp->size);
174             iccp->size = size;
175           }
176           break;
177         }
178       }
179     }
180   }
181   for (i = 0; i < count; ++i) {
182     if (color_contexts[i] != NULL) IUnknown_Release(color_contexts[i]);
183   }
184   free(color_contexts);
185   return hr;
186 }
187 
ExtractMetadata(IWICImagingFactory * const factory,IWICBitmapFrameDecode * const frame,Metadata * const metadata)188 static HRESULT ExtractMetadata(IWICImagingFactory* const factory,
189                                IWICBitmapFrameDecode* const frame,
190                                Metadata* const metadata) {
191   // TODO(jzern): add XMP/EXIF extraction.
192   const HRESULT hr = ExtractICCP(factory, frame, &metadata->iccp);
193   if (FAILED(hr)) MetadataFree(metadata);
194   return hr;
195 }
196 
197 // -----------------------------------------------------------------------------
198 
HasPalette(GUID pixel_format)199 static int HasPalette(GUID pixel_format) {
200   return (IsEqualGUID(MAKE_REFGUID(pixel_format),
201                       MAKE_REFGUID(GUID_WICPixelFormat1bppIndexed)) ||
202           IsEqualGUID(MAKE_REFGUID(pixel_format),
203                       MAKE_REFGUID(GUID_WICPixelFormat2bppIndexed)) ||
204           IsEqualGUID(MAKE_REFGUID(pixel_format),
205                       MAKE_REFGUID(GUID_WICPixelFormat4bppIndexed)) ||
206           IsEqualGUID(MAKE_REFGUID(pixel_format),
207                       MAKE_REFGUID(GUID_WICPixelFormat8bppIndexed)));
208 }
209 
HasAlpha(IWICImagingFactory * const factory,IWICBitmapDecoder * const decoder,IWICBitmapFrameDecode * const frame,GUID pixel_format)210 static int HasAlpha(IWICImagingFactory* const factory,
211                     IWICBitmapDecoder* const decoder,
212                     IWICBitmapFrameDecode* const frame,
213                     GUID pixel_format) {
214   int has_alpha;
215   if (HasPalette(pixel_format)) {
216     IWICPalette* frame_palette = NULL;
217     IWICPalette* global_palette = NULL;
218     BOOL frame_palette_has_alpha = FALSE;
219     BOOL global_palette_has_alpha = FALSE;
220 
221     // A palette may exist at the frame or container level,
222     // check IWICPalette::HasAlpha() for both if present.
223     if (SUCCEEDED(IWICImagingFactory_CreatePalette(factory, &frame_palette)) &&
224         SUCCEEDED(IWICBitmapFrameDecode_CopyPalette(frame, frame_palette))) {
225       IWICPalette_HasAlpha(frame_palette, &frame_palette_has_alpha);
226     }
227     if (SUCCEEDED(IWICImagingFactory_CreatePalette(factory, &global_palette)) &&
228         SUCCEEDED(IWICBitmapDecoder_CopyPalette(decoder, global_palette))) {
229       IWICPalette_HasAlpha(global_palette, &global_palette_has_alpha);
230     }
231     has_alpha = frame_palette_has_alpha || global_palette_has_alpha;
232 
233     if (frame_palette != NULL) IUnknown_Release(frame_palette);
234     if (global_palette != NULL) IUnknown_Release(global_palette);
235   } else {
236     has_alpha = IsEqualGUID(MAKE_REFGUID(pixel_format),
237                             MAKE_REFGUID(GUID_WICPixelFormat32bppRGBA_)) ||
238                 IsEqualGUID(MAKE_REFGUID(pixel_format),
239                             MAKE_REFGUID(GUID_WICPixelFormat32bppBGRA_)) ||
240                 IsEqualGUID(MAKE_REFGUID(pixel_format),
241                             MAKE_REFGUID(GUID_WICPixelFormat64bppRGBA_)) ||
242                 IsEqualGUID(MAKE_REFGUID(pixel_format),
243                             MAKE_REFGUID(GUID_WICPixelFormat64bppBGRA_));
244   }
245   return has_alpha;
246 }
247 
ReadPictureWithWIC(const char * const filename,WebPPicture * const pic,int keep_alpha,Metadata * const metadata)248 int ReadPictureWithWIC(const char* const filename,
249                        WebPPicture* const pic, int keep_alpha,
250                        Metadata* const metadata) {
251   // From Microsoft SDK 6.0a -- ks.h
252   // Define a local copy to avoid link errors under mingw.
253   WEBP_DEFINE_GUID(GUID_NULL_, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
254   static const WICFormatImporter kAlphaFormatImporters[] = {
255     { &GUID_WICPixelFormat32bppBGRA_, 4, WebPPictureImportBGRA },
256     { &GUID_WICPixelFormat32bppRGBA_, 4, WebPPictureImportRGBA },
257     { NULL, 0, NULL },
258   };
259   static const WICFormatImporter kNonAlphaFormatImporters[] = {
260     { &GUID_WICPixelFormat24bppBGR_, 3, WebPPictureImportBGR },
261     { &GUID_WICPixelFormat24bppRGB_, 3, WebPPictureImportRGB },
262     { NULL, 0, NULL },
263   };
264   HRESULT hr = S_OK;
265   IWICBitmapFrameDecode* frame = NULL;
266   IWICFormatConverter* converter = NULL;
267   IWICImagingFactory* factory = NULL;
268   IWICBitmapDecoder* decoder = NULL;
269   IStream* stream = NULL;
270   UINT frame_count = 0;
271   UINT width = 0, height = 0;
272   BYTE* rgb = NULL;
273   WICPixelFormatGUID src_pixel_format = GUID_WICPixelFormatUndefined;
274   const WICFormatImporter* importer = NULL;
275   GUID src_container_format = GUID_NULL_;
276   // From Windows Kits\10\Include\10.0.19041.0\um\wincodec.h
277   WEBP_DEFINE_GUID(GUID_ContainerFormatWebp_,
278                    0xe094b0e2, 0x67f2, 0x45b3,
279                    0xb0, 0xea, 0x11, 0x53, 0x37, 0xca, 0x7c, 0xf3);
280   static const GUID* kAlphaContainers[] = {
281     &GUID_ContainerFormatBmp,
282     &GUID_ContainerFormatPng,
283     &GUID_ContainerFormatTiff,
284     &GUID_ContainerFormatWebp_,
285     NULL
286   };
287   int has_alpha = 0;
288   int64_t stride;
289 
290   if (filename == NULL || pic == NULL) return 0;
291 
292   IFS(CoInitialize(NULL));
293   IFS(CoCreateInstance(MAKE_REFGUID(CLSID_WICImagingFactory), NULL,
294                        CLSCTX_INPROC_SERVER,
295                        MAKE_REFGUID(IID_IWICImagingFactory),
296                        (LPVOID*)&factory));
297   if (hr == REGDB_E_CLASSNOTREG) {
298     fprintf(stderr,
299             "Couldn't access Windows Imaging Component (are you running "
300             "Windows XP SP3 or newer?). Most formats not available. "
301             "Use -s for the available YUV input.\n");
302   }
303   // Prepare for image decoding.
304   IFS(OpenInputStream(filename, &stream));
305   IFS(IWICImagingFactory_CreateDecoderFromStream(
306           factory, stream, NULL,
307           WICDecodeMetadataCacheOnDemand, &decoder));
308   IFS(IWICBitmapDecoder_GetFrameCount(decoder, &frame_count));
309   if (SUCCEEDED(hr)) {
310     if (frame_count == 0) {
311       fprintf(stderr, "No frame found in input file.\n");
312       hr = E_FAIL;
313     } else if (frame_count > 1) {
314       // WIC will be tried before native WebP decoding so avoid duplicating the
315       // error message.
316       hr = E_FAIL;
317     }
318   }
319   IFS(IWICBitmapDecoder_GetFrame(decoder, 0, &frame));
320   IFS(IWICBitmapFrameDecode_GetPixelFormat(frame, &src_pixel_format));
321   IFS(IWICBitmapDecoder_GetContainerFormat(decoder, &src_container_format));
322 
323   if (SUCCEEDED(hr) && keep_alpha) {
324     const GUID** guid;
325     for (guid = kAlphaContainers; *guid != NULL; ++guid) {
326       if (IsEqualGUID(MAKE_REFGUID(src_container_format),
327                       MAKE_REFGUID(**guid))) {
328         has_alpha = HasAlpha(factory, decoder, frame, src_pixel_format);
329         break;
330       }
331     }
332   }
333 
334   // Prepare for pixel format conversion (if necessary).
335   IFS(IWICImagingFactory_CreateFormatConverter(factory, &converter));
336 
337   for (importer = has_alpha ? kAlphaFormatImporters : kNonAlphaFormatImporters;
338        hr == S_OK && importer->import != NULL; ++importer) {
339     BOOL can_convert;
340     const HRESULT cchr = IWICFormatConverter_CanConvert(
341         converter,
342         MAKE_REFGUID(src_pixel_format),
343         MAKE_REFGUID(*importer->pixel_format),
344         &can_convert);
345     if (SUCCEEDED(cchr) && can_convert) break;
346   }
347   if (importer->import == NULL) hr = E_FAIL;
348 
349   IFS(IWICFormatConverter_Initialize(converter, (IWICBitmapSource*)frame,
350                                      importer->pixel_format,
351                                      WICBitmapDitherTypeNone,
352                                      NULL, 0.0, WICBitmapPaletteTypeCustom));
353 
354   // Decode.
355   IFS(IWICFormatConverter_GetSize(converter, &width, &height));
356   stride = (int64_t)importer->bytes_per_pixel * width * sizeof(*rgb);
357   if (stride != (int)stride ||
358       !ImgIoUtilCheckSizeArgumentsOverflow(stride, height)) {
359     hr = E_FAIL;
360   }
361 
362   if (SUCCEEDED(hr)) {
363     rgb = (BYTE*)malloc((size_t)stride * height);
364     if (rgb == NULL)
365       hr = E_OUTOFMEMORY;
366   }
367   IFS(IWICFormatConverter_CopyPixels(converter, NULL,
368                                      (UINT)stride, (UINT)stride * height, rgb));
369 
370   // WebP conversion.
371   if (SUCCEEDED(hr)) {
372     int ok;
373     pic->width = width;
374     pic->height = height;
375     pic->use_argb = 1;    // For WIC, we always force to argb
376     ok = importer->import(pic, rgb, (int)stride);
377     if (!ok) hr = E_FAIL;
378   }
379   if (SUCCEEDED(hr)) {
380     if (metadata != NULL) {
381       hr = ExtractMetadata(factory, frame, metadata);
382       if (FAILED(hr)) {
383         fprintf(stderr, "Error extracting image metadata using WIC!\n");
384       }
385     }
386   }
387 
388   // Cleanup.
389   if (converter != NULL) IUnknown_Release(converter);
390   if (frame != NULL) IUnknown_Release(frame);
391   if (decoder != NULL) IUnknown_Release(decoder);
392   if (factory != NULL) IUnknown_Release(factory);
393   if (stream != NULL) IUnknown_Release(stream);
394   free(rgb);
395   return SUCCEEDED(hr);
396 }
397 #else  // !HAVE_WINCODEC_H
ReadPictureWithWIC(const char * const filename,struct WebPPicture * const pic,int keep_alpha,struct Metadata * const metadata)398 int ReadPictureWithWIC(const char* const filename,
399                        struct WebPPicture* const pic, int keep_alpha,
400                        struct Metadata* const metadata) {
401   (void)filename;
402   (void)pic;
403   (void)keep_alpha;
404   (void)metadata;
405   fprintf(stderr, "Windows Imaging Component (WIC) support not compiled. "
406                   "Visual Studio and mingw-w64 builds support WIC. Make sure "
407                   "wincodec.h detection is working correctly if using autoconf "
408                   "and HAVE_WINCODEC_H is defined before building.\n");
409   return 0;
410 }
411 #endif  // HAVE_WINCODEC_H
412 
413 // -----------------------------------------------------------------------------
414