]> git.cworth.org Git - apitrace/blobdiff - thirdparty/directxtex/DirectXTex/DirectXTexWIC.cpp
Merge branch 'directxtex'
[apitrace] / thirdparty / directxtex / DirectXTex / DirectXTexWIC.cpp
diff --git a/thirdparty/directxtex/DirectXTex/DirectXTexWIC.cpp b/thirdparty/directxtex/DirectXTex/DirectXTexWIC.cpp
new file mode 100644 (file)
index 0000000..05cb76a
--- /dev/null
@@ -0,0 +1,946 @@
+//-------------------------------------------------------------------------------------
+// DirectXTexWIC.cpp
+//  
+// DirectX Texture Library - WIC-based file reader/writer
+//
+// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
+// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
+// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
+// PARTICULAR PURPOSE.
+//
+// Copyright (c) Microsoft Corporation. All rights reserved.
+//
+// http://go.microsoft.com/fwlink/?LinkId=248926
+//-------------------------------------------------------------------------------------
+
+#include "DirectXTexP.h"
+
+//-------------------------------------------------------------------------------------
+// WIC Pixel Format nearest conversion table
+//-------------------------------------------------------------------------------------
+
+struct WICConvert
+{
+    GUID        source;
+    GUID        target;
+};
+
+static WICConvert g_WICConvert[] = 
+{
+    // Directly support the formats listed in XnaTexUtil::g_WICFormats, so no conversion required
+    // Note target GUID in this conversion table must be one of those directly supported formats.
+
+    { GUID_WICPixelFormat1bppIndexed,           GUID_WICPixelFormat32bppRGBA }, // DXGI_FORMAT_R8G8B8A8_UNORM 
+    { GUID_WICPixelFormat2bppIndexed,           GUID_WICPixelFormat32bppRGBA }, // DXGI_FORMAT_R8G8B8A8_UNORM 
+    { GUID_WICPixelFormat4bppIndexed,           GUID_WICPixelFormat32bppRGBA }, // DXGI_FORMAT_R8G8B8A8_UNORM 
+    { GUID_WICPixelFormat8bppIndexed,           GUID_WICPixelFormat32bppRGBA }, // DXGI_FORMAT_R8G8B8A8_UNORM 
+
+    { GUID_WICPixelFormat2bppGray,              GUID_WICPixelFormat8bppGray }, // DXGI_FORMAT_R8_UNORM 
+    { GUID_WICPixelFormat4bppGray,              GUID_WICPixelFormat8bppGray }, // DXGI_FORMAT_R8_UNORM 
+
+    { GUID_WICPixelFormat16bppGrayFixedPoint,   GUID_WICPixelFormat16bppGrayHalf }, // DXGI_FORMAT_R16_FLOAT 
+    { GUID_WICPixelFormat32bppGrayFixedPoint,   GUID_WICPixelFormat32bppGrayFloat }, // DXGI_FORMAT_R32_FLOAT 
+
+    { GUID_WICPixelFormat16bppBGR555,           GUID_WICPixelFormat16bppBGRA5551 }, // DXGI_FORMAT_B5G5R5A1_UNORM 
+    { GUID_WICPixelFormat32bppBGR101010,        GUID_WICPixelFormat32bppRGBA1010102 }, // DXGI_FORMAT_R10G10B10A2_UNORM
+
+    { GUID_WICPixelFormat24bppBGR,              GUID_WICPixelFormat32bppRGBA }, // DXGI_FORMAT_R8G8B8A8_UNORM 
+    { GUID_WICPixelFormat24bppRGB,              GUID_WICPixelFormat32bppRGBA }, // DXGI_FORMAT_R8G8B8A8_UNORM 
+    { GUID_WICPixelFormat32bppPBGRA,            GUID_WICPixelFormat32bppRGBA }, // DXGI_FORMAT_R8G8B8A8_UNORM 
+    { GUID_WICPixelFormat32bppPRGBA,            GUID_WICPixelFormat32bppRGBA }, // DXGI_FORMAT_R8G8B8A8_UNORM 
+
+    { GUID_WICPixelFormat48bppRGB,              GUID_WICPixelFormat64bppRGBA }, // DXGI_FORMAT_R16G16B16A16_UNORM
+    { GUID_WICPixelFormat48bppBGR,              GUID_WICPixelFormat64bppRGBA }, // DXGI_FORMAT_R16G16B16A16_UNORM
+    { GUID_WICPixelFormat64bppBGRA,             GUID_WICPixelFormat64bppRGBA }, // DXGI_FORMAT_R16G16B16A16_UNORM
+    { GUID_WICPixelFormat64bppPRGBA,            GUID_WICPixelFormat64bppRGBA }, // DXGI_FORMAT_R16G16B16A16_UNORM
+    { GUID_WICPixelFormat64bppPBGRA,            GUID_WICPixelFormat64bppRGBA }, // DXGI_FORMAT_R16G16B16A16_UNORM
+
+    { GUID_WICPixelFormat48bppRGBFixedPoint,    GUID_WICPixelFormat64bppRGBAHalf }, // DXGI_FORMAT_R16G16B16A16_FLOAT 
+    { GUID_WICPixelFormat48bppBGRFixedPoint,    GUID_WICPixelFormat64bppRGBAHalf }, // DXGI_FORMAT_R16G16B16A16_FLOAT 
+    { GUID_WICPixelFormat64bppRGBAFixedPoint,   GUID_WICPixelFormat64bppRGBAHalf }, // DXGI_FORMAT_R16G16B16A16_FLOAT 
+    { GUID_WICPixelFormat64bppBGRAFixedPoint,   GUID_WICPixelFormat64bppRGBAHalf }, // DXGI_FORMAT_R16G16B16A16_FLOAT 
+    { GUID_WICPixelFormat64bppRGBFixedPoint,    GUID_WICPixelFormat64bppRGBAHalf }, // DXGI_FORMAT_R16G16B16A16_FLOAT 
+    { GUID_WICPixelFormat64bppRGBHalf,          GUID_WICPixelFormat64bppRGBAHalf }, // DXGI_FORMAT_R16G16B16A16_FLOAT 
+    { GUID_WICPixelFormat48bppRGBHalf,          GUID_WICPixelFormat64bppRGBAHalf }, // DXGI_FORMAT_R16G16B16A16_FLOAT 
+
+    { GUID_WICPixelFormat128bppPRGBAFloat,      GUID_WICPixelFormat128bppRGBAFloat }, // DXGI_FORMAT_R32G32B32A32_FLOAT 
+    { GUID_WICPixelFormat128bppRGBFloat,        GUID_WICPixelFormat128bppRGBAFloat }, // DXGI_FORMAT_R32G32B32A32_FLOAT 
+    { GUID_WICPixelFormat128bppRGBAFixedPoint,  GUID_WICPixelFormat128bppRGBAFloat }, // DXGI_FORMAT_R32G32B32A32_FLOAT 
+    { GUID_WICPixelFormat128bppRGBFixedPoint,   GUID_WICPixelFormat128bppRGBAFloat }, // DXGI_FORMAT_R32G32B32A32_FLOAT 
+
+    { GUID_WICPixelFormat32bppCMYK,             GUID_WICPixelFormat32bppRGBA }, // DXGI_FORMAT_R8G8B8A8_UNORM 
+    { GUID_WICPixelFormat64bppCMYK,             GUID_WICPixelFormat64bppRGBA }, // DXGI_FORMAT_R16G16B16A16_UNORM
+    { GUID_WICPixelFormat40bppCMYKAlpha,        GUID_WICPixelFormat64bppRGBA }, // DXGI_FORMAT_R16G16B16A16_UNORM
+    { GUID_WICPixelFormat80bppCMYKAlpha,        GUID_WICPixelFormat64bppRGBA }, // DXGI_FORMAT_R16G16B16A16_UNORM
+
+#if (_WIN32_WINNT >= 0x0602 /*_WIN32_WINNT_WIN8*/) || defined(_WIN7_PLATFORM_UPDATE)
+    { GUID_WICPixelFormat32bppRGB,              GUID_WICPixelFormat32bppRGBA }, // DXGI_FORMAT_R8G8B8A8_UNORM
+    { GUID_WICPixelFormat64bppRGB,              GUID_WICPixelFormat64bppRGBA }, // DXGI_FORMAT_R16G16B16A16_UNORM
+    { GUID_WICPixelFormat64bppPRGBAHalf,        GUID_WICPixelFormat64bppRGBAHalf }, // DXGI_FORMAT_R16G16B16A16_FLOAT 
+#endif
+
+    // We don't support n-channel formats
+};
+
+namespace DirectX
+{
+
+//-------------------------------------------------------------------------------------
+// Returns the DXGI format and optionally the WIC pixel GUID to convert to
+//-------------------------------------------------------------------------------------
+static DXGI_FORMAT _DetermineFormat( _In_ const WICPixelFormatGUID& pixelFormat, _In_ DWORD flags,
+                                     _Out_opt_ WICPixelFormatGUID* pConvert )
+{
+    if ( pConvert )
+        memset( pConvert, 0, sizeof(WICPixelFormatGUID) );
+
+    DXGI_FORMAT format = _WICToDXGI( pixelFormat );
+
+    if ( format == DXGI_FORMAT_UNKNOWN )
+    {
+        if ( memcmp( &GUID_WICPixelFormat96bppRGBFixedPoint, &pixelFormat, sizeof(WICPixelFormatGUID) ) == 0 )
+        {
+#if (_WIN32_WINNT >= 0x0602 /*_WIN32_WINNT_WIN8*/) || defined(_WIN7_PLATFORM_UPDATE)
+            if ( _IsWIC2() )
+            {
+                if ( pConvert )
+                    memcpy( pConvert, &GUID_WICPixelFormat96bppRGBFloat, sizeof(WICPixelFormatGUID) );
+                format = DXGI_FORMAT_R32G32B32_FLOAT;
+            }
+            else
+#endif
+            {
+                if ( pConvert )
+                    memcpy( pConvert, &GUID_WICPixelFormat128bppRGBAFloat, sizeof(WICPixelFormatGUID) );
+                format = DXGI_FORMAT_R32G32B32A32_FLOAT;
+            }
+        }
+        else
+        {
+            for( size_t i=0; i < _countof(g_WICConvert); ++i )
+            {
+                if ( memcmp( &g_WICConvert[i].source, &pixelFormat, sizeof(WICPixelFormatGUID) ) == 0 )
+                {
+                    if ( pConvert )
+                        memcpy( pConvert, &g_WICConvert[i].target, sizeof(WICPixelFormatGUID) );
+
+                    format = _WICToDXGI( g_WICConvert[i].target );
+                    assert( format != DXGI_FORMAT_UNKNOWN );
+                    break;
+                }
+            }
+        }
+    }
+
+    // Handle special cases based on flags
+    switch (format)
+    {
+    case DXGI_FORMAT_B8G8R8A8_UNORM:    // BGRA
+    case DXGI_FORMAT_B8G8R8X8_UNORM:    // BGRX
+        if ( flags & WIC_FLAGS_FORCE_RGB )
+        {
+            format = DXGI_FORMAT_R8G8B8A8_UNORM;
+            if ( pConvert )
+                memcpy( pConvert, &GUID_WICPixelFormat32bppRGBA, sizeof(WICPixelFormatGUID) );
+        }
+        break;
+
+    case DXGI_FORMAT_R10G10B10_XR_BIAS_A2_UNORM:
+        if ( flags & WIC_FLAGS_NO_X2_BIAS )
+        {
+            format = DXGI_FORMAT_R10G10B10A2_UNORM;
+            if ( pConvert )
+                memcpy( pConvert, &GUID_WICPixelFormat32bppRGBA1010102, sizeof(WICPixelFormatGUID) );
+        }
+        break;
+
+    case DXGI_FORMAT_B5G5R5A1_UNORM:
+    case DXGI_FORMAT_B5G6R5_UNORM:
+        if ( flags & WIC_FLAGS_NO_16BPP ) 
+        {
+            format = DXGI_FORMAT_R8G8B8A8_UNORM;
+            if ( pConvert )
+                memcpy( pConvert, &GUID_WICPixelFormat32bppRGBA, sizeof(WICPixelFormatGUID) );
+        }
+        break;
+
+    case DXGI_FORMAT_R1_UNORM:
+        if ( !(flags & WIC_FLAGS_ALLOW_MONO ) )
+        {
+            // By default we want to promote a black & white to gresycale since R1 is not a generally supported D3D format
+            format = DXGI_FORMAT_R8_UNORM;
+            if ( pConvert )
+                memcpy( pConvert, &GUID_WICPixelFormat8bppGray, sizeof(WICPixelFormatGUID) );
+        }
+    }
+
+    return format;
+}
+
+
+//-------------------------------------------------------------------------------------
+// Determines metadata for image
+//-------------------------------------------------------------------------------------
+static HRESULT _DecodeMetadata( _In_ DWORD flags,
+                                _In_ IWICBitmapDecoder *decoder, _In_ IWICBitmapFrameDecode *frame,
+                                _Out_ TexMetadata& metadata, _Out_opt_ WICPixelFormatGUID* pConvert )
+{
+    if ( !decoder || !frame )
+        return E_POINTER;
+
+    memset( &metadata, 0, sizeof(TexMetadata) );
+    metadata.depth = 1;
+    metadata.mipLevels = 1;
+    metadata.dimension = TEX_DIMENSION_TEXTURE2D;
+
+    UINT w, h;
+    HRESULT hr = frame->GetSize( &w, &h );
+    if ( FAILED(hr) )
+        return hr;
+
+    metadata.width = w;
+    metadata.height = h;
+
+    if ( flags & WIC_FLAGS_ALL_FRAMES )
+    {
+        UINT fcount;
+        hr = decoder->GetFrameCount( &fcount );
+        if ( FAILED(hr) )
+            return hr;
+
+        metadata.arraySize = fcount;
+    }
+    else
+        metadata.arraySize = 1;
+
+    WICPixelFormatGUID pixelFormat;
+    hr = frame->GetPixelFormat( &pixelFormat );
+    if ( FAILED(hr) )
+        return hr;
+
+    metadata.format = _DetermineFormat( pixelFormat, flags, pConvert );
+    if ( metadata.format == DXGI_FORMAT_UNKNOWN )
+        return HRESULT_FROM_WIN32( ERROR_NOT_SUPPORTED );
+
+    return S_OK;
+}
+
+
+//-------------------------------------------------------------------------------------
+// Decodes a single frame
+//-------------------------------------------------------------------------------------
+static HRESULT _DecodeSingleFrame( _In_ DWORD flags, _In_ const TexMetadata& metadata, _In_ const WICPixelFormatGUID& convertGUID,
+                                   _In_ IWICBitmapFrameDecode *frame, _Inout_ ScratchImage& image )
+{
+    if ( !frame )
+        return E_POINTER;
+
+    HRESULT hr = image.Initialize2D( metadata.format, metadata.width, metadata.height, 1, 1 );
+    if ( FAILED(hr) )
+        return hr;
+
+    const Image *img = image.GetImage( 0, 0, 0 );
+    if ( !img )
+        return E_POINTER;
+
+    IWICImagingFactory* pWIC = _GetWIC();
+    if ( !pWIC )
+        return E_NOINTERFACE;
+
+    if ( memcmp( &convertGUID, &GUID_NULL, sizeof(GUID) ) == 0 )
+    {
+        hr = frame->CopyPixels( 0, static_cast<UINT>( img->rowPitch ), static_cast<UINT>( img->slicePitch ), img->pixels );  
+        if ( FAILED(hr) )
+            return hr;
+    }
+    else
+    {
+        ScopedObject<IWICFormatConverter> FC;
+        hr = pWIC->CreateFormatConverter( &FC );
+        if ( FAILED(hr) )
+            return hr;
+
+        hr = FC->Initialize( frame, convertGUID, _GetWICDither( flags ), 0, 0, WICBitmapPaletteTypeCustom );
+        if ( FAILED(hr) )
+            return hr;
+
+        hr = FC->CopyPixels( 0, static_cast<UINT>( img->rowPitch ), static_cast<UINT>( img->slicePitch ), img->pixels );  
+        if ( FAILED(hr) )
+            return hr;
+    }
+
+    return S_OK;
+}
+
+
+//-------------------------------------------------------------------------------------
+// Decodes an image array, resizing/format converting as needed
+//-------------------------------------------------------------------------------------
+static HRESULT _DecodeMultiframe( _In_ DWORD flags, _In_ const TexMetadata& metadata,
+                                  _In_ IWICBitmapDecoder *decoder, _Inout_ ScratchImage& image )
+{
+    if ( !decoder )
+        return E_POINTER;
+
+    HRESULT hr = image.Initialize2D( metadata.format, metadata.width, metadata.height, metadata.arraySize, 1 );
+    if ( FAILED(hr) )
+        return hr;
+
+    IWICImagingFactory* pWIC = _GetWIC();
+    if ( !pWIC )
+        return E_NOINTERFACE;
+
+    WICPixelFormatGUID sourceGUID;
+    if ( !_DXGIToWIC( metadata.format, sourceGUID ) )
+        return E_FAIL;
+
+    for( size_t index = 0; index < metadata.arraySize; ++index )
+    {
+        const Image* img = image.GetImage( 0, index, 0 );
+        if ( !img )
+            return E_POINTER;
+
+        ScopedObject<IWICBitmapFrameDecode> frame;
+        hr = decoder->GetFrame( static_cast<UINT>( index ), &frame );
+        if ( FAILED(hr) )
+            return hr;
+
+        WICPixelFormatGUID pfGuid;
+        hr = frame->GetPixelFormat( &pfGuid );
+        if ( FAILED(hr) )
+            return hr;
+
+        UINT w, h;
+        hr = frame->GetSize( &w, &h );
+        if ( FAILED(hr) )
+            return hr;
+
+        if ( memcmp( &pfGuid, &sourceGUID, sizeof(WICPixelFormatGUID) ) == 0 )
+        {
+            if ( w == metadata.width && h == metadata.height )
+            {
+                // This frame does not need resized or format converted, just copy...
+                hr = frame->CopyPixels( 0, static_cast<UINT>( img->rowPitch ), static_cast<UINT>( img->slicePitch ), img->pixels );  
+                if ( FAILED(hr) )
+                    return hr;
+            }
+            else
+            {
+                // This frame needs resizing, but not format converted
+                ScopedObject<IWICBitmapScaler> scaler;
+                hr = pWIC->CreateBitmapScaler( &scaler );
+                if ( FAILED(hr) )
+                    return hr;
+
+                hr = scaler->Initialize( frame.Get(), static_cast<UINT>( metadata.width ), static_cast<UINT>( metadata.height ), _GetWICInterp( flags ) );
+                if ( FAILED(hr) )
+                    return hr;
+
+                hr = scaler->CopyPixels( 0, static_cast<UINT>( img->rowPitch ), static_cast<UINT>( img->slicePitch ), img->pixels );
+                if ( FAILED(hr) )
+                    return hr;
+            }
+        }
+        else
+        {
+            // This frame required format conversion
+            ScopedObject<IWICFormatConverter> FC;
+            hr = pWIC->CreateFormatConverter( &FC );
+            if ( FAILED(hr) )
+                return hr;
+
+            hr = FC->Initialize( frame.Get(), pfGuid, _GetWICDither( flags ), 0, 0, WICBitmapPaletteTypeCustom );
+            if ( FAILED(hr) )
+                return hr;
+            
+            if ( w == metadata.width && h == metadata.height )
+            {
+                // This frame is the same size, no need to scale
+                hr = FC->CopyPixels( 0, static_cast<UINT>( img->rowPitch ), static_cast<UINT>( img->slicePitch ), img->pixels );  
+                if ( FAILED(hr) )
+                    return hr;
+            }
+            else
+            {
+                // This frame needs resizing and format converted
+                ScopedObject<IWICBitmapScaler> scaler;
+                hr = pWIC->CreateBitmapScaler( &scaler );
+                if ( FAILED(hr) )
+                    return hr;
+
+                hr = scaler->Initialize( FC.Get(), static_cast<UINT>( metadata.width ), static_cast<UINT>( metadata.height ), _GetWICInterp( flags ) );
+                if ( FAILED(hr) )
+                    return hr;
+
+                hr = scaler->CopyPixels( 0, static_cast<UINT>( img->rowPitch ), static_cast<UINT>( img->slicePitch ), img->pixels );
+                if ( FAILED(hr) )
+                    return hr;
+            }
+        }
+    }
+
+    return S_OK;
+}
+
+
+//-------------------------------------------------------------------------------------
+// Encodes a single frame
+//-------------------------------------------------------------------------------------
+static HRESULT _EncodeImage( _In_ const Image& image, _In_ DWORD flags, _In_ IWICBitmapFrameEncode* frame, _In_opt_ IPropertyBag2* props, _In_opt_ const GUID* targetFormat )
+{
+    if ( !frame )
+        return E_INVALIDARG;
+
+    if ( !image.pixels )
+        return E_POINTER;
+
+    WICPixelFormatGUID pfGuid;
+    if ( !_DXGIToWIC( image.format, pfGuid ) )
+        return HRESULT_FROM_WIN32( ERROR_NOT_SUPPORTED );
+
+    HRESULT hr = frame->Initialize( props );
+    if ( FAILED(hr) )
+        return hr;
+
+#ifdef _AMD64_
+    if ( (image.width > 0xFFFFFFFF) || (image.height > 0xFFFFFFFF) )
+        return E_INVALIDARG;
+#endif
+
+    hr = frame->SetSize( static_cast<UINT>( image.width ), static_cast<UINT>( image.height ) );
+    if ( FAILED(hr) )
+        return hr;
+
+    hr = frame->SetResolution( 72, 72 );
+    if ( FAILED(hr) )
+        return hr;
+
+    WICPixelFormatGUID targetGuid = (targetFormat) ? (*targetFormat) : pfGuid;
+    hr = frame->SetPixelFormat( &targetGuid );
+    if ( FAILED(hr) )
+        return hr;
+
+    if ( memcmp( &targetGuid, &pfGuid, sizeof(WICPixelFormatGUID) ) != 0 )
+    {
+        // Conversion required to write
+        IWICImagingFactory* pWIC = _GetWIC();
+        if ( !pWIC )
+            return E_NOINTERFACE;
+
+        ScopedObject<IWICBitmap> source;
+        hr = pWIC->CreateBitmapFromMemory( static_cast<UINT>( image.width ), static_cast<UINT>( image.height ), pfGuid,
+                                           static_cast<UINT>( image.rowPitch ), static_cast<UINT>( image.slicePitch ),
+                                           image.pixels, &source );
+        if ( FAILED(hr) )
+            return hr;
+
+        ScopedObject<IWICFormatConverter> FC;
+        hr = pWIC->CreateFormatConverter( &FC );
+        if ( FAILED(hr) )
+            return hr;
+
+        hr = FC->Initialize( source.Get(), targetGuid, _GetWICDither( flags ), 0, 0, WICBitmapPaletteTypeCustom );
+        if ( FAILED(hr) )
+            return hr;
+
+        WICRect rect = { 0, 0, static_cast<UINT>( image.width ), static_cast<UINT>( image.height ) };
+        hr = frame->WriteSource( FC.Get(), &rect );
+        if ( FAILED(hr) )
+            return hr;
+    }
+    else
+    {
+        // No conversion required
+        hr = frame->WritePixels( static_cast<UINT>( image.height ), static_cast<UINT>( image.rowPitch ), static_cast<UINT>( image.slicePitch ),
+                                 reinterpret_cast<uint8_t*>( image.pixels ) );
+        if ( FAILED(hr) )
+            return hr;
+    }
+
+    hr = frame->Commit();
+    if ( FAILED(hr) )
+        return hr;
+
+    return S_OK;
+}
+
+static HRESULT _EncodeSingleFrame( _In_ const Image& image, _In_ DWORD flags,
+                                   _In_ REFGUID guidContainerFormat, _Inout_ IStream* stream, _In_opt_ const GUID* targetFormat )
+{
+    if ( !stream )
+        return E_INVALIDARG;
+
+    // Initialize WIC
+    IWICImagingFactory* pWIC = _GetWIC();
+    if ( !pWIC )
+        return E_NOINTERFACE;
+
+    ScopedObject<IWICBitmapEncoder> encoder;
+    HRESULT hr = pWIC->CreateEncoder( guidContainerFormat, 0, &encoder );
+    if ( FAILED(hr) )
+        return hr;
+
+    hr = encoder->Initialize( stream, WICBitmapEncoderNoCache );
+    if ( FAILED(hr) )
+        return hr;
+
+    ScopedObject<IWICBitmapFrameEncode> frame;
+    ScopedObject<IPropertyBag2> props;
+    hr = encoder->CreateNewFrame( &frame, &props );
+    if ( FAILED(hr) )
+        return hr;
+
+    if ( memcmp( &guidContainerFormat, &GUID_ContainerFormatBmp, sizeof(WICPixelFormatGUID) ) == 0  )
+    {
+        // Opt-in to the Windows 8 support for writing 32-bit Windows BMP files with an alpha channel if supported
+        PROPBAG2 option = { 0 };
+        option.pstrName = L"EnableV5Header32bppBGRA";
+
+        VARIANT varValue;    
+        varValue.vt = VT_BOOL;
+        varValue.boolVal = VARIANT_TRUE;      
+        hr = props->Write( 1, &option, &varValue ); 
+        if ( FAILED(hr) )
+        {
+            // Fails on older versions of WIC, so we default to the null property bag
+            props.Reset();
+        }
+    }
+
+    hr = _EncodeImage( image, flags, frame.Get(), props.Get(), targetFormat );
+    if ( FAILED(hr) )
+        return hr;
+
+    hr = encoder->Commit();
+    if ( FAILED(hr) )
+        return hr;
+
+    return S_OK;
+}
+
+
+//-------------------------------------------------------------------------------------
+// Encodes an image array
+//-------------------------------------------------------------------------------------
+static HRESULT _EncodeMultiframe( _In_count_(nimages) const Image* images, _In_ size_t nimages, _In_ DWORD flags,
+                                  _In_ REFGUID guidContainerFormat, _Inout_ IStream* stream, _In_opt_ const GUID* targetFormat )
+{
+    if ( !stream || nimages < 2 )
+        return E_INVALIDARG;
+
+    if ( !images )
+        return E_POINTER;
+
+    // Initialize WIC
+    IWICImagingFactory* pWIC = _GetWIC();
+    if ( !pWIC )
+        return E_NOINTERFACE;
+
+    ScopedObject<IWICBitmapEncoder> encoder;
+    HRESULT hr = pWIC->CreateEncoder( guidContainerFormat, 0, &encoder );
+    if ( FAILED(hr) )
+        return hr;
+
+    ScopedObject<IWICBitmapEncoderInfo> einfo;
+    hr = encoder->GetEncoderInfo( &einfo );
+    if ( FAILED(hr) )
+        return hr;
+
+    BOOL mframe = FALSE;
+    hr = einfo->DoesSupportMultiframe( &mframe );
+    if ( FAILED(hr) )
+        return hr;
+
+    if ( !mframe )
+        return HRESULT_FROM_WIN32( ERROR_NOT_SUPPORTED );
+
+    hr = encoder->Initialize( stream, WICBitmapEncoderNoCache );
+    if ( FAILED(hr) )
+        return hr;
+
+    for( size_t index=0; index < nimages; ++index )
+    {
+        ScopedObject<IWICBitmapFrameEncode> frame;
+        hr = encoder->CreateNewFrame( &frame, nullptr );
+        if ( FAILED(hr) )
+            return hr;
+
+        hr = _EncodeImage( images[index], flags, frame.Get(), nullptr, targetFormat );
+        if ( FAILED(hr) )
+            return hr;
+    }
+
+    hr = encoder->Commit();
+    if ( FAILED(hr) )
+        return hr;
+
+    return S_OK;
+}
+
+
+//=====================================================================================
+// Entry-points
+//=====================================================================================
+
+//-------------------------------------------------------------------------------------
+// Obtain metadata from WIC-supported file in memory
+//-------------------------------------------------------------------------------------
+HRESULT GetMetadataFromWICMemory( LPCVOID pSource, size_t size, DWORD flags, TexMetadata& metadata )
+{
+    if ( !pSource || size == 0 )
+        return E_INVALIDARG;
+
+#ifdef _AMD64_
+    if ( size > 0xFFFFFFFF )
+        return HRESULT_FROM_WIN32( ERROR_FILE_TOO_LARGE );
+#endif
+
+    IWICImagingFactory* pWIC = _GetWIC();
+    if ( !pWIC )
+        return E_NOINTERFACE;
+
+    // Create input stream for memory
+    ScopedObject<IWICStream> stream;
+    HRESULT hr = pWIC->CreateStream( &stream );
+    if ( FAILED(hr) )
+        return hr;
+
+    hr = stream->InitializeFromMemory( reinterpret_cast<BYTE*>( const_cast<void*>( pSource ) ),
+                                       static_cast<UINT>( size ) );
+    if ( FAILED(hr) )
+        return hr;
+
+    // Initialize WIC
+    ScopedObject<IWICBitmapDecoder> decoder;
+    hr = pWIC->CreateDecoderFromStream( stream.Get(), 0, WICDecodeMetadataCacheOnDemand, &decoder );
+    if ( FAILED(hr) )
+        return hr;
+
+    ScopedObject<IWICBitmapFrameDecode> frame;
+    hr = decoder->GetFrame( 0, &frame );
+    if ( FAILED(hr) )
+        return hr;
+
+    // Get metadata
+    hr = _DecodeMetadata( flags, decoder.Get(), frame.Get(), metadata, 0 );
+    if ( FAILED(hr) )
+        return hr;
+
+    return S_OK;
+}
+
+
+//-------------------------------------------------------------------------------------
+// Obtain metadata from WIC-supported file on disk
+//-------------------------------------------------------------------------------------
+HRESULT GetMetadataFromWICFile( LPCWSTR szFile, DWORD flags, TexMetadata& metadata )
+{
+    if ( !szFile )
+        return E_INVALIDARG;
+
+    IWICImagingFactory* pWIC = _GetWIC();
+    if ( !pWIC )
+        return E_NOINTERFACE;
+    
+    // Initialize WIC
+    ScopedObject<IWICBitmapDecoder> decoder;
+    HRESULT hr = pWIC->CreateDecoderFromFilename( szFile, 0, GENERIC_READ, WICDecodeMetadataCacheOnDemand, &decoder );
+    if ( FAILED(hr) )
+        return hr;
+
+    ScopedObject<IWICBitmapFrameDecode> frame;
+    hr = decoder->GetFrame( 0, &frame );
+    if ( FAILED(hr) )
+        return hr;
+
+    // Get metadata
+    hr = _DecodeMetadata( flags, decoder.Get(), frame.Get(), metadata, 0 );
+    if ( FAILED(hr) )
+        return hr;
+
+    return S_OK;
+}
+
+
+//-------------------------------------------------------------------------------------
+// Load a WIC-supported file in memory
+//-------------------------------------------------------------------------------------
+HRESULT LoadFromWICMemory( LPCVOID pSource, size_t size, DWORD flags, TexMetadata* metadata, ScratchImage& image )
+{
+    if ( !pSource || size == 0 )
+        return E_INVALIDARG;
+
+#ifdef _AMD64_
+    if ( size > 0xFFFFFFFF )
+        return HRESULT_FROM_WIN32( ERROR_FILE_TOO_LARGE );
+#endif
+
+    IWICImagingFactory* pWIC = _GetWIC();
+    if ( !pWIC )
+        return E_NOINTERFACE;
+
+    image.Release();
+
+    // Create input stream for memory
+    ScopedObject<IWICStream> stream;
+    HRESULT hr = pWIC->CreateStream( &stream );
+    if ( FAILED(hr) )
+        return hr;
+
+    hr = stream->InitializeFromMemory( reinterpret_cast<uint8_t*>( const_cast<void*>( pSource ) ), static_cast<DWORD>( size ) );
+    if ( FAILED(hr) )
+        return hr;
+
+    // Initialize WIC
+    ScopedObject<IWICBitmapDecoder> decoder;
+    hr = pWIC->CreateDecoderFromStream( stream.Get(), 0, WICDecodeMetadataCacheOnDemand, &decoder );
+    if ( FAILED(hr) )
+        return hr;
+
+    ScopedObject<IWICBitmapFrameDecode> frame;
+    hr = decoder->GetFrame( 0, &frame );
+    if ( FAILED(hr) )
+        return hr;
+
+    // Get metadata
+    TexMetadata mdata;
+    WICPixelFormatGUID convertGUID = {0};
+    hr = _DecodeMetadata( flags, decoder.Get(), frame.Get(), mdata, &convertGUID );
+    if ( FAILED(hr) )
+        return hr;
+
+    if ( (mdata.arraySize > 1) && (flags & WIC_FLAGS_ALL_FRAMES) )
+    {
+        hr = _DecodeMultiframe( flags, mdata, decoder.Get(), image );
+    }
+    else
+    {
+        hr = _DecodeSingleFrame( flags, mdata, convertGUID, frame.Get(), image );
+    }
+
+    if ( FAILED(hr) )
+    {
+        image.Release();
+        return hr;
+    }
+
+    if ( metadata )
+        memcpy( metadata, &mdata, sizeof(TexMetadata) );
+
+    return S_OK;
+}
+
+
+//-------------------------------------------------------------------------------------
+// Load a WIC-supported file from disk
+//-------------------------------------------------------------------------------------
+HRESULT LoadFromWICFile( LPCWSTR szFile, DWORD flags, TexMetadata* metadata, ScratchImage& image )
+{
+    if ( !szFile )
+        return E_INVALIDARG;
+
+    IWICImagingFactory* pWIC = _GetWIC();
+    if ( !pWIC )
+        return E_NOINTERFACE;
+    
+    image.Release();
+
+    // Initialize WIC
+    ScopedObject<IWICBitmapDecoder> decoder;
+    HRESULT hr = pWIC->CreateDecoderFromFilename( szFile, 0, GENERIC_READ, WICDecodeMetadataCacheOnDemand, &decoder );
+    if ( FAILED(hr) )
+        return hr;
+
+    ScopedObject<IWICBitmapFrameDecode> frame;
+    hr = decoder->GetFrame( 0, &frame );
+    if ( FAILED(hr) )
+        return hr;
+
+    // Get metadata
+    TexMetadata mdata;
+    WICPixelFormatGUID convertGUID = {0};
+    hr = _DecodeMetadata( flags, decoder.Get(), frame.Get(), mdata, &convertGUID );
+    if ( FAILED(hr) )
+        return hr;
+
+    if ( (mdata.arraySize > 1) && (flags & WIC_FLAGS_ALL_FRAMES) )
+    {
+        hr = _DecodeMultiframe( flags, mdata, decoder.Get(), image );
+    }
+    else
+    {
+        hr = _DecodeSingleFrame( flags, mdata, convertGUID, frame.Get(), image );
+    }
+
+    if ( FAILED(hr) )
+    {
+        image.Release();
+        return hr;
+    }
+
+    if ( metadata )
+        memcpy( metadata, &mdata, sizeof(TexMetadata) );
+
+    return S_OK;
+}
+
+
+//-------------------------------------------------------------------------------------
+// Save a WIC-supported file to memory
+//-------------------------------------------------------------------------------------
+HRESULT SaveToWICMemory( const Image& image, DWORD flags, REFGUID guidContainerFormat, Blob& blob, const GUID* targetFormat )
+{
+    if ( !image.pixels )
+        return E_POINTER;
+
+    blob.Release();
+
+    ScopedObject<IStream> stream;
+    HRESULT hr = CreateStreamOnHGlobal( 0, TRUE, &stream );
+    if ( FAILED(hr) )
+        return hr;
+
+    hr = _EncodeSingleFrame( image, flags, guidContainerFormat, stream.Get(), targetFormat );
+    if ( FAILED(hr) )
+        return hr;
+
+    // Copy stream data into blob
+    STATSTG stat;
+    hr = stream->Stat( &stat, STATFLAG_NONAME );
+    if ( FAILED(hr) )
+        return hr;
+
+    if ( stat.cbSize.HighPart > 0 )
+        return HRESULT_FROM_WIN32( ERROR_FILE_TOO_LARGE );
+
+    hr = blob.Initialize( stat.cbSize.LowPart );
+    if ( FAILED(hr) )
+        return hr;
+
+    LARGE_INTEGER li = { 0 };
+    hr = stream->Seek( li, STREAM_SEEK_SET, 0 );
+    if ( FAILED(hr) )
+        return hr;
+
+    DWORD bytesRead;
+    hr = stream->Read( blob.GetBufferPointer(), static_cast<ULONG>( blob.GetBufferSize() ), &bytesRead );
+    if ( FAILED(hr) )
+        return hr;
+
+    if ( bytesRead != blob.GetBufferSize() )
+        return E_FAIL;
+
+    return S_OK;
+}
+
+HRESULT SaveToWICMemory( const Image* images, size_t nimages, DWORD flags, REFGUID guidContainerFormat, Blob& blob, const GUID* targetFormat )
+{
+    if ( !images || nimages == 0 )
+        return E_INVALIDARG;
+
+    blob.Release();
+
+    ScopedObject<IStream> stream;
+    HRESULT hr = CreateStreamOnHGlobal( 0, TRUE, &stream );
+    if ( FAILED(hr) )
+        return hr;
+
+    if ( nimages > 1 )
+        hr = _EncodeMultiframe( images, nimages, flags, guidContainerFormat, stream.Get(), targetFormat );
+    else
+        hr = _EncodeSingleFrame( images[0], flags, guidContainerFormat, stream.Get(), targetFormat );
+
+    if ( FAILED(hr) )
+        return hr;
+
+    // Copy stream data into blob
+    STATSTG stat;
+    hr = stream->Stat( &stat, STATFLAG_NONAME );
+    if ( FAILED(hr) )
+        return hr;
+
+    if ( stat.cbSize.HighPart > 0 )
+        return HRESULT_FROM_WIN32( ERROR_FILE_TOO_LARGE );
+
+    hr = blob.Initialize( stat.cbSize.LowPart );
+    if ( FAILED(hr) )
+        return hr;
+
+    LARGE_INTEGER li = { 0 };
+    hr = stream->Seek( li, STREAM_SEEK_SET, 0 );
+    if ( FAILED(hr) )
+        return hr;
+
+    DWORD bytesRead;
+    hr = stream->Read( blob.GetBufferPointer(), static_cast<ULONG>( blob.GetBufferSize() ), &bytesRead );
+    if ( FAILED(hr) )
+        return hr;
+
+    if ( bytesRead != blob.GetBufferSize() )
+        return E_FAIL;
+
+    return S_OK;
+}
+
+
+//-------------------------------------------------------------------------------------
+// Save a WIC-supported file to disk
+//-------------------------------------------------------------------------------------
+HRESULT SaveToWICFile( const Image& image, DWORD flags, REFGUID guidContainerFormat, LPCWSTR szFile, const GUID* targetFormat )
+{
+    if ( !szFile )
+        return E_INVALIDARG;
+
+    if ( !image.pixels )
+        return E_POINTER;
+
+    IWICImagingFactory* pWIC = _GetWIC();
+    if ( !pWIC )
+        return E_NOINTERFACE;
+
+    ScopedObject<IWICStream> stream;
+    HRESULT hr = pWIC->CreateStream( &stream );
+    if ( FAILED(hr) )
+        return hr;
+
+    hr = stream->InitializeFromFilename( szFile, GENERIC_WRITE );
+    if ( FAILED(hr) )
+        return hr;
+
+    hr = _EncodeSingleFrame( image, flags, guidContainerFormat, stream.Get(), targetFormat );
+    if ( FAILED(hr) )
+        return hr;
+
+    return S_OK;
+}
+
+HRESULT SaveToWICFile( const Image* images, size_t nimages, DWORD flags, REFGUID guidContainerFormat, LPCWSTR szFile, const GUID* targetFormat )
+{
+    if ( !szFile || !images || nimages == 0 )
+        return E_INVALIDARG;
+
+    IWICImagingFactory* pWIC = _GetWIC();
+    if ( !pWIC )
+        return E_NOINTERFACE;
+
+    ScopedObject<IWICStream> stream;
+    HRESULT hr = pWIC->CreateStream( &stream );
+    if ( FAILED(hr) )
+        return hr;
+
+    hr = stream->InitializeFromFilename( szFile, GENERIC_WRITE );
+    if ( FAILED(hr) )
+        return hr;
+
+    if ( nimages > 1 )
+        hr = _EncodeMultiframe( images, nimages, flags, guidContainerFormat, stream.Get(), targetFormat );
+    else
+        hr = _EncodeSingleFrame( images[0], flags, guidContainerFormat, stream.Get(), targetFormat );
+
+    if ( FAILED(hr) )
+        return hr;
+
+    return S_OK;
+}
+
+}; // namespace