DirectXTex: support writing DDS files where input Image struct rowPitch is not 1-byte aligned

This commit is contained in:
walbourn_cp 2014-02-20 15:24:34 -08:00
parent 76b79c42b5
commit d6976ed850
4 changed files with 214 additions and 57 deletions

View File

@ -64,6 +64,7 @@ namespace DirectX
{
CP_FLAGS_NONE = 0x0, // Normal operation
CP_FLAGS_LEGACY_DWORD = 0x1, // Assume pitch is DWORD aligned instead of BYTE aligned
CP_FLAGS_PARAGRAPH = 0x2, // Assume pitch is 16-byte aligned instead of BYTE aligned
CP_FLAGS_24BPP = 0x10000, // Override with a legacy 24 bits-per-pixel format size
CP_FLAGS_16BPP = 0x20000, // Override with a legacy 16 bits-per-pixel format size
CP_FLAGS_8BPP = 0x40000, // Override with a legacy 8 bits-per-pixel format size
@ -238,17 +239,17 @@ namespace DirectX
ScratchImage& operator= (ScratchImage&& moveFrom);
HRESULT Initialize( _In_ const TexMetadata& mdata );
HRESULT Initialize( _In_ const TexMetadata& mdata, _In_ DWORD flags = CP_FLAGS_NONE );
HRESULT Initialize1D( _In_ DXGI_FORMAT fmt, _In_ size_t length, _In_ size_t arraySize, _In_ size_t mipLevels );
HRESULT Initialize2D( _In_ DXGI_FORMAT fmt, _In_ size_t width, _In_ size_t height, _In_ size_t arraySize, _In_ size_t mipLevels );
HRESULT Initialize3D( _In_ DXGI_FORMAT fmt, _In_ size_t width, _In_ size_t height, _In_ size_t depth, _In_ size_t mipLevels );
HRESULT InitializeCube( _In_ DXGI_FORMAT fmt, _In_ size_t width, _In_ size_t height, _In_ size_t nCubes, _In_ size_t mipLevels );
HRESULT Initialize1D( _In_ DXGI_FORMAT fmt, _In_ size_t length, _In_ size_t arraySize, _In_ size_t mipLevels, _In_ DWORD flags = CP_FLAGS_NONE );
HRESULT Initialize2D( _In_ DXGI_FORMAT fmt, _In_ size_t width, _In_ size_t height, _In_ size_t arraySize, _In_ size_t mipLevels, _In_ DWORD flags = CP_FLAGS_NONE );
HRESULT Initialize3D( _In_ DXGI_FORMAT fmt, _In_ size_t width, _In_ size_t height, _In_ size_t depth, _In_ size_t mipLevels, _In_ DWORD flags = CP_FLAGS_NONE );
HRESULT InitializeCube( _In_ DXGI_FORMAT fmt, _In_ size_t width, _In_ size_t height, _In_ size_t nCubes, _In_ size_t mipLevels, _In_ DWORD flags = CP_FLAGS_NONE );
HRESULT InitializeFromImage( _In_ const Image& srcImage, _In_ bool allow1D = false );
HRESULT InitializeArrayFromImages( _In_reads_(nImages) const Image* images, _In_ size_t nImages, _In_ bool allow1D = false );
HRESULT InitializeCubeFromImages( _In_reads_(nImages) const Image* images, _In_ size_t nImages );
HRESULT Initialize3DFromImages( _In_reads_(depth) const Image* images, _In_ size_t depth );
HRESULT InitializeFromImage( _In_ const Image& srcImage, _In_ bool allow1D = false, _In_ DWORD flags = CP_FLAGS_NONE );
HRESULT InitializeArrayFromImages( _In_reads_(nImages) const Image* images, _In_ size_t nImages, _In_ bool allow1D = false, _In_ DWORD flags = CP_FLAGS_NONE );
HRESULT InitializeCubeFromImages( _In_reads_(nImages) const Image* images, _In_ size_t nImages, _In_ DWORD flags = CP_FLAGS_NONE );
HRESULT Initialize3DFromImages( _In_reads_(depth) const Image* images, _In_ size_t depth, _In_ DWORD flags = CP_FLAGS_NONE );
void Release();

View File

@ -1619,11 +1619,28 @@ HRESULT SaveToDDSMemory( const Image* images, size_t nimages, const TexMetadata&
if ( FAILED(hr) )
return hr;
bool fastpath = true;
for( size_t i = 0; i < nimages; ++i )
{
required += images[ i ].slicePitch;
if ( !images[ i ].pixels )
return E_POINTER;
if ( images[ i ].format != metadata.format )
return E_FAIL;
size_t ddsRowPitch, ddsSlicePitch;
ComputePitch( metadata.format, images[ i ].width, images[ i ].height, ddsRowPitch, ddsSlicePitch, CP_FLAGS_NONE );
assert( images[ i ].rowPitch > 0 );
assert( images[ i ].slicePitch > 0 );
if ( ( images[ i ].rowPitch != ddsRowPitch ) || ( images[ i ].slicePitch != ddsSlicePitch ) )
{
fastpath = false;
}
required += ddsSlicePitch;
}
assert( required > 0 );
@ -1669,14 +1686,47 @@ HRESULT SaveToDDSMemory( const Image* images, size_t nimages, const TexMetadata&
return E_FAIL;
}
if ( fastpath )
{
size_t pixsize = images[ index ].slicePitch;
if ( memcpy_s( pDestination, remaining, images[ index ].pixels, pixsize ) )
{
blob.Release();
return E_FAIL;
}
pDestination += pixsize;
remaining -= pixsize;
}
else
{
size_t ddsRowPitch, ddsSlicePitch;
ComputePitch( metadata.format, images[ index ].width, images[ index ].height, ddsRowPitch, ddsSlicePitch, CP_FLAGS_NONE );
size_t rowPitch = images[ index ].rowPitch;
const uint8_t * __restrict sPtr = reinterpret_cast<const uint8_t*>(images[ index ].pixels);
uint8_t * __restrict dPtr = reinterpret_cast<uint8_t*>(pDestination);
size_t lines = ComputeScanlines( metadata.format, images[ index ].height );
size_t csize = std::min<size_t>( rowPitch, ddsRowPitch );
size_t tremaining = remaining;
for( size_t j = 0; j < lines; ++j )
{
if ( memcpy_s( dPtr, tremaining, sPtr, csize ) )
{
blob.Release();
return E_FAIL;
}
sPtr += rowPitch;
dPtr += ddsRowPitch;
tremaining -= ddsRowPitch;
}
pDestination += ddsSlicePitch;
remaining -= ddsSlicePitch;
}
++index;
}
@ -1705,14 +1755,47 @@ HRESULT SaveToDDSMemory( const Image* images, size_t nimages, const TexMetadata&
return E_FAIL;
}
if ( fastpath )
{
size_t pixsize = images[ index ].slicePitch;
if ( memcpy_s( pDestination, remaining, images[ index ].pixels, pixsize ) )
{
blob.Release();
return E_FAIL;
}
pDestination += pixsize;
remaining -= pixsize;
}
else
{
size_t ddsRowPitch, ddsSlicePitch;
ComputePitch( metadata.format, images[ index ].width, images[ index ].height, ddsRowPitch, ddsSlicePitch, CP_FLAGS_NONE );
size_t rowPitch = images[ index ].rowPitch;
const uint8_t * __restrict sPtr = reinterpret_cast<const uint8_t*>(images[ index ].pixels);
uint8_t * __restrict dPtr = reinterpret_cast<uint8_t*>(pDestination);
size_t lines = ComputeScanlines( metadata.format, images[ index ].height );
size_t csize = std::min<size_t>( rowPitch, ddsRowPitch );
size_t tremaining = remaining;
for( size_t j = 0; j < lines; ++j )
{
if ( memcpy_s( dPtr, tremaining, sPtr, csize ) )
{
blob.Release();
return E_FAIL;
}
sPtr += rowPitch;
dPtr += ddsRowPitch;
tremaining -= ddsRowPitch;
}
pDestination += ddsSlicePitch;
remaining -= ddsSlicePitch;
}
++index;
}
@ -1788,18 +1871,52 @@ HRESULT SaveToDDSFile( const Image* images, size_t nimages, const TexMetadata& m
if ( !images[ index ].pixels )
return E_POINTER;
size_t pixsize = images[ index ].slicePitch;
assert( images[ index ].rowPitch > 0 );
assert( images[ index ].slicePitch > 0 );
if ( !WriteFile( hFile.get(), images[ index ].pixels, static_cast<DWORD>( pixsize ), &bytesWritten, 0 ) )
size_t ddsRowPitch, ddsSlicePitch;
ComputePitch( metadata.format, images[ index ].width, images[ index ].height, ddsRowPitch, ddsSlicePitch, CP_FLAGS_NONE );
if ( images[ index ].slicePitch == ddsSlicePitch )
{
if ( !WriteFile( hFile.get(), images[ index ].pixels, static_cast<DWORD>( ddsSlicePitch ), &bytesWritten, 0 ) )
{
return HRESULT_FROM_WIN32( GetLastError() );
}
if ( bytesWritten != pixsize )
if ( bytesWritten != ddsSlicePitch )
{
return E_FAIL;
}
}
else
{
size_t rowPitch = images[ index ].rowPitch;
if ( rowPitch < ddsRowPitch )
{
// DDS uses 1-byte alignment, so if this is happening then the input pitch isn't actually a full line of data
return E_FAIL;
}
const uint8_t * __restrict sPtr = reinterpret_cast<const uint8_t*>(images[ index ].pixels);
size_t lines = ComputeScanlines( metadata.format, images[ index ].height );
for( size_t j = 0; j < lines; ++j )
{
if ( !WriteFile( hFile.get(), sPtr, static_cast<DWORD>( ddsRowPitch ), &bytesWritten, 0 ) )
{
return HRESULT_FROM_WIN32( GetLastError() );
}
if ( bytesWritten != ddsRowPitch )
{
return E_FAIL;
}
sPtr += rowPitch;
}
}
}
}
}
break;
@ -1822,18 +1939,52 @@ HRESULT SaveToDDSFile( const Image* images, size_t nimages, const TexMetadata& m
if ( !images[ index ].pixels )
return E_POINTER;
size_t pixsize = images[ index ].slicePitch;
assert( images[ index ].rowPitch > 0 );
assert( images[ index ].slicePitch > 0 );
if ( !WriteFile( hFile.get(), images[ index ].pixels, static_cast<DWORD>( pixsize ), &bytesWritten, 0 ) )
size_t ddsRowPitch, ddsSlicePitch;
ComputePitch( metadata.format, images[ index ].width, images[ index ].height, ddsRowPitch, ddsSlicePitch, CP_FLAGS_NONE );
if ( images[ index ].slicePitch == ddsSlicePitch )
{
if ( !WriteFile( hFile.get(), images[ index ].pixels, static_cast<DWORD>( ddsSlicePitch ), &bytesWritten, 0 ) )
{
return HRESULT_FROM_WIN32( GetLastError() );
}
if ( bytesWritten != pixsize )
if ( bytesWritten != ddsSlicePitch )
{
return E_FAIL;
}
}
else
{
size_t rowPitch = images[ index ].rowPitch;
if ( rowPitch < ddsRowPitch )
{
// DDS uses 1-byte alignment, so if this is happening then the input pitch isn't actually a full line of data
return E_FAIL;
}
const uint8_t * __restrict sPtr = reinterpret_cast<const uint8_t*>(images[ index ].pixels);
size_t lines = ComputeScanlines( metadata.format, images[ index ].height );
for( size_t j = 0; j < lines; ++j )
{
if ( !WriteFile( hFile.get(), sPtr, static_cast<DWORD>( ddsRowPitch ), &bytesWritten, 0 ) )
{
return HRESULT_FROM_WIN32( GetLastError() );
}
if ( bytesWritten != ddsRowPitch )
{
return E_FAIL;
}
sPtr += rowPitch;
}
}
}
if ( d > 1 )
d >>= 1;

View File

@ -254,7 +254,7 @@ ScratchImage& ScratchImage::operator= (ScratchImage&& moveFrom)
// Methods
//-------------------------------------------------------------------------------------
_Use_decl_annotations_
HRESULT ScratchImage::Initialize( const TexMetadata& mdata )
HRESULT ScratchImage::Initialize( const TexMetadata& mdata, DWORD flags )
{
if ( !IsValid(mdata.format) )
return E_INVALIDARG;
@ -322,7 +322,7 @@ HRESULT ScratchImage::Initialize( const TexMetadata& mdata )
_metadata.dimension = mdata.dimension;
size_t pixelSize, nimages;
_DetermineImageArray( _metadata, CP_FLAGS_NONE, nimages, pixelSize );
_DetermineImageArray( _metadata, flags, nimages, pixelSize );
_image = new (std::nothrow) Image[ nimages ];
if ( !_image )
@ -338,7 +338,7 @@ HRESULT ScratchImage::Initialize( const TexMetadata& mdata )
return E_OUTOFMEMORY;
}
_size = pixelSize;
if ( !_SetupImageArray( _memory, pixelSize, _metadata, CP_FLAGS_NONE, _image, nimages ) )
if ( !_SetupImageArray( _memory, pixelSize, _metadata, flags, _image, nimages ) )
{
Release();
return E_FAIL;
@ -348,7 +348,7 @@ HRESULT ScratchImage::Initialize( const TexMetadata& mdata )
}
_Use_decl_annotations_
HRESULT ScratchImage::Initialize1D( DXGI_FORMAT fmt, size_t length, size_t arraySize, size_t mipLevels )
HRESULT ScratchImage::Initialize1D( DXGI_FORMAT fmt, size_t length, size_t arraySize, size_t mipLevels, DWORD flags )
{
if ( !length || !arraySize )
return E_INVALIDARG;
@ -357,7 +357,7 @@ HRESULT ScratchImage::Initialize1D( DXGI_FORMAT fmt, size_t length, size_t array
return HRESULT_FROM_WIN32( ERROR_NOT_SUPPORTED );
// 1D is a special case of the 2D case
HRESULT hr = Initialize2D( fmt, length, 1, arraySize, mipLevels );
HRESULT hr = Initialize2D( fmt, length, 1, arraySize, mipLevels, flags );
if ( FAILED(hr) )
return hr;
@ -367,7 +367,7 @@ HRESULT ScratchImage::Initialize1D( DXGI_FORMAT fmt, size_t length, size_t array
}
_Use_decl_annotations_
HRESULT ScratchImage::Initialize2D( DXGI_FORMAT fmt, size_t width, size_t height, size_t arraySize, size_t mipLevels )
HRESULT ScratchImage::Initialize2D( DXGI_FORMAT fmt, size_t width, size_t height, size_t arraySize, size_t mipLevels, DWORD flags )
{
if ( !IsValid(fmt) || !width || !height || !arraySize )
return E_INVALIDARG;
@ -391,7 +391,7 @@ HRESULT ScratchImage::Initialize2D( DXGI_FORMAT fmt, size_t width, size_t height
_metadata.dimension = TEX_DIMENSION_TEXTURE2D;
size_t pixelSize, nimages;
_DetermineImageArray( _metadata, CP_FLAGS_NONE, nimages, pixelSize );
_DetermineImageArray( _metadata, flags, nimages, pixelSize );
_image = new (std::nothrow) Image[ nimages ];
if ( !_image )
@ -407,7 +407,7 @@ HRESULT ScratchImage::Initialize2D( DXGI_FORMAT fmt, size_t width, size_t height
return E_OUTOFMEMORY;
}
_size = pixelSize;
if ( !_SetupImageArray( _memory, pixelSize, _metadata, CP_FLAGS_NONE, _image, nimages ) )
if ( !_SetupImageArray( _memory, pixelSize, _metadata, flags, _image, nimages ) )
{
Release();
return E_FAIL;
@ -417,7 +417,7 @@ HRESULT ScratchImage::Initialize2D( DXGI_FORMAT fmt, size_t width, size_t height
}
_Use_decl_annotations_
HRESULT ScratchImage::Initialize3D( DXGI_FORMAT fmt, size_t width, size_t height, size_t depth, size_t mipLevels )
HRESULT ScratchImage::Initialize3D( DXGI_FORMAT fmt, size_t width, size_t height, size_t depth, size_t mipLevels, DWORD flags )
{
if ( !IsValid(fmt) || !width || !height || !depth )
return E_INVALIDARG;
@ -441,7 +441,7 @@ HRESULT ScratchImage::Initialize3D( DXGI_FORMAT fmt, size_t width, size_t height
_metadata.dimension = TEX_DIMENSION_TEXTURE3D;
size_t pixelSize, nimages;
_DetermineImageArray( _metadata, CP_FLAGS_NONE, nimages, pixelSize );
_DetermineImageArray( _metadata, flags, nimages, pixelSize );
_image = new (std::nothrow) Image[ nimages ];
if ( !_image )
@ -460,7 +460,7 @@ HRESULT ScratchImage::Initialize3D( DXGI_FORMAT fmt, size_t width, size_t height
}
_size = pixelSize;
if ( !_SetupImageArray( _memory, pixelSize, _metadata, CP_FLAGS_NONE, _image, nimages ) )
if ( !_SetupImageArray( _memory, pixelSize, _metadata, flags, _image, nimages ) )
{
Release();
return E_FAIL;
@ -470,7 +470,7 @@ HRESULT ScratchImage::Initialize3D( DXGI_FORMAT fmt, size_t width, size_t height
}
_Use_decl_annotations_
HRESULT ScratchImage::InitializeCube( DXGI_FORMAT fmt, size_t width, size_t height, size_t nCubes, size_t mipLevels )
HRESULT ScratchImage::InitializeCube( DXGI_FORMAT fmt, size_t width, size_t height, size_t nCubes, size_t mipLevels, DWORD flags )
{
if ( !width || !height || !nCubes )
return E_INVALIDARG;
@ -479,7 +479,7 @@ HRESULT ScratchImage::InitializeCube( DXGI_FORMAT fmt, size_t width, size_t heig
return HRESULT_FROM_WIN32( ERROR_NOT_SUPPORTED );
// A DirectX11 cubemap is just a 2D texture array that is a multiple of 6 for each cube
HRESULT hr = Initialize2D( fmt, width, height, nCubes * 6, mipLevels );
HRESULT hr = Initialize2D( fmt, width, height, nCubes * 6, mipLevels, flags );
if ( FAILED(hr) )
return hr;
@ -489,11 +489,11 @@ HRESULT ScratchImage::InitializeCube( DXGI_FORMAT fmt, size_t width, size_t heig
}
_Use_decl_annotations_
HRESULT ScratchImage::InitializeFromImage( const Image& srcImage, bool allow1D )
HRESULT ScratchImage::InitializeFromImage( const Image& srcImage, bool allow1D, DWORD flags )
{
HRESULT hr = ( srcImage.height > 1 || !allow1D )
? Initialize2D( srcImage.format, srcImage.width, srcImage.height, 1, 1 )
: Initialize1D( srcImage.format, srcImage.width, 1, 1 );
? Initialize2D( srcImage.format, srcImage.width, srcImage.height, 1, 1, flags )
: Initialize1D( srcImage.format, srcImage.width, 1, 1, flags );
if ( FAILED(hr) )
return hr;
@ -526,7 +526,7 @@ HRESULT ScratchImage::InitializeFromImage( const Image& srcImage, bool allow1D )
}
_Use_decl_annotations_
HRESULT ScratchImage::InitializeArrayFromImages( const Image* images, size_t nImages, bool allow1D )
HRESULT ScratchImage::InitializeArrayFromImages( const Image* images, size_t nImages, bool allow1D, DWORD flags )
{
if ( !images || !nImages )
return E_INVALIDARG;
@ -548,8 +548,8 @@ HRESULT ScratchImage::InitializeArrayFromImages( const Image* images, size_t nIm
}
HRESULT hr = ( height > 1 || !allow1D )
? Initialize2D( format, width, height, nImages, 1 )
: Initialize1D( format, width, nImages, 1 );
? Initialize2D( format, width, height, nImages, 1, flags )
: Initialize1D( format, width, nImages, 1, flags );
if ( FAILED(hr) )
return hr;
@ -586,7 +586,7 @@ HRESULT ScratchImage::InitializeArrayFromImages( const Image* images, size_t nIm
}
_Use_decl_annotations_
HRESULT ScratchImage::InitializeCubeFromImages( const Image* images, size_t nImages )
HRESULT ScratchImage::InitializeCubeFromImages( const Image* images, size_t nImages, DWORD flags )
{
if ( !images || !nImages )
return E_INVALIDARG;
@ -598,7 +598,7 @@ HRESULT ScratchImage::InitializeCubeFromImages( const Image* images, size_t nIma
if ( IsVideo(images[0].format) || IsPalettized(images[0].format) )
return HRESULT_FROM_WIN32( ERROR_NOT_SUPPORTED );
HRESULT hr = InitializeArrayFromImages( images, nImages, false );
HRESULT hr = InitializeArrayFromImages( images, nImages, false, flags );
if ( FAILED(hr) )
return hr;
@ -608,7 +608,7 @@ HRESULT ScratchImage::InitializeCubeFromImages( const Image* images, size_t nIma
}
_Use_decl_annotations_
HRESULT ScratchImage::Initialize3DFromImages( const Image* images, size_t depth )
HRESULT ScratchImage::Initialize3DFromImages( const Image* images, size_t depth, DWORD flags )
{
if ( !images || !depth )
return E_INVALIDARG;
@ -629,7 +629,7 @@ HRESULT ScratchImage::Initialize3DFromImages( const Image* images, size_t depth
}
}
HRESULT hr = Initialize3D( format, width, height, depth, 1 );
HRESULT hr = Initialize3D( format, width, height, depth, 1, flags );
if ( FAILED(hr) )
return hr;

View File

@ -661,6 +661,11 @@ void ComputePitch( DXGI_FORMAT fmt, size_t width, size_t height,
rowPitch = ( ( width * bpp + 31 ) / 32 ) * sizeof(uint32_t);
slicePitch = rowPitch * height;
}
else if ( flags & CP_FLAGS_PARAGRAPH )
{
rowPitch = ( ( width * bpp + 127 ) / 128 ) * 16;
slicePitch = rowPitch * height;
}
else
{
rowPitch = ( width * bpp + 7 ) / 8;