2016-08-22 18:26:36 +00:00
//-------------------------------------------------------------------------------------
// DirectXTexCompress.cpp
//
// DirectX Texture Library - Texture compression
//
// Copyright (c) Microsoft Corporation. All rights reserved.
2018-02-24 06:24:46 +00:00
// Licensed under the MIT License.
2016-08-22 18:26:36 +00:00
//
// http://go.microsoft.com/fwlink/?LinkId=248926
//-------------------------------------------------------------------------------------
2019-05-25 23:00:32 +00:00
# include "DirectXTexP.h"
2016-08-22 18:26:36 +00:00
# ifdef _OPENMP
# include <omp.h>
# pragma warning(disable : 4616 6993)
# endif
2019-04-17 00:38:37 +00:00
# include "BC.h"
2016-08-22 18:26:36 +00:00
2016-09-09 02:09:46 +00:00
using namespace DirectX ;
2016-08-22 18:26:36 +00:00
2016-09-09 02:09:46 +00:00
namespace
2016-08-22 18:26:36 +00:00
{
2019-12-13 08:01:17 +00:00
inline DWORD GetBCFlags ( _In_ DWORD compress ) noexcept
2016-08-22 18:26:36 +00:00
{
2017-07-12 07:56:51 +00:00
static_assert ( static_cast < int > ( TEX_COMPRESS_RGB_DITHER ) = = static_cast < int > ( BC_FLAGS_DITHER_RGB ) , " TEX_COMPRESS_* flags should match BC_FLAGS_* " ) ;
static_assert ( static_cast < int > ( TEX_COMPRESS_A_DITHER ) = = static_cast < int > ( BC_FLAGS_DITHER_A ) , " TEX_COMPRESS_* flags should match BC_FLAGS_* " ) ;
static_assert ( static_cast < int > ( TEX_COMPRESS_DITHER ) = = static_cast < int > ( BC_FLAGS_DITHER_RGB | BC_FLAGS_DITHER_A ) , " TEX_COMPRESS_* flags should match BC_FLAGS_* " ) ;
static_assert ( static_cast < int > ( TEX_COMPRESS_UNIFORM ) = = static_cast < int > ( BC_FLAGS_UNIFORM ) , " TEX_COMPRESS_* flags should match BC_FLAGS_* " ) ;
static_assert ( static_cast < int > ( TEX_COMPRESS_BC7_USE_3SUBSETS ) = = static_cast < int > ( BC_FLAGS_USE_3SUBSETS ) , " TEX_COMPRESS_* flags should match BC_FLAGS_* " ) ;
static_assert ( static_cast < int > ( TEX_COMPRESS_BC7_QUICK ) = = static_cast < int > ( BC_FLAGS_FORCE_BC7_MODE6 ) , " TEX_COMPRESS_* flags should match BC_FLAGS_* " ) ;
2016-09-20 23:40:49 +00:00
return ( compress & ( BC_FLAGS_DITHER_RGB | BC_FLAGS_DITHER_A | BC_FLAGS_UNIFORM | BC_FLAGS_USE_3SUBSETS | BC_FLAGS_FORCE_BC7_MODE6 ) ) ;
2016-08-22 18:26:36 +00:00
}
2019-12-13 08:01:17 +00:00
inline DWORD GetSRGBFlags ( _In_ DWORD compress ) noexcept
2016-09-09 02:09:46 +00:00
{
2017-07-12 07:56:51 +00:00
static_assert ( static_cast < int > ( TEX_COMPRESS_SRGB_IN ) = = static_cast < int > ( TEX_FILTER_SRGB_IN ) , " TEX_COMPRESS_SRGB* should match TEX_FILTER_SRGB* " ) ;
static_assert ( static_cast < int > ( TEX_COMPRESS_SRGB_OUT ) = = static_cast < int > ( TEX_FILTER_SRGB_OUT ) , " TEX_COMPRESS_SRGB* should match TEX_FILTER_SRGB* " ) ;
static_assert ( static_cast < int > ( TEX_COMPRESS_SRGB ) = = static_cast < int > ( TEX_FILTER_SRGB ) , " TEX_COMPRESS_SRGB* should match TEX_FILTER_SRGB* " ) ;
2016-09-09 02:09:46 +00:00
return ( compress & TEX_COMPRESS_SRGB ) ;
}
2016-08-22 18:26:36 +00:00
2019-12-13 08:01:17 +00:00
inline bool DetermineEncoderSettings ( _In_ DXGI_FORMAT format , _Out_ BC_ENCODE & pfEncode , _Out_ size_t & blocksize , _Out_ DWORD & cflags ) noexcept
2016-09-09 02:09:46 +00:00
{
switch ( format )
{
case DXGI_FORMAT_BC1_UNORM :
case DXGI_FORMAT_BC1_UNORM_SRGB : pfEncode = nullptr ; blocksize = 8 ; cflags = 0 ; break ;
case DXGI_FORMAT_BC2_UNORM :
case DXGI_FORMAT_BC2_UNORM_SRGB : pfEncode = D3DXEncodeBC2 ; blocksize = 16 ; cflags = 0 ; break ;
case DXGI_FORMAT_BC3_UNORM :
case DXGI_FORMAT_BC3_UNORM_SRGB : pfEncode = D3DXEncodeBC3 ; blocksize = 16 ; cflags = 0 ; break ;
case DXGI_FORMAT_BC4_UNORM : pfEncode = D3DXEncodeBC4U ; blocksize = 8 ; cflags = TEX_FILTER_RGB_COPY_RED ; break ;
case DXGI_FORMAT_BC4_SNORM : pfEncode = D3DXEncodeBC4S ; blocksize = 8 ; cflags = TEX_FILTER_RGB_COPY_RED ; break ;
case DXGI_FORMAT_BC5_UNORM : pfEncode = D3DXEncodeBC5U ; blocksize = 16 ; cflags = TEX_FILTER_RGB_COPY_RED | TEX_FILTER_RGB_COPY_GREEN ; break ;
case DXGI_FORMAT_BC5_SNORM : pfEncode = D3DXEncodeBC5S ; blocksize = 16 ; cflags = TEX_FILTER_RGB_COPY_RED | TEX_FILTER_RGB_COPY_GREEN ; break ;
case DXGI_FORMAT_BC6H_UF16 : pfEncode = D3DXEncodeBC6HU ; blocksize = 16 ; cflags = 0 ; break ;
case DXGI_FORMAT_BC6H_SF16 : pfEncode = D3DXEncodeBC6HS ; blocksize = 16 ; cflags = 0 ; break ;
case DXGI_FORMAT_BC7_UNORM :
case DXGI_FORMAT_BC7_UNORM_SRGB : pfEncode = D3DXEncodeBC7 ; blocksize = 16 ; cflags = 0 ; break ;
default : pfEncode = nullptr ; blocksize = 0 ; cflags = 0 ; return false ;
}
2016-08-22 18:26:36 +00:00
2016-09-09 02:09:46 +00:00
return true ;
}
2016-08-22 18:26:36 +00:00
2016-09-09 02:09:46 +00:00
//-------------------------------------------------------------------------------------
HRESULT CompressBC (
const Image & image ,
const Image & result ,
DWORD bcflags ,
DWORD srgb ,
2016-09-14 17:56:38 +00:00
float threshold )
2016-08-22 18:26:36 +00:00
{
2016-09-09 02:09:46 +00:00
if ( ! image . pixels | | ! result . pixels )
return E_POINTER ;
2016-08-22 18:26:36 +00:00
2016-09-09 02:09:46 +00:00
assert ( image . width = = result . width ) ;
assert ( image . height = = result . height ) ;
2016-08-22 18:26:36 +00:00
2016-09-09 02:09:46 +00:00
const DXGI_FORMAT format = image . format ;
size_t sbpp = BitsPerPixel ( format ) ;
if ( ! sbpp )
return E_FAIL ;
2016-08-22 18:26:36 +00:00
2016-09-09 02:09:46 +00:00
if ( sbpp < 8 )
2016-08-22 18:26:36 +00:00
{
2016-09-09 02:09:46 +00:00
// We don't support compressing from monochrome (DXGI_FORMAT_R1_UNORM)
return HRESULT_FROM_WIN32 ( ERROR_NOT_SUPPORTED ) ;
}
// Round to bytes
sbpp = ( sbpp + 7 ) / 8 ;
2016-08-22 18:26:36 +00:00
2016-09-09 02:09:46 +00:00
uint8_t * pDest = result . pixels ;
2016-08-22 18:26:36 +00:00
2016-09-09 02:09:46 +00:00
// Determine BC format encoder
BC_ENCODE pfEncode ;
size_t blocksize ;
DWORD cflags ;
if ( ! DetermineEncoderSettings ( result . format , pfEncode , blocksize , cflags ) )
return HRESULT_FROM_WIN32 ( ERROR_NOT_SUPPORTED ) ;
__declspec ( align ( 16 ) ) XMVECTOR temp [ 16 ] ;
const uint8_t * pSrc = image . pixels ;
const uint8_t * pEnd = image . pixels + image . slicePitch ;
const size_t rowPitch = image . rowPitch ;
for ( size_t h = 0 ; h < image . height ; h + = 4 )
{
const uint8_t * sptr = pSrc ;
uint8_t * dptr = pDest ;
size_t ph = std : : min < size_t > ( 4 , image . height - h ) ;
size_t w = 0 ;
for ( size_t count = 0 ; ( count < result . rowPitch ) & & ( w < image . width ) ; count + = blocksize , w + = 4 )
2016-08-22 18:26:36 +00:00
{
2016-09-09 02:09:46 +00:00
size_t pw = std : : min < size_t > ( 4 , image . width - w ) ;
assert ( pw > 0 & & ph > 0 ) ;
ptrdiff_t bytesLeft = pEnd - sptr ;
assert ( bytesLeft > 0 ) ;
2019-05-25 23:00:32 +00:00
size_t bytesToRead = std : : min < size_t > ( rowPitch , static_cast < size_t > ( bytesLeft ) ) ;
2016-09-09 02:09:46 +00:00
if ( ! _LoadScanline ( & temp [ 0 ] , pw , sptr , bytesToRead , format ) )
2016-08-22 18:26:36 +00:00
return E_FAIL ;
2016-09-09 02:09:46 +00:00
if ( ph > 1 )
2016-08-22 18:26:36 +00:00
{
2019-05-25 23:00:32 +00:00
bytesToRead = std : : min < size_t > ( rowPitch , static_cast < size_t > ( bytesLeft ) - rowPitch ) ;
2016-09-09 02:09:46 +00:00
if ( ! _LoadScanline ( & temp [ 4 ] , pw , sptr + rowPitch , bytesToRead , format ) )
2016-08-22 18:26:36 +00:00
return E_FAIL ;
2016-09-09 02:09:46 +00:00
if ( ph > 2 )
2016-08-22 18:26:36 +00:00
{
2019-05-25 23:00:32 +00:00
bytesToRead = std : : min < size_t > ( rowPitch , static_cast < size_t > ( bytesLeft ) - rowPitch * 2 ) ;
2016-09-09 02:09:46 +00:00
if ( ! _LoadScanline ( & temp [ 8 ] , pw , sptr + rowPitch * 2 , bytesToRead , format ) )
2016-08-22 18:26:36 +00:00
return E_FAIL ;
2016-09-09 02:09:46 +00:00
if ( ph > 3 )
{
2019-05-25 23:00:32 +00:00
bytesToRead = std : : min < size_t > ( rowPitch , static_cast < size_t > ( bytesLeft ) - rowPitch * 3 ) ;
2016-09-09 02:09:46 +00:00
if ( ! _LoadScanline ( & temp [ 12 ] , pw , sptr + rowPitch * 3 , bytesToRead , format ) )
return E_FAIL ;
}
2016-08-22 18:26:36 +00:00
}
}
2016-09-09 02:09:46 +00:00
if ( pw ! = 4 | | ph ! = 4 )
2016-08-22 18:26:36 +00:00
{
2016-09-09 02:09:46 +00:00
// Replicate pixels for partial block
static const size_t uSrc [ ] = { 0 , 0 , 0 , 1 } ;
if ( pw < 4 )
2016-08-22 18:26:36 +00:00
{
2016-09-09 02:09:46 +00:00
for ( size_t t = 0 ; t < ph & & t < 4 ; + + t )
2016-08-22 18:26:36 +00:00
{
2016-09-09 02:09:46 +00:00
for ( size_t s = pw ; s < 4 ; + + s )
{
2016-08-22 18:26:36 +00:00
# pragma prefast(suppress: 26000, "PREFAST false positive")
2016-09-09 02:09:46 +00:00
temp [ ( t < < 2 ) | s ] = temp [ ( t < < 2 ) | uSrc [ s ] ] ;
}
2016-08-22 18:26:36 +00:00
}
}
2016-09-09 02:09:46 +00:00
if ( ph < 4 )
2016-08-22 18:26:36 +00:00
{
2016-09-09 02:09:46 +00:00
for ( size_t t = ph ; t < 4 ; + + t )
2016-08-22 18:26:36 +00:00
{
2016-09-09 02:09:46 +00:00
for ( size_t s = 0 ; s < 4 ; + + s )
{
2016-08-22 18:26:36 +00:00
# pragma prefast(suppress: 26000, "PREFAST false positive")
2016-09-09 02:09:46 +00:00
temp [ ( t < < 2 ) | s ] = temp [ ( uSrc [ t ] < < 2 ) | s ] ;
}
2016-08-22 18:26:36 +00:00
}
}
}
2016-09-09 02:09:46 +00:00
_ConvertScanline ( temp , 16 , result . format , format , cflags | srgb ) ;
if ( pfEncode )
pfEncode ( dptr , temp , bcflags ) ;
else
2016-09-14 17:56:38 +00:00
D3DXEncodeBC1 ( dptr , temp , threshold , bcflags ) ;
2016-08-22 18:26:36 +00:00
2016-09-09 02:09:46 +00:00
sptr + = sbpp * 4 ;
dptr + = blocksize ;
}
pSrc + = rowPitch * 4 ;
pDest + = result . rowPitch ;
2016-08-22 18:26:36 +00:00
}
2016-09-09 02:09:46 +00:00
return S_OK ;
2016-08-22 18:26:36 +00:00
}
2016-09-09 02:09:46 +00:00
//-------------------------------------------------------------------------------------
2016-08-22 18:26:36 +00:00
# ifdef _OPENMP
2016-09-09 02:09:46 +00:00
HRESULT CompressBC_Parallel (
const Image & image ,
const Image & result ,
DWORD bcflags ,
DWORD srgb ,
2016-09-14 17:56:38 +00:00
float threshold )
2016-09-09 02:09:46 +00:00
{
if ( ! image . pixels | | ! result . pixels )
return E_POINTER ;
2016-08-22 18:26:36 +00:00
2016-09-09 02:09:46 +00:00
assert ( image . width = = result . width ) ;
assert ( image . height = = result . height ) ;
2016-08-22 18:26:36 +00:00
2016-09-09 02:09:46 +00:00
const DXGI_FORMAT format = image . format ;
size_t sbpp = BitsPerPixel ( format ) ;
if ( ! sbpp )
return E_FAIL ;
2016-08-22 18:26:36 +00:00
2016-09-09 02:09:46 +00:00
if ( sbpp < 8 )
{
// We don't support compressing from monochrome (DXGI_FORMAT_R1_UNORM)
return HRESULT_FROM_WIN32 ( ERROR_NOT_SUPPORTED ) ;
}
2016-08-22 18:26:36 +00:00
2016-09-09 02:09:46 +00:00
// Round to bytes
sbpp = ( sbpp + 7 ) / 8 ;
2016-08-22 18:26:36 +00:00
2016-09-09 02:09:46 +00:00
const uint8_t * pEnd = image . pixels + image . slicePitch ;
2016-08-22 18:26:36 +00:00
2016-09-09 02:09:46 +00:00
// Determine BC format encoder
BC_ENCODE pfEncode ;
size_t blocksize ;
DWORD cflags ;
if ( ! DetermineEncoderSettings ( result . format , pfEncode , blocksize , cflags ) )
return HRESULT_FROM_WIN32 ( ERROR_NOT_SUPPORTED ) ;
2016-08-22 18:26:36 +00:00
2016-09-09 02:09:46 +00:00
// Refactored version of loop to support parallel independance
const size_t nBlocks = std : : max < size_t > ( 1 , ( image . width + 3 ) / 4 ) * std : : max < size_t > ( 1 , ( image . height + 3 ) / 4 ) ;
2016-08-22 18:26:36 +00:00
2016-09-09 02:09:46 +00:00
bool fail = false ;
2016-08-22 18:26:36 +00:00
# pragma omp parallel for
2016-09-09 02:09:46 +00:00
for ( int nb = 0 ; nb < static_cast < int > ( nBlocks ) ; + + nb )
{
int nbWidth = std : : max < int > ( 1 , int ( ( image . width + 3 ) / 4 ) ) ;
2016-08-22 18:26:36 +00:00
2016-09-09 02:09:46 +00:00
int y = nb / nbWidth ;
int x = ( nb - ( y * nbWidth ) ) * 4 ;
y * = 4 ;
2016-08-22 18:26:36 +00:00
2016-09-09 02:09:46 +00:00
assert ( ( x > = 0 ) & & ( x < int ( image . width ) ) ) ;
assert ( ( y > = 0 ) & & ( y < int ( image . height ) ) ) ;
2016-08-22 18:26:36 +00:00
2016-09-09 02:09:46 +00:00
size_t rowPitch = image . rowPitch ;
2019-12-14 06:26:18 +00:00
const uint8_t * pSrc = image . pixels + ( size_t ( y ) * rowPitch ) + ( size_t ( x ) * sbpp ) ;
2016-08-22 18:26:36 +00:00
2019-12-14 06:26:18 +00:00
uint8_t * pDest = result . pixels + ( size_t ( nb ) * blocksize ) ;
2016-08-22 18:26:36 +00:00
2019-12-14 06:26:18 +00:00
size_t ph = std : : min < size_t > ( 4 , image . height - size_t ( y ) ) ;
size_t pw = std : : min < size_t > ( 4 , image . width - size_t ( x ) ) ;
2016-09-09 02:09:46 +00:00
assert ( pw > 0 & & ph > 0 ) ;
2016-08-22 18:26:36 +00:00
2016-09-09 02:09:46 +00:00
ptrdiff_t bytesLeft = pEnd - pSrc ;
assert ( bytesLeft > 0 ) ;
2019-05-25 23:00:32 +00:00
size_t bytesToRead = std : : min < size_t > ( rowPitch , size_t ( bytesLeft ) ) ;
2016-08-22 18:26:36 +00:00
2016-09-09 02:09:46 +00:00
__declspec ( align ( 16 ) ) XMVECTOR temp [ 16 ] ;
if ( ! _LoadScanline ( & temp [ 0 ] , pw , pSrc , bytesToRead , format ) )
2016-08-22 18:26:36 +00:00
fail = true ;
2016-09-09 02:09:46 +00:00
if ( ph > 1 )
2016-08-22 18:26:36 +00:00
{
2019-12-14 06:26:18 +00:00
bytesToRead = std : : min < size_t > ( rowPitch , size_t ( bytesLeft ) - rowPitch ) ;
2016-09-09 02:09:46 +00:00
if ( ! _LoadScanline ( & temp [ 4 ] , pw , pSrc + rowPitch , bytesToRead , format ) )
2016-08-22 18:26:36 +00:00
fail = true ;
2016-09-09 02:09:46 +00:00
if ( ph > 2 )
2016-08-22 18:26:36 +00:00
{
2019-12-14 06:26:18 +00:00
bytesToRead = std : : min < size_t > ( rowPitch , size_t ( bytesLeft ) - rowPitch * 2 ) ;
2016-09-09 02:09:46 +00:00
if ( ! _LoadScanline ( & temp [ 8 ] , pw , pSrc + rowPitch * 2 , bytesToRead , format ) )
2016-08-22 18:26:36 +00:00
fail = true ;
2016-09-09 02:09:46 +00:00
if ( ph > 3 )
{
2019-12-14 06:26:18 +00:00
bytesToRead = std : : min < size_t > ( rowPitch , size_t ( bytesLeft ) - rowPitch * 3 ) ;
2016-09-09 02:09:46 +00:00
if ( ! _LoadScanline ( & temp [ 12 ] , pw , pSrc + rowPitch * 3 , bytesToRead , format ) )
fail = true ;
}
2016-08-22 18:26:36 +00:00
}
}
2016-09-09 02:09:46 +00:00
if ( pw ! = 4 | | ph ! = 4 )
2016-08-22 18:26:36 +00:00
{
2016-09-09 02:09:46 +00:00
// Replicate pixels for partial block
static const size_t uSrc [ ] = { 0 , 0 , 0 , 1 } ;
if ( pw < 4 )
2016-08-22 18:26:36 +00:00
{
2016-09-09 02:09:46 +00:00
for ( size_t t = 0 ; t < ph & & t < 4 ; + + t )
2016-08-22 18:26:36 +00:00
{
2016-09-09 02:09:46 +00:00
for ( size_t s = pw ; s < 4 ; + + s )
{
temp [ ( t < < 2 ) | s ] = temp [ ( t < < 2 ) | uSrc [ s ] ] ;
}
2016-08-22 18:26:36 +00:00
}
}
2016-09-09 02:09:46 +00:00
if ( ph < 4 )
2016-08-22 18:26:36 +00:00
{
2016-09-09 02:09:46 +00:00
for ( size_t t = ph ; t < 4 ; + + t )
2016-08-22 18:26:36 +00:00
{
2016-09-09 02:09:46 +00:00
for ( size_t s = 0 ; s < 4 ; + + s )
{
temp [ ( t < < 2 ) | s ] = temp [ ( uSrc [ t ] < < 2 ) | s ] ;
}
2016-08-22 18:26:36 +00:00
}
}
}
2016-09-09 02:09:46 +00:00
_ConvertScanline ( temp , 16 , result . format , format , cflags | srgb ) ;
2016-08-22 18:26:36 +00:00
2016-09-09 02:09:46 +00:00
if ( pfEncode )
pfEncode ( pDest , temp , bcflags ) ;
else
2016-09-14 17:56:38 +00:00
D3DXEncodeBC1 ( pDest , temp , threshold , bcflags ) ;
2016-09-09 02:09:46 +00:00
}
2016-08-22 18:26:36 +00:00
2016-09-09 02:09:46 +00:00
return ( fail ) ? E_FAIL : S_OK ;
}
2016-08-22 18:26:36 +00:00
# endif // _OPENMP
2016-09-09 02:09:46 +00:00
//-------------------------------------------------------------------------------------
2019-12-13 08:01:17 +00:00
DXGI_FORMAT DefaultDecompress ( _In_ DXGI_FORMAT format ) noexcept
2016-08-22 18:26:36 +00:00
{
2016-09-09 02:09:46 +00:00
switch ( format )
{
case DXGI_FORMAT_BC1_TYPELESS :
case DXGI_FORMAT_BC1_UNORM :
case DXGI_FORMAT_BC2_TYPELESS :
case DXGI_FORMAT_BC2_UNORM :
case DXGI_FORMAT_BC3_TYPELESS :
case DXGI_FORMAT_BC3_UNORM :
case DXGI_FORMAT_BC7_TYPELESS :
case DXGI_FORMAT_BC7_UNORM :
return DXGI_FORMAT_R8G8B8A8_UNORM ;
case DXGI_FORMAT_BC1_UNORM_SRGB :
case DXGI_FORMAT_BC2_UNORM_SRGB :
case DXGI_FORMAT_BC3_UNORM_SRGB :
case DXGI_FORMAT_BC7_UNORM_SRGB :
return DXGI_FORMAT_R8G8B8A8_UNORM_SRGB ;
case DXGI_FORMAT_BC4_TYPELESS :
case DXGI_FORMAT_BC4_UNORM :
return DXGI_FORMAT_R8_UNORM ;
case DXGI_FORMAT_BC4_SNORM :
return DXGI_FORMAT_R8_SNORM ;
case DXGI_FORMAT_BC5_TYPELESS :
case DXGI_FORMAT_BC5_UNORM :
return DXGI_FORMAT_R8G8_UNORM ;
case DXGI_FORMAT_BC5_SNORM :
return DXGI_FORMAT_R8G8_SNORM ;
case DXGI_FORMAT_BC6H_TYPELESS :
case DXGI_FORMAT_BC6H_UF16 :
case DXGI_FORMAT_BC6H_SF16 :
// We could use DXGI_FORMAT_R32G32B32_FLOAT here since BC6H is always Alpha 1.0,
// but this format is more supported by viewers
return DXGI_FORMAT_R32G32B32A32_FLOAT ;
default :
return DXGI_FORMAT_UNKNOWN ;
}
2016-08-22 18:26:36 +00:00
}
2016-09-09 02:09:46 +00:00
//-------------------------------------------------------------------------------------
HRESULT DecompressBC ( _In_ const Image & cImage , _In_ const Image & result )
{
if ( ! cImage . pixels | | ! result . pixels )
return E_POINTER ;
2016-08-22 18:26:36 +00:00
2016-09-09 02:09:46 +00:00
assert ( cImage . width = = result . width ) ;
assert ( cImage . height = = result . height ) ;
2016-08-22 18:26:36 +00:00
2016-09-09 02:09:46 +00:00
const DXGI_FORMAT format = result . format ;
size_t dbpp = BitsPerPixel ( format ) ;
if ( ! dbpp )
return E_FAIL ;
2016-08-22 18:26:36 +00:00
2016-09-09 02:09:46 +00:00
if ( dbpp < 8 )
{
// We don't support decompressing to monochrome (DXGI_FORMAT_R1_UNORM)
return HRESULT_FROM_WIN32 ( ERROR_NOT_SUPPORTED ) ;
}
2016-08-22 18:26:36 +00:00
2016-09-09 02:09:46 +00:00
// Round to bytes
dbpp = ( dbpp + 7 ) / 8 ;
2016-08-22 18:26:36 +00:00
2016-09-09 02:09:46 +00:00
uint8_t * pDest = result . pixels ;
if ( ! pDest )
return E_POINTER ;
2016-08-22 18:26:36 +00:00
2016-09-09 02:09:46 +00:00
// 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_BC4_TYPELESS : cformat = DXGI_FORMAT_BC4_UNORM ; break ;
case DXGI_FORMAT_BC5_TYPELESS : cformat = DXGI_FORMAT_BC5_UNORM ; break ;
case DXGI_FORMAT_BC6H_TYPELESS : cformat = DXGI_FORMAT_BC6H_UF16 ; break ;
case DXGI_FORMAT_BC7_TYPELESS : cformat = DXGI_FORMAT_BC7_UNORM ; break ;
default : cformat = cImage . format ; break ;
}
2016-08-22 18:26:36 +00:00
2016-09-09 02:09:46 +00:00
// Determine BC format decoder
BC_DECODE pfDecode ;
size_t sbpp ;
switch ( cformat )
2016-08-22 18:26:36 +00:00
{
2016-09-09 02:09:46 +00:00
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_BC4_UNORM : pfDecode = D3DXDecodeBC4U ; sbpp = 8 ; break ;
case DXGI_FORMAT_BC4_SNORM : pfDecode = D3DXDecodeBC4S ; sbpp = 8 ; break ;
case DXGI_FORMAT_BC5_UNORM : pfDecode = D3DXDecodeBC5U ; sbpp = 16 ; break ;
case DXGI_FORMAT_BC5_SNORM : pfDecode = D3DXDecodeBC5S ; sbpp = 16 ; break ;
case DXGI_FORMAT_BC6H_UF16 : pfDecode = D3DXDecodeBC6HU ; sbpp = 16 ; break ;
case DXGI_FORMAT_BC6H_SF16 : pfDecode = D3DXDecodeBC6HS ; sbpp = 16 ; break ;
case DXGI_FORMAT_BC7_UNORM :
case DXGI_FORMAT_BC7_UNORM_SRGB : pfDecode = D3DXDecodeBC7 ; sbpp = 16 ; break ;
default :
return HRESULT_FROM_WIN32 ( ERROR_NOT_SUPPORTED ) ;
}
2016-08-22 18:26:36 +00:00
2016-09-09 02:09:46 +00:00
__declspec ( align ( 16 ) ) XMVECTOR temp [ 16 ] ;
const uint8_t * pSrc = cImage . pixels ;
const size_t rowPitch = result . rowPitch ;
for ( size_t h = 0 ; h < cImage . height ; h + = 4 )
{
const uint8_t * sptr = pSrc ;
uint8_t * dptr = pDest ;
size_t ph = std : : min < size_t > ( 4 , cImage . height - h ) ;
size_t w = 0 ;
for ( size_t count = 0 ; ( count < cImage . rowPitch ) & & ( w < cImage . width ) ; count + = sbpp , w + = 4 )
{
pfDecode ( temp , sptr ) ;
_ConvertScanline ( temp , 16 , format , cformat , 0 ) ;
2016-08-22 18:26:36 +00:00
2016-09-09 02:09:46 +00:00
size_t pw = std : : min < size_t > ( 4 , cImage . width - w ) ;
assert ( pw > 0 & & ph > 0 ) ;
2016-08-22 18:26:36 +00:00
2016-09-09 02:09:46 +00:00
if ( ! _StoreScanline ( dptr , rowPitch , format , & temp [ 0 ] , pw ) )
2016-08-22 18:26:36 +00:00
return E_FAIL ;
2016-09-09 02:09:46 +00:00
if ( ph > 1 )
2016-08-22 18:26:36 +00:00
{
2016-09-09 02:09:46 +00:00
if ( ! _StoreScanline ( dptr + rowPitch , rowPitch , format , & temp [ 4 ] , pw ) )
2016-08-22 18:26:36 +00:00
return E_FAIL ;
2016-09-09 02:09:46 +00:00
if ( ph > 2 )
2016-08-22 18:26:36 +00:00
{
2016-09-09 02:09:46 +00:00
if ( ! _StoreScanline ( dptr + rowPitch * 2 , rowPitch , format , & temp [ 8 ] , pw ) )
2016-08-22 18:26:36 +00:00
return E_FAIL ;
2016-09-09 02:09:46 +00:00
if ( ph > 3 )
{
if ( ! _StoreScanline ( dptr + rowPitch * 3 , rowPitch , format , & temp [ 12 ] , pw ) )
return E_FAIL ;
}
2016-08-22 18:26:36 +00:00
}
}
2016-09-09 02:09:46 +00:00
sptr + = sbpp ;
dptr + = dbpp * 4 ;
2016-08-22 18:26:36 +00:00
}
2016-09-09 02:09:46 +00:00
pSrc + = cImage . rowPitch ;
pDest + = rowPitch * 4 ;
2016-08-22 18:26:36 +00:00
}
2016-09-09 02:09:46 +00:00
return S_OK ;
2016-08-22 18:26:36 +00:00
}
}
//-------------------------------------------------------------------------------------
2016-09-09 02:09:46 +00:00
namespace DirectX
2016-08-22 18:26:36 +00:00
{
2019-12-13 08:01:17 +00:00
bool _IsAlphaAllOpaqueBC ( _In_ const Image & cImage ) noexcept ;
2019-05-29 22:52:15 +00:00
// Also used by Image
2019-12-13 08:01:17 +00:00
bool _IsAlphaAllOpaqueBC ( _In_ const Image & cImage ) noexcept
2016-08-22 18:26:36 +00:00
{
2016-09-09 02:09:46 +00:00
if ( ! cImage . pixels )
return false ;
2016-08-22 18:26:36 +00:00
2016-09-09 02:09:46 +00:00
// 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 ;
}
2016-08-22 18:26:36 +00:00
2016-09-09 02:09:46 +00:00
// Determine BC format decoder
BC_DECODE pfDecode ;
size_t sbpp ;
switch ( cformat )
2016-08-22 18:26:36 +00:00
{
2016-09-09 02:09:46 +00:00
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 ;
}
2016-08-22 18:26:36 +00:00
2016-09-09 02:09:46 +00:00
// Scan blocks for non-opaque alpha
2017-07-12 07:56:51 +00:00
static const XMVECTORF32 threshold = { { { 0.99f , 0.99f , 0.99f , 0.99f } } } ;
2016-08-22 18:26:36 +00:00
2016-09-09 02:09:46 +00:00
__declspec ( align ( 16 ) ) XMVECTOR temp [ 16 ] ;
const uint8_t * pPixels = cImage . pixels ;
for ( size_t h = 0 ; h < cImage . height ; h + = 4 )
{
const uint8_t * ptr = pPixels ;
size_t ph = std : : min < size_t > ( 4 , cImage . height - h ) ;
size_t w = 0 ;
for ( size_t count = 0 ; ( count < cImage . rowPitch ) & & ( w < cImage . width ) ; count + = sbpp , w + = 4 )
2016-08-22 18:26:36 +00:00
{
2016-09-09 02:09:46 +00:00
pfDecode ( temp , ptr ) ;
size_t pw = std : : min < size_t > ( 4 , cImage . width - w ) ;
assert ( pw > 0 & & ph > 0 ) ;
if ( pw = = 4 & & ph = = 4 )
2016-08-22 18:26:36 +00:00
{
2016-09-09 02:09:46 +00:00
// Full blocks
for ( size_t j = 0 ; j < 16 ; + + j )
{
XMVECTOR alpha = XMVectorSplatW ( temp [ j ] ) ;
if ( XMVector4Less ( alpha , threshold ) )
return false ;
}
2016-08-22 18:26:36 +00:00
}
2016-09-09 02:09:46 +00:00
else
2016-08-22 18:26:36 +00:00
{
2016-09-09 02:09:46 +00:00
// Handle partial blocks
for ( size_t y = 0 ; y < ph ; + + y )
2016-08-22 18:26:36 +00:00
{
2016-09-09 02:09:46 +00:00
for ( size_t x = 0 ; x < pw ; + + x )
{
XMVECTOR alpha = XMVectorSplatW ( temp [ y * 4 + x ] ) ;
if ( XMVector4Less ( alpha , threshold ) )
return false ;
}
2016-08-22 18:26:36 +00:00
}
}
2016-09-09 02:09:46 +00:00
ptr + = sbpp ;
2016-08-22 18:26:36 +00:00
}
2016-09-09 02:09:46 +00:00
pPixels + = cImage . rowPitch ;
2016-08-22 18:26:36 +00:00
}
2016-09-09 02:09:46 +00:00
return true ;
2016-08-22 18:26:36 +00:00
}
2016-09-09 02:09:46 +00:00
} ;
2016-08-22 18:26:36 +00:00
//=====================================================================================
// Entry-points
//=====================================================================================
//-------------------------------------------------------------------------------------
// Compression
//-------------------------------------------------------------------------------------
_Use_decl_annotations_
2016-09-09 02:09:46 +00:00
HRESULT DirectX : : Compress (
const Image & srcImage ,
DXGI_FORMAT format ,
DWORD compress ,
2016-09-14 17:56:38 +00:00
float threshold ,
2016-09-09 02:09:46 +00:00
ScratchImage & image )
2016-08-22 18:26:36 +00:00
{
2016-09-09 02:09:46 +00:00
if ( IsCompressed ( srcImage . format ) | | ! IsCompressed ( format ) )
2016-08-22 18:26:36 +00:00
return E_INVALIDARG ;
2016-09-09 02:09:46 +00:00
if ( IsTypeless ( format )
| | IsTypeless ( srcImage . format ) | | IsPlanar ( srcImage . format ) | | IsPalettized ( srcImage . format ) )
return HRESULT_FROM_WIN32 ( ERROR_NOT_SUPPORTED ) ;
2016-08-22 18:26:36 +00:00
// Create compressed image
2016-09-09 02:09:46 +00:00
HRESULT hr = image . Initialize2D ( format , srcImage . width , srcImage . height , 1 , 1 ) ;
if ( FAILED ( hr ) )
2016-08-22 18:26:36 +00:00
return hr ;
2016-09-09 02:09:46 +00:00
const Image * img = image . GetImage ( 0 , 0 , 0 ) ;
if ( ! img )
2016-08-22 18:26:36 +00:00
{
image . Release ( ) ;
return E_POINTER ;
}
// Compress single image
if ( compress & TEX_COMPRESS_PARALLEL )
{
# ifndef _OPENMP
return E_NOTIMPL ;
# else
2016-09-14 17:56:38 +00:00
hr = CompressBC_Parallel ( srcImage , * img , GetBCFlags ( compress ) , GetSRGBFlags ( compress ) , threshold ) ;
2016-08-22 18:26:36 +00:00
# endif // _OPENMP
}
else
{
2016-09-14 17:56:38 +00:00
hr = CompressBC ( srcImage , * img , GetBCFlags ( compress ) , GetSRGBFlags ( compress ) , threshold ) ;
2016-08-22 18:26:36 +00:00
}
2016-09-09 02:09:46 +00:00
if ( FAILED ( hr ) )
2016-08-22 18:26:36 +00:00
image . Release ( ) ;
return hr ;
}
_Use_decl_annotations_
2016-09-09 02:09:46 +00:00
HRESULT DirectX : : Compress (
const Image * srcImages ,
size_t nimages ,
const TexMetadata & metadata ,
DXGI_FORMAT format ,
DWORD compress ,
2016-09-14 17:56:38 +00:00
float threshold ,
2016-09-09 02:09:46 +00:00
ScratchImage & cImages )
2016-08-22 18:26:36 +00:00
{
2016-09-09 02:09:46 +00:00
if ( ! srcImages | | ! nimages )
2016-08-22 18:26:36 +00:00
return E_INVALIDARG ;
2016-09-09 02:09:46 +00:00
if ( IsCompressed ( metadata . format ) | | ! IsCompressed ( format ) )
2016-08-22 18:26:36 +00:00
return E_INVALIDARG ;
2016-09-09 02:09:46 +00:00
if ( IsTypeless ( format )
| | IsTypeless ( metadata . format ) | | IsPlanar ( metadata . format ) | | IsPalettized ( metadata . format ) )
return HRESULT_FROM_WIN32 ( ERROR_NOT_SUPPORTED ) ;
2016-08-22 18:26:36 +00:00
cImages . Release ( ) ;
TexMetadata mdata2 = metadata ;
mdata2 . format = format ;
2016-09-09 02:09:46 +00:00
HRESULT hr = cImages . Initialize ( mdata2 ) ;
if ( FAILED ( hr ) )
2016-08-22 18:26:36 +00:00
return hr ;
2016-09-09 02:09:46 +00:00
if ( nimages ! = cImages . GetImageCount ( ) )
2016-08-22 18:26:36 +00:00
{
cImages . Release ( ) ;
return E_FAIL ;
}
const Image * dest = cImages . GetImages ( ) ;
2016-09-09 02:09:46 +00:00
if ( ! dest )
2016-08-22 18:26:36 +00:00
{
cImages . Release ( ) ;
return E_POINTER ;
}
2016-09-09 02:09:46 +00:00
for ( size_t index = 0 ; index < nimages ; + + index )
2016-08-22 18:26:36 +00:00
{
2016-09-09 02:09:46 +00:00
assert ( dest [ index ] . format = = format ) ;
2016-08-22 18:26:36 +00:00
2016-09-09 02:09:46 +00:00
const Image & src = srcImages [ index ] ;
2016-08-22 18:26:36 +00:00
2016-09-09 02:09:46 +00:00
if ( src . width ! = dest [ index ] . width | | src . height ! = dest [ index ] . height )
2016-08-22 18:26:36 +00:00
{
cImages . Release ( ) ;
return E_FAIL ;
}
2016-09-09 02:09:46 +00:00
if ( ( compress & TEX_COMPRESS_PARALLEL ) )
2016-08-22 18:26:36 +00:00
{
# ifndef _OPENMP
return E_NOTIMPL ;
# else
2016-09-09 02:09:46 +00:00
if ( compress & TEX_COMPRESS_PARALLEL )
2016-08-22 18:26:36 +00:00
{
2016-09-14 17:56:38 +00:00
hr = CompressBC_Parallel ( src , dest [ index ] , GetBCFlags ( compress ) , GetSRGBFlags ( compress ) , threshold ) ;
2016-09-09 02:09:46 +00:00
if ( FAILED ( hr ) )
2016-08-22 18:26:36 +00:00
{
cImages . Release ( ) ;
return hr ;
}
}
# endif // _OPENMP
}
else
{
2016-09-14 17:56:38 +00:00
hr = CompressBC ( src , dest [ index ] , GetBCFlags ( compress ) , GetSRGBFlags ( compress ) , threshold ) ;
2016-09-09 02:09:46 +00:00
if ( FAILED ( hr ) )
2016-08-22 18:26:36 +00:00
{
cImages . Release ( ) ;
return hr ;
}
}
}
return S_OK ;
}
//-------------------------------------------------------------------------------------
// Decompression
//-------------------------------------------------------------------------------------
_Use_decl_annotations_
2016-09-09 02:09:46 +00:00
HRESULT DirectX : : Decompress (
const Image & cImage ,
DXGI_FORMAT format ,
2019-12-13 08:01:17 +00:00
ScratchImage & image ) noexcept
2016-08-22 18:26:36 +00:00
{
2016-09-09 02:09:46 +00:00
if ( ! IsCompressed ( cImage . format ) | | IsCompressed ( format ) )
2016-08-22 18:26:36 +00:00
return E_INVALIDARG ;
2016-09-09 02:09:46 +00:00
if ( format = = DXGI_FORMAT_UNKNOWN )
2016-08-22 18:26:36 +00:00
{
// Pick a default decompressed format based on BC input format
2016-09-09 02:09:46 +00:00
format = DefaultDecompress ( cImage . format ) ;
if ( format = = DXGI_FORMAT_UNKNOWN )
2016-08-22 18:26:36 +00:00
{
// Input is not a compressed format
return E_INVALIDARG ;
}
}
else
{
2016-09-09 02:09:46 +00:00
if ( ! IsValid ( format ) )
2016-08-22 18:26:36 +00:00
return E_INVALIDARG ;
2016-09-09 02:09:46 +00:00
if ( IsTypeless ( format ) | | IsPlanar ( format ) | | IsPalettized ( format ) )
return HRESULT_FROM_WIN32 ( ERROR_NOT_SUPPORTED ) ;
2016-08-22 18:26:36 +00:00
}
// Create decompressed image
2016-09-09 02:09:46 +00:00
HRESULT hr = image . Initialize2D ( format , cImage . width , cImage . height , 1 , 1 ) ;
if ( FAILED ( hr ) )
2016-08-22 18:26:36 +00:00
return hr ;
2016-09-09 02:09:46 +00:00
const Image * img = image . GetImage ( 0 , 0 , 0 ) ;
if ( ! img )
2016-08-22 18:26:36 +00:00
{
image . Release ( ) ;
return E_POINTER ;
}
// Decompress single image
2016-09-09 02:09:46 +00:00
hr = DecompressBC ( cImage , * img ) ;
if ( FAILED ( hr ) )
2016-08-22 18:26:36 +00:00
image . Release ( ) ;
return hr ;
}
_Use_decl_annotations_
2016-09-09 02:09:46 +00:00
HRESULT DirectX : : Decompress (
const Image * cImages ,
size_t nimages ,
const TexMetadata & metadata ,
DXGI_FORMAT format ,
2019-12-13 08:01:17 +00:00
ScratchImage & images ) noexcept
2016-08-22 18:26:36 +00:00
{
2016-09-09 02:09:46 +00:00
if ( ! cImages | | ! nimages )
2016-08-22 18:26:36 +00:00
return E_INVALIDARG ;
2016-09-09 02:09:46 +00:00
if ( ! IsCompressed ( metadata . format ) | | IsCompressed ( format ) )
2016-08-22 18:26:36 +00:00
return E_INVALIDARG ;
2016-09-09 02:09:46 +00:00
if ( format = = DXGI_FORMAT_UNKNOWN )
2016-08-22 18:26:36 +00:00
{
// Pick a default decompressed format based on BC input format
2016-09-09 02:09:46 +00:00
format = DefaultDecompress ( cImages [ 0 ] . format ) ;
if ( format = = DXGI_FORMAT_UNKNOWN )
2016-08-22 18:26:36 +00:00
{
// Input is not a compressed format
return E_FAIL ;
}
}
else
{
2016-09-09 02:09:46 +00:00
if ( ! IsValid ( format ) )
2016-08-22 18:26:36 +00:00
return E_INVALIDARG ;
2016-09-09 02:09:46 +00:00
if ( IsTypeless ( format ) | | IsPlanar ( format ) | | IsPalettized ( format ) )
2016-10-28 00:45:47 +00:00
return HRESULT_FROM_WIN32 ( ERROR_NOT_SUPPORTED ) ;
2016-08-22 18:26:36 +00:00
}
images . Release ( ) ;
TexMetadata mdata2 = metadata ;
mdata2 . format = format ;
2016-09-09 02:09:46 +00:00
HRESULT hr = images . Initialize ( mdata2 ) ;
if ( FAILED ( hr ) )
2016-08-22 18:26:36 +00:00
return hr ;
2016-09-09 02:09:46 +00:00
if ( nimages ! = images . GetImageCount ( ) )
2016-08-22 18:26:36 +00:00
{
images . Release ( ) ;
return E_FAIL ;
}
const Image * dest = images . GetImages ( ) ;
2016-09-09 02:09:46 +00:00
if ( ! dest )
2016-08-22 18:26:36 +00:00
{
images . Release ( ) ;
return E_POINTER ;
}
2016-09-09 02:09:46 +00:00
for ( size_t index = 0 ; index < nimages ; + + index )
2016-08-22 18:26:36 +00:00
{
2016-09-09 02:09:46 +00:00
assert ( dest [ index ] . format = = format ) ;
2016-08-22 18:26:36 +00:00
2016-09-09 02:09:46 +00:00
const Image & src = cImages [ index ] ;
if ( ! IsCompressed ( src . format ) )
2016-08-22 18:26:36 +00:00
{
images . Release ( ) ;
return E_FAIL ;
}
2016-09-09 02:09:46 +00:00
if ( src . width ! = dest [ index ] . width | | src . height ! = dest [ index ] . height )
2016-08-22 18:26:36 +00:00
{
images . Release ( ) ;
return E_FAIL ;
}
2016-09-09 02:09:46 +00:00
hr = DecompressBC ( src , dest [ index ] ) ;
if ( FAILED ( hr ) )
2016-08-22 18:26:36 +00:00
{
images . Release ( ) ;
return hr ;
}
}
return S_OK ;
}