DirectXTex: Resolve SRGB handling problems with conversions

- Convert: TEX_FILTER_FORCE_NON_WIC, TEX_FILTER_FORCE_WIC, TEX_FILTER_RGB_COPY_RED/BLUE/GREEN
 - ComputeMSE flags for sRGB colorspace and channel ignore options
This commit is contained in:
walbourn_cp 2013-04-16 17:51:36 -07:00
parent 33db361592
commit c398ac1711
6 changed files with 374 additions and 234 deletions

View File

@ -177,7 +177,7 @@ namespace DirectX
// Loads 565, 5551, and 4444 formats as 8888 to avoid use of optional WDDM 1.2 formats // Loads 565, 5551, and 4444 formats as 8888 to avoid use of optional WDDM 1.2 formats
WIC_FLAGS_ALLOW_MONO = 0x8, WIC_FLAGS_ALLOW_MONO = 0x8,
// Loads 1-bit monochrome (black & white) as R1_UNORM rather than 8-bit greyscale // Loads 1-bit monochrome (black & white) as R1_UNORM rather than 8-bit grayscale
WIC_FLAGS_ALL_FRAMES = 0x10, WIC_FLAGS_ALL_FRAMES = 0x10,
// Loads all images in a multi-frame file, converting/resizing to match the first frame as needed, defaults to 0th frame otherwise // Loads all images in a multi-frame file, converting/resizing to match the first frame as needed, defaults to 0th frame otherwise
@ -373,6 +373,12 @@ namespace DirectX
TEX_FILTER_SEPARATE_ALPHA = 0x100, TEX_FILTER_SEPARATE_ALPHA = 0x100,
// Resize color and alpha channel independently // Resize color and alpha channel independently
TEX_FILTER_RGB_COPY_RED = 0x1000,
TEX_FILTER_RGB_COPY_GREEN = 0x2000,
TEX_FILTER_RGB_COPY_BLUE = 0x4000,
// When converting RGB to R, defaults to using grayscale. These flags indicate copying a specific channel instead
// When converting RGB to RG, defaults to copying RED | GREEN. These flags control which channels are selected instead.
TEX_FILTER_DITHER = 0x10000, TEX_FILTER_DITHER = 0x10000,
// Use ordered 4x4 dithering for any required conversions // Use ordered 4x4 dithering for any required conversions
TEX_FILTER_DITHER_DIFFUSION = 0x20000, TEX_FILTER_DITHER_DIFFUSION = 0x20000,
@ -390,6 +396,12 @@ namespace DirectX
// sRGB <-> RGB for use in conversion operations // sRGB <-> RGB for use in conversion operations
// if the input format type is IsSRGB(), then SRGB_IN is on by default // if the input format type is IsSRGB(), then SRGB_IN is on by default
// if the output format type is IsSRGB(), then SRGB_OUT is on by default // if the output format type is IsSRGB(), then SRGB_OUT is on by default
TEX_FILTER_FORCE_NON_WIC = 0x10000000,
// Forces use of the non-WIC path when both are an option
TEX_FILTER_FORCE_WIC = 0x20000000,
// Forces use of the WIC path even when logic would have picked a non-WIC path when both are an option
}; };
HRESULT Resize( _In_ const Image& srcImage, _In_ size_t width, _In_ size_t height, _In_ DWORD filter, HRESULT Resize( _In_ const Image& srcImage, _In_ size_t width, _In_ size_t height, _In_ DWORD filter,
@ -501,7 +513,22 @@ namespace DirectX
HRESULT CopyRectangle( _In_ const Image& srcImage, _In_ const Rect& srcRect, _In_ const Image& dstImage, HRESULT CopyRectangle( _In_ const Image& srcImage, _In_ const Rect& srcRect, _In_ const Image& dstImage,
_In_ DWORD filter, _In_ size_t xOffset, _In_ size_t yOffset ); _In_ DWORD filter, _In_ size_t xOffset, _In_ size_t yOffset );
HRESULT ComputeMSE( _In_ const Image& image1, _In_ const Image& image2, _Out_ float& mse, _Out_writes_opt_(4) float* mseV ); enum CMSE_FLAGS
{
CMSE_DEFAULT = 0,
CMSE_IMAGE1_SRGB = 0x1,
CMSE_IMAGE2_SRGB = 0x2,
// Indicates that image needs gamma correction before comparision
CMSE_IGNORE_RED = 0x10,
CMSE_IGNORE_GREEN = 0x20,
CMSE_IGNORE_BLUE = 0x40,
CMSE_IGNORE_ALPHA = 0x80,
// Ignore the channel when computing MSE
};
HRESULT ComputeMSE( _In_ const Image& image1, _In_ const Image& image2, _Out_ float& mse, _Out_writes_opt_(4) float* mseV, _In_ DWORD flags = 0 );
//--------------------------------------------------------------------------------- //---------------------------------------------------------------------------------
// Direct3D 11 functions // Direct3D 11 functions

View File

@ -27,6 +27,7 @@ namespace
{ {
inline float round_to_nearest( float x ) inline float round_to_nearest( float x )
{ {
// Round to nearest (even)
float i = floorf(x); float i = floorf(x);
x -= i; x -= i;
if(x < 0.5f) if(x < 0.5f)
@ -47,6 +48,7 @@ namespace
namespace DirectX namespace DirectX
{ {
static const XMVECTORF32 g_Grayscale = { 0.2125f, 0.7154f, 0.0721f, 0.0f };
//------------------------------------------------------------------------------------- //-------------------------------------------------------------------------------------
// Copies an image row with optional clearing of alpha value to 1.0 // Copies an image row with optional clearing of alpha value to 1.0
@ -2028,12 +2030,6 @@ void _ConvertScanline( XMVECTOR* pBuffer, size_t count, DXGI_FORMAT outFormat, D
if ( IsSRGB( outFormat ) ) if ( IsSRGB( outFormat ) )
flags |= TEX_FILTER_SRGB_OUT; flags |= TEX_FILTER_SRGB_OUT;
if ( in->flags & CONVF_SNORM )
flags &= ~TEX_FILTER_SRGB_IN;
if ( out->flags & CONVF_SNORM )
flags &= ~TEX_FILTER_SRGB_OUT;
if ( (flags & (TEX_FILTER_SRGB_IN|TEX_FILTER_SRGB_OUT)) == (TEX_FILTER_SRGB_IN|TEX_FILTER_SRGB_OUT) ) if ( (flags & (TEX_FILTER_SRGB_IN|TEX_FILTER_SRGB_OUT)) == (TEX_FILTER_SRGB_IN|TEX_FILTER_SRGB_OUT) )
{ {
flags &= ~(TEX_FILTER_SRGB_IN|TEX_FILTER_SRGB_OUT); flags &= ~(TEX_FILTER_SRGB_IN|TEX_FILTER_SRGB_OUT);
@ -2049,6 +2045,8 @@ void _ConvertScanline( XMVECTOR* pBuffer, size_t count, DXGI_FORMAT outFormat, D
{ {
// rgb = rgb^(2.2); a=a // rgb = rgb^(2.2); a=a
XMVECTOR v = *ptr; XMVECTOR v = *ptr;
// Use table instead of XMVectorPow( v, [2.2f 2.2f 2.2f 1]).
// Note table lookup will also saturate the result
XMVECTOR v1 = _TableDecodeGamma22( v ); XMVECTOR v1 = _TableDecodeGamma22( v );
*ptr++ = XMVectorSelect( v, v1, g_XMSelect1110 ); *ptr++ = XMVectorSelect( v, v1, g_XMSelect1110 );
} }
@ -2131,15 +2129,113 @@ void _ConvertScanline( XMVECTOR* pBuffer, size_t count, DXGI_FORMAT outFormat, D
*ptr++ = XMVectorSplatW( v ); *ptr++ = XMVectorSplatW( v );
} }
} }
else if ( ((in->flags & CONVF_RGB_MASK) == CONVF_R) && ((out->flags & CONVF_RGB_MASK) == (CONVF_R|CONVF_G|CONVF_B)) ) else if ( (in->flags & CONVF_RGB_MASK) == CONVF_R )
{ {
// R format -> RGB format if ( (out->flags & CONVF_RGB_MASK) == (CONVF_R|CONVF_G|CONVF_B) )
XMVECTOR* ptr = pBuffer;
for( size_t i=0; i < count; ++i )
{ {
XMVECTOR v = *ptr; // R format -> RGB format
XMVECTOR v1 = XMVectorSplatX( v ); XMVECTOR* ptr = pBuffer;
*ptr++ = XMVectorSelect( v, v1, g_XMSelect1110 ); for( size_t i=0; i < count; ++i )
{
XMVECTOR v = *ptr;
XMVECTOR v1 = XMVectorSplatX( v );
*ptr++ = XMVectorSelect( v, v1, g_XMSelect1110 );
}
}
else if ( (out->flags & CONVF_RGB_MASK) == (CONVF_R|CONVF_G) )
{
// R format -> RG format
XMVECTOR* ptr = pBuffer;
for( size_t i=0; i < count; ++i )
{
XMVECTOR v = *ptr;
XMVECTOR v1 = XMVectorSplatX( v );
*ptr++ = XMVectorSelect( v, v1, g_XMSelect1100 );
}
}
}
else if ( (in->flags & CONVF_RGB_MASK) == (CONVF_R|CONVF_G|CONVF_B) )
{
if ( (out->flags & CONVF_RGB_MASK) == CONVF_R )
{
// RGB format -> R format
switch( flags & ( TEX_FILTER_RGB_COPY_RED | TEX_FILTER_RGB_COPY_GREEN | TEX_FILTER_RGB_COPY_BLUE ) )
{
case TEX_FILTER_RGB_COPY_RED:
// Leave data unchanged and the store will handle this...
break;
case TEX_FILTER_RGB_COPY_GREEN:
{
XMVECTOR* ptr = pBuffer;
for( size_t i=0; i < count; ++i )
{
XMVECTOR v = *ptr;
XMVECTOR v1 = XMVectorSplatY( v );
*ptr++ = XMVectorSelect( v, v1, g_XMSelect1110 );
}
}
break;
case TEX_FILTER_RGB_COPY_BLUE:
{
XMVECTOR* ptr = pBuffer;
for( size_t i=0; i < count; ++i )
{
XMVECTOR v = *ptr;
XMVECTOR v1 = XMVectorSplatZ( v );
*ptr++ = XMVectorSelect( v, v1, g_XMSelect1110 );
}
}
break;
default:
{
XMVECTOR* ptr = pBuffer;
for( size_t i=0; i < count; ++i )
{
XMVECTOR v = *ptr;
XMVECTOR v1 = XMVector3Dot( v, g_Grayscale );
*ptr++ = XMVectorSelect( v, v1, g_XMSelect1110 );
}
}
break;
}
}
else if ( (out->flags & CONVF_RGB_MASK) == (CONVF_R|CONVF_G) )
{
// RGB format -> RG format
switch( flags & ( TEX_FILTER_RGB_COPY_RED | TEX_FILTER_RGB_COPY_GREEN | TEX_FILTER_RGB_COPY_BLUE ) )
{
case TEX_FILTER_RGB_COPY_RED | TEX_FILTER_RGB_COPY_BLUE:
{
XMVECTOR* ptr = pBuffer;
for( size_t i=0; i < count; ++i )
{
XMVECTOR v = *ptr;
XMVECTOR v1 = XMVectorSwizzle<0,2,0,2>( v );
*ptr++ = XMVectorSelect( v, v1, g_XMSelect1100 );
}
}
break;
case TEX_FILTER_RGB_COPY_GREEN | TEX_FILTER_RGB_COPY_BLUE:
{
XMVECTOR* ptr = pBuffer;
for( size_t i=0; i < count; ++i )
{
XMVECTOR v = *ptr;
XMVECTOR v1 = XMVectorSwizzle<1,2,3,0>( v );
*ptr++ = XMVectorSelect( v, v1, g_XMSelect1100 );
}
}
break;
case TEX_FILTER_RGB_COPY_RED | TEX_FILTER_RGB_COPY_GREEN:
default:
// Leave data unchanged and the store will handle this...
break;
}
} }
} }
} }
@ -2155,6 +2251,8 @@ void _ConvertScanline( XMVECTOR* pBuffer, size_t count, DXGI_FORMAT outFormat, D
// rgb = rgb^(1/2.2); a=a // rgb = rgb^(1/2.2); a=a
XMVECTOR v = *ptr; XMVECTOR v = *ptr;
XMVECTOR v1 = _TableEncodeGamma22( v ); XMVECTOR v1 = _TableEncodeGamma22( v );
// Use table instead of XMVectorPow( v, [1/2.2f 1/2.2f 1/2.2f 1]).
// Note table lookup will also saturate the result
*ptr++ = XMVectorSelect( v, v1, g_XMSelect1110 ); *ptr++ = XMVectorSelect( v, v1, g_XMSelect1110 );
} }
} }
@ -2162,6 +2260,100 @@ void _ConvertScanline( XMVECTOR* pBuffer, size_t count, DXGI_FORMAT outFormat, D
} }
//-------------------------------------------------------------------------------------
// Selection logic for using WIC vs. our own routines
//-------------------------------------------------------------------------------------
static inline bool _UseWICConversion( _In_ DWORD filter, _In_ DXGI_FORMAT sformat, _In_ DXGI_FORMAT tformat,
_Out_ WICPixelFormatGUID& pfGUID, _Out_ WICPixelFormatGUID& targetGUID )
{
memcpy( &pfGUID, &GUID_NULL, sizeof(GUID) );
memcpy( &targetGUID, &GUID_NULL, sizeof(GUID) );
if ( filter & TEX_FILTER_FORCE_NON_WIC )
{
// Explicit flag indicates use of non-WIC code paths
return false;
}
if ( !_DXGIToWIC( sformat, pfGUID ) || !_DXGIToWIC( tformat, targetGUID ) )
{
// Source or target format are not WIC supported native pixel formats
return false;
}
if ( filter & TEX_FILTER_FORCE_WIC )
{
// Explicit flag to use WIC code paths, skips all the case checks below
return true;
}
// Check for special cases
switch ( sformat )
{
case DXGI_FORMAT_R32G32B32A32_FLOAT:
case DXGI_FORMAT_R32G32B32_FLOAT:
case DXGI_FORMAT_R16G16B16A16_FLOAT:
switch( tformat )
{
case DXGI_FORMAT_R16_FLOAT:
case DXGI_FORMAT_R32_FLOAT:
case DXGI_FORMAT_D32_FLOAT:
// WIC converts via UNORM formats and ends up converting colorspaces for these cases
case DXGI_FORMAT_A8_UNORM:
// Conversion logic for these kinds of textures is unintuitive for WIC code paths
return false;
}
break;
case DXGI_FORMAT_R16_FLOAT:
switch( tformat )
{
case DXGI_FORMAT_R32_FLOAT:
case DXGI_FORMAT_D32_FLOAT:
// WIC converts via UNORM formats and ends up converting colorspaces for these cases
case DXGI_FORMAT_A8_UNORM:
// Conversion logic for these kinds of textures is unintuitive for WIC code paths
return false;
}
break;
case DXGI_FORMAT_A8_UNORM:
// Conversion logic for these kinds of textures is unintuitive for WIC code paths
return false;
default:
switch( tformat )
{
case DXGI_FORMAT_A8_UNORM:
// Conversion logic for these kinds of textures is unintuitive for WIC code paths
return false;
}
}
// Check for implicit color space changes
if ( IsSRGB( sformat ) )
filter |= TEX_FILTER_SRGB_IN;
if ( IsSRGB( tformat ) )
filter |= TEX_FILTER_SRGB_OUT;
if ( (filter & (TEX_FILTER_SRGB_IN|TEX_FILTER_SRGB_OUT)) == (TEX_FILTER_SRGB_IN|TEX_FILTER_SRGB_OUT) )
{
filter &= ~(TEX_FILTER_SRGB_IN|TEX_FILTER_SRGB_OUT);
}
DWORD wicsrgb = _CheckWICColorSpace( pfGUID, targetGUID );
if ( wicsrgb != (filter & (TEX_FILTER_SRGB_IN|TEX_FILTER_SRGB_OUT)) )
{
// WIC will perform a colorspace conversion we didn't request
return false;
}
return true;
}
//------------------------------------------------------------------------------------- //-------------------------------------------------------------------------------------
// Convert the source image using WIC // Convert the source image using WIC
//------------------------------------------------------------------------------------- //-------------------------------------------------------------------------------------
@ -2181,7 +2373,8 @@ static HRESULT _ConvertUsingWIC( _In_ const Image& srcImage, _In_ const WICPixel
if ( FAILED(hr) ) if ( FAILED(hr) )
return hr; return hr;
// Need to add logic to use TEX_FILTER_SRGB_IN/TEX_FILTER_SRGB_OUT // Note that WIC conversion ignores the TEX_FILTER_SRGB_IN and TEX_FILTER_SRGB_OUT flags,
// but also always assumes UNORM <-> FLOAT conversions are changing color spaces sRGB <-> scRGB
BOOL canConvert = FALSE; BOOL canConvert = FALSE;
hr = FC->CanConvert( pfGUID, targetGUID, &canConvert ); hr = FC->CanConvert( pfGUID, targetGUID, &canConvert );
@ -2210,151 +2403,6 @@ static HRESULT _ConvertUsingWIC( _In_ const Image& srcImage, _In_ const WICPixel
} }
//-------------------------------------------------------------------------------------
// Convert the source using WIC and then convert to DXGI format from there
//-------------------------------------------------------------------------------------
static HRESULT _ConvertFromWIC( _In_ const Image& srcImage, _In_ const WICPixelFormatGUID& pfGUID,
_In_ DWORD filter, _In_ float threshold, _In_ const Image& destImage )
{
assert( srcImage.width == destImage.width );
assert( srcImage.height == destImage.height );
IWICImagingFactory* pWIC = _GetWIC();
if ( !pWIC )
return E_NOINTERFACE;
ScopedObject<IWICFormatConverter> FC;
HRESULT hr = pWIC->CreateFormatConverter( &FC );
if ( FAILED(hr) )
return hr;
BOOL canConvert = FALSE;
hr = FC->CanConvert( pfGUID, GUID_WICPixelFormat128bppRGBAFloat, &canConvert );
if ( FAILED(hr) || !canConvert )
{
// This case is not an issue for the subset of WIC formats that map directly to DXGI
return E_UNEXPECTED;
}
ScratchImage temp;
hr = temp.Initialize2D( DXGI_FORMAT_R32G32B32A32_FLOAT, srcImage.width, srcImage.height, 1, 1 );
if ( FAILED(hr) )
return hr;
const Image *timg = temp.GetImage( 0, 0, 0 );
if ( !timg )
return E_POINTER;
ScopedObject<IWICBitmap> source;
hr = pWIC->CreateBitmapFromMemory( static_cast<UINT>( srcImage.width ), static_cast<UINT>( srcImage.height ), pfGUID,
static_cast<UINT>( srcImage.rowPitch ), static_cast<UINT>( srcImage.slicePitch ),
srcImage.pixels, &source );
if ( FAILED(hr) )
return hr;
hr = FC->Initialize( source.Get(), GUID_WICPixelFormat128bppRGBAFloat, _GetWICDither( filter ), 0, threshold, WICBitmapPaletteTypeCustom );
if ( FAILED(hr) )
return hr;
hr = FC->CopyPixels( 0, static_cast<UINT>( timg->rowPitch ), static_cast<UINT>( timg->slicePitch ), timg->pixels );
if ( FAILED(hr) )
return hr;
// Perform conversion on temp image which is now in R32G32B32A32_FLOAT format to final image
uint8_t *pSrc = timg->pixels;
uint8_t *pDest = destImage.pixels;
if ( !pSrc || !pDest )
return E_POINTER;
for( size_t h = 0; h < srcImage.height; ++h )
{
_ConvertScanline( reinterpret_cast<XMVECTOR*>(pSrc), srcImage.width, destImage.format, DXGI_FORMAT_R32G32B32A32_FLOAT, filter );
if ( !_StoreScanline( pDest, destImage.rowPitch, destImage.format, reinterpret_cast<const XMVECTOR*>(pSrc), srcImage.width ) )
return E_FAIL;
pSrc += timg->rowPitch;
pDest += destImage.rowPitch;
}
return S_OK;
}
//-------------------------------------------------------------------------------------
// Convert the source from DXGI format then use WIC to convert to final format
//-------------------------------------------------------------------------------------
static HRESULT _ConvertToWIC( _In_ const Image& srcImage,
_In_ const WICPixelFormatGUID& targetGUID, _In_ DWORD filter, _In_ float threshold, _In_ const Image& destImage )
{
assert( srcImage.width == destImage.width );
assert( srcImage.height == destImage.height );
IWICImagingFactory* pWIC = _GetWIC();
if ( !pWIC )
return E_NOINTERFACE;
ScopedObject<IWICFormatConverter> FC;
HRESULT hr = pWIC->CreateFormatConverter( &FC );
if ( FAILED(hr) )
return hr;
BOOL canConvert = FALSE;
hr = FC->CanConvert( GUID_WICPixelFormat128bppRGBAFloat, targetGUID, &canConvert );
if ( FAILED(hr) || !canConvert )
{
// This case is not an issue for the subset of WIC formats that map directly to DXGI
return E_UNEXPECTED;
}
ScratchImage temp;
hr = temp.Initialize2D( DXGI_FORMAT_R32G32B32A32_FLOAT, srcImage.width, srcImage.height, 1, 1 );
if ( FAILED(hr) )
return hr;
const Image *timg = temp.GetImage( 0, 0, 0 );
if ( !timg )
return E_POINTER;
const uint8_t *pSrc = srcImage.pixels;
if ( !pSrc )
return E_POINTER;
uint8_t *pDest = timg->pixels;
if ( !pDest )
return E_POINTER;
for( size_t h = 0; h < srcImage.height; ++h )
{
if ( !_LoadScanline( reinterpret_cast<XMVECTOR*>(pDest), srcImage.width, pSrc, srcImage.rowPitch, srcImage.format ) )
return E_FAIL;
_ConvertScanline( reinterpret_cast<XMVECTOR*>(pDest), srcImage.width, DXGI_FORMAT_R32G32B32A32_FLOAT, srcImage.format, filter );
pSrc += srcImage.rowPitch;
pDest += timg->rowPitch;
}
// Perform conversion on temp image which is now in R32G32B32A32_FLOAT format
ScopedObject<IWICBitmap> source;
hr = pWIC->CreateBitmapFromMemory( static_cast<UINT>( timg->width ), static_cast<UINT>( timg->height ), GUID_WICPixelFormat128bppRGBAFloat,
static_cast<UINT>( timg->rowPitch ), static_cast<UINT>( timg->slicePitch ),
timg->pixels, &source );
if ( FAILED(hr) )
return hr;
hr = FC->Initialize( source.Get(), targetGUID, _GetWICDither( filter ), 0, threshold, WICBitmapPaletteTypeCustom );
if ( FAILED(hr) )
return hr;
hr = FC->CopyPixels( 0, static_cast<UINT>( destImage.rowPitch ), static_cast<UINT>( destImage.slicePitch ), destImage.pixels );
if ( FAILED(hr) )
return hr;
return S_OK;
}
//------------------------------------------------------------------------------------- //-------------------------------------------------------------------------------------
// Convert the source image (not using WIC) // Convert the source image (not using WIC)
//------------------------------------------------------------------------------------- //-------------------------------------------------------------------------------------
@ -2427,34 +2475,14 @@ HRESULT Convert( const Image& srcImage, DXGI_FORMAT format, DWORD filter, float
return E_POINTER; return E_POINTER;
} }
WICPixelFormatGUID pfGUID; WICPixelFormatGUID pfGUID, targetGUID;
if ( _DXGIToWIC( srcImage.format, pfGUID ) ) if ( _UseWICConversion( filter, srcImage.format, format, pfGUID, targetGUID ) )
{ {
WICPixelFormatGUID targetGUID; hr = _ConvertUsingWIC( srcImage, pfGUID, targetGUID, filter, threshold, *rimage );
if ( _DXGIToWIC( format, targetGUID ) )
{
// Case 1: Both source and target formats are WIC supported
hr = _ConvertUsingWIC( srcImage, pfGUID, targetGUID, filter, threshold, *rimage );
}
else
{
// Case 2: Source format is supported by WIC, but not the target format
hr = _ConvertFromWIC( srcImage, pfGUID, filter, threshold, *rimage );
}
} }
else else
{ {
WICPixelFormatGUID targetGUID; hr = _Convert( srcImage, filter, *rimage );
if ( _DXGIToWIC( format, targetGUID ) )
{
// Case 3: Source format is not supported by WIC, but does support the target format
hr = _ConvertToWIC( srcImage, targetGUID, filter, threshold, *rimage );
}
else
{
// Case 4: Both source and target format are not supported by WIC
hr = _Convert( srcImage, filter, *rimage );
}
} }
if ( FAILED(hr) ) if ( FAILED(hr) )
@ -2507,8 +2535,7 @@ HRESULT Convert( const Image* srcImages, size_t nimages, const TexMetadata& meta
} }
WICPixelFormatGUID pfGUID, targetGUID; WICPixelFormatGUID pfGUID, targetGUID;
bool wicpf = _DXGIToWIC( metadata.format, pfGUID ); bool usewic = _UseWICConversion( filter, metadata.format, format, pfGUID, targetGUID );
bool wictargetpf = _DXGIToWIC( format, targetGUID );
for( size_t index=0; index < nimages; ++index ) for( size_t index=0; index < nimages; ++index )
{ {
@ -2533,31 +2560,13 @@ HRESULT Convert( const Image* srcImages, size_t nimages, const TexMetadata& meta
return E_FAIL; return E_FAIL;
} }
if ( wicpf ) if ( usewic )
{ {
if ( wictargetpf ) hr = _ConvertUsingWIC( src, pfGUID, targetGUID, filter, threshold, dst );
{
// Case 1: Both source and target formats are WIC supported
hr = _ConvertUsingWIC( src, pfGUID, targetGUID, filter, threshold, dst );
}
else
{
// Case 2: Source format is supported by WIC, but not the target format
hr = _ConvertFromWIC( src, pfGUID, filter, threshold, dst );
}
} }
else else
{ {
if ( wictargetpf ) hr = _Convert( src, filter, dst );
{
// Case 3: Source format is not supported by WIC, but does support the target format
hr = _ConvertToWIC( src, targetGUID, filter, threshold, dst );
}
else
{
// Case 4: Both source and target format are not supported by WIC
hr = _Convert( src, filter, dst );
}
} }
if ( FAILED(hr) ) if ( FAILED(hr) )

View File

@ -17,10 +17,12 @@
namespace DirectX namespace DirectX
{ {
static const XMVECTORF32 g_Gamma22 = { 2.2f, 2.2f, 2.2f, 1.f };
//------------------------------------------------------------------------------------- //-------------------------------------------------------------------------------------
static HRESULT _ComputeMSE( _In_ const Image& image1, _In_ const Image& image2, static HRESULT _ComputeMSE( _In_ const Image& image1, _In_ const Image& image2,
_Out_ float& mse, _Out_writes_opt_(4) float* mseV ) _Out_ float& mse, _Out_writes_opt_(4) float* mseV,
_In_ DWORD flags )
{ {
if ( !image1.pixels || !image2.pixels ) if ( !image1.pixels || !image2.pixels )
return E_POINTER; return E_POINTER;
@ -34,13 +36,54 @@ static HRESULT _ComputeMSE( _In_ const Image& image1, _In_ const Image& image2,
if ( !scanline ) if ( !scanline )
return E_OUTOFMEMORY; return E_OUTOFMEMORY;
// Flags implied from image formats
switch( image1.format )
{
case DXGI_FORMAT_B8G8R8X8_UNORM:
flags |= CMSE_IGNORE_ALPHA;
break;
case DXGI_FORMAT_B8G8R8X8_UNORM_SRGB:
flags |= CMSE_IMAGE1_SRGB | CMSE_IGNORE_ALPHA;
break;
case DXGI_FORMAT_R8G8B8A8_UNORM_SRGB:
case DXGI_FORMAT_BC1_UNORM_SRGB:
case DXGI_FORMAT_BC2_UNORM_SRGB:
case DXGI_FORMAT_BC3_UNORM_SRGB:
case DXGI_FORMAT_B8G8R8A8_UNORM_SRGB:
case DXGI_FORMAT_BC7_UNORM_SRGB:
flags |= CMSE_IMAGE1_SRGB;
break;
}
switch( image2.format )
{
case DXGI_FORMAT_B8G8R8X8_UNORM:
flags |= CMSE_IGNORE_ALPHA;
break;
case DXGI_FORMAT_B8G8R8X8_UNORM_SRGB:
flags |= CMSE_IMAGE2_SRGB | CMSE_IGNORE_ALPHA;
break;
case DXGI_FORMAT_R8G8B8A8_UNORM_SRGB:
case DXGI_FORMAT_BC1_UNORM_SRGB:
case DXGI_FORMAT_BC2_UNORM_SRGB:
case DXGI_FORMAT_BC3_UNORM_SRGB:
case DXGI_FORMAT_B8G8R8A8_UNORM_SRGB:
case DXGI_FORMAT_BC7_UNORM_SRGB:
flags |= CMSE_IMAGE2_SRGB;
break;
}
const uint8_t *pSrc1 = image1.pixels; const uint8_t *pSrc1 = image1.pixels;
const size_t rowPitch1 = image1.rowPitch; const size_t rowPitch1 = image1.rowPitch;
const uint8_t *pSrc2 = image2.pixels; const uint8_t *pSrc2 = image2.pixels;
const size_t rowPitch2 = image2.rowPitch; const size_t rowPitch2 = image2.rowPitch;
XMVECTOR acc = XMVectorZero(); XMVECTOR acc = g_XMZero;
for( size_t h = 0; h < image1.height; ++h ) for( size_t h = 0; h < image1.height; ++h )
{ {
@ -52,10 +95,39 @@ static HRESULT _ComputeMSE( _In_ const Image& image1, _In_ const Image& image2,
if ( !_LoadScanline( ptr2, width, pSrc2, rowPitch2, image2.format ) ) if ( !_LoadScanline( ptr2, width, pSrc2, rowPitch2, image2.format ) )
return E_FAIL; return E_FAIL;
for( size_t i = 0; i < width; ++i, ++ptr1, ++ptr2 ) for( size_t i = 0; i < width; ++i )
{ {
XMVECTOR v1 = *(ptr1++);
if ( flags & CMSE_IMAGE1_SRGB )
{
v1 = XMVectorPow( v1, g_Gamma22 );
}
XMVECTOR v2 = *(ptr2++);
if ( flags & CMSE_IMAGE2_SRGB )
{
v2 = XMVectorPow( v2, g_Gamma22 );
}
// sum[ (I1 - I2)^2 ] // sum[ (I1 - I2)^2 ]
XMVECTOR v = XMVectorSubtract( *ptr1, *ptr2 ); XMVECTOR v = XMVectorSubtract( v1, v2 );
if ( flags & CMSE_IGNORE_RED )
{
v = XMVectorSelect( v, g_XMZero, g_XMMaskX );
}
if ( flags & CMSE_IGNORE_GREEN )
{
v = XMVectorSelect( v, g_XMZero, g_XMMaskY );
}
if ( flags & CMSE_IGNORE_BLUE )
{
v = XMVectorSelect( v, g_XMZero, g_XMMaskZ );
}
if ( flags & CMSE_IGNORE_ALPHA )
{
v = XMVectorSelect( v, g_XMZero, g_XMMaskW );
}
acc = XMVectorMultiplyAdd( v, v, acc ); acc = XMVectorMultiplyAdd( v, v, acc );
} }
@ -195,7 +267,7 @@ HRESULT CopyRectangle( const Image& srcImage, const Rect& srcRect, const Image&
// Computes the Mean-Squared-Error (MSE) between two images // Computes the Mean-Squared-Error (MSE) between two images
//------------------------------------------------------------------------------------- //-------------------------------------------------------------------------------------
_Use_decl_annotations_ _Use_decl_annotations_
HRESULT ComputeMSE( const Image& image1, const Image& image2, float& mse, float* mseV ) HRESULT ComputeMSE( const Image& image1, const Image& image2, float& mse, float* mseV, DWORD flags )
{ {
if ( !image1.pixels || !image2.pixels ) if ( !image1.pixels || !image2.pixels )
return E_POINTER; return E_POINTER;
@ -223,7 +295,7 @@ HRESULT ComputeMSE( const Image& image1, const Image& image2, float& mse, float*
if ( !img1 || !img2 ) if ( !img1 || !img2 )
return E_POINTER; return E_POINTER;
return _ComputeMSE( *img1, *img2, mse, mseV ); return _ComputeMSE( *img1, *img2, mse, mseV, flags );
} }
else else
{ {
@ -237,7 +309,7 @@ HRESULT ComputeMSE( const Image& image1, const Image& image2, float& mse, float*
if ( !img ) if ( !img )
return E_POINTER; return E_POINTER;
return _ComputeMSE( *img, image2, mse, mseV ); return _ComputeMSE( *img, image2, mse, mseV, flags );
} }
} }
else else
@ -254,12 +326,12 @@ HRESULT ComputeMSE( const Image& image1, const Image& image2, float& mse, float*
if ( !img ) if ( !img )
return E_POINTER; return E_POINTER;
return _ComputeMSE( image1, *img, mse, mseV ); return _ComputeMSE( image1, *img, mse, mseV, flags );
} }
else else
{ {
// Case 4: neither image is compressed // Case 4: neither image is compressed
return _ComputeMSE( image1, image2, mse, mseV ); return _ComputeMSE( image1, image2, mse, mseV, flags );
} }
} }
} }

View File

@ -67,6 +67,8 @@ namespace DirectX
DXGI_FORMAT _WICToDXGI( _In_ const GUID& guid ); DXGI_FORMAT _WICToDXGI( _In_ const GUID& guid );
bool _DXGIToWIC( _In_ DXGI_FORMAT format, _Out_ GUID& guid ); bool _DXGIToWIC( _In_ DXGI_FORMAT format, _Out_ GUID& guid );
DWORD _CheckWICColorSpace( _In_ const GUID& sourceGUID, _In_ const GUID& targetGUID );
IWICImagingFactory* _GetWIC(); IWICImagingFactory* _GetWIC();
bool _IsWIC2(); bool _IsWIC2();

View File

@ -19,7 +19,7 @@
// The implementation here has the following limitations: // The implementation here has the following limitations:
// * Does not support files that contain color maps (these are rare in practice) // * Does not support files that contain color maps (these are rare in practice)
// * Interleaved files are not supported (deprecated aspect of TGA format) // * Interleaved files are not supported (deprecated aspect of TGA format)
// * Only supports 8-bit greyscale; 16-, 24-, and 32-bit truecolor images // * Only supports 8-bit grayscale; 16-, 24-, and 32-bit truecolor images
// * Always writes uncompressed files (i.e. can read RLE compression, but does not write it) // * Always writes uncompressed files (i.e. can read RLE compression, but does not write it)
// //

View File

@ -1,3 +1,5 @@
//------------------------------------------------------------------------------------- //-------------------------------------------------------------------------------------
// DirectXTexUtil.cpp // DirectXTexUtil.cpp
// //
@ -22,33 +24,34 @@ struct WICTranslate
{ {
GUID wic; GUID wic;
DXGI_FORMAT format; DXGI_FORMAT format;
bool srgb;
}; };
static WICTranslate g_WICFormats[] = static WICTranslate g_WICFormats[] =
{ {
{ GUID_WICPixelFormat128bppRGBAFloat, DXGI_FORMAT_R32G32B32A32_FLOAT }, { GUID_WICPixelFormat128bppRGBAFloat, DXGI_FORMAT_R32G32B32A32_FLOAT, false },
{ GUID_WICPixelFormat64bppRGBAHalf, DXGI_FORMAT_R16G16B16A16_FLOAT }, { GUID_WICPixelFormat64bppRGBAHalf, DXGI_FORMAT_R16G16B16A16_FLOAT, false },
{ GUID_WICPixelFormat64bppRGBA, DXGI_FORMAT_R16G16B16A16_UNORM }, { GUID_WICPixelFormat64bppRGBA, DXGI_FORMAT_R16G16B16A16_UNORM, true },
{ GUID_WICPixelFormat32bppRGBA, DXGI_FORMAT_R8G8B8A8_UNORM }, { GUID_WICPixelFormat32bppRGBA, DXGI_FORMAT_R8G8B8A8_UNORM, true },
{ GUID_WICPixelFormat32bppBGRA, DXGI_FORMAT_B8G8R8A8_UNORM }, // DXGI 1.1 { GUID_WICPixelFormat32bppBGRA, DXGI_FORMAT_B8G8R8A8_UNORM, true }, // DXGI 1.1
{ GUID_WICPixelFormat32bppBGR, DXGI_FORMAT_B8G8R8X8_UNORM }, // DXGI 1.1 { GUID_WICPixelFormat32bppBGR, DXGI_FORMAT_B8G8R8X8_UNORM, true }, // DXGI 1.1
{ GUID_WICPixelFormat32bppRGBA1010102XR, DXGI_FORMAT_R10G10B10_XR_BIAS_A2_UNORM }, // DXGI 1.1 { GUID_WICPixelFormat32bppRGBA1010102XR, DXGI_FORMAT_R10G10B10_XR_BIAS_A2_UNORM, true }, // DXGI 1.1
{ GUID_WICPixelFormat32bppRGBA1010102, DXGI_FORMAT_R10G10B10A2_UNORM }, { GUID_WICPixelFormat32bppRGBA1010102, DXGI_FORMAT_R10G10B10A2_UNORM, true },
{ GUID_WICPixelFormat16bppBGRA5551, DXGI_FORMAT_B5G5R5A1_UNORM }, { GUID_WICPixelFormat16bppBGRA5551, DXGI_FORMAT_B5G5R5A1_UNORM, true },
{ GUID_WICPixelFormat16bppBGR565, DXGI_FORMAT_B5G6R5_UNORM }, { GUID_WICPixelFormat16bppBGR565, DXGI_FORMAT_B5G6R5_UNORM, true },
{ GUID_WICPixelFormat32bppGrayFloat, DXGI_FORMAT_R32_FLOAT }, { GUID_WICPixelFormat32bppGrayFloat, DXGI_FORMAT_R32_FLOAT, false },
{ GUID_WICPixelFormat16bppGrayHalf, DXGI_FORMAT_R16_FLOAT }, { GUID_WICPixelFormat16bppGrayHalf, DXGI_FORMAT_R16_FLOAT, false },
{ GUID_WICPixelFormat16bppGray, DXGI_FORMAT_R16_UNORM }, { GUID_WICPixelFormat16bppGray, DXGI_FORMAT_R16_UNORM, true },
{ GUID_WICPixelFormat8bppGray, DXGI_FORMAT_R8_UNORM }, { GUID_WICPixelFormat8bppGray, DXGI_FORMAT_R8_UNORM, true },
{ GUID_WICPixelFormat8bppAlpha, DXGI_FORMAT_A8_UNORM }, { GUID_WICPixelFormat8bppAlpha, DXGI_FORMAT_A8_UNORM, false },
{ GUID_WICPixelFormatBlackWhite, DXGI_FORMAT_R1_UNORM }, { GUID_WICPixelFormatBlackWhite, DXGI_FORMAT_R1_UNORM, false },
}; };
static bool g_WIC2 = false; static bool g_WIC2 = false;
@ -131,6 +134,33 @@ bool _DXGIToWIC( DXGI_FORMAT format, GUID& guid )
return false; return false;
} }
DWORD _CheckWICColorSpace( _In_ const GUID& sourceGUID, _In_ const GUID& targetGUID )
{
DWORD srgb = 0;
for( size_t i=0; i < _countof(g_WICFormats); ++i )
{
if ( memcmp( &g_WICFormats[i].wic, &sourceGUID, sizeof(GUID) ) == 0 )
{
if ( g_WICFormats[i].srgb )
srgb |= TEX_FILTER_SRGB_IN;
}
if ( memcmp( &g_WICFormats[i].wic, &targetGUID, sizeof(GUID) ) == 0 )
{
if ( g_WICFormats[i].srgb )
srgb |= TEX_FILTER_SRGB_OUT;
}
}
if ( (srgb & (TEX_FILTER_SRGB_IN|TEX_FILTER_SRGB_OUT)) == (TEX_FILTER_SRGB_IN|TEX_FILTER_SRGB_OUT) )
{
srgb &= ~(TEX_FILTER_SRGB_IN|TEX_FILTER_SRGB_OUT);
}
return srgb;
}
bool _IsWIC2() bool _IsWIC2()
{ {
return g_WIC2; return g_WIC2;