wicdec.c (15633B)
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 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. 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 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 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 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 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 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 // -----------------------------------------------------------------------------