badtails flag to enable loading of legacy DXTn DDS files with incomplete mipchain tails

This commit is contained in:
Chuck Walbourn 2016-09-11 14:29:10 -07:00
Родитель 04143d621c
Коммит 7275893dbe
4 изменённых файлов: 103 добавлений и 19 удалений

Просмотреть файл

@ -70,6 +70,7 @@ namespace DirectX
CP_FLAGS_YMM = 0x4, // Assume pitch is 32-byte aligned instead of BYTE aligned
CP_FLAGS_ZMM = 0x8, // Assume pitch is 64-byte aligned instead of BYTE aligned
CP_FLAGS_PAGE4K = 0x200, // Assume pitch is 4096-byte aligned instead of BYTE aligned
CP_FLAGS_BAD_DXTN_TAILS = 0x1000, // BC formats with malformed mipchain blocks smaller than 4x4
CP_FLAGS_24BPP = 0x10000, // Override with a legacy 24 bits-per-pixel format size
CP_FLAGS_16BPP = 0x20000, // Override with a legacy 16 bits-per-pixel format size
CP_FLAGS_8BPP = 0x40000, // Override with a legacy 8 bits-per-pixel format size
@ -164,6 +165,9 @@ namespace DirectX
DDS_FLAGS_EXPAND_LUMINANCE = 0x20,
// When loading legacy luminance formats expand replicating the color channels rather than leaving them packed (L8, L16, A8L8)
DDS_FLAGS_BAD_DXTN_TAILS = 0x40,
// Some older DXTn DDS files incorrectly handle mipchain tails for blocks smaller than 4x4
DDS_FLAGS_FORCE_DX10_EXT = 0x10000,
// Always use the 'DX10' header extension for DDS writer (i.e. don't try to write DX9 compatible DDS files)

Просмотреть файл

@ -1068,7 +1068,7 @@ namespace
if (pixelSize > size)
{
return E_FAIL;
return HRESULT_FROM_WIN32( ERROR_HANDLE_EOF );
}
std::unique_ptr<Image[]> timages(new (std::nothrow) Image[nimages]);
@ -1077,7 +1077,7 @@ namespace
return E_OUTOFMEMORY;
}
if (!_SetupImageArray((uint8_t*)pPixels, size, metadata, cpFlags, timages.get(), nimages))
if (!_SetupImageArray((uint8_t*)pPixels, pixelSize, metadata, cpFlags, timages.get(), nimages))
{
return E_FAIL;
}
@ -1105,6 +1105,7 @@ namespace
size_t index = 0;
for (size_t item = 0; item < metadata.arraySize; ++item)
{
size_t lastgood = 0;
for (size_t level = 0; level < metadata.mipLevels; ++level, ++index)
{
if (index >= nimages)
@ -1128,6 +1129,19 @@ namespace
{
size_t csize = std::min<size_t>(images[index].slicePitch, timages[index].slicePitch);
memcpy_s(pDest, images[index].slicePitch, pSrc, csize);
if (cpFlags & CP_FLAGS_BAD_DXTN_TAILS)
{
if (images[index].width < 4 || images[index].height < 4)
{
csize = std::min<size_t>(images[index].slicePitch, timages[lastgood].slicePitch);
memcpy_s(pDest, images[index].slicePitch, timages[lastgood].pixels, csize);
}
else
{
lastgood = index;
}
}
}
else if (IsPlanar(metadata.format))
{
@ -1191,6 +1205,7 @@ namespace
size_t index = 0;
size_t d = metadata.depth;
size_t lastgood = 0;
for (size_t level = 0; level < metadata.mipLevels; ++level)
{
for (size_t slice = 0; slice < d; ++slice, ++index)
@ -1216,6 +1231,19 @@ namespace
{
size_t csize = std::min<size_t>(images[index].slicePitch, timages[index].slicePitch);
memcpy_s(pDest, images[index].slicePitch, pSrc, csize);
if (cpFlags & CP_FLAGS_BAD_DXTN_TAILS)
{
if (images[index].width < 4 || images[index].height < 4)
{
csize = std::min<size_t>(images[index].slicePitch, timages[lastgood + slice].slicePitch);
memcpy_s(pDest, images[index].slicePitch, timages[lastgood + slice].pixels, csize);
}
else if (!slice)
{
lastgood = index;
}
}
}
else if (IsPlanar(metadata.format))
{
@ -1438,10 +1466,25 @@ HRESULT DirectX::LoadFromDDSMemory(
if (FAILED(hr))
return hr;
DWORD cflags = CP_FLAGS_NONE;
if (flags & DDS_FLAGS_LEGACY_DWORD)
{
cflags |= CP_FLAGS_LEGACY_DWORD;
}
if (flags & DDS_FLAGS_BAD_DXTN_TAILS)
{
cflags |= CP_FLAGS_BAD_DXTN_TAILS;
}
auto pPixels = reinterpret_cast<const void*>(reinterpret_cast<const uint8_t*>(pSource) + offset);
assert(pPixels);
hr = CopyImage(pPixels, size - offset, mdata,
(flags & DDS_FLAGS_LEGACY_DWORD) ? CP_FLAGS_LEGACY_DWORD : CP_FLAGS_NONE, convFlags, pal8, image);
hr = CopyImage(pPixels,
size - offset,
mdata,
cflags,
convFlags,
pal8,
image);
if (FAILED(hr))
{
image.Release();
@ -1560,7 +1603,7 @@ HRESULT DirectX::LoadFromDDSFile(
if (FAILED(hr))
return hr;
if ((convFlags & CONV_FLAGS_EXPAND) || (flags & DDS_FLAGS_LEGACY_DWORD))
if ((convFlags & CONV_FLAGS_EXPAND) || (flags & (DDS_FLAGS_LEGACY_DWORD | DDS_FLAGS_BAD_DXTN_TAILS)))
{
std::unique_ptr<uint8_t[]> temp(new (std::nothrow) uint8_t[remaining]);
if (!temp)
@ -1581,9 +1624,23 @@ HRESULT DirectX::LoadFromDDSFile(
return E_FAIL;
}
hr = CopyImage(temp.get(), remaining, mdata,
(flags & DDS_FLAGS_LEGACY_DWORD) ? CP_FLAGS_LEGACY_DWORD : CP_FLAGS_NONE,
convFlags, pal8.get(), image);
DWORD cflags = CP_FLAGS_NONE;
if (flags & DDS_FLAGS_LEGACY_DWORD)
{
cflags |= CP_FLAGS_LEGACY_DWORD;
}
if (flags & DDS_FLAGS_BAD_DXTN_TAILS)
{
cflags |= CP_FLAGS_BAD_DXTN_TAILS;
}
hr = CopyImage(temp.get(),
remaining,
mdata,
cflags,
convFlags,
pal8.get(),
image);
if (FAILED(hr))
{
image.Release();
@ -1595,7 +1652,7 @@ HRESULT DirectX::LoadFromDDSFile(
if (remaining < image.GetPixelsSize())
{
image.Release();
return E_FAIL;
return HRESULT_FROM_WIN32( ERROR_HANDLE_EOF );
}
if (!ReadFile(hFile.get(), image.GetPixels(), static_cast<DWORD>(image.GetPixelsSize()), &bytesRead, nullptr))

Просмотреть файл

@ -881,11 +881,20 @@ void DirectX::ComputePitch(DXGI_FORMAT fmt, size_t width, size_t height,
case DXGI_FORMAT_BC4_SNORM:
assert(IsCompressed(fmt));
{
size_t nbw = std::max<size_t>(1, (width + 3) / 4);
size_t nbh = std::max<size_t>(1, (height + 3) / 4);
rowPitch = nbw * 8;
slicePitch = rowPitch * nbh;
if (flags & CP_FLAGS_BAD_DXTN_TAILS)
{
size_t nbw = width >> 2;
size_t nbh = height >> 2;
rowPitch = std::max<size_t>(1, nbw * 8);
slicePitch = std::max<size_t>(1, rowPitch * nbh);
}
else
{
size_t nbw = std::max<size_t>(1, (width + 3) / 4);
size_t nbh = std::max<size_t>(1, (height + 3) / 4);
rowPitch = nbw * 8;
slicePitch = rowPitch * nbh;
}
}
break;
@ -906,11 +915,20 @@ void DirectX::ComputePitch(DXGI_FORMAT fmt, size_t width, size_t height,
case DXGI_FORMAT_BC7_UNORM_SRGB:
assert(IsCompressed(fmt));
{
size_t nbw = std::max<size_t>(1, (width + 3) / 4);
size_t nbh = std::max<size_t>(1, (height + 3) / 4);
rowPitch = nbw * 16;
slicePitch = rowPitch * nbh;
if (flags & CP_FLAGS_BAD_DXTN_TAILS)
{
size_t nbw = width >> 2;
size_t nbh = height >> 2;
rowPitch = std::max<size_t>(1, nbw * 16);
slicePitch = std::max<size_t>(1, rowPitch * nbh);
}
else
{
size_t nbw = std::max<size_t>(1, (width + 3) / 4);
size_t nbh = std::max<size_t>(1, (height + 3) / 4);
rowPitch = nbw * 16;
slicePitch = rowPitch * nbh;
}
}
break;

Просмотреть файл

@ -55,6 +55,7 @@ enum OPTIONS // Note: dwOptions below assumes 64 or less options.
OPT_HFLIP,
OPT_VFLIP,
OPT_DDS_DWORD_ALIGN,
OPT_DDS_BAD_DXTN_TAILS,
OPT_USE_DX10,
OPT_NOLOGO,
OPT_TIMING,
@ -116,6 +117,7 @@ SValue g_pOptions[] =
{ L"hflip", OPT_HFLIP },
{ L"vflip", OPT_VFLIP },
{ L"dword", OPT_DDS_DWORD_ALIGN },
{ L"badtails", OPT_DDS_BAD_DXTN_TAILS },
{ L"dx10", OPT_USE_DX10 },
{ L"nologo", OPT_NOLOGO },
{ L"timing", OPT_TIMING },
@ -529,6 +531,7 @@ void PrintUsage()
wprintf( L"\n (DDS input only)\n");
wprintf( L" -t{u|f} TYPELESS format is treated as UNORM or FLOAT\n");
wprintf( L" -dword Use DWORD instead of BYTE alignment\n");
wprintf( L" -badtails Fix for older DXTn with bad mipchain tails\n");
wprintf( L" -xlum expand legacy L8, L16, and A8P8 formats\n");
wprintf( L"\n (DDS output only)\n");
wprintf( L" -dx10 Force use of 'DX10' extended header\n");
@ -1178,6 +1181,8 @@ int __cdecl wmain(_In_ int argc, _In_z_count_(argc) wchar_t* argv[])
ddsFlags |= DDS_FLAGS_LEGACY_DWORD;
if ( dwOptions & (DWORD64(1) << OPT_EXPAND_LUMINANCE) )
ddsFlags |= DDS_FLAGS_EXPAND_LUMINANCE;
if ( dwOptions & (DWORD64(1) << OPT_DDS_BAD_DXTN_TAILS) )
ddsFlags |= DDS_FLAGS_BAD_DXTN_TAILS;
hr = LoadFromDDSFile( pConv->szSrc, ddsFlags, &info, *image );
if ( FAILED(hr) )