mirror of
https://github.com/microsoft/DirectXTex
synced 2024-11-08 14:00:05 +00:00
DirectXTex: added finite low-pass triangle custom filter
- TEX_FILTER_TRIANGLE - texconv updated -if image filter options
This commit is contained in:
parent
83372f9b7f
commit
2fdf1f6d0a
@ -27,7 +27,7 @@
|
|||||||
#include <dxgiformat.h>
|
#include <dxgiformat.h>
|
||||||
#include <d3d11.h>
|
#include <d3d11.h>
|
||||||
|
|
||||||
#define DIRECTX_TEX_VERSION 102
|
#define DIRECTX_TEX_VERSION 110
|
||||||
|
|
||||||
#if defined(_MSC_VER) && (_MSC_VER<1610) && !defined(_In_reads_)
|
#if defined(_MSC_VER) && (_MSC_VER<1610) && !defined(_In_reads_)
|
||||||
#define _Analysis_assume_(exp)
|
#define _Analysis_assume_(exp)
|
||||||
@ -403,6 +403,7 @@ namespace DirectX
|
|||||||
TEX_FILTER_CUBIC = 0x300000,
|
TEX_FILTER_CUBIC = 0x300000,
|
||||||
TEX_FILTER_BOX = 0x400000,
|
TEX_FILTER_BOX = 0x400000,
|
||||||
TEX_FILTER_FANT = 0x400000, // Equiv to Box filtering for mipmap generation
|
TEX_FILTER_FANT = 0x400000, // Equiv to Box filtering for mipmap generation
|
||||||
|
TEX_FILTER_TRIANGLE = 0x500000,
|
||||||
// Filtering mode to use for any required image resizing
|
// Filtering mode to use for any required image resizing
|
||||||
|
|
||||||
TEX_FILTER_SRGB_IN = 0x1000000,
|
TEX_FILTER_SRGB_IN = 0x1000000,
|
||||||
|
@ -417,6 +417,10 @@ static bool _UseWICFiltering( _In_ DXGI_FORMAT format, _In_ DWORD filter )
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case TEX_FILTER_TRIANGLE:
|
||||||
|
// WIC does not implement this filter
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
@ -1077,6 +1081,193 @@ static HRESULT _Generate2DMipsCubicFilter( _In_ size_t levels, _In_ DWORD filter
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//--- 2D Triangle Filter ---
|
||||||
|
static HRESULT _Generate2DMipsTriangleFilter( _In_ size_t levels, _In_ DWORD filter, _In_ const ScratchImage& mipChain, _In_ size_t item )
|
||||||
|
{
|
||||||
|
if ( !mipChain.GetImages() )
|
||||||
|
return E_INVALIDARG;
|
||||||
|
|
||||||
|
using namespace TriangleFilter;
|
||||||
|
|
||||||
|
// This assumes that the base image is already placed into the mipChain at the top level... (see _Setup2DMips)
|
||||||
|
|
||||||
|
assert( levels > 1 );
|
||||||
|
|
||||||
|
size_t width = mipChain.GetMetadata().width;
|
||||||
|
size_t height = mipChain.GetMetadata().height;
|
||||||
|
|
||||||
|
// Allocate initial temporary space (1 scanline, accumulation rows, plus X and Y filters)
|
||||||
|
ScopedAlignedArrayXMVECTOR scanline( reinterpret_cast<XMVECTOR*>( _aligned_malloc( sizeof(XMVECTOR) * width, 16 ) ) );
|
||||||
|
if ( !scanline )
|
||||||
|
return E_OUTOFMEMORY;
|
||||||
|
|
||||||
|
std::unique_ptr<TriangleRow[]> rowActive( new (std::nothrow) TriangleRow[ height ] );
|
||||||
|
if ( !rowActive )
|
||||||
|
return E_OUTOFMEMORY;
|
||||||
|
|
||||||
|
TriangleRow * rowFree = nullptr;
|
||||||
|
|
||||||
|
std::unique_ptr<Filter> tfX, tfY;
|
||||||
|
|
||||||
|
XMVECTOR* row = scanline.get();
|
||||||
|
|
||||||
|
// Resize base image to each target mip level
|
||||||
|
for( size_t level=1; level < levels; ++level )
|
||||||
|
{
|
||||||
|
// 2D triangle filter
|
||||||
|
const Image* src = mipChain.GetImage( level-1, item, 0 );
|
||||||
|
const Image* dest = mipChain.GetImage( level, item, 0 );
|
||||||
|
|
||||||
|
if ( !src || !dest )
|
||||||
|
return E_POINTER;
|
||||||
|
|
||||||
|
const uint8_t* pSrc = src->pixels;
|
||||||
|
size_t rowPitch = src->rowPitch;
|
||||||
|
const uint8_t* pEndSrc = pSrc + rowPitch * height;
|
||||||
|
|
||||||
|
uint8_t* pDest = dest->pixels;
|
||||||
|
|
||||||
|
size_t nwidth = (width > 1) ? (width >> 1) : 1;
|
||||||
|
HRESULT hr = _Create( width, nwidth, (filter & TEX_FILTER_WRAP_U) != 0, tfX );
|
||||||
|
if ( FAILED(hr) )
|
||||||
|
return hr;
|
||||||
|
|
||||||
|
size_t nheight = (height > 1) ? (height >> 1) : 1;
|
||||||
|
hr = _Create( height, nheight, (filter & TEX_FILTER_WRAP_V) != 0, tfY );
|
||||||
|
if ( FAILED(hr) )
|
||||||
|
return hr;
|
||||||
|
|
||||||
|
#ifdef _DEBUG
|
||||||
|
memset( row, 0xCD, sizeof(XMVECTOR)*width );
|
||||||
|
#endif
|
||||||
|
|
||||||
|
auto xFromEnd = reinterpret_cast<const FilterFrom*>( reinterpret_cast<const uint8_t*>( tfX.get() ) + tfX->sizeInBytes );
|
||||||
|
auto yFromEnd = reinterpret_cast<const FilterFrom*>( reinterpret_cast<const uint8_t*>( tfY.get() ) + tfY->sizeInBytes );
|
||||||
|
|
||||||
|
// Count times rows get written (and clear out any leftover accumulation rows from last miplevel)
|
||||||
|
for( FilterFrom* yFrom = tfY->from; yFrom < yFromEnd; )
|
||||||
|
{
|
||||||
|
for ( size_t j = 0; j < yFrom->count; ++j )
|
||||||
|
{
|
||||||
|
size_t v = yFrom->to[ j ].u;
|
||||||
|
assert( v < nheight );
|
||||||
|
TriangleRow* rowAcc = &rowActive.get()[ v ];
|
||||||
|
|
||||||
|
++rowAcc->remaining;
|
||||||
|
|
||||||
|
if ( rowAcc->scanline )
|
||||||
|
{
|
||||||
|
memset( rowAcc->scanline.get(), 0, sizeof(XMVECTOR) * nwidth );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
yFrom = reinterpret_cast<FilterFrom*>( reinterpret_cast<uint8_t*>( yFrom ) + yFrom->sizeInBytes );
|
||||||
|
}
|
||||||
|
|
||||||
|
// Filter image
|
||||||
|
for( FilterFrom* yFrom = tfY->from; yFrom < yFromEnd; )
|
||||||
|
{
|
||||||
|
// Create accumulation rows as needed
|
||||||
|
for ( size_t j = 0; j < yFrom->count; ++j )
|
||||||
|
{
|
||||||
|
size_t v = yFrom->to[ j ].u;
|
||||||
|
assert( v < nheight );
|
||||||
|
TriangleRow* rowAcc = &rowActive.get()[ v ];
|
||||||
|
|
||||||
|
if ( !rowAcc->scanline )
|
||||||
|
{
|
||||||
|
if ( rowFree )
|
||||||
|
{
|
||||||
|
// Steal and reuse scanline from 'free row' list
|
||||||
|
// (it will always be at least as wide as nwidth due to loop decending order)
|
||||||
|
assert( rowFree->scanline != 0 );
|
||||||
|
rowAcc->scanline.reset( rowFree->scanline.release() );
|
||||||
|
rowFree = rowFree->next;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
rowAcc->scanline.reset( reinterpret_cast<XMVECTOR*>( _aligned_malloc( sizeof(XMVECTOR) * nwidth, 16 ) ) );
|
||||||
|
if ( !rowAcc->scanline )
|
||||||
|
return E_OUTOFMEMORY;
|
||||||
|
}
|
||||||
|
|
||||||
|
memset( rowAcc->scanline.get(), 0, sizeof(XMVECTOR) * nwidth );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load source scanline
|
||||||
|
if ( (pSrc + rowPitch) > pEndSrc )
|
||||||
|
return E_FAIL;
|
||||||
|
|
||||||
|
if ( !_LoadScanlineLinear( row, width, pSrc, rowPitch, src->format, filter ) )
|
||||||
|
return E_FAIL;
|
||||||
|
|
||||||
|
pSrc += rowPitch;
|
||||||
|
|
||||||
|
// Process row
|
||||||
|
size_t x = 0;
|
||||||
|
for( FilterFrom* xFrom = tfX->from; xFrom < xFromEnd; ++x )
|
||||||
|
{
|
||||||
|
for ( size_t j = 0; j < yFrom->count; ++j )
|
||||||
|
{
|
||||||
|
size_t v = yFrom->to[ j ].u;
|
||||||
|
assert( v < nheight );
|
||||||
|
float yweight = yFrom->to[ j ].weight;
|
||||||
|
|
||||||
|
XMVECTOR* accPtr = rowActive[ v ].scanline.get();
|
||||||
|
if ( !accPtr )
|
||||||
|
return E_POINTER;
|
||||||
|
|
||||||
|
for ( size_t k = 0; k < xFrom->count; ++k )
|
||||||
|
{
|
||||||
|
size_t u = xFrom->to[ k ].u;
|
||||||
|
assert( u < nwidth );
|
||||||
|
|
||||||
|
XMVECTOR weight = XMVectorReplicate( yweight * xFrom->to[ k ].weight );
|
||||||
|
|
||||||
|
assert( x < width );
|
||||||
|
accPtr[ u ] = XMVectorMultiplyAdd( row[ x ], weight, accPtr[ u ] );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
xFrom = reinterpret_cast<FilterFrom*>( reinterpret_cast<uint8_t*>( xFrom ) + xFrom->sizeInBytes );
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write completed accumulation rows
|
||||||
|
for ( size_t j = 0; j < yFrom->count; ++j )
|
||||||
|
{
|
||||||
|
size_t v = yFrom->to[ j ].u;
|
||||||
|
assert( v < nheight );
|
||||||
|
TriangleRow* rowAcc = &rowActive.get()[ v ];
|
||||||
|
|
||||||
|
assert( rowAcc->remaining > 0 );
|
||||||
|
--rowAcc->remaining;
|
||||||
|
|
||||||
|
if ( !rowAcc->remaining )
|
||||||
|
{
|
||||||
|
if ( !_StoreScanlineLinear( pDest + (dest->rowPitch * v), dest->rowPitch, dest->format, rowAcc->scanline.get(), dest->width, filter ) )
|
||||||
|
return E_FAIL;
|
||||||
|
|
||||||
|
// Put row on freelist to reuse it's allocated scanline
|
||||||
|
rowAcc->next = rowFree;
|
||||||
|
rowFree = rowAcc;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
yFrom = reinterpret_cast<FilterFrom*>( reinterpret_cast<uint8_t*>( yFrom ) + yFrom->sizeInBytes );
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( height > 1 )
|
||||||
|
height >>= 1;
|
||||||
|
|
||||||
|
if ( width > 1 )
|
||||||
|
width >>= 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return S_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
//-------------------------------------------------------------------------------------
|
//-------------------------------------------------------------------------------------
|
||||||
// Generate volume mip-map helpers
|
// Generate volume mip-map helpers
|
||||||
//-------------------------------------------------------------------------------------
|
//-------------------------------------------------------------------------------------
|
||||||
@ -2021,6 +2212,226 @@ static HRESULT _Generate3DMipsCubicFilter( _In_ size_t depth, _In_ size_t levels
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//--- 3D Triangle Filter ---
|
||||||
|
static HRESULT _Generate3DMipsTriangleFilter( _In_ size_t depth, _In_ size_t levels, _In_ DWORD filter, _In_ const ScratchImage& mipChain )
|
||||||
|
{
|
||||||
|
if ( !depth || !mipChain.GetImages() )
|
||||||
|
return E_INVALIDARG;
|
||||||
|
|
||||||
|
using namespace TriangleFilter;
|
||||||
|
|
||||||
|
// This assumes that the base images are already placed into the mipChain at the top level... (see _Setup3DMips)
|
||||||
|
|
||||||
|
assert( levels > 1 );
|
||||||
|
|
||||||
|
size_t width = mipChain.GetMetadata().width;
|
||||||
|
size_t height = mipChain.GetMetadata().height;
|
||||||
|
|
||||||
|
// Allocate initial temporary space (1 scanline, accumulation rows, plus X/Y/Z filters)
|
||||||
|
ScopedAlignedArrayXMVECTOR scanline( reinterpret_cast<XMVECTOR*>( _aligned_malloc( sizeof(XMVECTOR) * width, 16 ) ) );
|
||||||
|
if ( !scanline )
|
||||||
|
return E_OUTOFMEMORY;
|
||||||
|
|
||||||
|
std::unique_ptr<TriangleRow[]> sliceActive( new (std::nothrow) TriangleRow[ depth ] );
|
||||||
|
if ( !sliceActive )
|
||||||
|
return E_OUTOFMEMORY;
|
||||||
|
|
||||||
|
TriangleRow * sliceFree = nullptr;
|
||||||
|
|
||||||
|
std::unique_ptr<Filter> tfX, tfY, tfZ;
|
||||||
|
|
||||||
|
XMVECTOR* row = scanline.get();
|
||||||
|
|
||||||
|
// Resize base image to each target mip level
|
||||||
|
for( size_t level=1; level < levels; ++level )
|
||||||
|
{
|
||||||
|
size_t nwidth = (width > 1) ? (width >> 1) : 1;
|
||||||
|
HRESULT hr = _Create( width, nwidth, (filter & TEX_FILTER_WRAP_U) != 0, tfX );
|
||||||
|
if ( FAILED(hr) )
|
||||||
|
return hr;
|
||||||
|
|
||||||
|
size_t nheight = (height > 1) ? (height >> 1) : 1;
|
||||||
|
hr = _Create( height, nheight, (filter & TEX_FILTER_WRAP_V) != 0, tfY );
|
||||||
|
if ( FAILED(hr) )
|
||||||
|
return hr;
|
||||||
|
|
||||||
|
size_t ndepth = (depth > 1 ) ? (depth >> 1) : 1;
|
||||||
|
hr = _Create( depth, ndepth, (filter & TEX_FILTER_WRAP_W) != 0, tfZ );
|
||||||
|
if ( FAILED(hr) )
|
||||||
|
return hr;
|
||||||
|
|
||||||
|
#ifdef _DEBUG
|
||||||
|
memset( row, 0xCD, sizeof(XMVECTOR)*width );
|
||||||
|
#endif
|
||||||
|
|
||||||
|
auto xFromEnd = reinterpret_cast<const FilterFrom*>( reinterpret_cast<const uint8_t*>( tfX.get() ) + tfX->sizeInBytes );
|
||||||
|
auto yFromEnd = reinterpret_cast<const FilterFrom*>( reinterpret_cast<const uint8_t*>( tfY.get() ) + tfY->sizeInBytes );
|
||||||
|
auto zFromEnd = reinterpret_cast<const FilterFrom*>( reinterpret_cast<const uint8_t*>( tfZ.get() ) + tfZ->sizeInBytes );
|
||||||
|
|
||||||
|
// Count times slices get written (and clear out any leftover accumulation slices from last miplevel)
|
||||||
|
for( FilterFrom* zFrom = tfZ->from; zFrom < zFromEnd; )
|
||||||
|
{
|
||||||
|
for ( size_t j = 0; j < zFrom->count; ++j )
|
||||||
|
{
|
||||||
|
size_t w = zFrom->to[ j ].u;
|
||||||
|
assert( w < ndepth );
|
||||||
|
TriangleRow* sliceAcc = &sliceActive.get()[ w ];
|
||||||
|
|
||||||
|
++sliceAcc->remaining;
|
||||||
|
|
||||||
|
if ( sliceAcc->scanline )
|
||||||
|
{
|
||||||
|
memset( sliceAcc->scanline.get(), 0, sizeof(XMVECTOR) * nwidth * nheight );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
zFrom = reinterpret_cast<FilterFrom*>( reinterpret_cast<uint8_t*>( zFrom ) + zFrom->sizeInBytes );
|
||||||
|
}
|
||||||
|
|
||||||
|
// Filter image
|
||||||
|
size_t z = 0;
|
||||||
|
for( FilterFrom* zFrom = tfZ->from; zFrom < zFromEnd; ++z )
|
||||||
|
{
|
||||||
|
// Create accumulation slices as needed
|
||||||
|
for ( size_t j = 0; j < zFrom->count; ++j )
|
||||||
|
{
|
||||||
|
size_t w = zFrom->to[ j ].u;
|
||||||
|
assert( w < ndepth );
|
||||||
|
TriangleRow* sliceAcc = &sliceActive.get()[ w ];
|
||||||
|
|
||||||
|
if ( !sliceAcc->scanline )
|
||||||
|
{
|
||||||
|
if ( sliceFree )
|
||||||
|
{
|
||||||
|
// Steal and reuse scanline from 'free slice' list
|
||||||
|
// (it will always be at least as large as nwidth*nheight due to loop decending order)
|
||||||
|
assert( sliceFree->scanline != 0 );
|
||||||
|
sliceAcc->scanline.reset( sliceFree->scanline.release() );
|
||||||
|
sliceFree = sliceFree->next;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
sliceAcc->scanline.reset( reinterpret_cast<XMVECTOR*>( _aligned_malloc( sizeof(XMVECTOR) * nwidth * nheight, 16 ) ) );
|
||||||
|
if ( !sliceAcc->scanline )
|
||||||
|
return E_OUTOFMEMORY;
|
||||||
|
}
|
||||||
|
|
||||||
|
memset( sliceAcc->scanline.get(), 0, sizeof(XMVECTOR) * nwidth * nheight );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
assert( z < depth );
|
||||||
|
const Image* src = mipChain.GetImage( level-1, 0, z );
|
||||||
|
if ( !src )
|
||||||
|
return E_POINTER;
|
||||||
|
|
||||||
|
const uint8_t* pSrc = src->pixels;
|
||||||
|
size_t rowPitch = src->rowPitch;
|
||||||
|
const uint8_t* pEndSrc = pSrc + rowPitch * height;
|
||||||
|
|
||||||
|
for( FilterFrom* yFrom = tfY->from; yFrom < yFromEnd; )
|
||||||
|
{
|
||||||
|
// Load source scanline
|
||||||
|
if ( (pSrc + rowPitch) > pEndSrc )
|
||||||
|
return E_FAIL;
|
||||||
|
|
||||||
|
if ( !_LoadScanlineLinear( row, width, pSrc, rowPitch, src->format, filter ) )
|
||||||
|
return E_FAIL;
|
||||||
|
|
||||||
|
pSrc += rowPitch;
|
||||||
|
|
||||||
|
// Process row
|
||||||
|
size_t x = 0;
|
||||||
|
for( FilterFrom* xFrom = tfX->from; xFrom < xFromEnd; ++x )
|
||||||
|
{
|
||||||
|
for ( size_t j = 0; j < zFrom->count; ++j )
|
||||||
|
{
|
||||||
|
size_t w = zFrom->to[ j ].u;
|
||||||
|
assert( w < ndepth );
|
||||||
|
float zweight = zFrom->to[ j ].weight;
|
||||||
|
|
||||||
|
XMVECTOR* accSlice = sliceActive[ w ].scanline.get();
|
||||||
|
if ( !accSlice )
|
||||||
|
return E_POINTER;
|
||||||
|
|
||||||
|
for ( size_t k = 0; k < yFrom->count; ++k )
|
||||||
|
{
|
||||||
|
size_t v = yFrom->to[ k ].u;
|
||||||
|
assert( v < nheight );
|
||||||
|
float yweight = yFrom->to[ k ].weight;
|
||||||
|
|
||||||
|
XMVECTOR * accPtr = accSlice + v * nwidth;
|
||||||
|
|
||||||
|
for ( size_t l = 0; l < xFrom->count; ++l )
|
||||||
|
{
|
||||||
|
size_t u = xFrom->to[ l ].u;
|
||||||
|
assert( u < nwidth );
|
||||||
|
|
||||||
|
XMVECTOR weight = XMVectorReplicate( zweight * yweight * xFrom->to[ l ].weight );
|
||||||
|
|
||||||
|
assert( x < width );
|
||||||
|
accPtr[ u ] = XMVectorMultiplyAdd( row[ x ], weight, accPtr[ u ] );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
xFrom = reinterpret_cast<FilterFrom*>( reinterpret_cast<uint8_t*>( xFrom ) + xFrom->sizeInBytes );
|
||||||
|
}
|
||||||
|
|
||||||
|
yFrom = reinterpret_cast<FilterFrom*>( reinterpret_cast<uint8_t*>( yFrom ) + yFrom->sizeInBytes );
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write completed accumulation slices
|
||||||
|
for ( size_t j = 0; j < zFrom->count; ++j )
|
||||||
|
{
|
||||||
|
size_t w = zFrom->to[ j ].u;
|
||||||
|
assert( w < ndepth );
|
||||||
|
TriangleRow* sliceAcc = &sliceActive.get()[ w ];
|
||||||
|
|
||||||
|
assert( sliceAcc->remaining > 0 );
|
||||||
|
--sliceAcc->remaining;
|
||||||
|
|
||||||
|
if ( !sliceAcc->remaining )
|
||||||
|
{
|
||||||
|
const Image* dest = mipChain.GetImage( level, 0, w );
|
||||||
|
XMVECTOR* pAccSrc = sliceAcc->scanline.get();
|
||||||
|
if ( !dest || !pAccSrc )
|
||||||
|
return E_POINTER;
|
||||||
|
|
||||||
|
uint8_t* pDest = dest->pixels;
|
||||||
|
|
||||||
|
for( size_t h = 0; h < nheight; ++h )
|
||||||
|
{
|
||||||
|
if ( !_StoreScanlineLinear( pDest, dest->rowPitch, dest->format, pAccSrc, dest->width, filter ) )
|
||||||
|
return E_FAIL;
|
||||||
|
|
||||||
|
pDest += dest->rowPitch;
|
||||||
|
pAccSrc += nwidth;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Put slice on freelist to reuse it's allocated scanline
|
||||||
|
sliceAcc->next = sliceFree;
|
||||||
|
sliceFree = sliceAcc;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
zFrom = reinterpret_cast<FilterFrom*>( reinterpret_cast<uint8_t*>( zFrom ) + zFrom->sizeInBytes );
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( height > 1 )
|
||||||
|
height >>= 1;
|
||||||
|
|
||||||
|
if ( width > 1 )
|
||||||
|
width >>= 1;
|
||||||
|
|
||||||
|
if ( depth > 1 )
|
||||||
|
depth >>= 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return S_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
//=====================================================================================
|
//=====================================================================================
|
||||||
// Entry-points
|
// Entry-points
|
||||||
//=====================================================================================
|
//=====================================================================================
|
||||||
@ -2172,6 +2583,16 @@ HRESULT GenerateMipMaps( const Image& baseImage, DWORD filter, size_t levels, Sc
|
|||||||
mipChain.Release();
|
mipChain.Release();
|
||||||
return hr;
|
return hr;
|
||||||
|
|
||||||
|
case TEX_FILTER_TRIANGLE:
|
||||||
|
hr = _Setup2DMips( &baseImage, 1, mdata, mipChain );
|
||||||
|
if ( FAILED(hr) )
|
||||||
|
return hr;
|
||||||
|
|
||||||
|
hr = _Generate2DMipsTriangleFilter( levels, filter, mipChain, 0 );
|
||||||
|
if ( FAILED(hr) )
|
||||||
|
mipChain.Release();
|
||||||
|
return hr;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return HRESULT_FROM_WIN32( ERROR_NOT_SUPPORTED );
|
return HRESULT_FROM_WIN32( ERROR_NOT_SUPPORTED );
|
||||||
}
|
}
|
||||||
@ -2359,6 +2780,19 @@ HRESULT GenerateMipMaps( const Image* srcImages, size_t nimages, const TexMetada
|
|||||||
}
|
}
|
||||||
return hr;
|
return hr;
|
||||||
|
|
||||||
|
case TEX_FILTER_TRIANGLE:
|
||||||
|
hr = _Setup2DMips( &baseImages[0], metadata.arraySize, mdata2, mipChain );
|
||||||
|
if ( FAILED(hr) )
|
||||||
|
return hr;
|
||||||
|
|
||||||
|
for( size_t item = 0; item < metadata.arraySize; ++item )
|
||||||
|
{
|
||||||
|
hr = _Generate2DMipsTriangleFilter( levels, filter, mipChain, item );
|
||||||
|
if ( FAILED(hr) )
|
||||||
|
mipChain.Release();
|
||||||
|
}
|
||||||
|
return hr;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return HRESULT_FROM_WIN32( ERROR_NOT_SUPPORTED );
|
return HRESULT_FROM_WIN32( ERROR_NOT_SUPPORTED );
|
||||||
}
|
}
|
||||||
@ -2456,6 +2890,16 @@ HRESULT GenerateMipMaps3D( const Image* baseImages, size_t depth, DWORD filter,
|
|||||||
mipChain.Release();
|
mipChain.Release();
|
||||||
return hr;
|
return hr;
|
||||||
|
|
||||||
|
case TEX_FILTER_TRIANGLE:
|
||||||
|
hr = _Setup3DMips( baseImages, depth, levels, mipChain );
|
||||||
|
if ( FAILED(hr) )
|
||||||
|
return hr;
|
||||||
|
|
||||||
|
hr = _Generate3DMipsTriangleFilter( depth, levels, filter, mipChain );
|
||||||
|
if ( FAILED(hr) )
|
||||||
|
mipChain.Release();
|
||||||
|
return hr;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return HRESULT_FROM_WIN32( ERROR_NOT_SUPPORTED );
|
return HRESULT_FROM_WIN32( ERROR_NOT_SUPPORTED );
|
||||||
}
|
}
|
||||||
@ -2554,6 +2998,16 @@ HRESULT GenerateMipMaps3D( const Image* srcImages, size_t nimages, const TexMeta
|
|||||||
mipChain.Release();
|
mipChain.Release();
|
||||||
return hr;
|
return hr;
|
||||||
|
|
||||||
|
case TEX_FILTER_TRIANGLE:
|
||||||
|
hr = _Setup3DMips( &baseImages[0], metadata.depth, levels, mipChain );
|
||||||
|
if ( FAILED(hr) )
|
||||||
|
return hr;
|
||||||
|
|
||||||
|
hr = _Generate3DMipsTriangleFilter( metadata.depth, levels, filter, mipChain );
|
||||||
|
if ( FAILED(hr) )
|
||||||
|
mipChain.Release();
|
||||||
|
return hr;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return HRESULT_FROM_WIN32( ERROR_NOT_SUPPORTED );
|
return HRESULT_FROM_WIN32( ERROR_NOT_SUPPORTED );
|
||||||
}
|
}
|
||||||
|
@ -206,6 +206,10 @@ static bool _UseWICFiltering( _In_ DXGI_FORMAT format, _In_ DWORD filter )
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case TEX_FILTER_TRIANGLE:
|
||||||
|
// WIC does not implement this filter
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
@ -582,6 +586,159 @@ static HRESULT _ResizeCubicFilter( _In_ const Image& srcImage, _In_ DWORD filter
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//--- Triangle Filter ---
|
||||||
|
static HRESULT _ResizeTriangleFilter( _In_ const Image& srcImage, _In_ DWORD filter, _In_ const Image& destImage )
|
||||||
|
{
|
||||||
|
assert( srcImage.pixels && destImage.pixels );
|
||||||
|
assert( srcImage.format == destImage.format );
|
||||||
|
|
||||||
|
using namespace TriangleFilter;
|
||||||
|
|
||||||
|
// Allocate initial temporary space (1 scanline, accumulation rows, plus X and Y filters)
|
||||||
|
ScopedAlignedArrayXMVECTOR scanline( reinterpret_cast<XMVECTOR*>( _aligned_malloc( sizeof(XMVECTOR) * srcImage.width, 16 ) ) );
|
||||||
|
if ( !scanline )
|
||||||
|
return E_OUTOFMEMORY;
|
||||||
|
|
||||||
|
std::unique_ptr<TriangleRow[]> rowActive( new (std::nothrow) TriangleRow[ destImage.height ] );
|
||||||
|
if ( !rowActive )
|
||||||
|
return E_OUTOFMEMORY;
|
||||||
|
|
||||||
|
TriangleRow * rowFree = nullptr;
|
||||||
|
|
||||||
|
std::unique_ptr<Filter> tfX;
|
||||||
|
HRESULT hr = _Create( srcImage.width, destImage.width, (filter & TEX_FILTER_WRAP_U) != 0, tfX );
|
||||||
|
if ( FAILED(hr) )
|
||||||
|
return hr;
|
||||||
|
|
||||||
|
std::unique_ptr<Filter> tfY;
|
||||||
|
hr = _Create( srcImage.height, destImage.height, (filter & TEX_FILTER_WRAP_V) != 0, tfY );
|
||||||
|
if ( FAILED(hr) )
|
||||||
|
return hr;
|
||||||
|
|
||||||
|
XMVECTOR* row = scanline.get();
|
||||||
|
|
||||||
|
#ifdef _DEBUG
|
||||||
|
memset( row, 0xCD, sizeof(XMVECTOR)*srcImage.width );
|
||||||
|
#endif
|
||||||
|
|
||||||
|
auto xFromEnd = reinterpret_cast<const FilterFrom*>( reinterpret_cast<const uint8_t*>( tfX.get() ) + tfX->sizeInBytes );
|
||||||
|
auto yFromEnd = reinterpret_cast<const FilterFrom*>( reinterpret_cast<const uint8_t*>( tfY.get() ) + tfY->sizeInBytes );
|
||||||
|
|
||||||
|
// Count times rows get written
|
||||||
|
for( FilterFrom* yFrom = tfY->from; yFrom < yFromEnd; )
|
||||||
|
{
|
||||||
|
for ( size_t j = 0; j < yFrom->count; ++j )
|
||||||
|
{
|
||||||
|
size_t v = yFrom->to[ j ].u;
|
||||||
|
assert( v < destImage.height );
|
||||||
|
++rowActive.get()[ v ].remaining;
|
||||||
|
}
|
||||||
|
|
||||||
|
yFrom = reinterpret_cast<FilterFrom*>( reinterpret_cast<uint8_t*>( yFrom ) + yFrom->sizeInBytes );
|
||||||
|
}
|
||||||
|
|
||||||
|
// Filter image
|
||||||
|
const uint8_t* pSrc = srcImage.pixels;
|
||||||
|
size_t rowPitch = srcImage.rowPitch;
|
||||||
|
const uint8_t* pEndSrc = pSrc + rowPitch * srcImage.height;
|
||||||
|
|
||||||
|
uint8_t* pDest = destImage.pixels;
|
||||||
|
|
||||||
|
for( FilterFrom* yFrom = tfY->from; yFrom < yFromEnd; )
|
||||||
|
{
|
||||||
|
// Create accumulation rows as needed
|
||||||
|
for ( size_t j = 0; j < yFrom->count; ++j )
|
||||||
|
{
|
||||||
|
size_t v = yFrom->to[ j ].u;
|
||||||
|
assert( v < destImage.height );
|
||||||
|
TriangleRow* rowAcc = &rowActive.get()[ v ];
|
||||||
|
|
||||||
|
if ( !rowAcc->scanline )
|
||||||
|
{
|
||||||
|
if ( rowFree )
|
||||||
|
{
|
||||||
|
// Steal and reuse scanline from 'free row' list
|
||||||
|
assert( rowFree->scanline != 0 );
|
||||||
|
rowAcc->scanline.reset( rowFree->scanline.release() );
|
||||||
|
rowFree = rowFree->next;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
rowAcc->scanline.reset( reinterpret_cast<XMVECTOR*>( _aligned_malloc( sizeof(XMVECTOR) * destImage.width, 16 ) ) );
|
||||||
|
if ( !rowAcc->scanline )
|
||||||
|
return E_OUTOFMEMORY;
|
||||||
|
}
|
||||||
|
|
||||||
|
memset( rowAcc->scanline.get(), 0, sizeof(XMVECTOR) * destImage.width );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load source scanline
|
||||||
|
if ( (pSrc + rowPitch) > pEndSrc )
|
||||||
|
return E_FAIL;
|
||||||
|
|
||||||
|
if ( !_LoadScanlineLinear( row, srcImage.width, pSrc, rowPitch, srcImage.format, filter ) )
|
||||||
|
return E_FAIL;
|
||||||
|
|
||||||
|
pSrc += rowPitch;
|
||||||
|
|
||||||
|
// Process row
|
||||||
|
size_t x = 0;
|
||||||
|
for( FilterFrom* xFrom = tfX->from; xFrom < xFromEnd; ++x )
|
||||||
|
{
|
||||||
|
for ( size_t j = 0; j < yFrom->count; ++j )
|
||||||
|
{
|
||||||
|
size_t v = yFrom->to[ j ].u;
|
||||||
|
assert( v < destImage.height );
|
||||||
|
float yweight = yFrom->to[ j ].weight;
|
||||||
|
|
||||||
|
XMVECTOR* accPtr = rowActive[ v ].scanline.get();
|
||||||
|
if ( !accPtr )
|
||||||
|
return E_POINTER;
|
||||||
|
|
||||||
|
for ( size_t k = 0; k < xFrom->count; ++k )
|
||||||
|
{
|
||||||
|
size_t u = xFrom->to[ k ].u;
|
||||||
|
assert( u < destImage.width );
|
||||||
|
|
||||||
|
XMVECTOR weight = XMVectorReplicate( yweight * xFrom->to[ k ].weight );
|
||||||
|
|
||||||
|
assert( x < srcImage.width );
|
||||||
|
accPtr[ u ] = XMVectorMultiplyAdd( row[ x ], weight, accPtr[ u ] );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
xFrom = reinterpret_cast<FilterFrom*>( reinterpret_cast<uint8_t*>( xFrom ) + xFrom->sizeInBytes );
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write completed accumulation rows
|
||||||
|
for ( size_t j = 0; j < yFrom->count; ++j )
|
||||||
|
{
|
||||||
|
size_t v = yFrom->to[ j ].u;
|
||||||
|
assert( v < destImage.height );
|
||||||
|
TriangleRow* rowAcc = &rowActive.get()[ v ];
|
||||||
|
|
||||||
|
assert( rowAcc->remaining > 0 );
|
||||||
|
--rowAcc->remaining;
|
||||||
|
|
||||||
|
if ( !rowAcc->remaining )
|
||||||
|
{
|
||||||
|
if ( !_StoreScanlineLinear( pDest + (destImage.rowPitch * v), destImage.rowPitch, destImage.format, rowAcc->scanline.get(), destImage.width, filter ) )
|
||||||
|
return E_FAIL;
|
||||||
|
|
||||||
|
// Put row on freelist to reuse it's allocated scanline
|
||||||
|
rowAcc->next = rowFree;
|
||||||
|
rowFree = rowAcc;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
yFrom = reinterpret_cast<FilterFrom*>( reinterpret_cast<uint8_t*>( yFrom ) + yFrom->sizeInBytes );
|
||||||
|
}
|
||||||
|
|
||||||
|
return S_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
//--- Custom filter resize ---
|
//--- Custom filter resize ---
|
||||||
static HRESULT _PerformResizeUsingCustomFilters( _In_ const Image& srcImage, _In_ DWORD filter, _In_ const Image& destImage )
|
static HRESULT _PerformResizeUsingCustomFilters( _In_ const Image& srcImage, _In_ DWORD filter, _In_ const Image& destImage )
|
||||||
{
|
{
|
||||||
@ -612,6 +769,9 @@ static HRESULT _PerformResizeUsingCustomFilters( _In_ const Image& srcImage, _In
|
|||||||
case TEX_FILTER_CUBIC:
|
case TEX_FILTER_CUBIC:
|
||||||
return _ResizeCubicFilter( srcImage, filter, destImage );
|
return _ResizeCubicFilter( srcImage, filter, destImage );
|
||||||
|
|
||||||
|
case TEX_FILTER_TRIANGLE:
|
||||||
|
return _ResizeTriangleFilter( srcImage, filter, destImage );
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return HRESULT_FROM_WIN32( ERROR_NOT_SUPPORTED );
|
return HRESULT_FROM_WIN32( ERROR_NOT_SUPPORTED );
|
||||||
}
|
}
|
||||||
|
@ -22,6 +22,10 @@
|
|||||||
#include <directxpackedvector.h>
|
#include <directxpackedvector.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
#include "scoped.h"
|
||||||
|
|
||||||
namespace DirectX
|
namespace DirectX
|
||||||
{
|
{
|
||||||
|
|
||||||
@ -163,7 +167,7 @@ struct CubicFilter
|
|||||||
float x;
|
float x;
|
||||||
};
|
};
|
||||||
|
|
||||||
inline void _CreateCubicFilter( _In_ size_t source, _In_ size_t dest, _In_ bool wrap, bool mirror, _Out_writes_(dest) CubicFilter* cf )
|
inline void _CreateCubicFilter( _In_ size_t source, _In_ size_t dest, _In_ bool wrap, _In_ bool mirror, _Out_writes_(dest) CubicFilter* cf )
|
||||||
{
|
{
|
||||||
assert( source > 0 );
|
assert( source > 0 );
|
||||||
assert( dest > 0 );
|
assert( dest > 0 );
|
||||||
@ -206,4 +210,219 @@ inline void _CreateCubicFilter( _In_ size_t source, _In_ size_t dest, _In_ bool
|
|||||||
res = a0 + a1*vdx + a2*vdx2 + a3*vdx3; \
|
res = a0 + a1*vdx + a2*vdx2 + a3*vdx3; \
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//-------------------------------------------------------------------------------------
|
||||||
|
// Triangle filtering helpers
|
||||||
|
//-------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
namespace TriangleFilter
|
||||||
|
{
|
||||||
|
struct FilterTo
|
||||||
|
{
|
||||||
|
size_t u;
|
||||||
|
float weight;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct FilterFrom
|
||||||
|
{
|
||||||
|
size_t count;
|
||||||
|
size_t sizeInBytes;
|
||||||
|
FilterTo to[1]; // variable-sized array
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Filter
|
||||||
|
{
|
||||||
|
size_t sizeInBytes;
|
||||||
|
size_t totalSize;
|
||||||
|
FilterFrom from[1]; // variable-sized array
|
||||||
|
};
|
||||||
|
|
||||||
|
struct TriangleRow
|
||||||
|
{
|
||||||
|
size_t remaining;
|
||||||
|
TriangleRow* next;
|
||||||
|
ScopedAlignedArrayXMVECTOR scanline;
|
||||||
|
|
||||||
|
TriangleRow() : remaining(0), next(nullptr) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
static const size_t TF_FILTER_SIZE = sizeof(Filter) - sizeof(FilterFrom);
|
||||||
|
static const size_t TF_FROM_SIZE = sizeof(FilterFrom) - sizeof(FilterTo);
|
||||||
|
static const size_t TF_TO_SIZE = sizeof(FilterTo);
|
||||||
|
|
||||||
|
static const float TF_EPSILON = 0.00001f;
|
||||||
|
|
||||||
|
inline HRESULT _Create( _In_ size_t source, _In_ size_t dest, _In_ bool wrap, _Inout_ std::unique_ptr<Filter>& tf )
|
||||||
|
{
|
||||||
|
assert( source > 0 );
|
||||||
|
assert( dest > 0 );
|
||||||
|
|
||||||
|
float scale = float(dest) / float(source);
|
||||||
|
float scaleInv = 0.5f / scale;
|
||||||
|
|
||||||
|
// Determine storage required for filter and allocate memory if needed
|
||||||
|
size_t totalSize = TF_FILTER_SIZE + TF_FROM_SIZE + TF_TO_SIZE;
|
||||||
|
float repeat = (wrap) ? 1.f : 0.f;
|
||||||
|
|
||||||
|
for( size_t u = 0; u < source; ++u )
|
||||||
|
{
|
||||||
|
float src = float(u) - 0.5f;
|
||||||
|
float destMin = src * scale;
|
||||||
|
float destMax = destMin + scale;
|
||||||
|
|
||||||
|
totalSize += TF_FROM_SIZE + TF_TO_SIZE + size_t( destMax - destMin + repeat + 1.f ) * TF_TO_SIZE * 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t* pFilter = nullptr;
|
||||||
|
|
||||||
|
if ( tf )
|
||||||
|
{
|
||||||
|
// See if existing filter memory block is large enough to reuse
|
||||||
|
if ( tf->totalSize >= totalSize )
|
||||||
|
{
|
||||||
|
pFilter = reinterpret_cast<uint8_t*>( tf.get() );
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Need to reallocate filter memory block
|
||||||
|
tf.reset( nullptr );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( !tf )
|
||||||
|
{
|
||||||
|
// Allocate filter memory block
|
||||||
|
pFilter = new (std::nothrow) uint8_t[ totalSize ];
|
||||||
|
if ( !pFilter )
|
||||||
|
return E_OUTOFMEMORY;
|
||||||
|
|
||||||
|
tf.reset( reinterpret_cast<Filter*>( pFilter ) );
|
||||||
|
tf->totalSize = totalSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
assert( pFilter != 0 );
|
||||||
|
|
||||||
|
// Filter setup
|
||||||
|
size_t sizeInBytes = TF_FILTER_SIZE;
|
||||||
|
size_t accumU = 0;
|
||||||
|
float accumWeight = 0.f;
|
||||||
|
|
||||||
|
for( size_t u = 0; u < source; ++u )
|
||||||
|
{
|
||||||
|
// Setup from entry
|
||||||
|
size_t sizeFrom = sizeInBytes;
|
||||||
|
auto pFrom = reinterpret_cast<FilterFrom*>( pFilter + sizeInBytes );
|
||||||
|
sizeInBytes += TF_FROM_SIZE;
|
||||||
|
|
||||||
|
if ( sizeInBytes > totalSize )
|
||||||
|
return E_FAIL;
|
||||||
|
|
||||||
|
size_t toCount = 0;
|
||||||
|
|
||||||
|
// Perform two passes to capture the influences from both sides
|
||||||
|
for( size_t j = 0; j < 2; ++j )
|
||||||
|
{
|
||||||
|
float src = float( u + j ) - 0.5f;
|
||||||
|
|
||||||
|
float destMin = src * scale;
|
||||||
|
float destMax = destMin + scale;
|
||||||
|
|
||||||
|
if ( !wrap )
|
||||||
|
{
|
||||||
|
// Clamp
|
||||||
|
if ( destMin < 0.f )
|
||||||
|
destMin = 0.f;
|
||||||
|
if ( destMax > float(dest) )
|
||||||
|
destMax = float(dest);
|
||||||
|
}
|
||||||
|
|
||||||
|
for( auto k = static_cast<ptrdiff_t>( floorf( destMin ) ); float(k) < destMax; ++k )
|
||||||
|
{
|
||||||
|
float d0 = float(k);
|
||||||
|
float d1 = d0 + 1.f;
|
||||||
|
|
||||||
|
size_t u0;
|
||||||
|
if ( k < 0 )
|
||||||
|
{
|
||||||
|
// Handle wrap
|
||||||
|
u0 = size_t( k + ptrdiff_t(dest) );
|
||||||
|
}
|
||||||
|
else if ( k >= ptrdiff_t(dest) )
|
||||||
|
{
|
||||||
|
// Handle wrap
|
||||||
|
u0 = size_t( k - ptrdiff_t(dest) );
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
u0 = size_t( k );
|
||||||
|
}
|
||||||
|
|
||||||
|
// Save previous accumulated weight (if any)
|
||||||
|
if ( u0 != accumU )
|
||||||
|
{
|
||||||
|
if ( accumWeight > TF_EPSILON )
|
||||||
|
{
|
||||||
|
auto pTo = reinterpret_cast<FilterTo*>( pFilter + sizeInBytes );
|
||||||
|
sizeInBytes += TF_TO_SIZE;
|
||||||
|
++toCount;
|
||||||
|
|
||||||
|
if ( sizeInBytes > totalSize )
|
||||||
|
return E_FAIL;
|
||||||
|
|
||||||
|
pTo->u = accumU;
|
||||||
|
pTo->weight = accumWeight;
|
||||||
|
}
|
||||||
|
|
||||||
|
accumWeight = 0.f;
|
||||||
|
accumU = u0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clip destination
|
||||||
|
if ( d0 < destMin )
|
||||||
|
d0 = destMin;
|
||||||
|
if ( d1 > destMax )
|
||||||
|
d1 = destMax;
|
||||||
|
|
||||||
|
// Calculate average weight over destination pixel
|
||||||
|
|
||||||
|
float weight;
|
||||||
|
if ( !wrap && src < 0.f )
|
||||||
|
weight = 1.f;
|
||||||
|
else if ( !wrap && ( ( src + 1.f ) >= float(source) ) )
|
||||||
|
weight = 0.f;
|
||||||
|
else
|
||||||
|
weight = (d0 + d1) * scaleInv - src;
|
||||||
|
|
||||||
|
accumWeight += (d1 - d0) * ( j ? (1.f - weight) : weight );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Store accumulated weight
|
||||||
|
if ( accumWeight > TF_EPSILON )
|
||||||
|
{
|
||||||
|
auto pTo = reinterpret_cast<FilterTo*>( pFilter + sizeInBytes );
|
||||||
|
sizeInBytes += TF_TO_SIZE;
|
||||||
|
++toCount;
|
||||||
|
|
||||||
|
if ( sizeInBytes > totalSize )
|
||||||
|
return E_FAIL;
|
||||||
|
|
||||||
|
pTo->u = accumU;
|
||||||
|
pTo->weight = accumWeight;
|
||||||
|
}
|
||||||
|
|
||||||
|
accumWeight = 0.f;
|
||||||
|
|
||||||
|
// Finalize from entry
|
||||||
|
pFrom->count = toCount;
|
||||||
|
pFrom->sizeInBytes = sizeInBytes - sizeFrom;
|
||||||
|
}
|
||||||
|
|
||||||
|
tf->sizeInBytes = sizeInBytes;
|
||||||
|
|
||||||
|
return S_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
}; // namespace
|
||||||
|
|
||||||
}; // namespace
|
}; // namespace
|
@ -183,16 +183,19 @@ SValue g_pFilters[] =
|
|||||||
{ L"CUBIC", TEX_FILTER_CUBIC },
|
{ L"CUBIC", TEX_FILTER_CUBIC },
|
||||||
{ L"FANT", TEX_FILTER_FANT },
|
{ L"FANT", TEX_FILTER_FANT },
|
||||||
{ L"BOX", TEX_FILTER_BOX },
|
{ L"BOX", TEX_FILTER_BOX },
|
||||||
|
{ L"TRIANGLE", TEX_FILTER_TRIANGLE },
|
||||||
{ L"POINT_DITHER", TEX_FILTER_POINT | TEX_FILTER_DITHER },
|
{ L"POINT_DITHER", TEX_FILTER_POINT | TEX_FILTER_DITHER },
|
||||||
{ L"LINEAR_DITHER", TEX_FILTER_LINEAR | TEX_FILTER_DITHER },
|
{ L"LINEAR_DITHER", TEX_FILTER_LINEAR | TEX_FILTER_DITHER },
|
||||||
{ L"CUBIC_DITHER", TEX_FILTER_CUBIC | TEX_FILTER_DITHER },
|
{ L"CUBIC_DITHER", TEX_FILTER_CUBIC | TEX_FILTER_DITHER },
|
||||||
{ L"FANT_DITHER", TEX_FILTER_FANT | TEX_FILTER_DITHER },
|
{ L"FANT_DITHER", TEX_FILTER_FANT | TEX_FILTER_DITHER },
|
||||||
{ L"BOX_DITHER", TEX_FILTER_BOX | TEX_FILTER_DITHER },
|
{ L"BOX_DITHER", TEX_FILTER_BOX | TEX_FILTER_DITHER },
|
||||||
|
{ L"TRIANGLE_DITHER", TEX_FILTER_TRIANGLE | TEX_FILTER_DITHER },
|
||||||
{ L"POINT_DITHER_DIFFUSION", TEX_FILTER_POINT | TEX_FILTER_DITHER_DIFFUSION },
|
{ L"POINT_DITHER_DIFFUSION", TEX_FILTER_POINT | TEX_FILTER_DITHER_DIFFUSION },
|
||||||
{ L"LINEAR_DITHER_DIFFUSION", TEX_FILTER_LINEAR | TEX_FILTER_DITHER_DIFFUSION },
|
{ L"LINEAR_DITHER_DIFFUSION", TEX_FILTER_LINEAR | TEX_FILTER_DITHER_DIFFUSION },
|
||||||
{ L"CUBIC_DITHER_DIFFUSION", TEX_FILTER_CUBIC | TEX_FILTER_DITHER_DIFFUSION },
|
{ L"CUBIC_DITHER_DIFFUSION", TEX_FILTER_CUBIC | TEX_FILTER_DITHER_DIFFUSION },
|
||||||
{ L"FANT_DITHER_DIFFUSION", TEX_FILTER_FANT | TEX_FILTER_DITHER_DIFFUSION },
|
{ L"FANT_DITHER_DIFFUSION", TEX_FILTER_FANT | TEX_FILTER_DITHER_DIFFUSION },
|
||||||
{ L"BOX_DITHER_DIFFUSION", TEX_FILTER_BOX | TEX_FILTER_DITHER_DIFFUSION },
|
{ L"BOX_DITHER_DIFFUSION", TEX_FILTER_BOX | TEX_FILTER_DITHER_DIFFUSION },
|
||||||
|
{ L"TRIANGLE_DITHER_DIFFUSION", TEX_FILTER_TRIANGLE | TEX_FILTER_DITHER_DIFFUSION },
|
||||||
{ nullptr, TEX_FILTER_DEFAULT }
|
{ nullptr, TEX_FILTER_DEFAULT }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user