mirror of
https://github.com/microsoft/DirectXTex.git
synced 2025-07-13 21:50:13 +02:00
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:
parent
33db361592
commit
c398ac1711
@ -177,7 +177,7 @@ namespace DirectX
|
||||
// Loads 565, 5551, and 4444 formats as 8888 to avoid use of optional WDDM 1.2 formats
|
||||
|
||||
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,
|
||||
// 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,
|
||||
// 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,
|
||||
// Use ordered 4x4 dithering for any required conversions
|
||||
TEX_FILTER_DITHER_DIFFUSION = 0x20000,
|
||||
@ -390,6 +396,12 @@ namespace DirectX
|
||||
// sRGB <-> RGB for use in conversion operations
|
||||
// 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
|
||||
|
||||
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,
|
||||
@ -501,7 +513,22 @@ namespace DirectX
|
||||
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 );
|
||||
|
||||
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
|
||||
|
@ -27,6 +27,7 @@ namespace
|
||||
{
|
||||
inline float round_to_nearest( float x )
|
||||
{
|
||||
// Round to nearest (even)
|
||||
float i = floorf(x);
|
||||
x -= i;
|
||||
if(x < 0.5f)
|
||||
@ -47,6 +48,7 @@ namespace
|
||||
|
||||
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
|
||||
@ -2028,12 +2030,6 @@ void _ConvertScanline( XMVECTOR* pBuffer, size_t count, DXGI_FORMAT outFormat, D
|
||||
if ( IsSRGB( outFormat ) )
|
||||
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) )
|
||||
{
|
||||
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
|
||||
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 );
|
||||
*ptr++ = XMVectorSelect( v, v1, g_XMSelect1110 );
|
||||
}
|
||||
@ -2131,7 +2129,9 @@ void _ConvertScanline( XMVECTOR* pBuffer, size_t count, DXGI_FORMAT outFormat, D
|
||||
*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 )
|
||||
{
|
||||
if ( (out->flags & CONVF_RGB_MASK) == (CONVF_R|CONVF_G|CONVF_B) )
|
||||
{
|
||||
// R format -> RGB format
|
||||
XMVECTOR* ptr = pBuffer;
|
||||
@ -2142,6 +2142,102 @@ void _ConvertScanline( XMVECTOR* pBuffer, size_t count, DXGI_FORMAT outFormat, D
|
||||
*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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// sRGB output processing (RGB -> sRGB)
|
||||
@ -2155,6 +2251,8 @@ void _ConvertScanline( XMVECTOR* pBuffer, size_t count, DXGI_FORMAT outFormat, D
|
||||
// rgb = rgb^(1/2.2); a=a
|
||||
XMVECTOR v = *ptr;
|
||||
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 );
|
||||
}
|
||||
}
|
||||
@ -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
|
||||
//-------------------------------------------------------------------------------------
|
||||
@ -2181,7 +2373,8 @@ static HRESULT _ConvertUsingWIC( _In_ const Image& srcImage, _In_ const WICPixel
|
||||
if ( FAILED(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;
|
||||
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)
|
||||
//-------------------------------------------------------------------------------------
|
||||
@ -2427,35 +2475,15 @@ HRESULT Convert( const Image& srcImage, DXGI_FORMAT format, DWORD filter, float
|
||||
return E_POINTER;
|
||||
}
|
||||
|
||||
WICPixelFormatGUID pfGUID;
|
||||
if ( _DXGIToWIC( srcImage.format, pfGUID ) )
|
||||
WICPixelFormatGUID pfGUID, targetGUID;
|
||||
if ( _UseWICConversion( filter, srcImage.format, format, pfGUID, targetGUID ) )
|
||||
{
|
||||
WICPixelFormatGUID targetGUID;
|
||||
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
|
||||
{
|
||||
WICPixelFormatGUID targetGUID;
|
||||
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) )
|
||||
{
|
||||
@ -2507,8 +2535,7 @@ HRESULT Convert( const Image* srcImages, size_t nimages, const TexMetadata& meta
|
||||
}
|
||||
|
||||
WICPixelFormatGUID pfGUID, targetGUID;
|
||||
bool wicpf = _DXGIToWIC( metadata.format, pfGUID );
|
||||
bool wictargetpf = _DXGIToWIC( format, targetGUID );
|
||||
bool usewic = _UseWICConversion( filter, metadata.format, format, pfGUID, targetGUID );
|
||||
|
||||
for( size_t index=0; index < nimages; ++index )
|
||||
{
|
||||
@ -2533,32 +2560,14 @@ HRESULT Convert( const Image* srcImages, size_t nimages, const TexMetadata& meta
|
||||
return E_FAIL;
|
||||
}
|
||||
|
||||
if ( wicpf )
|
||||
if ( usewic )
|
||||
{
|
||||
if ( wictargetpf )
|
||||
{
|
||||
// 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
|
||||
{
|
||||
if ( wictargetpf )
|
||||
{
|
||||
// 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) )
|
||||
{
|
||||
|
@ -17,10 +17,12 @@
|
||||
|
||||
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,
|
||||
_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 )
|
||||
return E_POINTER;
|
||||
@ -34,13 +36,54 @@ static HRESULT _ComputeMSE( _In_ const Image& image1, _In_ const Image& image2,
|
||||
if ( !scanline )
|
||||
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 size_t rowPitch1 = image1.rowPitch;
|
||||
|
||||
const uint8_t *pSrc2 = image2.pixels;
|
||||
const size_t rowPitch2 = image2.rowPitch;
|
||||
|
||||
XMVECTOR acc = XMVectorZero();
|
||||
XMVECTOR acc = g_XMZero;
|
||||
|
||||
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 ) )
|
||||
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 ]
|
||||
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 );
|
||||
}
|
||||
|
||||
@ -195,7 +267,7 @@ HRESULT CopyRectangle( const Image& srcImage, const Rect& srcRect, const Image&
|
||||
// Computes the Mean-Squared-Error (MSE) between two images
|
||||
//-------------------------------------------------------------------------------------
|
||||
_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 )
|
||||
return E_POINTER;
|
||||
@ -223,7 +295,7 @@ HRESULT ComputeMSE( const Image& image1, const Image& image2, float& mse, float*
|
||||
if ( !img1 || !img2 )
|
||||
return E_POINTER;
|
||||
|
||||
return _ComputeMSE( *img1, *img2, mse, mseV );
|
||||
return _ComputeMSE( *img1, *img2, mse, mseV, flags );
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -237,7 +309,7 @@ HRESULT ComputeMSE( const Image& image1, const Image& image2, float& mse, float*
|
||||
if ( !img )
|
||||
return E_POINTER;
|
||||
|
||||
return _ComputeMSE( *img, image2, mse, mseV );
|
||||
return _ComputeMSE( *img, image2, mse, mseV, flags );
|
||||
}
|
||||
}
|
||||
else
|
||||
@ -254,12 +326,12 @@ HRESULT ComputeMSE( const Image& image1, const Image& image2, float& mse, float*
|
||||
if ( !img )
|
||||
return E_POINTER;
|
||||
|
||||
return _ComputeMSE( image1, *img, mse, mseV );
|
||||
return _ComputeMSE( image1, *img, mse, mseV, flags );
|
||||
}
|
||||
else
|
||||
{
|
||||
// Case 4: neither image is compressed
|
||||
return _ComputeMSE( image1, image2, mse, mseV );
|
||||
return _ComputeMSE( image1, image2, mse, mseV, flags );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -67,6 +67,8 @@ namespace DirectX
|
||||
DXGI_FORMAT _WICToDXGI( _In_ const GUID& guid );
|
||||
bool _DXGIToWIC( _In_ DXGI_FORMAT format, _Out_ GUID& guid );
|
||||
|
||||
DWORD _CheckWICColorSpace( _In_ const GUID& sourceGUID, _In_ const GUID& targetGUID );
|
||||
|
||||
IWICImagingFactory* _GetWIC();
|
||||
|
||||
bool _IsWIC2();
|
||||
|
@ -19,7 +19,7 @@
|
||||
// The implementation here has the following limitations:
|
||||
// * Does not support files that contain color maps (these are rare in practice)
|
||||
// * 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)
|
||||
//
|
||||
|
||||
|
@ -1,3 +1,5 @@
|
||||
|
||||
|
||||
//-------------------------------------------------------------------------------------
|
||||
// DirectXTexUtil.cpp
|
||||
//
|
||||
@ -22,33 +24,34 @@ struct WICTranslate
|
||||
{
|
||||
GUID wic;
|
||||
DXGI_FORMAT format;
|
||||
bool srgb;
|
||||
};
|
||||
|
||||
static WICTranslate g_WICFormats[] =
|
||||
{
|
||||
{ GUID_WICPixelFormat128bppRGBAFloat, DXGI_FORMAT_R32G32B32A32_FLOAT },
|
||||
{ GUID_WICPixelFormat128bppRGBAFloat, DXGI_FORMAT_R32G32B32A32_FLOAT, false },
|
||||
|
||||
{ GUID_WICPixelFormat64bppRGBAHalf, DXGI_FORMAT_R16G16B16A16_FLOAT },
|
||||
{ GUID_WICPixelFormat64bppRGBA, DXGI_FORMAT_R16G16B16A16_UNORM },
|
||||
{ GUID_WICPixelFormat64bppRGBAHalf, DXGI_FORMAT_R16G16B16A16_FLOAT, false },
|
||||
{ GUID_WICPixelFormat64bppRGBA, DXGI_FORMAT_R16G16B16A16_UNORM, true },
|
||||
|
||||
{ GUID_WICPixelFormat32bppRGBA, DXGI_FORMAT_R8G8B8A8_UNORM },
|
||||
{ GUID_WICPixelFormat32bppBGRA, DXGI_FORMAT_B8G8R8A8_UNORM }, // DXGI 1.1
|
||||
{ GUID_WICPixelFormat32bppBGR, DXGI_FORMAT_B8G8R8X8_UNORM }, // DXGI 1.1
|
||||
{ GUID_WICPixelFormat32bppRGBA, DXGI_FORMAT_R8G8B8A8_UNORM, true },
|
||||
{ GUID_WICPixelFormat32bppBGRA, DXGI_FORMAT_B8G8R8A8_UNORM, true }, // 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_WICPixelFormat32bppRGBA1010102, DXGI_FORMAT_R10G10B10A2_UNORM },
|
||||
{ GUID_WICPixelFormat32bppRGBA1010102XR, DXGI_FORMAT_R10G10B10_XR_BIAS_A2_UNORM, true }, // DXGI 1.1
|
||||
{ GUID_WICPixelFormat32bppRGBA1010102, DXGI_FORMAT_R10G10B10A2_UNORM, true },
|
||||
|
||||
{ GUID_WICPixelFormat16bppBGRA5551, DXGI_FORMAT_B5G5R5A1_UNORM },
|
||||
{ GUID_WICPixelFormat16bppBGR565, DXGI_FORMAT_B5G6R5_UNORM },
|
||||
{ GUID_WICPixelFormat16bppBGRA5551, DXGI_FORMAT_B5G5R5A1_UNORM, true },
|
||||
{ GUID_WICPixelFormat16bppBGR565, DXGI_FORMAT_B5G6R5_UNORM, true },
|
||||
|
||||
{ GUID_WICPixelFormat32bppGrayFloat, DXGI_FORMAT_R32_FLOAT },
|
||||
{ GUID_WICPixelFormat16bppGrayHalf, DXGI_FORMAT_R16_FLOAT },
|
||||
{ GUID_WICPixelFormat16bppGray, DXGI_FORMAT_R16_UNORM },
|
||||
{ GUID_WICPixelFormat8bppGray, DXGI_FORMAT_R8_UNORM },
|
||||
{ GUID_WICPixelFormat32bppGrayFloat, DXGI_FORMAT_R32_FLOAT, false },
|
||||
{ GUID_WICPixelFormat16bppGrayHalf, DXGI_FORMAT_R16_FLOAT, false },
|
||||
{ GUID_WICPixelFormat16bppGray, DXGI_FORMAT_R16_UNORM, true },
|
||||
{ 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;
|
||||
@ -131,6 +134,33 @@ bool _DXGIToWIC( DXGI_FORMAT format, GUID& guid )
|
||||
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()
|
||||
{
|
||||
return g_WIC2;
|
||||
|
Loading…
x
Reference in New Issue
Block a user