mirror of
https://github.com/microsoft/DirectXTex.git
synced 2026-02-09 22:26:13 +01:00
[texconv] Implemented alpha coverage preservation option (#82)
This commit is contained in:
committed by
Chuck Walbourn
parent
a79989a4f0
commit
f2c4d94a35
@@ -534,6 +534,11 @@ namespace DirectX
|
||||
// levels of '0' indicates a full mipchain, otherwise is generates that number of total levels (including the source base image)
|
||||
// Defaults to Fant filtering which is equivalent to a box filter
|
||||
|
||||
HRESULT __cdecl ScaleMipMapsAlphaForCoverage(
|
||||
_In_ const Image* srcImages, _In_ const TexMetadata& metadata, _In_ size_t item,
|
||||
_In_ float alphaReference, _Out_ ScratchImage& mipChain);
|
||||
|
||||
|
||||
enum TEX_PMALPHA_FLAGS
|
||||
{
|
||||
TEX_PMALPHA_DEFAULT = 0,
|
||||
|
||||
@@ -116,6 +116,211 @@ namespace
|
||||
|
||||
return hr;
|
||||
}
|
||||
|
||||
|
||||
HRESULT ScaleAlpha(
|
||||
_In_ const Image& srcImage,
|
||||
_In_ float alphaScale,
|
||||
_Out_ const Image& destImage)
|
||||
{
|
||||
assert(srcImage.width == destImage.width);
|
||||
assert(srcImage.height == destImage.height);
|
||||
|
||||
ScopedAlignedArrayXMVECTOR scanline(reinterpret_cast<XMVECTOR*>(_aligned_malloc((sizeof(XMVECTOR)*srcImage.width), 16)));
|
||||
if (!scanline)
|
||||
{
|
||||
return E_OUTOFMEMORY;
|
||||
}
|
||||
|
||||
const uint8_t* pSrc = srcImage.pixels;
|
||||
uint8_t* pDest = destImage.pixels;
|
||||
if (!pSrc || !pDest)
|
||||
{
|
||||
return E_POINTER;
|
||||
}
|
||||
|
||||
const XMVECTOR vscale = XMVectorReplicate(alphaScale);
|
||||
|
||||
for (size_t h = 0; h < srcImage.height; ++h)
|
||||
{
|
||||
if (!_LoadScanline(scanline.get(), srcImage.width, pSrc, srcImage.rowPitch, srcImage.format))
|
||||
{
|
||||
return E_FAIL;
|
||||
}
|
||||
|
||||
XMVECTOR* ptr = scanline.get();
|
||||
for (size_t w = 0; w < srcImage.width; ++w)
|
||||
{
|
||||
XMVECTOR v = *ptr;
|
||||
XMVECTOR alpha = XMVectorMultiply(XMVectorSplatW(v), vscale);
|
||||
*(ptr++) = XMVectorSelect(alpha, v, g_XMSelect1110);
|
||||
}
|
||||
|
||||
if (!_StoreScanline(pDest, destImage.rowPitch, destImage.format, scanline.get(), srcImage.width))
|
||||
{
|
||||
return E_FAIL;
|
||||
}
|
||||
|
||||
pSrc += srcImage.rowPitch;
|
||||
pDest += destImage.rowPitch;
|
||||
}
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
|
||||
void GenerateAlphaCoverageConvolutionVectors(
|
||||
_In_ size_t N,
|
||||
_Out_writes_(N*N) XMVECTOR* vectors)
|
||||
{
|
||||
for (size_t sy = 0; sy < N; ++sy)
|
||||
{
|
||||
const float fy = (sy + 0.5f) / N;
|
||||
const float ify = 1.0f - fy;
|
||||
|
||||
for (size_t sx = 0; sx < N; ++sx)
|
||||
{
|
||||
const float fx = (sx + 0.5f) / N;
|
||||
const float ifx = 1.0f - fx;
|
||||
|
||||
// [0]=(x+0, y+0), [1]=(x+0, y+1), [2]=(x+1, y+0), [3]=(x+1, y+1)
|
||||
vectors[sy * N + sx] = XMVectorSet(ifx * ify, ifx * fy, fx * ify, fx * fy);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
HRESULT CalculateAlphaCoverage(
|
||||
const Image& srcImage,
|
||||
float alphaReference,
|
||||
float alphaScale,
|
||||
float& coverage)
|
||||
{
|
||||
coverage = 0.0f;
|
||||
|
||||
ScopedAlignedArrayXMVECTOR row0(reinterpret_cast<XMVECTOR*>(_aligned_malloc((sizeof(XMVECTOR)*srcImage.width), 16)));
|
||||
if (!row0)
|
||||
{
|
||||
return E_OUTOFMEMORY;
|
||||
}
|
||||
|
||||
ScopedAlignedArrayXMVECTOR row1(reinterpret_cast<XMVECTOR*>(_aligned_malloc((sizeof(XMVECTOR)*srcImage.width), 16)));
|
||||
if (!row1)
|
||||
{
|
||||
return E_OUTOFMEMORY;
|
||||
}
|
||||
|
||||
const DWORD flags = 0;
|
||||
const XMVECTOR scale = XMVectorReplicate(alphaScale);
|
||||
|
||||
const uint8_t *pSrcRow0 = srcImage.pixels;
|
||||
if (!pSrcRow0)
|
||||
{
|
||||
return E_POINTER;
|
||||
}
|
||||
|
||||
const size_t N = 8;
|
||||
XMVECTOR convolution[N * N];
|
||||
GenerateAlphaCoverageConvolutionVectors(N, convolution);
|
||||
|
||||
XMMATRIX alpha;
|
||||
|
||||
size_t coverageCount = 0;
|
||||
for (size_t y = 0; y < srcImage.height - 1; ++y)
|
||||
{
|
||||
if (!_LoadScanlineLinear(row0.get(), srcImage.width, pSrcRow0, srcImage.rowPitch, srcImage.format, flags))
|
||||
{
|
||||
return E_FAIL;
|
||||
}
|
||||
|
||||
const uint8_t *pSrcRow1 = pSrcRow0 + srcImage.rowPitch;
|
||||
if (!_LoadScanlineLinear(row1.get(), srcImage.width, pSrcRow1, srcImage.rowPitch, srcImage.format, flags))
|
||||
{
|
||||
return E_FAIL;
|
||||
}
|
||||
|
||||
const XMVECTOR* pRow0 = row0.get();
|
||||
const XMVECTOR* pRow1 = row1.get();
|
||||
for (size_t x = 0; x < srcImage.width - 1; ++x)
|
||||
{
|
||||
// [0]=(x+0, y+0), [1]=(x+0, y+1), [2]=(x+1, y+0), [3]=(x+1, y+1)
|
||||
alpha.r[0] = XMVectorSaturate(XMVectorMultiply(XMVectorSplatW(*pRow0), scale));
|
||||
alpha.r[1] = XMVectorSaturate(XMVectorMultiply(XMVectorSplatW(*pRow1), scale));
|
||||
alpha.r[2] = XMVectorSaturate(XMVectorMultiply(XMVectorSplatW(*(pRow0++)), scale));
|
||||
alpha.r[3] = XMVectorSaturate(XMVectorMultiply(XMVectorSplatW(*(pRow1++)), scale));
|
||||
XMVECTOR v = XMVectorSet(XMVectorGetX(alpha.r[0]), XMVectorGetX(alpha.r[1]), XMVectorGetX(alpha.r[2]), XMVectorGetX(alpha.r[3]));
|
||||
|
||||
for (size_t sy = 0; sy < N; ++sy)
|
||||
{
|
||||
const size_t ry = sy * N;
|
||||
for (size_t sx = 0; sx < N; ++sx)
|
||||
{
|
||||
v = XMVectorSum(XMVectorMultiply(v, convolution[ry + sx]));
|
||||
if (XMVectorGetX(v) > alphaReference)
|
||||
{
|
||||
++coverageCount;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pSrcRow0 = pSrcRow1;
|
||||
}
|
||||
|
||||
coverage = static_cast<float>(coverageCount) / static_cast<float>((srcImage.width - 1) * (srcImage.height - 1) * N * N);
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
|
||||
HRESULT EstimateAlphaScaleForCoverage(
|
||||
_In_ const Image& srcImage,
|
||||
_In_ float alphaReference,
|
||||
_In_ float targetCoverage,
|
||||
_Out_ float& alphaScale)
|
||||
{
|
||||
float minAlphaScale = 0.0f;
|
||||
float maxAlphaScale = 4.0f;
|
||||
float bestAlphaScale = 1.0f;
|
||||
float bestError = FLT_MAX;
|
||||
|
||||
// Determine desired scale using a binary search. Hardcoded to 10 steps max.
|
||||
alphaScale = 1.0f;
|
||||
const size_t N = 10;
|
||||
for (size_t i = 0; i < N; ++i)
|
||||
{
|
||||
float currentCoverage = 0.0f;
|
||||
HRESULT hr = CalculateAlphaCoverage(srcImage, alphaReference, alphaScale, currentCoverage);
|
||||
if (FAILED(hr))
|
||||
{
|
||||
return hr;
|
||||
}
|
||||
|
||||
const float error = fabsf(currentCoverage - targetCoverage);
|
||||
if (error < bestError)
|
||||
{
|
||||
bestError = error;
|
||||
bestAlphaScale = alphaScale;
|
||||
}
|
||||
|
||||
if (currentCoverage < targetCoverage)
|
||||
{
|
||||
minAlphaScale = alphaScale;
|
||||
}
|
||||
else if (currentCoverage > targetCoverage)
|
||||
{
|
||||
maxAlphaScale = alphaScale;
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
alphaScale = (minAlphaScale + maxAlphaScale) * 0.5f;
|
||||
}
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -3179,3 +3384,40 @@ HRESULT DirectX::GenerateMipMaps3D(
|
||||
return HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED);
|
||||
}
|
||||
}
|
||||
|
||||
_Use_decl_annotations_
|
||||
HRESULT DirectX::ScaleMipMapsAlphaForCoverage(
|
||||
const Image* srcImages,
|
||||
const TexMetadata& metadata,
|
||||
size_t item,
|
||||
float alphaReference,
|
||||
ScratchImage& mipChain)
|
||||
{
|
||||
HRESULT hr;
|
||||
|
||||
float targetCoverage = 0.0f;
|
||||
hr = CalculateAlphaCoverage(*srcImages, alphaReference, 1.0f, targetCoverage);
|
||||
if (FAILED(hr))
|
||||
{
|
||||
return hr;
|
||||
}
|
||||
|
||||
const size_t levels = metadata.mipLevels;
|
||||
for (size_t level = 1; level < levels; ++level)
|
||||
{
|
||||
float alphaScale = 0.0f;
|
||||
hr = EstimateAlphaScaleForCoverage(srcImages[level], alphaReference, targetCoverage, alphaScale);
|
||||
if (FAILED(hr))
|
||||
{
|
||||
return hr;
|
||||
}
|
||||
|
||||
hr = ScaleAlpha(srcImages[level], alphaScale, *mipChain.GetImage(level, item, 0));
|
||||
if (FAILED(hr))
|
||||
{
|
||||
return hr;
|
||||
}
|
||||
}
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
Reference in New Issue
Block a user