go-libwebp

Experimental translation from libwebp to Go source.
Log | Files | Refs | README | LICENSE

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 // -----------------------------------------------------------------------------