github.com/cellofellow/gopkg@v0.0.0-20140722061823-eec0544a62ad/image/webp/libwebp/examples/wicdec.c (about)

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