diff --git a/DirectXTex/DirectXTex.h b/DirectXTex/DirectXTex.h index efe82c5..81b9171 100644 --- a/DirectXTex/DirectXTex.h +++ b/DirectXTex/DirectXTex.h @@ -60,6 +60,8 @@ namespace DirectX bool IsSRGB( _In_ DXGI_FORMAT fmt ); bool IsTypeless( _In_ DXGI_FORMAT fmt ); + bool HasAlpha( _In_ DXGI_FORMAT fmt ); + size_t BitsPerPixel( _In_ DXGI_FORMAT fmt ); enum CP_FLAGS @@ -389,6 +391,10 @@ namespace DirectX // levels of '0' indicates a full mipchain, otherwise is generates that number of total levels (including the source base image) // Defaults to Fant filtering which is equivalent to a box filter + HRESULT PremultiplyAlpha( _In_ const Image& srcImage, _Out_ ScratchImage& image ); + HRESULT PremultiplyAlpha( _In_reads_(nimages) const Image* srcImages, _In_ size_t nimages, _In_ const TexMetadata& metadata, _Out_ ScratchImage& result ); + // Converts to a premultiplied alpha version of the texture + enum TEX_COMPRESS_FLAGS { TEX_COMPRESS_DEFAULT = 0, diff --git a/DirectXTex/DirectXTex.inl b/DirectXTex/DirectXTex.inl index 58fab3a..3f5a2cc 100644 --- a/DirectXTex/DirectXTex.inl +++ b/DirectXTex/DirectXTex.inl @@ -164,6 +164,55 @@ inline bool IsTypeless( DXGI_FORMAT fmt ) } } +_Use_decl_annotations_ +inline bool HasAlpha( DXGI_FORMAT fmt ) +{ + switch( fmt ) + { + case DXGI_FORMAT_R32G32B32A32_TYPELESS: + case DXGI_FORMAT_R32G32B32A32_FLOAT: + case DXGI_FORMAT_R32G32B32A32_UINT: + case DXGI_FORMAT_R32G32B32A32_SINT: + case DXGI_FORMAT_R16G16B16A16_TYPELESS: + case DXGI_FORMAT_R16G16B16A16_FLOAT: + case DXGI_FORMAT_R16G16B16A16_UNORM: + case DXGI_FORMAT_R16G16B16A16_UINT: + case DXGI_FORMAT_R16G16B16A16_SNORM: + case DXGI_FORMAT_R16G16B16A16_SINT: + case DXGI_FORMAT_R10G10B10A2_TYPELESS: + case DXGI_FORMAT_R10G10B10A2_UNORM: + case DXGI_FORMAT_R10G10B10A2_UINT: + case DXGI_FORMAT_R8G8B8A8_TYPELESS: + case DXGI_FORMAT_R8G8B8A8_UNORM: + case DXGI_FORMAT_R8G8B8A8_UNORM_SRGB: + case DXGI_FORMAT_R8G8B8A8_UINT: + case DXGI_FORMAT_R8G8B8A8_SNORM: + case DXGI_FORMAT_R8G8B8A8_SINT: + case DXGI_FORMAT_A8_UNORM: + case DXGI_FORMAT_BC2_TYPELESS: + case DXGI_FORMAT_BC2_UNORM: + case DXGI_FORMAT_BC2_UNORM_SRGB: + case DXGI_FORMAT_BC3_TYPELESS: + case DXGI_FORMAT_BC3_UNORM: + case DXGI_FORMAT_BC3_UNORM_SRGB: + case DXGI_FORMAT_B5G5R5A1_UNORM: + case DXGI_FORMAT_B8G8R8A8_UNORM: + case DXGI_FORMAT_R10G10B10_XR_BIAS_A2_UNORM: + case DXGI_FORMAT_B8G8R8A8_TYPELESS: + case DXGI_FORMAT_B8G8R8A8_UNORM_SRGB: + case DXGI_FORMAT_BC7_TYPELESS: + case DXGI_FORMAT_BC7_UNORM: + case DXGI_FORMAT_BC7_UNORM_SRGB: +#ifdef DXGI_1_2_FORMATS + case DXGI_FORMAT_B4G4R4A4_UNORM: +#endif + return true; + + default: + return false; + } +} + _Use_decl_annotations_ inline size_t ComputeScanlines( DXGI_FORMAT fmt, size_t height ) { diff --git a/DirectXTex/DirectXTexPMAlpha.cpp b/DirectXTex/DirectXTexPMAlpha.cpp new file mode 100644 index 0000000..c58e698 --- /dev/null +++ b/DirectXTex/DirectXTexPMAlpha.cpp @@ -0,0 +1,176 @@ +//------------------------------------------------------------------------------------- +// DirectXTexPMAlpha.cpp +// +// DirectX Texture Library - Premultiplied alpha operations +// +// 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" + +namespace DirectX +{ + +static HRESULT _PremultiplyAlpha( _In_ const Image& srcImage, _In_ const Image& destImage ) +{ + assert( srcImage.width == destImage.width ); + assert( srcImage.height == destImage.height ); + + ScopedAlignedArrayXMVECTOR scanline( reinterpret_cast( _aligned_malloc( (sizeof(XMVECTOR)*srcImage.width), 16 ) ) ); + if ( !scanline ) + return E_OUTOFMEMORY; + + const uint8_t *pSrc = srcImage.pixels; + uint8_t *pDest = destImage.pixels; + if ( !pSrc || !pDest ) + return E_POINTER; + + for( size_t h = 0; h < srcImage.height; ++h ) + { + if ( !_LoadScanline( scanline.get(), srcImage.width, pSrc, srcImage.rowPitch, srcImage.format ) ) + return E_FAIL; + + XMVECTOR* ptr = scanline.get(); + for( size_t w = 0; w < srcImage.width; ++w ) + { + XMVECTOR v = *ptr; + XMVECTOR alpha = XMVectorSplatW( *ptr ); + alpha = XMVectorMultiply( v, alpha ); + *(ptr++) = XMVectorSelect( v, alpha, g_XMSelect1110 ); + } + + if ( !_StoreScanline( pDest, destImage.rowPitch, destImage.format, scanline.get(), srcImage.width ) ) + return E_FAIL; + + pSrc += srcImage.rowPitch; + pDest += destImage.rowPitch; + } + + return S_OK; +} + + +//===================================================================================== +// Entry-points +//===================================================================================== + +//------------------------------------------------------------------------------------- +// Converts to a premultiplied alpha version of the texture +//------------------------------------------------------------------------------------- +_Use_decl_annotations_ +HRESULT PremultiplyAlpha( const Image& srcImage, ScratchImage& image ) +{ + if ( !srcImage.pixels ) + return E_POINTER; + + if ( IsCompressed(srcImage.format) + || IsVideo(srcImage.format) + || IsTypeless(srcImage.format) + || !HasAlpha(srcImage.format) ) + return HRESULT_FROM_WIN32( ERROR_NOT_SUPPORTED ); + +#ifdef _AMD64_ + if ( (srcImage.width > 0xFFFFFFFF) || (srcImage.height > 0xFFFFFFFF) ) + return E_INVALIDARG; +#endif + + HRESULT hr = image.Initialize2D( srcImage.format, srcImage.width, srcImage.height, 1, 1 ); + if ( FAILED(hr) ) + return hr; + + const Image *rimage = image.GetImage( 0, 0, 0 ); + if ( !rimage ) + { + image.Release(); + return E_POINTER; + } + + hr = _PremultiplyAlpha( srcImage, *rimage ); + if ( FAILED(hr) ) + { + image.Release(); + return hr; + } + + return S_OK; +} + + +//------------------------------------------------------------------------------------- +// Converts to a premultiplied alpha version of the texture (complex) +//------------------------------------------------------------------------------------- +_Use_decl_annotations_ +HRESULT PremultiplyAlpha( const Image* srcImages, size_t nimages, const TexMetadata& metadata, ScratchImage& result ) +{ + if ( !srcImages || !nimages ) + return E_INVALIDARG; + + if ( IsCompressed(metadata.format) + || IsVideo(metadata.format) + || IsTypeless(metadata.format) + || !HasAlpha(metadata.format) ) + return HRESULT_FROM_WIN32( ERROR_NOT_SUPPORTED ); + +#ifdef _AMD64_ + if ( (metadata.width > 0xFFFFFFFF) || (metadata.height > 0xFFFFFFFF) ) + return E_INVALIDARG; +#endif + + HRESULT hr = result.Initialize( metadata ); + if ( FAILED(hr) ) + return hr; + + if ( nimages != result.GetImageCount() ) + { + result.Release(); + return E_FAIL; + } + + const Image* dest = result.GetImages(); + if ( !dest ) + { + result.Release(); + return E_POINTER; + } + + for( size_t index=0; index < nimages; ++index ) + { + const Image& src = srcImages[ index ]; + if ( src.format != metadata.format ) + { + result.Release(); + return E_FAIL; + } + +#ifdef _AMD64_ + if ( (src.width > 0xFFFFFFFF) || (src.height > 0xFFFFFFFF) ) + return E_FAIL; +#endif + const Image& dst = dest[ index ]; + assert( dst.format == metadata.format ); + + if ( src.width != dst.width || src.height != dst.height ) + { + result.Release(); + return E_FAIL; + } + + hr = _PremultiplyAlpha( src, dst ); + if ( FAILED(hr) ) + { + result.Release(); + return hr; + } + } + + return S_OK; +} + +}; // namespace diff --git a/DirectXTex/DirectXTex_Desktop_2010.vcxproj b/DirectXTex/DirectXTex_Desktop_2010.vcxproj index 72244ff..4de0a5d 100644 --- a/DirectXTex/DirectXTex_Desktop_2010.vcxproj +++ b/DirectXTex/DirectXTex_Desktop_2010.vcxproj @@ -396,6 +396,7 @@ + diff --git a/DirectXTex/DirectXTex_Desktop_2010.vcxproj.filters b/DirectXTex/DirectXTex_Desktop_2010.vcxproj.filters index d528cc8..0b295f6 100644 --- a/DirectXTex/DirectXTex_Desktop_2010.vcxproj.filters +++ b/DirectXTex/DirectXTex_Desktop_2010.vcxproj.filters @@ -26,6 +26,7 @@ + diff --git a/DirectXTex/DirectXTex_Desktop_2010_SDK80.vcxproj b/DirectXTex/DirectXTex_Desktop_2010_SDK80.vcxproj index f60fabc..42779ba 100644 --- a/DirectXTex/DirectXTex_Desktop_2010_SDK80.vcxproj +++ b/DirectXTex/DirectXTex_Desktop_2010_SDK80.vcxproj @@ -378,6 +378,7 @@ + diff --git a/DirectXTex/DirectXTex_Desktop_2010_SDK80.vcxproj.filters b/DirectXTex/DirectXTex_Desktop_2010_SDK80.vcxproj.filters index d528cc8..0b295f6 100644 --- a/DirectXTex/DirectXTex_Desktop_2010_SDK80.vcxproj.filters +++ b/DirectXTex/DirectXTex_Desktop_2010_SDK80.vcxproj.filters @@ -26,6 +26,7 @@ + diff --git a/DirectXTex/DirectXTex_Desktop_2012.vcxproj b/DirectXTex/DirectXTex_Desktop_2012.vcxproj index 26a6e43..5c1d018 100644 --- a/DirectXTex/DirectXTex_Desktop_2012.vcxproj +++ b/DirectXTex/DirectXTex_Desktop_2012.vcxproj @@ -403,6 +403,7 @@ + diff --git a/DirectXTex/DirectXTex_Desktop_2012.vcxproj.filters b/DirectXTex/DirectXTex_Desktop_2012.vcxproj.filters index d528cc8..0b295f6 100644 --- a/DirectXTex/DirectXTex_Desktop_2012.vcxproj.filters +++ b/DirectXTex/DirectXTex_Desktop_2012.vcxproj.filters @@ -26,6 +26,7 @@ + diff --git a/DirectXTex/DirectXTex_Windows8.vcxproj b/DirectXTex/DirectXTex_Windows8.vcxproj index 07fa9fe..6beaf36 100644 --- a/DirectXTex/DirectXTex_Windows8.vcxproj +++ b/DirectXTex/DirectXTex_Windows8.vcxproj @@ -603,6 +603,7 @@ + diff --git a/DirectXTex/DirectXTex_Windows8.vcxproj.filters b/DirectXTex/DirectXTex_Windows8.vcxproj.filters index d528cc8..0b295f6 100644 --- a/DirectXTex/DirectXTex_Windows8.vcxproj.filters +++ b/DirectXTex/DirectXTex_Windows8.vcxproj.filters @@ -26,6 +26,7 @@ + diff --git a/Texconv/texconv.cpp b/Texconv/texconv.cpp index ac8e83f..03f4d05 100644 --- a/Texconv/texconv.cpp +++ b/Texconv/texconv.cpp @@ -38,6 +38,7 @@ enum OPTIONS // Note: dwOptions below assumes 32 or less options. OPT_SEPALPHA, OPT_TYPELESS_UNORM, OPT_TYPELESS_FLOAT, + OPT_PREMUL_ALPHA, }; struct SConversion @@ -80,6 +81,7 @@ SValue g_pOptions[] = { L"sepalpha", OPT_SEPALPHA }, { L"tu", OPT_TYPELESS_UNORM }, { L"tf", OPT_TYPELESS_FLOAT }, + { L"pmalpha", OPT_PREMUL_ALPHA }, { nullptr, 0 } }; @@ -336,6 +338,7 @@ void PrintUsage() wprintf( L" -hflip horizonal flip of source image\n"); wprintf( L" -vflip vertical flip of source image\n"); wprintf( L" -sepalpha resize/generate mips alpha channel separately from color channels\n"); + wprintf( L" -pmalpha convert final texture to premultiply alpha\n"); wprintf( L" -t{u|f} DDS files with TYPELESS format is treated as UNORM or FLOAT\n"); wprintf( L" -dword Use DWORD instead of BYTE alignment (DDS input only)\n"); wprintf( L" -dx10 Force use of 'DX10' extended header (DDS output only)\n"); @@ -417,7 +420,8 @@ int __cdecl wmain(_In_ int argc, _In_z_count_(argc) wchar_t* argv[]) dwOptions |= 1 << dwOption; - if( (OPT_NOLOGO != dwOption) && (OPT_SEPALPHA != dwOption) && (OPT_TYPELESS_UNORM != dwOption) && (OPT_TYPELESS_FLOAT != dwOption) + if( (OPT_NOLOGO != dwOption) && (OPT_TYPELESS_UNORM != dwOption) && (OPT_TYPELESS_FLOAT != dwOption) + && (OPT_SEPALPHA != dwOption) && (OPT_PREMUL_ALPHA != dwOption) && (OPT_SRGB != dwOption) && (OPT_SRGBI != dwOption) && (OPT_SRGBO != dwOption) && (OPT_HFLIP != dwOption) && (OPT_VFLIP != dwOption) && (OPT_DDS_DWORD_ALIGN != dwOption) && (OPT_USE_DX10 != dwOption) ) @@ -965,6 +969,36 @@ int __cdecl wmain(_In_ int argc, _In_z_count_(argc) wchar_t* argv[]) image = timage; } + // --- Premultiplied alpha (if requested) -------------------------------------- + if ( ( dwOptions & (1 << OPT_PREMUL_ALPHA) ) + && HasAlpha( info.format ) + && info.format != DXGI_FORMAT_A8_UNORM ) + { + const Image* img = image->GetImage(0,0,0); + assert( img ); + size_t nimg = image->GetImageCount(); + + ScratchImage *timage = new ScratchImage; + if ( !timage ) + { + wprintf( L" ERROR: Memory allocation failed\n" ); + delete image; + goto LError; + } + + hr = PremultiplyAlpha( img, nimg, info, *timage ); + if ( FAILED(hr) ) + { + wprintf( L" FAILED [premultiply alpha] (%x)\n", hr); + delete timage; + delete image; + continue; + } + + delete image; + image = timage; + } + // --- Compress ---------------------------------------------------------------- if ( IsCompressed( tformat ) && (FileType == CODEC_DDS) ) {