in DirectXTex/DirectXTexConvert.cpp [3011:3711]
void DirectX::_ConvertScanline(
XMVECTOR* pBuffer,
size_t count,
DXGI_FORMAT outFormat,
DXGI_FORMAT inFormat,
TEX_FILTER_FLAGS flags) noexcept
{
assert(pBuffer && count > 0 && ((reinterpret_cast<uintptr_t>(pBuffer) & 0xF) == 0));
assert(IsValid(outFormat) && !IsTypeless(outFormat) && !IsPlanar(outFormat) && !IsPalettized(outFormat));
assert(IsValid(inFormat) && !IsTypeless(inFormat) && !IsPlanar(inFormat) && !IsPalettized(inFormat));
if (!pBuffer)
return;
#ifdef _DEBUG
// Ensure conversion table is in ascending order
assert(std::size(g_ConvertTable) > 0);
DXGI_FORMAT lastvalue = g_ConvertTable[0].format;
for (size_t index = 1; index < std::size(g_ConvertTable); ++index)
{
assert(g_ConvertTable[index].format > lastvalue);
lastvalue = g_ConvertTable[index].format;
}
#endif
// Determine conversion details about source and dest formats
ConvertData key = { inFormat, 0, 0 };
auto in = reinterpret_cast<const ConvertData*>(
bsearch(&key, g_ConvertTable, std::size(g_ConvertTable), sizeof(ConvertData), ConvertCompare));
key.format = outFormat;
auto out = reinterpret_cast<const ConvertData*>(
bsearch(&key, g_ConvertTable, std::size(g_ConvertTable), sizeof(ConvertData), ConvertCompare));
if (!in || !out)
{
assert(false);
return;
}
assert(_GetConvertFlags(inFormat) == in->flags);
assert(_GetConvertFlags(outFormat) == out->flags);
// Handle SRGB filtering modes
switch (inFormat)
{
case DXGI_FORMAT_R8G8B8A8_UNORM_SRGB:
case DXGI_FORMAT_BC1_UNORM_SRGB:
case DXGI_FORMAT_BC2_UNORM_SRGB:
case DXGI_FORMAT_BC3_UNORM_SRGB:
case DXGI_FORMAT_B8G8R8A8_UNORM_SRGB:
case DXGI_FORMAT_B8G8R8X8_UNORM_SRGB:
case DXGI_FORMAT_BC7_UNORM_SRGB:
flags |= TEX_FILTER_SRGB_IN;
break;
case DXGI_FORMAT_A8_UNORM:
case DXGI_FORMAT_R10G10B10_XR_BIAS_A2_UNORM:
flags &= ~TEX_FILTER_SRGB_IN;
break;
default:
break;
}
switch (outFormat)
{
case DXGI_FORMAT_R8G8B8A8_UNORM_SRGB:
case DXGI_FORMAT_BC1_UNORM_SRGB:
case DXGI_FORMAT_BC2_UNORM_SRGB:
case DXGI_FORMAT_BC3_UNORM_SRGB:
case DXGI_FORMAT_B8G8R8A8_UNORM_SRGB:
case DXGI_FORMAT_B8G8R8X8_UNORM_SRGB:
case DXGI_FORMAT_BC7_UNORM_SRGB:
flags |= TEX_FILTER_SRGB_OUT;
break;
case DXGI_FORMAT_A8_UNORM:
case DXGI_FORMAT_R10G10B10_XR_BIAS_A2_UNORM:
flags &= ~TEX_FILTER_SRGB_OUT;
break;
default:
break;
}
if ((flags & (TEX_FILTER_SRGB_IN | TEX_FILTER_SRGB_OUT)) == (TEX_FILTER_SRGB_IN | TEX_FILTER_SRGB_OUT))
{
flags &= ~(TEX_FILTER_SRGB_IN | TEX_FILTER_SRGB_OUT);
}
// sRGB input processing (sRGB -> Linear RGB)
if (flags & TEX_FILTER_SRGB_IN)
{
if (!(in->flags & CONVF_DEPTH) && ((in->flags & CONVF_FLOAT) || (in->flags & CONVF_UNORM)))
{
XMVECTOR* ptr = pBuffer;
for (size_t i = 0; i < count; ++i, ++ptr)
{
*ptr = XMColorSRGBToRGB(*ptr);
}
}
}
// Handle conversion special cases
uint32_t diffFlags = in->flags ^ out->flags;
if (diffFlags != 0)
{
if (diffFlags & CONVF_DEPTH)
{
//--- Depth conversions ---
if (in->flags & CONVF_DEPTH)
{
// CONVF_DEPTH -> !CONVF_DEPTH
if (in->flags & CONVF_STENCIL)
{
// Stencil -> Alpha
static const XMVECTORF32 S = { { { 1.f, 1.f, 1.f, 255.f } } };
if (out->flags & CONVF_UNORM)
{
// UINT -> UNORM
XMVECTOR* ptr = pBuffer;
for (size_t i = 0; i < count; ++i)
{
XMVECTOR v = *ptr;
XMVECTOR v1 = XMVectorSplatY(v);
v1 = XMVectorClamp(v1, g_XMZero, S);
v1 = XMVectorDivide(v1, S);
*ptr++ = XMVectorSelect(v1, v, g_XMSelect1110);
}
}
else if (out->flags & CONVF_SNORM)
{
// UINT -> SNORM
XMVECTOR* ptr = pBuffer;
for (size_t i = 0; i < count; ++i)
{
XMVECTOR v = *ptr;
XMVECTOR v1 = XMVectorSplatY(v);
v1 = XMVectorClamp(v1, g_XMZero, S);
v1 = XMVectorDivide(v1, S);
v1 = XMVectorMultiplyAdd(v1, g_XMTwo, g_XMNegativeOne);
*ptr++ = XMVectorSelect(v1, v, g_XMSelect1110);
}
}
else
{
XMVECTOR* ptr = pBuffer;
for (size_t i = 0; i < count; ++i)
{
XMVECTOR v = *ptr;
XMVECTOR v1 = XMVectorSplatY(v);
*ptr++ = XMVectorSelect(v1, v, g_XMSelect1110);
}
}
}
// Depth -> RGB
if ((out->flags & CONVF_UNORM) && (in->flags & CONVF_FLOAT))
{
// Depth FLOAT -> UNORM
XMVECTOR* ptr = pBuffer;
for (size_t i = 0; i < count; ++i)
{
XMVECTOR v = *ptr;
XMVECTOR v1 = XMVectorSaturate(v);
v1 = XMVectorSplatX(v1);
*ptr++ = XMVectorSelect(v, v1, g_XMSelect1110);
}
}
else if (out->flags & CONVF_SNORM)
{
if (in->flags & CONVF_UNORM)
{
// Depth UNORM -> SNORM
XMVECTOR* ptr = pBuffer;
for (size_t i = 0; i < count; ++i)
{
XMVECTOR v = *ptr;
XMVECTOR v1 = XMVectorMultiplyAdd(v, g_XMTwo, g_XMNegativeOne);
v1 = XMVectorSplatX(v1);
*ptr++ = XMVectorSelect(v, v1, g_XMSelect1110);
}
}
else
{
// Depth FLOAT -> SNORM
XMVECTOR* ptr = pBuffer;
for (size_t i = 0; i < count; ++i)
{
XMVECTOR v = *ptr;
XMVECTOR v1 = XMVectorClamp(v, g_XMNegativeOne, g_XMOne);
v1 = XMVectorSplatX(v1);
*ptr++ = XMVectorSelect(v, v1, g_XMSelect1110);
}
}
}
else
{
XMVECTOR* ptr = pBuffer;
for (size_t i = 0; i < count; ++i)
{
XMVECTOR v = *ptr;
XMVECTOR v1 = XMVectorSplatX(v);
*ptr++ = XMVectorSelect(v, v1, g_XMSelect1110);
}
}
}
else
{
// !CONVF_DEPTH -> CONVF_DEPTH
// RGB -> Depth (red channel)
switch (flags & (TEX_FILTER_RGB_COPY_RED | TEX_FILTER_RGB_COPY_GREEN | TEX_FILTER_RGB_COPY_BLUE))
{
case TEX_FILTER_RGB_COPY_GREEN:
{
XMVECTOR* ptr = pBuffer;
for (size_t i = 0; i < count; ++i)
{
XMVECTOR v = *ptr;
XMVECTOR v1 = XMVectorSplatY(v);
*ptr++ = XMVectorSelect(v, v1, g_XMSelect1000);
}
}
break;
case TEX_FILTER_RGB_COPY_BLUE:
{
XMVECTOR* ptr = pBuffer;
for (size_t i = 0; i < count; ++i)
{
XMVECTOR v = *ptr;
XMVECTOR v1 = XMVectorSplatZ(v);
*ptr++ = XMVectorSelect(v, v1, g_XMSelect1000);
}
}
break;
default:
if ((in->flags & CONVF_UNORM) && ((in->flags & CONVF_RGB_MASK) == (CONVF_R | CONVF_G | CONVF_B)))
{
XMVECTOR* ptr = pBuffer;
for (size_t i = 0; i < count; ++i)
{
XMVECTOR v = *ptr;
XMVECTOR v1 = XMVector3Dot(v, g_Grayscale);
*ptr++ = XMVectorSelect(v, v1, g_XMSelect1000);
}
break;
}
#ifdef _MSC_VER
__fallthrough;
#endif
#ifdef __clang__
[[clang::fallthrough]];
#endif
case TEX_FILTER_RGB_COPY_RED:
{
XMVECTOR* ptr = pBuffer;
for (size_t i = 0; i < count; ++i)
{
XMVECTOR v = *ptr;
XMVECTOR v1 = XMVectorSplatX(v);
*ptr++ = XMVectorSelect(v, v1, g_XMSelect1000);
}
}
break;
}
// Finialize type conversion for depth (red channel)
if (out->flags & CONVF_UNORM)
{
if (in->flags & CONVF_SNORM)
{
// SNORM -> UNORM
XMVECTOR* ptr = pBuffer;
for (size_t i = 0; i < count; ++i)
{
XMVECTOR v = *ptr;
XMVECTOR v1 = XMVectorMultiplyAdd(v, g_XMOneHalf, g_XMOneHalf);
*ptr++ = XMVectorSelect(v, v1, g_XMSelect1000);
}
}
else if (in->flags & CONVF_FLOAT)
{
// FLOAT -> UNORM
XMVECTOR* ptr = pBuffer;
for (size_t i = 0; i < count; ++i)
{
XMVECTOR v = *ptr;
XMVECTOR v1 = XMVectorSaturate(v);
*ptr++ = XMVectorSelect(v, v1, g_XMSelect1000);
}
}
}
if (out->flags & CONVF_STENCIL)
{
// Alpha -> Stencil (green channel)
static const XMVECTORU32 select0100 = { { { XM_SELECT_0, XM_SELECT_1, XM_SELECT_0, XM_SELECT_0 } } };
static const XMVECTORF32 S = { { { 255.f, 255.f, 255.f, 255.f } } };
if (in->flags & CONVF_UNORM)
{
// UNORM -> UINT
XMVECTOR* ptr = pBuffer;
for (size_t i = 0; i < count; ++i)
{
XMVECTOR v = *ptr;
XMVECTOR v1 = XMVectorMultiply(v, S);
v1 = XMVectorSplatW(v1);
*ptr++ = XMVectorSelect(v, v1, select0100);
}
}
else if (in->flags & CONVF_SNORM)
{
// SNORM -> UINT
XMVECTOR* ptr = pBuffer;
for (size_t i = 0; i < count; ++i)
{
XMVECTOR v = *ptr;
XMVECTOR v1 = XMVectorMultiplyAdd(v, g_XMOneHalf, g_XMOneHalf);
v1 = XMVectorMultiply(v1, S);
v1 = XMVectorSplatW(v1);
*ptr++ = XMVectorSelect(v, v1, select0100);
}
}
else
{
XMVECTOR* ptr = pBuffer;
for (size_t i = 0; i < count; ++i)
{
XMVECTOR v = *ptr;
XMVECTOR v1 = XMVectorSplatW(v);
*ptr++ = XMVectorSelect(v, v1, select0100);
}
}
}
}
}
else if (out->flags & CONVF_DEPTH)
{
// CONVF_DEPTH -> CONVF_DEPTH
if (diffFlags & CONVF_FLOAT)
{
if (in->flags & CONVF_FLOAT)
{
// FLOAT -> UNORM depth, preserve stencil
XMVECTOR* ptr = pBuffer;
for (size_t i = 0; i < count; ++i)
{
XMVECTOR v = *ptr;
XMVECTOR v1 = XMVectorSaturate(v);
*ptr++ = XMVectorSelect(v, v1, g_XMSelect1000);
}
}
}
}
else if (out->flags & CONVF_UNORM)
{
//--- Converting to a UNORM ---
if (in->flags & CONVF_SNORM)
{
// SNORM -> UNORM
XMVECTOR* ptr = pBuffer;
for (size_t i = 0; i < count; ++i)
{
XMVECTOR v = *ptr;
*ptr++ = XMVectorMultiplyAdd(v, g_XMOneHalf, g_XMOneHalf);
}
}
else if (in->flags & CONVF_FLOAT)
{
XMVECTOR* ptr = pBuffer;
if (!(in->flags & CONVF_POS_ONLY) && (flags & TEX_FILTER_FLOAT_X2BIAS))
{
// FLOAT -> UNORM (x2 bias)
for (size_t i = 0; i < count; ++i)
{
XMVECTOR v = *ptr;
v = XMVectorClamp(v, g_XMNegativeOne, g_XMOne);
*ptr++ = XMVectorMultiplyAdd(v, g_XMOneHalf, g_XMOneHalf);
}
}
else
{
// FLOAT -> UNORM
for (size_t i = 0; i < count; ++i)
{
XMVECTOR v = *ptr;
*ptr++ = XMVectorSaturate(v);
}
}
}
}
else if (out->flags & CONVF_SNORM)
{
//--- Converting to a SNORM ---
if (in->flags & CONVF_UNORM)
{
// UNORM -> SNORM
XMVECTOR* ptr = pBuffer;
for (size_t i = 0; i < count; ++i)
{
XMVECTOR v = *ptr;
*ptr++ = XMVectorMultiplyAdd(v, g_XMTwo, g_XMNegativeOne);
}
}
else if (in->flags & CONVF_FLOAT)
{
XMVECTOR* ptr = pBuffer;
if ((in->flags & CONVF_POS_ONLY) && (flags & TEX_FILTER_FLOAT_X2BIAS))
{
// FLOAT (positive only, x2 bias) -> SNORM
for (size_t i = 0; i < count; ++i)
{
XMVECTOR v = *ptr;
v = XMVectorSaturate(v);
*ptr++ = XMVectorMultiplyAdd(v, g_XMTwo, g_XMNegativeOne);
}
}
else
{
// FLOAT -> SNORM
for (size_t i = 0; i < count; ++i)
{
XMVECTOR v = *ptr;
*ptr++ = XMVectorClamp(v, g_XMNegativeOne, g_XMOne);
}
}
}
}
else if (diffFlags & CONVF_UNORM)
{
//--- Converting from a UNORM ---
assert(in->flags & CONVF_UNORM);
if (out->flags & CONVF_FLOAT)
{
if (!(out->flags & CONVF_POS_ONLY) && (flags & TEX_FILTER_FLOAT_X2BIAS))
{
// UNORM (x2 bias) -> FLOAT
XMVECTOR* ptr = pBuffer;
for (size_t i = 0; i < count; ++i)
{
XMVECTOR v = *ptr;
*ptr++ = XMVectorMultiplyAdd(v, g_XMTwo, g_XMNegativeOne);
}
}
}
}
else if (diffFlags & CONVF_POS_ONLY)
{
if (flags & TEX_FILTER_FLOAT_X2BIAS)
{
if (in->flags & CONVF_POS_ONLY)
{
if (out->flags & CONVF_FLOAT)
{
// FLOAT (positive only, x2 bias) -> FLOAT
XMVECTOR* ptr = pBuffer;
for (size_t i = 0; i < count; ++i)
{
XMVECTOR v = *ptr;
v = XMVectorSaturate(v);
*ptr++ = XMVectorMultiplyAdd(v, g_XMTwo, g_XMNegativeOne);
}
}
}
else if (out->flags & CONVF_POS_ONLY)
{
if (in->flags & CONVF_FLOAT)
{
// FLOAT -> FLOAT (positive only, x2 bias)
XMVECTOR* ptr = pBuffer;
for (size_t i = 0; i < count; ++i)
{
XMVECTOR v = *ptr;
v = XMVectorClamp(v, g_XMNegativeOne, g_XMOne);
*ptr++ = XMVectorMultiplyAdd(v, g_XMOneHalf, g_XMOneHalf);
}
}
else if (in->flags & CONVF_SNORM)
{
// SNORM -> FLOAT (positive only, x2 bias)
XMVECTOR* ptr = pBuffer;
for (size_t i = 0; i < count; ++i)
{
XMVECTOR v = *ptr;
*ptr++ = XMVectorMultiplyAdd(v, g_XMOneHalf, g_XMOneHalf);
}
}
}
}
}
// !CONVF_A -> CONVF_A is handled because LoadScanline ensures alpha defaults to 1.0 for no-alpha formats
// CONVF_PACKED cases are handled because LoadScanline/StoreScanline handles packing/unpacking
if (((out->flags & CONVF_RGBA_MASK) == CONVF_A) && !(in->flags & CONVF_A))
{
// !CONVF_A -> A format
switch (flags & (TEX_FILTER_RGB_COPY_RED | TEX_FILTER_RGB_COPY_GREEN | TEX_FILTER_RGB_COPY_BLUE))
{
case TEX_FILTER_RGB_COPY_GREEN:
{
XMVECTOR* ptr = pBuffer;
for (size_t i = 0; i < count; ++i)
{
XMVECTOR v = *ptr;
*ptr++ = XMVectorSplatY(v);
}
}
break;
case TEX_FILTER_RGB_COPY_BLUE:
{
XMVECTOR* ptr = pBuffer;
for (size_t i = 0; i < count; ++i)
{
XMVECTOR v = *ptr;
*ptr++ = XMVectorSplatZ(v);
}
}
break;
default:
if ((in->flags & CONVF_UNORM) && ((in->flags & CONVF_RGB_MASK) == (CONVF_R | CONVF_G | CONVF_B)))
{
XMVECTOR* ptr = pBuffer;
for (size_t i = 0; i < count; ++i)
{
XMVECTOR v = *ptr;
*ptr++ = XMVector3Dot(v, g_Grayscale);
}
break;
}
#ifdef _MSC_VER
__fallthrough;
#endif
#ifdef __clang__
[[clang::fallthrough]];
#endif
case TEX_FILTER_RGB_COPY_RED:
{
XMVECTOR* ptr = pBuffer;
for (size_t i = 0; i < count; ++i)
{
XMVECTOR v = *ptr;
*ptr++ = XMVectorSplatX(v);
}
}
break;
}
}
else if (((in->flags & CONVF_RGBA_MASK) == CONVF_A) && !(out->flags & CONVF_A))
{
// A format -> !CONVF_A
XMVECTOR* ptr = pBuffer;
for (size_t i = 0; i < count; ++i)
{
XMVECTOR v = *ptr;
*ptr++ = XMVectorSplatW(v);
}
}
else if ((in->flags & CONVF_RGB_MASK) == CONVF_R)
{
if ((out->flags & CONVF_RGB_MASK) == (CONVF_R | CONVF_G | CONVF_B))
{
// R format -> RGB format
XMVECTOR* ptr = pBuffer;
for (size_t i = 0; i < count; ++i)
{
XMVECTOR v = *ptr;
XMVECTOR v1 = XMVectorSplatX(v);
*ptr++ = XMVectorSelect(v, v1, g_XMSelect1110);
}
}
else if ((out->flags & CONVF_RGB_MASK) == (CONVF_R | CONVF_G))
{
// R format -> RG format
XMVECTOR* ptr = pBuffer;
for (size_t i = 0; i < count; ++i)
{
XMVECTOR v = *ptr;
XMVECTOR v1 = XMVectorSplatX(v);
*ptr++ = XMVectorSelect(v, v1, g_XMSelect1100);
}
}
}
else if ((in->flags & CONVF_RGB_MASK) == (CONVF_R | CONVF_G | CONVF_B))
{
if ((out->flags & CONVF_RGB_MASK) == CONVF_R)
{
// RGB format -> R format
switch (flags & (TEX_FILTER_RGB_COPY_RED | TEX_FILTER_RGB_COPY_GREEN | TEX_FILTER_RGB_COPY_BLUE))
{
case TEX_FILTER_RGB_COPY_GREEN:
{
XMVECTOR* ptr = pBuffer;
for (size_t i = 0; i < count; ++i)
{
XMVECTOR v = *ptr;
XMVECTOR v1 = XMVectorSplatY(v);
*ptr++ = XMVectorSelect(v, v1, g_XMSelect1110);
}
}
break;
case TEX_FILTER_RGB_COPY_BLUE:
{
XMVECTOR* ptr = pBuffer;
for (size_t i = 0; i < count; ++i)
{
XMVECTOR v = *ptr;
XMVECTOR v1 = XMVectorSplatZ(v);
*ptr++ = XMVectorSelect(v, v1, g_XMSelect1110);
}
}
break;
default:
if (in->flags & CONVF_UNORM)
{
XMVECTOR* ptr = pBuffer;
for (size_t i = 0; i < count; ++i)
{
XMVECTOR v = *ptr;
XMVECTOR v1 = XMVector3Dot(v, g_Grayscale);
*ptr++ = XMVectorSelect(v, v1, g_XMSelect1110);
}
break;
}
#ifdef _MSC_VER
__fallthrough;
#endif
#ifdef __clang__
[[clang::fallthrough]];
#endif
case TEX_FILTER_RGB_COPY_RED:
// Leave data unchanged and the store will handle this...
break;
}
}
else if ((out->flags & CONVF_RGB_MASK) == (CONVF_R | CONVF_G))
{
// RGB format -> RG format
switch (static_cast<int>(flags & (TEX_FILTER_RGB_COPY_RED | TEX_FILTER_RGB_COPY_GREEN | TEX_FILTER_RGB_COPY_BLUE)))
{
case static_cast<int>(TEX_FILTER_RGB_COPY_RED) | static_cast<int>(TEX_FILTER_RGB_COPY_BLUE):
{
XMVECTOR* ptr = pBuffer;
for (size_t i = 0; i < count; ++i)
{
XMVECTOR v = *ptr;
XMVECTOR v1 = XMVectorSwizzle<0, 2, 0, 2>(v);
*ptr++ = XMVectorSelect(v, v1, g_XMSelect1100);
}
}
break;
case static_cast<int>(TEX_FILTER_RGB_COPY_GREEN) | static_cast<int>(TEX_FILTER_RGB_COPY_BLUE):
{
XMVECTOR* ptr = pBuffer;
for (size_t i = 0; i < count; ++i)
{
XMVECTOR v = *ptr;
XMVECTOR v1 = XMVectorSwizzle<1, 2, 3, 0>(v);
*ptr++ = XMVectorSelect(v, v1, g_XMSelect1100);
}
}
break;
case static_cast<int>(TEX_FILTER_RGB_COPY_RED) | static_cast<int>(TEX_FILTER_RGB_COPY_GREEN):
default:
// Leave data unchanged and the store will handle this...
break;
}
}
}
}
// sRGB output processing (Linear RGB -> sRGB)
if (flags & TEX_FILTER_SRGB_OUT)
{
if (!(out->flags & CONVF_DEPTH) && ((out->flags & CONVF_FLOAT) || (out->flags & CONVF_UNORM)))
{
XMVECTOR* ptr = pBuffer;
for (size_t i = 0; i < count; ++i, ++ptr)
{
*ptr = XMColorRGBToSRGB(*ptr);
}
}
}
}