mirror of
https://github.com/microsoft/DirectXTex.git
synced 2025-07-13 05:30:14 +02:00
DirectXTex updated to read & write new miscFlags2 alpha mode metadata for DDS files
- Added helper to see if a texure's alpha channel is all opaque - Reads and writes DXT2 and DXT4 DDS files - Updated PremultiplyAlpha function to set miscFlags2 appropriately - Texconv updated for new feature, "-dx10" switch now allows write of miscFlags2 data
This commit is contained in:
parent
ee978ad449
commit
0d382fbe25
@ -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
|
||||
|
@ -27,7 +27,7 @@
|
||||
#include <dxgiformat.h>
|
||||
#include <d3d11.h>
|
||||
|
||||
#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<uint32_t>(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;
|
||||
|
@ -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:
|
||||
|
@ -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
|
||||
//=====================================================================================
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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 DDS_HEADER_DXT10*>( (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<uint32_t>( 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<UINT>( 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
|
||||
{
|
||||
@ -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;
|
||||
|
||||
|
@ -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<XMVECTOR*>( _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
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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;
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user