diff --git a/DirectXTex/DDS.h b/DirectXTex/DDS.h index 142e79e..ccf802c 100644 --- a/DirectXTex/DDS.h +++ b/DirectXTex/DDS.h @@ -169,20 +169,33 @@ extern __declspec(selectany) const DDS_PIXELFORMAT DDSPF_DX10 = #define DDS_FLAGS_VOLUME 0x00200000 // DDSCAPS2_VOLUME // Subset here matches D3D10_RESOURCE_DIMENSION and D3D11_RESOURCE_DIMENSION -typedef enum DDS_RESOURCE_DIMENSION +enum DDS_RESOURCE_DIMENSION { DDS_DIMENSION_TEXTURE1D = 2, DDS_DIMENSION_TEXTURE2D = 3, DDS_DIMENSION_TEXTURE3D = 4, -} DDS_RESOURCE_DIMENSION; +}; // Subset here matches D3D10_RESOURCE_MISC_FLAG and D3D11_RESOURCE_MISC_FLAG -typedef enum DDS_RESOURCE_MISC_FLAG +enum DDS_RESOURCE_MISC_FLAG { - DDS_RESOURCE_MISC_TEXTURECUBE = 0x4L, -} DDS_RESOURCE_MISC_FLAG; + DDS_RESOURCE_MISC_TEXTURECUBE = 0x4L, +}; -typedef struct +enum DDS_MISC_FLAGS2 +{ + DDS_MISC_FLAGS2_ALPHA_MODE_MASK = 0x3L, +}; + +enum DDS_ALPHA_MODE +{ + DDS_ALPHA_MODE_STRAIGHT = 0, + DDS_ALPHA_MODE_PREMULTIPLIED = 1, + DDS_ALPHA_MODE_4TH_CHANNEL = 2, + DDS_ALPHA_MODE_OPAQUE = 3, +}; + +struct DDS_HEADER { uint32_t dwSize; uint32_t dwFlags; @@ -198,17 +211,20 @@ typedef struct uint32_t dwCaps3; uint32_t dwCaps4; uint32_t dwReserved2; -} DDS_HEADER; +}; -typedef struct +struct DDS_HEADER_DXT10 { DXGI_FORMAT dxgiFormat; uint32_t resourceDimension; uint32_t miscFlag; // see DDS_RESOURCE_MISC_FLAG uint32_t arraySize; - uint32_t reserved; -} DDS_HEADER_DXT10; + uint32_t miscFlags2; // see DDS_MISC_FLAGS2 +}; #pragma pack(pop) +static_assert( sizeof(DDS_HEADER) == 124, "DDS Header size mismatch" ); +static_assert( sizeof(DDS_HEADER_DXT10) == 20, "DDS DX10 Extended Header size mismatch"); + }; // namespace diff --git a/DirectXTex/DirectXTex.h b/DirectXTex/DirectXTex.h index d473dd0..0b6587d 100644 --- a/DirectXTex/DirectXTex.h +++ b/DirectXTex/DirectXTex.h @@ -27,7 +27,7 @@ #include #include -#define DIRECTX_TEX_VERSION 101 +#define DIRECTX_TEX_VERSION 102 #if defined(_MSC_VER) && (_MSC_VER<1610) && !defined(_In_reads_) #define _Analysis_assume_(exp) @@ -99,6 +99,20 @@ namespace DirectX TEX_MISC_TEXTURECUBE = 0x4L, }; + enum TEX_MISC_FLAG2 + { + TEX_MISC2_ALPHA_MODE_MASK = 0x3L, + }; + + enum TEX_ALPHA_MODE + // Matches DDS_ALPHA_MODE, encoded in MISC_FLAGS2 + { + TEX_ALPHA_MODE_STRAIGHT = 0, + TEX_ALPHA_MODE_PREMULTIPLIED = 1, + TEX_ALPHA_MODE_4TH_CHANNEL = 2, + TEX_ALPHA_MODE_OPAQUE = 3, + }; + struct TexMetadata { size_t width; @@ -107,11 +121,19 @@ namespace DirectX size_t arraySize; // For cubemap, this is a multiple of 6 size_t mipLevels; uint32_t miscFlags; + uint32_t miscFlags2; DXGI_FORMAT format; TEX_DIMENSION dimension; size_t ComputeIndex( _In_ size_t mip, _In_ size_t item, _In_ size_t slice ) const; // Returns size_t(-1) to indicate an out-of-range error + + bool IsCubemap() const { return (miscFlags & TEX_MISC_TEXTURECUBE) != 0; } + // Helpers for miscFlags + + bool IsPMAlpha() const { return ((miscFlags2 & TEX_MISC2_ALPHA_MODE_MASK) == TEX_ALPHA_MODE_PREMULTIPLIED) != 0; } + void SetAlphaMode( TEX_ALPHA_MODE mode ) { miscFlags2 = (miscFlags2 & ~TEX_MISC2_ALPHA_MODE_MASK) | static_cast(mode); } + // Helpers for miscFlags2 }; enum DDS_FLAGS @@ -135,6 +157,9 @@ namespace DirectX DDS_FLAGS_FORCE_DX10_EXT = 0x10000, // Always use the 'DX10' header extension for DDS writer (i.e. don't try to write DX9 compatible DDS files) + + DDS_FLAGS_FORCE_DX10_EXT_MISC2 = 0x20000, + // DDS_FLAGS_FORCE_DX10_EXT including miscFlags2 information (result may not be compatible with D3DX10 or D3DX11) }; enum WIC_FLAGS @@ -227,6 +252,8 @@ namespace DirectX uint8_t* GetPixels() const { return _memory; } size_t GetPixelsSize() const { return _size; } + bool IsAlphaAllOpaque() const; + private: size_t _nimages; size_t _size; diff --git a/DirectXTex/DirectXTex.inl b/DirectXTex/DirectXTex.inl index 3f5a2cc..46f0c30 100644 --- a/DirectXTex/DirectXTex.inl +++ b/DirectXTex/DirectXTex.inl @@ -189,6 +189,9 @@ inline bool HasAlpha( DXGI_FORMAT fmt ) case DXGI_FORMAT_R8G8B8A8_SNORM: case DXGI_FORMAT_R8G8B8A8_SINT: case DXGI_FORMAT_A8_UNORM: + case DXGI_FORMAT_BC1_TYPELESS: + case DXGI_FORMAT_BC1_UNORM: + case DXGI_FORMAT_BC1_UNORM_SRGB: case DXGI_FORMAT_BC2_TYPELESS: case DXGI_FORMAT_BC2_UNORM: case DXGI_FORMAT_BC2_UNORM_SRGB: diff --git a/DirectXTex/DirectXTexCompress.cpp b/DirectXTex/DirectXTexCompress.cpp index 19e8ec2..d6c202d 100644 --- a/DirectXTex/DirectXTexCompress.cpp +++ b/DirectXTex/DirectXTexCompress.cpp @@ -426,6 +426,85 @@ static HRESULT _DecompressBC( _In_ const Image& cImage, _In_ const Image& result } +//------------------------------------------------------------------------------------- +bool _IsAlphaAllOpaqueBC( _In_ const Image& cImage ) +{ + if ( !cImage.pixels ) + return false; + + // Image must be a multiple of 4 (degenerate cases of 1x1, 1x2, 2x1, and 2x2 are allowed) + size_t width = cImage.width; + if ( (width % 4) != 0 ) + { + if ( width != 1 && width != 2 ) + return false; + } + + size_t height = cImage.height; + if ( (height % 4) != 0 ) + { + if ( height != 1 && height != 2 ) + return false; + } + + // Promote "typeless" BC formats + DXGI_FORMAT cformat; + switch( cImage.format ) + { + case DXGI_FORMAT_BC1_TYPELESS: cformat = DXGI_FORMAT_BC1_UNORM; break; + case DXGI_FORMAT_BC2_TYPELESS: cformat = DXGI_FORMAT_BC2_UNORM; break; + case DXGI_FORMAT_BC3_TYPELESS: cformat = DXGI_FORMAT_BC3_UNORM; break; + case DXGI_FORMAT_BC7_TYPELESS: cformat = DXGI_FORMAT_BC7_UNORM; break; + default: cformat = cImage.format; break; + } + + // Determine BC format decoder + BC_DECODE pfDecode; + size_t sbpp; + switch(cformat) + { + case DXGI_FORMAT_BC1_UNORM: + case DXGI_FORMAT_BC1_UNORM_SRGB: pfDecode = D3DXDecodeBC1; sbpp = 8; break; + case DXGI_FORMAT_BC2_UNORM: + case DXGI_FORMAT_BC2_UNORM_SRGB: pfDecode = D3DXDecodeBC2; sbpp = 16; break; + case DXGI_FORMAT_BC3_UNORM: + case DXGI_FORMAT_BC3_UNORM_SRGB: pfDecode = D3DXDecodeBC3; sbpp = 16; break; + case DXGI_FORMAT_BC7_UNORM: + case DXGI_FORMAT_BC7_UNORM_SRGB: pfDecode = D3DXDecodeBC7; sbpp = 16; break; + default: + // BC4, BC5, and BC6 don't have alpha channels + return false; + } + + // Scan blocks for non-opaque alpha + static const XMVECTORF32 threshold = { 0.99f, 0.99f, 0.99f, 0.99f }; + + XMVECTOR temp[16]; + const uint8_t *pPixels = cImage.pixels; + for( size_t h=0; h < cImage.height; h += 4 ) + { + const uint8_t *ptr = pPixels; + for( size_t count = 0; count < cImage.rowPitch; count += sbpp ) + { + pfDecode( temp, ptr ); + + for( size_t j = 0; j < 16; ++j ) + { + XMVECTOR alpha = XMVectorSplatW( temp[j] ); + if ( XMVector4Less( alpha, threshold ) ) + return false; + } + + ptr += sbpp; + } + + pPixels += cImage.rowPitch; + } + + return true; +} + + //===================================================================================== // Entry-points //===================================================================================== diff --git a/DirectXTex/DirectXTexD3D11.cpp b/DirectXTex/DirectXTexD3D11.cpp index bc724a3..166fd27 100644 --- a/DirectXTex/DirectXTexD3D11.cpp +++ b/DirectXTex/DirectXTexD3D11.cpp @@ -236,7 +236,7 @@ bool IsSupportedTexture( ID3D11Device* pDevice, const TexMetadata& metadata ) break; case TEX_DIMENSION_TEXTURE2D: - if ( metadata.miscFlags & TEX_MISC_TEXTURECUBE ) + if ( metadata.IsCubemap() ) { if ( !(formatSupport & D3D11_FORMAT_SUPPORT_TEXTURECUBE) ) return false; @@ -505,7 +505,7 @@ HRESULT CreateTextureEx( ID3D11Device* pDevice, const Image* srcImages, size_t n desc.Usage = usage; desc.BindFlags = bindFlags; desc.CPUAccessFlags = cpuAccessFlags; - if (metadata.miscFlags & TEX_MISC_TEXTURECUBE) + if ( metadata.IsCubemap() ) desc.MiscFlags = miscFlags | D3D11_RESOURCE_MISC_TEXTURECUBE; else desc.MiscFlags = miscFlags & ~D3D11_RESOURCE_MISC_TEXTURECUBE; @@ -589,7 +589,7 @@ HRESULT CreateShaderResourceViewEx( ID3D11Device* pDevice, const Image* srcImage break; case TEX_DIMENSION_TEXTURE2D: - if ( metadata.miscFlags & TEX_MISC_TEXTURECUBE ) + if ( metadata.IsCubemap() ) { if (metadata.arraySize > 6) { @@ -685,6 +685,7 @@ HRESULT CaptureTexture( ID3D11Device* pDevice, ID3D11DeviceContext* pContext, ID mdata.arraySize = desc.ArraySize; mdata.mipLevels = desc.MipLevels; mdata.miscFlags = 0; + mdata.miscFlags2 = 0; mdata.format = desc.Format; mdata.dimension = TEX_DIMENSION_TEXTURE1D; @@ -786,6 +787,7 @@ HRESULT CaptureTexture( ID3D11Device* pDevice, ID3D11DeviceContext* pContext, ID mdata.arraySize = desc.ArraySize; mdata.mipLevels = desc.MipLevels; mdata.miscFlags = (desc.MiscFlags & D3D11_RESOURCE_MISC_TEXTURECUBE) ? TEX_MISC_TEXTURECUBE : 0; + mdata.miscFlags2 = 0; mdata.format = desc.Format; mdata.dimension = TEX_DIMENSION_TEXTURE2D; @@ -830,6 +832,7 @@ HRESULT CaptureTexture( ID3D11Device* pDevice, ID3D11DeviceContext* pContext, ID mdata.arraySize = 1; mdata.mipLevels = desc.MipLevels; mdata.miscFlags = 0; + mdata.miscFlags2 = 0; mdata.format = desc.Format; mdata.dimension = TEX_DIMENSION_TEXTURE3D; diff --git a/DirectXTex/DirectXTexDDS.cpp b/DirectXTex/DirectXTexDDS.cpp index ef3487d..5790577 100644 --- a/DirectXTex/DirectXTexDDS.cpp +++ b/DirectXTex/DirectXTexDDS.cpp @@ -39,6 +39,7 @@ enum CONVERSION_FLAGS CONV_FLAGS_8332 = 0x400, // Source is a 8:3:3:2 (16bpp) format CONV_FLAGS_A8P8 = 0x800, // Has an 8-bit palette with an alpha channel CONV_FLAGS_DX10 = 0x10000, // Has the 'DX10' extension header + CONV_FLAGS_PMALPHA = 0x20000, // Contains premultiplied alpha data }; struct LegacyDDS @@ -54,8 +55,8 @@ const LegacyDDS g_LegacyDDSMap[] = { DXGI_FORMAT_BC2_UNORM, CONV_FLAGS_NONE, DDSPF_DXT3 }, // D3DFMT_DXT3 { DXGI_FORMAT_BC3_UNORM, CONV_FLAGS_NONE, DDSPF_DXT5 }, // D3DFMT_DXT5 - { DXGI_FORMAT_BC2_UNORM, CONV_FLAGS_NONE, DDSPF_DXT2 }, // D3DFMT_DXT2 (ignore premultiply) - { DXGI_FORMAT_BC3_UNORM, CONV_FLAGS_NONE, DDSPF_DXT4 }, // D3DFMT_DXT4 (ignore premultiply) + { DXGI_FORMAT_BC2_UNORM, CONV_FLAGS_PMALPHA, DDSPF_DXT2 }, // D3DFMT_DXT2 + { DXGI_FORMAT_BC3_UNORM, CONV_FLAGS_PMALPHA, DDSPF_DXT4 }, // D3DFMT_DXT4 { DXGI_FORMAT_BC4_UNORM, CONV_FLAGS_NONE, DDSPF_BC4_UNORM }, { DXGI_FORMAT_BC4_SNORM, CONV_FLAGS_NONE, DDSPF_BC4_SNORM }, @@ -146,7 +147,7 @@ const LegacyDDS g_LegacyDDSMap[] = // ZBuffer D3DFMT_D16_LOCKABLE // FourCC 82 D3DFMT_D32F_LOCKABLE -static DXGI_FORMAT _GetDXGIFormat( const DDS_PIXELFORMAT& ddpf, DWORD flags, _Inout_opt_ DWORD* convFlags ) +static DXGI_FORMAT _GetDXGIFormat( const DDS_PIXELFORMAT& ddpf, DWORD flags, _Inout_ DWORD& convFlags ) { const size_t MAP_SIZE = sizeof(g_LegacyDDSMap) / sizeof(LegacyDDS); size_t index = 0; @@ -192,8 +193,7 @@ static DXGI_FORMAT _GetDXGIFormat( const DDS_PIXELFORMAT& ddpf, DWORD flags, _In cflags ^= CONV_FLAGS_SWIZZLE; } - if ( convFlags ) - *convFlags = cflags; + convFlags = cflags; return format; } @@ -203,7 +203,7 @@ static DXGI_FORMAT _GetDXGIFormat( const DDS_PIXELFORMAT& ddpf, DWORD flags, _In // Decodes DDS header including optional DX10 extended header //------------------------------------------------------------------------------------- static HRESULT _DecodeDDSHeader( _In_reads_bytes_(size) LPCVOID pSource, size_t size, DWORD flags, _Out_ TexMetadata& metadata, - _Inout_opt_ DWORD* convFlags ) + _Inout_ DWORD& convFlags ) { if ( !pSource ) return E_INVALIDARG; @@ -247,8 +247,7 @@ static HRESULT _DecodeDDSHeader( _In_reads_bytes_(size) LPCVOID pSource, size_t } const DDS_HEADER_DXT10* d3d10ext = reinterpret_cast( (const uint8_t*)pSource + sizeof( uint32_t ) + sizeof(DDS_HEADER) ); - if ( convFlags ) - *convFlags |= CONV_FLAGS_DX10; + convFlags |= CONV_FLAGS_DX10; metadata.arraySize = d3d10ext->arraySize; if ( metadata.arraySize == 0 ) @@ -262,6 +261,10 @@ static HRESULT _DecodeDDSHeader( _In_reads_bytes_(size) LPCVOID pSource, size_t HRESULT_FROM_WIN32( ERROR_INVALID_DATA ); } + static_assert( TEX_MISC_TEXTURECUBE == DDS_RESOURCE_MISC_TEXTURECUBE, "DDS header mismatch"); + + metadata.miscFlags = d3d10ext->miscFlag & ~TEX_MISC_TEXTURECUBE; + switch ( d3d10ext->resourceDimension ) { case DDS_DIMENSION_TEXTURE1D: @@ -309,6 +312,15 @@ static HRESULT _DecodeDDSHeader( _In_reads_bytes_(size) LPCVOID pSource, size_t default: return HRESULT_FROM_WIN32( ERROR_INVALID_DATA ); } + + static_assert( TEX_MISC2_ALPHA_MODE_MASK == DDS_MISC_FLAGS2_ALPHA_MODE_MASK, "DDS header mismatch"); + + static_assert( TEX_ALPHA_MODE_STRAIGHT == DDS_ALPHA_MODE_STRAIGHT, "DDS header mismatch"); + static_assert( TEX_ALPHA_MODE_PREMULTIPLIED == DDS_ALPHA_MODE_PREMULTIPLIED, "DDS header mismatch"); + static_assert( TEX_ALPHA_MODE_4TH_CHANNEL == DDS_ALPHA_MODE_4TH_CHANNEL, "DDS header mismatch"); + static_assert( TEX_ALPHA_MODE_OPAQUE == DDS_ALPHA_MODE_OPAQUE, "DDS header mismatch"); + + metadata.miscFlags2 = d3d10ext->miscFlags2; } else { @@ -345,6 +357,9 @@ static HRESULT _DecodeDDSHeader( _In_reads_bytes_(size) LPCVOID pSource, size_t if ( metadata.format == DXGI_FORMAT_UNKNOWN ) return HRESULT_FROM_WIN32( ERROR_NOT_SUPPORTED ); + + if ( convFlags & CONV_FLAGS_PMALPHA ) + metadata.miscFlags2 |= TEX_ALPHA_MODE_PREMULTIPLIED; } // Special flag for handling BGR DXGI 1.1 formats @@ -354,38 +369,32 @@ static HRESULT _DecodeDDSHeader( _In_reads_bytes_(size) LPCVOID pSource, size_t { case DXGI_FORMAT_B8G8R8A8_UNORM: metadata.format = DXGI_FORMAT_R8G8B8A8_UNORM; - if ( convFlags ) - *convFlags |= CONV_FLAGS_SWIZZLE; + convFlags |= CONV_FLAGS_SWIZZLE; break; case DXGI_FORMAT_B8G8R8X8_UNORM: metadata.format = DXGI_FORMAT_R8G8B8A8_UNORM; - if ( convFlags ) - *convFlags |= CONV_FLAGS_SWIZZLE | CONV_FLAGS_NOALPHA; + convFlags |= CONV_FLAGS_SWIZZLE | CONV_FLAGS_NOALPHA; break; case DXGI_FORMAT_B8G8R8A8_TYPELESS: metadata.format = DXGI_FORMAT_R8G8B8A8_TYPELESS; - if ( convFlags ) - *convFlags |= CONV_FLAGS_SWIZZLE; + convFlags |= CONV_FLAGS_SWIZZLE; break; case DXGI_FORMAT_B8G8R8A8_UNORM_SRGB: metadata.format = DXGI_FORMAT_R8G8B8A8_UNORM_SRGB; - if ( convFlags ) - *convFlags |= CONV_FLAGS_SWIZZLE; + convFlags |= CONV_FLAGS_SWIZZLE; break; case DXGI_FORMAT_B8G8R8X8_TYPELESS: metadata.format = DXGI_FORMAT_R8G8B8A8_TYPELESS; - if ( convFlags ) - *convFlags |= CONV_FLAGS_SWIZZLE | CONV_FLAGS_NOALPHA; + convFlags |= CONV_FLAGS_SWIZZLE | CONV_FLAGS_NOALPHA; break; case DXGI_FORMAT_B8G8R8X8_UNORM_SRGB: metadata.format = DXGI_FORMAT_R8G8B8A8_UNORM_SRGB; - if ( convFlags ) - *convFlags |= CONV_FLAGS_SWIZZLE | CONV_FLAGS_NOALPHA; + convFlags |= CONV_FLAGS_SWIZZLE | CONV_FLAGS_NOALPHA; break; } } @@ -401,12 +410,9 @@ static HRESULT _DecodeDDSHeader( _In_reads_bytes_(size) LPCVOID pSource, size_t case DXGI_FORMAT_B4G4R4A4_UNORM: #endif metadata.format = DXGI_FORMAT_R8G8B8A8_UNORM; - if ( convFlags ) - { - *convFlags |= CONV_FLAGS_EXPAND; - if ( metadata.format == DXGI_FORMAT_B5G6R5_UNORM ) - *convFlags |= CONV_FLAGS_NOALPHA; - } + convFlags |= CONV_FLAGS_EXPAND; + if ( metadata.format == DXGI_FORMAT_B5G6R5_UNORM ) + convFlags |= CONV_FLAGS_NOALPHA; } } @@ -425,12 +431,18 @@ HRESULT _EncodeDDSHeader( const TexMetadata& metadata, DWORD flags, if ( metadata.arraySize > 1 ) { - if ( (metadata.arraySize != 6) || (metadata.dimension != TEX_DIMENSION_TEXTURE2D) || !(metadata.miscFlags & TEX_MISC_TEXTURECUBE) ) + if ( (metadata.arraySize != 6) || (metadata.dimension != TEX_DIMENSION_TEXTURE2D) || !(metadata.IsCubemap()) ) { + // Texture1D arrays, Texture2D arrays, and Cubemap arrays must be stored using 'DX10' extended header flags |= DDS_FLAGS_FORCE_DX10_EXT; } } + if ( flags & DDS_FLAGS_FORCE_DX10_EXT_MISC2 ) + { + flags |= DDS_FLAGS_FORCE_DX10_EXT; + } + DDS_PIXELFORMAT ddpf = { 0 }; if ( !(flags & DDS_FLAGS_FORCE_DX10_EXT) ) { @@ -445,8 +457,8 @@ HRESULT _EncodeDDSHeader( const TexMetadata& metadata, DWORD flags, case DXGI_FORMAT_R8G8_B8G8_UNORM: memcpy_s( &ddpf, sizeof(ddpf), &DDSPF_R8G8_B8G8, sizeof(DDS_PIXELFORMAT) ); break; case DXGI_FORMAT_G8R8_G8B8_UNORM: memcpy_s( &ddpf, sizeof(ddpf), &DDSPF_G8R8_G8B8, sizeof(DDS_PIXELFORMAT) ); break; case DXGI_FORMAT_BC1_UNORM: memcpy_s( &ddpf, sizeof(ddpf), &DDSPF_DXT1, sizeof(DDS_PIXELFORMAT) ); break; - case DXGI_FORMAT_BC2_UNORM: memcpy_s( &ddpf, sizeof(ddpf), &DDSPF_DXT3, sizeof(DDS_PIXELFORMAT) ); break; - case DXGI_FORMAT_BC3_UNORM: memcpy_s( &ddpf, sizeof(ddpf), &DDSPF_DXT5, sizeof(DDS_PIXELFORMAT) ); break; + case DXGI_FORMAT_BC2_UNORM: memcpy_s( &ddpf, sizeof(ddpf), metadata.IsPMAlpha() ? (&DDSPF_DXT2) : (&DDSPF_DXT3), sizeof(DDS_PIXELFORMAT) ); break; + case DXGI_FORMAT_BC3_UNORM: memcpy_s( &ddpf, sizeof(ddpf), metadata.IsPMAlpha() ? (&DDSPF_DXT4) : (&DDSPF_DXT5), sizeof(DDS_PIXELFORMAT) ); break; case DXGI_FORMAT_BC4_UNORM: memcpy_s( &ddpf, sizeof(ddpf), &DDSPF_BC4_UNORM, sizeof(DDS_PIXELFORMAT) ); break; case DXGI_FORMAT_BC4_SNORM: memcpy_s( &ddpf, sizeof(ddpf), &DDSPF_BC4_SNORM, sizeof(DDS_PIXELFORMAT) ); break; case DXGI_FORMAT_BC5_UNORM: memcpy_s( &ddpf, sizeof(ddpf), &DDSPF_BC5_UNORM, sizeof(DDS_PIXELFORMAT) ); break; @@ -547,7 +559,7 @@ HRESULT _EncodeDDSHeader( const TexMetadata& metadata, DWORD flags, header->dwWidth = static_cast( metadata.width ); header->dwDepth = 1; - if ( metadata.miscFlags & TEX_MISC_TEXTURECUBE ) + if ( metadata.IsCubemap() ) { header->dwCaps |= DDS_SURFACE_FLAGS_CUBEMAP; header->dwCaps2 |= DDS_CUBEMAP_ALLFACES; @@ -609,6 +621,10 @@ HRESULT _EncodeDDSHeader( const TexMetadata& metadata, DWORD flags, return E_INVALIDARG; #endif + static_assert( TEX_MISC_TEXTURECUBE == DDS_RESOURCE_MISC_TEXTURECUBE, "DDS header mismatch"); + + ext->miscFlag = metadata.miscFlags & ~TEX_MISC_TEXTURECUBE; + if ( metadata.miscFlags & TEX_MISC_TEXTURECUBE ) { ext->miscFlag |= TEX_MISC_TEXTURECUBE; @@ -619,6 +635,19 @@ HRESULT _EncodeDDSHeader( const TexMetadata& metadata, DWORD flags, { ext->arraySize = static_cast( metadata.arraySize ); } + + static_assert( TEX_MISC2_ALPHA_MODE_MASK == DDS_MISC_FLAGS2_ALPHA_MODE_MASK, "DDS header mismatch"); + + static_assert( TEX_ALPHA_MODE_STRAIGHT == DDS_ALPHA_MODE_STRAIGHT, "DDS header mismatch"); + static_assert( TEX_ALPHA_MODE_PREMULTIPLIED == DDS_ALPHA_MODE_PREMULTIPLIED, "DDS header mismatch"); + static_assert( TEX_ALPHA_MODE_4TH_CHANNEL == DDS_ALPHA_MODE_4TH_CHANNEL, "DDS header mismatch"); + static_assert( TEX_ALPHA_MODE_OPAQUE == DDS_ALPHA_MODE_OPAQUE, "DDS header mismatch"); + + if ( flags & DDS_FLAGS_FORCE_DX10_EXT_MISC2 ) + { + // This was formerly 'reserved'. D3DX10 and D3DX11 will fail if this value is anything other than 0 + ext->miscFlags2 = metadata.miscFlags2; + } } else { @@ -891,7 +920,7 @@ static HRESULT _CopyImage( _In_reads_bytes_(size) const void* pPixels, _In_ size if ( !size ) return E_FAIL; - + if ( convFlags & CONV_FLAGS_EXPAND ) { if ( convFlags & CONV_FLAGS_888 ) @@ -1154,7 +1183,8 @@ HRESULT GetMetadataFromDDSMemory( LPCVOID pSource, size_t size, DWORD flags, Tex if ( !pSource || size == 0 ) return E_INVALIDARG; - return _DecodeDDSHeader( pSource, size, flags, metadata, 0 ); + DWORD convFlags = 0; + return _DecodeDDSHeader( pSource, size, flags, metadata, convFlags ); } _Use_decl_annotations_ @@ -1213,7 +1243,8 @@ HRESULT GetMetadataFromDDSFile( LPCWSTR szFile, DWORD flags, TexMetadata& metada return HRESULT_FROM_WIN32( GetLastError() ); } - return _DecodeDDSHeader( header, bytesRead, flags, metadata, 0 ); + DWORD convFlags = 0; + return _DecodeDDSHeader( header, bytesRead, flags, metadata, convFlags ); } @@ -1230,7 +1261,7 @@ HRESULT LoadFromDDSMemory( LPCVOID pSource, size_t size, DWORD flags, TexMetadat DWORD convFlags = 0; TexMetadata mdata; - HRESULT hr = _DecodeDDSHeader( pSource, size, flags, mdata, &convFlags ); + HRESULT hr = _DecodeDDSHeader( pSource, size, flags, mdata, convFlags ); if ( FAILED(hr) ) return hr; @@ -1334,7 +1365,7 @@ HRESULT LoadFromDDSFile( LPCWSTR szFile, DWORD flags, TexMetadata* metadata, Scr DWORD convFlags = 0; TexMetadata mdata; - HRESULT hr = _DecodeDDSHeader( header, bytesRead, flags, mdata, &convFlags ); + HRESULT hr = _DecodeDDSHeader( header, bytesRead, flags, mdata, convFlags ); if ( FAILED(hr) ) return hr; diff --git a/DirectXTex/DirectXTexImage.cpp b/DirectXTex/DirectXTexImage.cpp index 007d507..f834e26 100644 --- a/DirectXTex/DirectXTexImage.cpp +++ b/DirectXTex/DirectXTexImage.cpp @@ -20,6 +20,7 @@ namespace DirectX extern bool _CalculateMipLevels( _In_ size_t width, _In_ size_t height, _Inout_ size_t& mipLevels ); extern bool _CalculateMipLevels3D( _In_ size_t width, _In_ size_t height, _In_ size_t depth, _Inout_ size_t& mipLevels ); +extern bool _IsAlphaAllOpaqueBC( _In_ const Image& cImage ); //------------------------------------------------------------------------------------- // Determines number of image array entries and pixel size @@ -253,7 +254,7 @@ HRESULT ScratchImage::Initialize( const TexMetadata& mdata ) if ( !mdata.width || !mdata.height || mdata.depth != 1 || !mdata.arraySize ) return E_INVALIDARG; - if ( mdata.miscFlags & TEX_MISC_TEXTURECUBE ) + if ( mdata.IsCubemap() ) { if ( (mdata.arraySize % 6) != 0 ) return E_INVALIDARG; @@ -282,7 +283,8 @@ HRESULT ScratchImage::Initialize( const TexMetadata& mdata ) _metadata.depth = mdata.depth; _metadata.arraySize = mdata.arraySize; _metadata.mipLevels = mipLevels; - _metadata.miscFlags = mdata.miscFlags & TEX_MISC_TEXTURECUBE; + _metadata.miscFlags = mdata.miscFlags; + _metadata.miscFlags2 = mdata.miscFlags2; _metadata.format = mdata.format; _metadata.dimension = mdata.dimension; @@ -345,6 +347,7 @@ HRESULT ScratchImage::Initialize2D( DXGI_FORMAT fmt, size_t width, size_t height _metadata.arraySize = arraySize; _metadata.mipLevels = mipLevels; _metadata.miscFlags = 0; + _metadata.miscFlags2 = 0; _metadata.format = fmt; _metadata.dimension = TEX_DIMENSION_TEXTURE2D; @@ -391,6 +394,7 @@ HRESULT ScratchImage::Initialize3D( DXGI_FORMAT fmt, size_t width, size_t height _metadata.arraySize = 1; // Direct3D 10.x/11 does not support arrays of 3D textures _metadata.mipLevels = mipLevels; _metadata.miscFlags = 0; + _metadata.miscFlags2 = 0; _metadata.format = fmt; _metadata.dimension = TEX_DIMENSION_TEXTURE3D; @@ -684,4 +688,54 @@ const Image* ScratchImage::GetImage(size_t mip, size_t item, size_t slice) const return &_image[index]; } +bool ScratchImage::IsAlphaAllOpaque() const +{ + if ( !HasAlpha( _metadata.format ) ) + return true; + + if ( IsCompressed( _metadata.format ) ) + { + for( size_t index = 0; index < _nimages; ++index ) + { + if ( !_IsAlphaAllOpaqueBC( _image[ index ] ) ) + return false; + } + } + else + { + ScopedAlignedArrayXMVECTOR scanline( reinterpret_cast( _aligned_malloc( (sizeof(XMVECTOR)*_metadata.width), 16 ) ) ); + if ( !scanline ) + return false; + + static const XMVECTORF32 threshold = { 0.99f, 0.99f, 0.99f, 0.99f }; + + for( size_t index = 0; index < _nimages; ++index ) + { + const Image& img = _image[ index ]; + + const uint8_t *pPixels = img.pixels; + assert( pPixels ); + + for( size_t h = 0; h < img.height; ++h ) + { + if ( !_LoadScanline( scanline.get(), img.width, pPixels, img.rowPitch, img.format ) ) + return false; + + XMVECTOR* ptr = scanline.get(); + for( size_t w = 0; w < img.width; ++w ) + { + XMVECTOR alpha = XMVectorSplatW( *ptr ); + if ( XMVector4Less( alpha, threshold ) ) + return false; + ++ptr; + } + + pPixels += img.rowPitch; + } + } + } + + return true; +} + }; // namespace diff --git a/DirectXTex/DirectXTexPMAlpha.cpp b/DirectXTex/DirectXTexPMAlpha.cpp index c58e698..a168692 100644 --- a/DirectXTex/DirectXTexPMAlpha.cpp +++ b/DirectXTex/DirectXTexPMAlpha.cpp @@ -123,7 +123,15 @@ HRESULT PremultiplyAlpha( const Image* srcImages, size_t nimages, const TexMetad return E_INVALIDARG; #endif - HRESULT hr = result.Initialize( metadata ); + if ( metadata.IsPMAlpha() ) + { + // Already premultiplied + return E_FAIL; + } + + TexMetadata mdata2 = metadata; + mdata2.SetAlphaMode(TEX_ALPHA_MODE_PREMULTIPLIED); + HRESULT hr = result.Initialize( mdata2 ); if ( FAILED(hr) ) return hr; diff --git a/Texconv/texconv.cpp b/Texconv/texconv.cpp index 7863398..f9d2ef9 100644 --- a/Texconv/texconv.cpp +++ b/Texconv/texconv.cpp @@ -270,7 +270,7 @@ void PrintInfo( const TexMetadata& info ) break; case TEX_DIMENSION_TEXTURE2D: - if ( info.miscFlags & TEX_MISC_TEXTURECUBE ) + if ( info.IsCubemap() ) { wprintf( (info.arraySize > 1) ? L" CubeArray" : L" Cube" ); } @@ -717,6 +717,7 @@ int __cdecl wmain(_In_ int argc, _In_z_count_(argc) wchar_t* argv[]) assert( info.arraySize == tinfo.arraySize ); assert( info.mipLevels == tinfo.mipLevels ); assert( info.miscFlags == tinfo.miscFlags ); + assert( info.miscFlags2 == tinfo.miscFlags2 ); assert( info.dimension == tinfo.dimension ); delete image; @@ -724,7 +725,6 @@ int __cdecl wmain(_In_ int argc, _In_z_count_(argc) wchar_t* argv[]) } // --- Flip/Rotate ------------------------------------------------------------- - // TODO - OPT_ROTATE ? (90, 180, 270) if ( dwOptions & ( (1 << OPT_HFLIP) | (1 << OPT_VFLIP) ) ) { ScratchImage *timage = new ScratchImage; @@ -765,6 +765,7 @@ int __cdecl wmain(_In_ int argc, _In_z_count_(argc) wchar_t* argv[]) assert( info.arraySize == tinfo.arraySize ); assert( info.mipLevels == tinfo.mipLevels ); assert( info.miscFlags == tinfo.miscFlags ); + assert( info.miscFlags2 == tinfo.miscFlags2 ); assert( info.format == tinfo.format ); assert( info.dimension == tinfo.dimension ); @@ -802,6 +803,7 @@ int __cdecl wmain(_In_ int argc, _In_z_count_(argc) wchar_t* argv[]) assert( info.depth == tinfo.depth ); assert( info.arraySize == tinfo.arraySize ); assert( info.miscFlags == tinfo.miscFlags ); + assert( info.miscFlags2 == tinfo.miscFlags2 ); assert( info.format == tinfo.format ); assert( info.dimension == tinfo.dimension ); @@ -840,6 +842,7 @@ int __cdecl wmain(_In_ int argc, _In_z_count_(argc) wchar_t* argv[]) assert( info.arraySize == tinfo.arraySize ); assert( info.mipLevels == tinfo.mipLevels ); assert( info.miscFlags == tinfo.miscFlags ); + assert( info.miscFlags2 == tinfo.miscFlags2 ); assert( info.dimension == tinfo.dimension ); delete image; @@ -963,6 +966,7 @@ int __cdecl wmain(_In_ int argc, _In_z_count_(argc) wchar_t* argv[]) assert( info.arraySize == tinfo.arraySize ); assert( info.mipLevels == tinfo.mipLevels ); assert( info.miscFlags == tinfo.miscFlags ); + assert( info.miscFlags2 == tinfo.miscFlags2 ); assert( info.dimension == tinfo.dimension ); delete image; @@ -974,29 +978,48 @@ int __cdecl wmain(_In_ int argc, _In_z_count_(argc) wchar_t* argv[]) && 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 ) + if ( info.IsPMAlpha() ) { - wprintf( L" ERROR: Memory allocation failed\n" ); - delete image; - goto LError; + printf("WARNING: Image is already using premultiplied alpha\n"); } - - hr = PremultiplyAlpha( img, nimg, info, *timage ); - if ( FAILED(hr) ) + else { - wprintf( L" FAILED [premultiply alpha] (%x)\n", hr); - delete timage; - delete image; - continue; - } + const Image* img = image->GetImage(0,0,0); + assert( img ); + size_t nimg = image->GetImageCount(); - delete image; - image = timage; + 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; + } + + const TexMetadata& tinfo = timage->GetMetadata(); + info.miscFlags2 = tinfo.miscFlags2; + + assert( info.width == tinfo.width ); + assert( info.height == tinfo.height ); + assert( info.depth == tinfo.depth ); + assert( info.arraySize == tinfo.arraySize ); + assert( info.mipLevels == tinfo.mipLevels ); + assert( info.miscFlags == tinfo.miscFlags ); + assert( info.miscFlags2 == tinfo.miscFlags2 ); + assert( info.dimension == tinfo.dimension ); + + delete image; + image = timage; + } } // --- Compress ---------------------------------------------------------------- @@ -1032,12 +1055,35 @@ int __cdecl wmain(_In_ int argc, _In_z_count_(argc) wchar_t* argv[]) assert( info.arraySize == tinfo.arraySize ); assert( info.mipLevels == tinfo.mipLevels ); assert( info.miscFlags == tinfo.miscFlags ); + assert( info.miscFlags2 == tinfo.miscFlags2 ); assert( info.dimension == tinfo.dimension ); delete image; image = timage; } + // --- Set alpha mode ---------------------------------------------------------- + if ( HasAlpha( info.format ) + && info.format != DXGI_FORMAT_A8_UNORM ) + { + if ( image->IsAlphaAllOpaque() ) + { + info.SetAlphaMode(TEX_ALPHA_MODE_OPAQUE); + } + else if ( info.IsPMAlpha() ) + { + // Aleady set TEX_ALPHA_MODE_PREMULTIPLIED + } + else if ( dwOptions & (1 << OPT_SEPALPHA) ) + { + info.SetAlphaMode(TEX_ALPHA_MODE_4TH_CHANNEL); + } + } + else + { + info.miscFlags2 &= ~TEX_MISC2_ALPHA_MODE_MASK; + } + // --- Save result ------------------------------------------------------------- { const Image* img = image->GetImage(0,0,0); @@ -1074,7 +1120,7 @@ int __cdecl wmain(_In_ int argc, _In_z_count_(argc) wchar_t* argv[]) { case CODEC_DDS: hr = SaveToDDSFile( img, nimg, info, - (dwOptions & (1 << OPT_USE_DX10) ) ? DDS_FLAGS_FORCE_DX10_EXT : DDS_FLAGS_NONE, + (dwOptions & (1 << OPT_USE_DX10) ) ? (DDS_FLAGS_FORCE_DX10_EXT|DDS_FLAGS_FORCE_DX10_EXT_MISC2) : DDS_FLAGS_NONE, pConv->szDest ); break;