From 3474bcbab91a9388626a31dbe1ec818991a28ac7 Mon Sep 17 00:00:00 2001 From: walbourn_cp Date: Mon, 10 Jun 2013 15:34:13 -0700 Subject: [PATCH] DirectXTex: WIC metadata usage - Check/writes the sRGB information for DXGI_FORMAT_*_SRGB formats - Updated WICTextureLoader & ScreenGrab --- DirectXTex/DirectXTexWIC.cpp | 139 ++++++++++++++++++++++---- ScreenGrab/ScreenGrab.cpp | 57 ++++++++++- WICTextureLoader/WICTextureLoader.cpp | 42 +++++++- 3 files changed, 215 insertions(+), 23 deletions(-) diff --git a/DirectXTex/DirectXTexWIC.cpp b/DirectXTex/DirectXTexWIC.cpp index bc2516f..a0ed1e8 100644 --- a/DirectXTex/DirectXTexWIC.cpp +++ b/DirectXTex/DirectXTexWIC.cpp @@ -223,7 +223,46 @@ static HRESULT _DecodeMetadata( _In_ DWORD flags, if ( metadata.format == DXGI_FORMAT_UNKNOWN ) return HRESULT_FROM_WIN32( ERROR_NOT_SUPPORTED ); - return S_OK; + GUID containerFormat; + hr = decoder->GetContainerFormat( &containerFormat ); + if ( FAILED(hr) ) + return hr; + + ScopedObject metareader; + hr = frame->GetMetadataQueryReader( &metareader ); + if ( SUCCEEDED(hr) ) + { + // Check for sRGB colorspace metadata + bool sRGB = false; + + PROPVARIANT value; + PropVariantInit( &value ); + + if ( memcmp( &containerFormat, &GUID_ContainerFormatPng, sizeof(GUID) ) == 0 ) + { + // Check for sRGB chunk + if ( SUCCEEDED( metareader->GetMetadataByName( L"/sRGB/RenderingIntent", &value ) ) && value.vt == VT_UI1 ) + { + sRGB = true; + } + } + else if ( SUCCEEDED( metareader->GetMetadataByName( L"System.Image.ColorSpace", &value ) ) && value.vt == VT_UI2 && value.uiVal == 1 ) + { + sRGB = true; + } + + PropVariantClear( &value ); + + if ( sRGB ) + metadata.format = MakeSRGB( metadata.format ); + } + else if ( hr == WINCODEC_ERR_UNSUPPORTEDOPERATION ) + { + // Some formats just don't support metadata (BMP, ICO, etc.), so ignore this failure + hr = S_OK; + } + + return hr; } @@ -384,10 +423,68 @@ static HRESULT _DecodeMultiframe( _In_ DWORD flags, _In_ const TexMetadata& meta } +//------------------------------------------------------------------------------------- +// Encodes image metadata +//------------------------------------------------------------------------------------- +static HRESULT _EncodeMetadata( _In_ IWICBitmapFrameEncode* frame, _In_ const GUID& containerFormat, _In_ DXGI_FORMAT format ) +{ + if ( !frame ) + return E_POINTER; + + ScopedObject metawriter; + HRESULT hr = frame->GetMetadataQueryWriter( &metawriter ); + if ( SUCCEEDED( hr ) ) + { + PROPVARIANT value; + PropVariantInit( &value ); + + bool sRGB = IsSRGB( format ); + + value.vt = VT_LPSTR; + value.pszVal = "DirectXTex"; + + if ( memcmp( &containerFormat, &GUID_ContainerFormatPng, sizeof(GUID) ) == 0 ) + { + // Set Software name + (void)metawriter->SetMetadataByName( L"/tEXt/{str=Software}", &value ); + + // Set sRGB chunk + if ( sRGB ) + { + value.vt = VT_UI1; + value.bVal = 0; + (void)metawriter->SetMetadataByName( L"/sRGB/RenderingIntent", &value ); + } + } + else + { + // Set Software name + (void)metawriter->SetMetadataByName( L"System.ApplicationName", &value ); + + if ( sRGB ) + { + // Set JPEG EXIF Colorspace of sRGB + value.vt = VT_UI2; + value.uiVal = 1; + (void)metawriter->SetMetadataByName( L"System.Image.ColorSpace", &value ); + } + } + } + else if ( hr == WINCODEC_ERR_UNSUPPORTEDOPERATION ) + { + // Some formats just don't support metadata (BMP, ICO, etc.), so ignore this failure + hr = S_OK; + } + + return hr; +} + + //------------------------------------------------------------------------------------- // 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 ) +static HRESULT _EncodeImage( _In_ const Image& image, _In_ DWORD flags, _In_ REFGUID containerFormat, + _In_ IWICBitmapFrameEncode* frame, _In_opt_ IPropertyBag2* props, _In_opt_ const GUID* targetFormat ) { if ( !frame ) return E_INVALIDARG; @@ -427,6 +524,10 @@ static HRESULT _EncodeImage( _In_ const Image& image, _In_ DWORD flags, _In_ IWI return E_FAIL; } + hr = _EncodeMetadata( frame, containerFormat, image.format ); + if ( FAILED(hr) ) + return hr; + if ( memcmp( &targetGuid, &pfGuid, sizeof(WICPixelFormatGUID) ) != 0 ) { // Conversion required to write @@ -472,7 +573,7 @@ static HRESULT _EncodeImage( _In_ const Image& image, _In_ DWORD flags, _In_ IWI } static HRESULT _EncodeSingleFrame( _In_ const Image& image, _In_ DWORD flags, - _In_ REFGUID guidContainerFormat, _Inout_ IStream* stream, _In_opt_ const GUID* targetFormat ) + _In_ REFGUID containerFormat, _Inout_ IStream* stream, _In_opt_ const GUID* targetFormat ) { if ( !stream ) return E_INVALIDARG; @@ -483,7 +584,7 @@ static HRESULT _EncodeSingleFrame( _In_ const Image& image, _In_ DWORD flags, return E_NOINTERFACE; ScopedObject encoder; - HRESULT hr = pWIC->CreateEncoder( guidContainerFormat, 0, &encoder ); + HRESULT hr = pWIC->CreateEncoder( containerFormat, 0, &encoder ); if ( FAILED(hr) ) return hr; @@ -497,7 +598,7 @@ static HRESULT _EncodeSingleFrame( _In_ const Image& image, _In_ DWORD flags, if ( FAILED(hr) ) return hr; - if ( memcmp( &guidContainerFormat, &GUID_ContainerFormatBmp, sizeof(WICPixelFormatGUID) ) == 0 ) + if ( memcmp( &containerFormat, &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 }; @@ -514,7 +615,7 @@ static HRESULT _EncodeSingleFrame( _In_ const Image& image, _In_ DWORD flags, } } - hr = _EncodeImage( image, flags, frame.Get(), props.Get(), targetFormat ); + hr = _EncodeImage( image, flags, containerFormat, frame.Get(), props.Get(), targetFormat ); if ( FAILED(hr) ) return hr; @@ -530,7 +631,7 @@ static HRESULT _EncodeSingleFrame( _In_ const Image& image, _In_ DWORD flags, // Encodes an image array //------------------------------------------------------------------------------------- static HRESULT _EncodeMultiframe( _In_reads_(nimages) const Image* images, _In_ size_t nimages, _In_ DWORD flags, - _In_ REFGUID guidContainerFormat, _Inout_ IStream* stream, _In_opt_ const GUID* targetFormat ) + _In_ REFGUID containerFormat, _Inout_ IStream* stream, _In_opt_ const GUID* targetFormat ) { if ( !stream || nimages < 2 ) return E_INVALIDARG; @@ -544,7 +645,7 @@ static HRESULT _EncodeMultiframe( _In_reads_(nimages) const Image* images, _In_ return E_NOINTERFACE; ScopedObject encoder; - HRESULT hr = pWIC->CreateEncoder( guidContainerFormat, 0, &encoder ); + HRESULT hr = pWIC->CreateEncoder( containerFormat, 0, &encoder ); if ( FAILED(hr) ) return hr; @@ -572,7 +673,7 @@ static HRESULT _EncodeMultiframe( _In_reads_(nimages) const Image* images, _In_ if ( FAILED(hr) ) return hr; - hr = _EncodeImage( images[index], flags, frame.Get(), nullptr, targetFormat ); + hr = _EncodeImage( images[index], flags, containerFormat, frame.Get(), nullptr, targetFormat ); if ( FAILED(hr) ) return hr; } @@ -800,7 +901,7 @@ HRESULT LoadFromWICFile( LPCWSTR szFile, DWORD flags, TexMetadata* metadata, Scr // Save a WIC-supported file to memory //------------------------------------------------------------------------------------- _Use_decl_annotations_ -HRESULT SaveToWICMemory( const Image& image, DWORD flags, REFGUID guidContainerFormat, Blob& blob, const GUID* targetFormat ) +HRESULT SaveToWICMemory( const Image& image, DWORD flags, REFGUID containerFormat, Blob& blob, const GUID* targetFormat ) { if ( !image.pixels ) return E_POINTER; @@ -812,7 +913,7 @@ HRESULT SaveToWICMemory( const Image& image, DWORD flags, REFGUID guidContainerF if ( FAILED(hr) ) return hr; - hr = _EncodeSingleFrame( image, flags, guidContainerFormat, stream.Get(), targetFormat ); + hr = _EncodeSingleFrame( image, flags, containerFormat, stream.Get(), targetFormat ); if ( FAILED(hr) ) return hr; @@ -846,7 +947,7 @@ HRESULT SaveToWICMemory( const Image& image, DWORD flags, REFGUID guidContainerF } _Use_decl_annotations_ -HRESULT SaveToWICMemory( const Image* images, size_t nimages, DWORD flags, REFGUID guidContainerFormat, Blob& blob, const GUID* targetFormat ) +HRESULT SaveToWICMemory( const Image* images, size_t nimages, DWORD flags, REFGUID containerFormat, Blob& blob, const GUID* targetFormat ) { if ( !images || nimages == 0 ) return E_INVALIDARG; @@ -859,9 +960,9 @@ HRESULT SaveToWICMemory( const Image* images, size_t nimages, DWORD flags, REFGU return hr; if ( nimages > 1 ) - hr = _EncodeMultiframe( images, nimages, flags, guidContainerFormat, stream.Get(), targetFormat ); + hr = _EncodeMultiframe( images, nimages, flags, containerFormat, stream.Get(), targetFormat ); else - hr = _EncodeSingleFrame( images[0], flags, guidContainerFormat, stream.Get(), targetFormat ); + hr = _EncodeSingleFrame( images[0], flags, containerFormat, stream.Get(), targetFormat ); if ( FAILED(hr) ) return hr; @@ -900,7 +1001,7 @@ HRESULT SaveToWICMemory( const Image* images, size_t nimages, DWORD flags, REFGU // Save a WIC-supported file to disk //------------------------------------------------------------------------------------- _Use_decl_annotations_ -HRESULT SaveToWICFile( const Image& image, DWORD flags, REFGUID guidContainerFormat, LPCWSTR szFile, const GUID* targetFormat ) +HRESULT SaveToWICFile( const Image& image, DWORD flags, REFGUID containerFormat, LPCWSTR szFile, const GUID* targetFormat ) { if ( !szFile ) return E_INVALIDARG; @@ -921,7 +1022,7 @@ HRESULT SaveToWICFile( const Image& image, DWORD flags, REFGUID guidContainerFor if ( FAILED(hr) ) return hr; - hr = _EncodeSingleFrame( image, flags, guidContainerFormat, stream.Get(), targetFormat ); + hr = _EncodeSingleFrame( image, flags, containerFormat, stream.Get(), targetFormat ); if ( FAILED(hr) ) return hr; @@ -929,7 +1030,7 @@ HRESULT SaveToWICFile( const Image& image, DWORD flags, REFGUID guidContainerFor } _Use_decl_annotations_ -HRESULT SaveToWICFile( const Image* images, size_t nimages, DWORD flags, REFGUID guidContainerFormat, LPCWSTR szFile, const GUID* targetFormat ) +HRESULT SaveToWICFile( const Image* images, size_t nimages, DWORD flags, REFGUID containerFormat, LPCWSTR szFile, const GUID* targetFormat ) { if ( !szFile || !images || nimages == 0 ) return E_INVALIDARG; @@ -948,9 +1049,9 @@ HRESULT SaveToWICFile( const Image* images, size_t nimages, DWORD flags, REFGUID return hr; if ( nimages > 1 ) - hr = _EncodeMultiframe( images, nimages, flags, guidContainerFormat, stream.Get(), targetFormat ); + hr = _EncodeMultiframe( images, nimages, flags, containerFormat, stream.Get(), targetFormat ); else - hr = _EncodeSingleFrame( images[0], flags, guidContainerFormat, stream.Get(), targetFormat ); + hr = _EncodeSingleFrame( images[0], flags, containerFormat, stream.Get(), targetFormat ); if ( FAILED(hr) ) return hr; diff --git a/ScreenGrab/ScreenGrab.cpp b/ScreenGrab/ScreenGrab.cpp index dde8bc3..06c39e0 100644 --- a/ScreenGrab/ScreenGrab.cpp +++ b/ScreenGrab/ScreenGrab.cpp @@ -893,6 +893,7 @@ HRESULT DirectX::SaveWICTextureToFile( _In_ ID3D11DeviceContext* pContext, // Determine source format's WIC equivalent WICPixelFormatGUID pfGuid; + bool sRGB = false; switch ( desc.Format ) { case DXGI_FORMAT_R32G32B32A32_FLOAT: pfGuid = GUID_WICPixelFormat128bppRGBAFloat; break; @@ -909,20 +910,32 @@ HRESULT DirectX::SaveWICTextureToFile( _In_ ID3D11DeviceContext* pContext, case DXGI_FORMAT_A8_UNORM: pfGuid = GUID_WICPixelFormat8bppAlpha; break; case DXGI_FORMAT_R8G8B8A8_UNORM: - case DXGI_FORMAT_R8G8B8A8_UNORM_SRGB: pfGuid = GUID_WICPixelFormat32bppRGBA; break; + case DXGI_FORMAT_R8G8B8A8_UNORM_SRGB: + pfGuid = GUID_WICPixelFormat32bppRGBA; + sRGB = true; + break; + case DXGI_FORMAT_B8G8R8A8_UNORM: // DXGI 1.1 - case DXGI_FORMAT_B8G8R8A8_UNORM_SRGB: pfGuid = GUID_WICPixelFormat32bppBGRA; break; + case DXGI_FORMAT_B8G8R8A8_UNORM_SRGB: // DXGI 1.1 + pfGuid = GUID_WICPixelFormat32bppBGRA; + sRGB = true; + break; + case DXGI_FORMAT_B8G8R8X8_UNORM: // DXGI 1.1 - case DXGI_FORMAT_B8G8R8X8_UNORM_SRGB: pfGuid = GUID_WICPixelFormat32bppBGR; break; + case DXGI_FORMAT_B8G8R8X8_UNORM_SRGB: // DXGI 1.1 + pfGuid = GUID_WICPixelFormat32bppBGR; + sRGB = true; + break; + default: return HRESULT_FROM_WIN32( ERROR_NOT_SUPPORTED ); } @@ -1037,6 +1050,44 @@ HRESULT DirectX::SaveWICTextureToFile( _In_ ID3D11DeviceContext* pContext, return E_FAIL; } + // Encode WIC metadata + ScopedObject metawriter; + if ( SUCCEEDED( frame->GetMetadataQueryWriter( &metawriter ) ) ) + { + PROPVARIANT value; + PropVariantInit( &value ); + + value.vt = VT_LPSTR; + value.pszVal = "DirectXTK"; + + if ( memcmp( &guidContainerFormat, &GUID_ContainerFormatPng, sizeof(GUID) ) == 0 ) + { + // Set Software name + (void)metawriter->SetMetadataByName( L"/tEXt/{str=Software}", &value ); + + // Set sRGB chunk + if ( sRGB ) + { + value.vt = VT_UI1; + value.bVal = 0; + (void)metawriter->SetMetadataByName( L"/sRGB/RenderingIntent", &value ); + } + } + else + { + // Set Software name + (void)metawriter->SetMetadataByName( L"System.ApplicationName", &value ); + + if ( sRGB ) + { + // Set JPEG EXIF Colorspace of sRGB + value.vt = VT_UI2; + value.uiVal = 1; + (void)metawriter->SetMetadataByName( L"System.Image.ColorSpace", &value ); + } + } + } + D3D11_MAPPED_SUBRESOURCE mapped; hr = pContext->Map( pStaging.Get(), 0, D3D11_MAP_READ, 0, &mapped ); if ( FAILED(hr) ) diff --git a/WICTextureLoader/WICTextureLoader.cpp b/WICTextureLoader/WICTextureLoader.cpp index c5309cf..fd01db0 100644 --- a/WICTextureLoader/WICTextureLoader.cpp +++ b/WICTextureLoader/WICTextureLoader.cpp @@ -532,6 +532,46 @@ static HRESULT CreateTextureFromWIC( _In_ ID3D11Device* d3dDevice, if ( !bpp ) return E_FAIL; + // Handle sRGB formats + if ( forceSRGB ) + { + format = MakeSRGB( format ); + } + else + { + ScopedObject metareader; + if ( SUCCEEDED( frame->GetMetadataQueryReader( &metareader ) ) ) + { + GUID containerFormat; + if ( SUCCEEDED( metareader->GetContainerFormat( &containerFormat ) ) ) + { + // Check for sRGB colorspace metadata + bool sRGB = false; + + PROPVARIANT value; + PropVariantInit( &value ); + + if ( memcmp( &containerFormat, &GUID_ContainerFormatPng, sizeof(GUID) ) == 0 ) + { + // Check for sRGB chunk + if ( SUCCEEDED( metareader->GetMetadataByName( L"/sRGB/RenderingIntent", &value ) ) && value.vt == VT_UI1 ) + { + sRGB = true; + } + } + else if ( SUCCEEDED( metareader->GetMetadataByName( L"System.Image.ColorSpace", &value ) ) && value.vt == VT_UI2 && value.uiVal == 1 ) + { + sRGB = true; + } + + PropVariantClear( &value ); + + if ( sRGB ) + format = MakeSRGB( format ); + } + } + } + // Verify our target format is supported by the current device // (handles WDDM 1.0 or WDDM 1.1 device driver cases as well as DirectX 11.0 Runtime without 16bpp format support) UINT support = 0; @@ -645,7 +685,7 @@ static HRESULT CreateTextureFromWIC( _In_ ID3D11Device* d3dDevice, desc.Height = theight; desc.MipLevels = (autogen) ? 0 : 1; desc.ArraySize = 1; - desc.Format = (forceSRGB) ? MakeSRGB( format ) : format; + desc.Format = format; desc.SampleDesc.Count = 1; desc.SampleDesc.Quality = 0; desc.Usage = usage;