Skip to content

Commit 327c200

Browse files
authored
Support loading legacy DDS files using mixed channel formats (#501)
1 parent e63f915 commit 327c200

File tree

1 file changed

+186
-37
lines changed

1 file changed

+186
-37
lines changed

DirectXTex/DirectXTexDDS.cpp

+186-37
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,9 @@ namespace
4646
CONV_FLAGS_L8 = 0x40000, // Source is a 8 luminance format
4747
CONV_FLAGS_L16 = 0x80000, // Source is a 16 luminance format
4848
CONV_FLAGS_A8L8 = 0x100000, // Source is a 8:8 luminance format
49+
CONV_FLAGS_L6V5U5 = 0x200000, // Source is a 6:5:5 bumpluminance format
50+
CONV_FLAGS_L8U8V8 = 0x400000, // Source is a X:8:8:8 bumpluminance format
51+
CONV_FLAGS_WUV10 = 0x800000, // Source is a 2:10:10:10 bump format
4952
};
5053

5154
struct LegacyDDS
@@ -141,7 +144,7 @@ namespace
141144

142145
{ DXGI_FORMAT_R8G8B8A8_UNORM, CONV_FLAGS_EXPAND
143146
| CONV_FLAGS_PAL8
144-
| CONV_FLAGS_A8P8, { sizeof(DDS_PIXELFORMAT), DDS_PAL8A, 0, 16, 0, 0, 0, 0 } }, // D3DFMT_A8P8
147+
| CONV_FLAGS_A8P8, { sizeof(DDS_PIXELFORMAT), DDS_PAL8A, 0, 16, 0, 0, 0, 0xff00 } }, // D3DFMT_A8P8
145148
{ DXGI_FORMAT_R8G8B8A8_UNORM, CONV_FLAGS_EXPAND
146149
| CONV_FLAGS_PAL8, { sizeof(DDS_PIXELFORMAT), DDS_PAL8, 0, 8, 0, 0, 0, 0 } }, // D3DFMT_P8
147150

@@ -157,6 +160,11 @@ namespace
157160
{ DXGI_FORMAT_R8G8_SNORM, CONV_FLAGS_NONE, DDSPF_V8U8 }, // D3DFMT_V8U8
158161
{ DXGI_FORMAT_R8G8B8A8_SNORM, CONV_FLAGS_NONE, DDSPF_Q8W8V8U8 }, // D3DFMT_Q8W8V8U8
159162
{ DXGI_FORMAT_R16G16_SNORM, CONV_FLAGS_NONE, DDSPF_V16U16 }, // D3DFMT_V16U16
163+
164+
{ DXGI_FORMAT_R8G8B8A8_UNORM, CONV_FLAGS_L6V5U5
165+
| CONV_FLAGS_EXPAND, DDSPF_L6V5U5 }, // D3DFMT_L6V5U5
166+
{ DXGI_FORMAT_R8G8B8A8_UNORM, CONV_FLAGS_L8U8V8, DDSPF_X8L8V8U8 }, // D3DFMT_X8L8V8U8
167+
{ DXGI_FORMAT_R10G10B10A2_UNORM, CONV_FLAGS_WUV10, DDSPF_A2W10V10U10 }, // D3DFMT_A2W10V10U10
160168
};
161169

162170
// Note that many common DDS reader/writers (including D3DX) swap the
@@ -166,15 +174,14 @@ namespace
166174
// header extension and specify the DXGI_FORMAT_R10G10B10A2_UNORM format directly
167175

168176
// We do not support the following legacy Direct3D 9 formats:
169-
// BumpDuDv D3DFMT_A2W10V10U10
170-
// BumpLuminance D3DFMT_L6V5U5, D3DFMT_X8L8V8U8
171-
// FourCC 117 D3DFMT_CxV8U8
172-
// ZBuffer D3DFMT_D16_LOCKABLE
177+
// D3DFMT_D16_LOCKABLE (DDPF_ZBUFFER: 0x00000400)
173178
// FourCC 82 D3DFMT_D32F_LOCKABLE
179+
// FourCC 117 D3DFMT_CxV8U8
174180

175181
// We do not support the following known FourCC codes:
176182
// FourCC CTX1 (Xbox 360 only)
177183
// FourCC EAR, EARG, ET2, ET2A (Ericsson Texture Compression)
184+
// FourCC MET1 (a.k.a. D3DFMT_MULTI2_ARGB8; rarely supported by any hardware)
178185

179186
DXGI_FORMAT GetDXGIFormat(const DDS_HEADER& hdr, const DDS_PIXELFORMAT& ddpf,
180187
DDS_FLAGS flags,
@@ -215,66 +222,70 @@ namespace
215222
if (ddpf.fourCC == entry->ddpf.fourCC)
216223
break;
217224
}
218-
else if (ddpfFlags == entry->ddpf.flags)
225+
else if ((ddpfFlags == entry->ddpf.flags) && (ddpf.RGBBitCount == entry->ddpf.RGBBitCount))
219226
{
220227
if (entry->ddpf.flags & DDS_PAL8)
221228
{
222-
if (ddpf.RGBBitCount == entry->ddpf.RGBBitCount)
223-
break;
229+
// PAL8 / PAL8A
230+
break;
224231
}
225232
else if (entry->ddpf.flags & DDS_ALPHA)
226233
{
227-
if (ddpf.RGBBitCount == entry->ddpf.RGBBitCount
228-
&& ddpf.ABitMask == entry->ddpf.ABitMask)
234+
if (ddpf.ABitMask == entry->ddpf.ABitMask)
229235
break;
230236
}
231237
else if (entry->ddpf.flags & DDS_LUMINANCE)
232238
{
233239
if (entry->ddpf.flags & DDS_ALPHAPIXELS)
234240
{
235241
// LUMINANCEA
236-
if (ddpf.RGBBitCount == entry->ddpf.RGBBitCount
237-
&& ddpf.RBitMask == entry->ddpf.RBitMask
242+
if (ddpf.RBitMask == entry->ddpf.RBitMask
238243
&& ddpf.ABitMask == entry->ddpf.ABitMask)
239244
break;
240245
}
241246
else
242247
{
243248
// LUMINANCE
244-
if (ddpf.RGBBitCount == entry->ddpf.RGBBitCount
245-
&& ddpf.RBitMask == entry->ddpf.RBitMask)
249+
if (ddpf.RBitMask == entry->ddpf.RBitMask)
246250
break;
247251
}
248252
}
249253
else if (entry->ddpf.flags & DDS_BUMPDUDV)
250-
{
251-
if (ddpf.RGBBitCount == entry->ddpf.RGBBitCount
252-
&& ddpf.RBitMask == entry->ddpf.RBitMask
253-
&& ddpf.GBitMask == entry->ddpf.GBitMask
254-
&& ddpf.BBitMask == entry->ddpf.BBitMask
255-
&& ddpf.ABitMask == entry->ddpf.ABitMask)
256-
break;
257-
}
258-
else if (ddpf.RGBBitCount == entry->ddpf.RGBBitCount)
259254
{
260255
if (entry->ddpf.flags & DDS_ALPHAPIXELS)
261256
{
262-
// RGBA
257+
// BUMPDUDVA
263258
if (ddpf.RBitMask == entry->ddpf.RBitMask
264-
&& ddpf.GBitMask == entry->ddpf.GBitMask
265-
&& ddpf.BBitMask == entry->ddpf.BBitMask
266259
&& ddpf.ABitMask == entry->ddpf.ABitMask)
260+
{
261+
flags &= ~DDS_FLAGS_NO_R10B10G10A2_FIXUP;
267262
break;
263+
}
268264
}
269265
else
270266
{
271-
// RGB
272-
if (ddpf.RBitMask == entry->ddpf.RBitMask
273-
&& ddpf.GBitMask == entry->ddpf.GBitMask
274-
&& ddpf.BBitMask == entry->ddpf.BBitMask)
267+
// BUMPDUDV
268+
if (ddpf.RBitMask == entry->ddpf.RBitMask)
275269
break;
276270
}
277271
}
272+
else if (entry->ddpf.flags & DDS_ALPHAPIXELS)
273+
{
274+
// RGBA
275+
if (ddpf.RBitMask == entry->ddpf.RBitMask
276+
&& ddpf.GBitMask == entry->ddpf.GBitMask
277+
&& ddpf.BBitMask == entry->ddpf.BBitMask
278+
&& ddpf.ABitMask == entry->ddpf.ABitMask)
279+
break;
280+
}
281+
else
282+
{
283+
// RGB
284+
if (ddpf.RBitMask == entry->ddpf.RBitMask
285+
&& ddpf.GBitMask == entry->ddpf.GBitMask
286+
&& ddpf.BBitMask == entry->ddpf.BBitMask)
287+
break;
288+
}
278289
}
279290
}
280291
}
@@ -996,7 +1007,10 @@ namespace
9961007
TEXP_LEGACY_B4G4R4A4,
9971008
TEXP_LEGACY_L8,
9981009
TEXP_LEGACY_L16,
999-
TEXP_LEGACY_A8L8
1010+
TEXP_LEGACY_A8L8,
1011+
TEXP_LEGACY_L6V5U5,
1012+
TEXP_LEGACY_X8L8V8U8,
1013+
TEXP_LEGACY_A2W10V10U10
10001014
};
10011015

10021016
constexpr TEXP_LEGACY_FORMAT FindLegacyFormat(uint32_t flags) noexcept
@@ -1023,6 +1037,12 @@ namespace
10231037
lformat = TEXP_LEGACY_L16;
10241038
else if (flags & CONV_FLAGS_A8L8)
10251039
lformat = TEXP_LEGACY_A8L8;
1040+
else if (flags & CONV_FLAGS_L6V5U5)
1041+
lformat = TEXP_LEGACY_L6V5U5;
1042+
else if (flags & CONV_FLAGS_L8U8V8)
1043+
lformat = TEXP_LEGACY_X8L8V8U8;
1044+
else if (flags & CONV_FLAGS_WUV10)
1045+
lformat = TEXP_LEGACY_A2W10V10U10;
10261046

10271047
return lformat;
10281048
}
@@ -1328,11 +1348,120 @@ namespace
13281348
}
13291349
return false;
13301350

1351+
case TEXP_LEGACY_L6V5U5:
1352+
if (outFormat != DXGI_FORMAT_R8G8B8A8_UNORM)
1353+
return false;
1354+
1355+
// D3DFMT_L6V5U5 -> DXGI_FORMAT_R8G8B8A8_UNORM (LUVA)
1356+
if (inSize >= 2 && outSize >= 4)
1357+
{
1358+
const uint16_t* __restrict sPtr = static_cast<const uint16_t*>(pSource);
1359+
uint32_t * __restrict dPtr = static_cast<uint32_t*>(pDestination);
1360+
1361+
for (size_t ocount = 0, icount = 0; ((icount < (inSize - 1)) && (ocount < (outSize - 3))); icount += 2, ocount += 4)
1362+
{
1363+
const uint16_t t = *(sPtr++);
1364+
1365+
// Converts unsigned 6-bit/signed 5-bit/signed 5-bit bump luminance to 8:8:8:8 unsigned
1366+
uint32_t t1 = ((t & 0xFC00) >> 8) | ((t & 0xC000) >> 14);
1367+
1368+
constexpr int m = 1U << 4;
1369+
int8_t v = ((((t >> 5) & 0x1f) ^ m) - m) + 16;
1370+
int8_t u = (((t & 0x1f) ^ m) - m) + 16;
1371+
1372+
uint32_t t2 = u << 3 | u >> 2;
1373+
uint32_t t3 = v << 3 | v >> 2;
1374+
1375+
*(dPtr++) = t1 | (t2 << 8) | (t3 << 16) | 0xff000000;
1376+
}
1377+
return true;
1378+
}
1379+
return false;
1380+
13311381
default:
13321382
return false;
13331383
}
13341384
}
13351385

1386+
_Success_(return)
1387+
bool LegacyConvertScanline(
1388+
_Out_writes_bytes_(outSize) void* pDestination,
1389+
size_t outSize,
1390+
_In_ DXGI_FORMAT outFormat,
1391+
_In_reads_bytes_(inSize) const void* pSource,
1392+
size_t inSize,
1393+
_In_ TEXP_LEGACY_FORMAT inFormat,
1394+
uint32_t tflags) noexcept
1395+
{
1396+
assert(pDestination && outSize > 0);
1397+
assert(pSource && inSize > 0);
1398+
1399+
switch (inFormat)
1400+
{
1401+
case TEXP_LEGACY_X8L8V8U8:
1402+
if (outFormat != DXGI_FORMAT_R8G8B8A8_UNORM)
1403+
return false;
1404+
1405+
// D3DFMT_X8L8V8U8 -> DXGI_FORMAT_R8G8B8A8_UNORM (LUVA)
1406+
if (inSize >= 4 && outSize >= 4)
1407+
{
1408+
auto sPtr = static_cast<const uint32_t*>(pSource);
1409+
auto dPtr = static_cast<uint32_t*>(pDestination);
1410+
1411+
for (size_t ocount = 0, icount = 0; ((icount < (inSize - 3)) && (ocount < (outSize - 3))); icount += 4, ocount += 4)
1412+
{
1413+
const uint32_t t = *(sPtr++);
1414+
1415+
// Converts 8-bit unsigned / 8-bit signed / 8-bit signed to 8:8:8:8 unsigned
1416+
uint32_t t1 = (t >> 16) & 0xff;
1417+
constexpr int m = 1U << 7;
1418+
uint32_t v = ((((t >> 8) & 0xff) ^ m) - m) + 128;
1419+
uint32_t u = (((t & 0xff) ^ m) - m) + 128;
1420+
1421+
uint32_t t2 = u << 8;
1422+
uint32_t t3 = v << 16;
1423+
1424+
*(dPtr++) = t1 | t2 | t3 | 0xff000000;
1425+
}
1426+
return true;
1427+
}
1428+
return false;
1429+
1430+
case TEXP_LEGACY_A2W10V10U10:
1431+
if (outFormat != DXGI_FORMAT_R10G10B10A2_UNORM)
1432+
return false;
1433+
1434+
// D3DFMT_A2W10V10U10 -> DXGI_FORMAT_R10G10B10A2_UNORM (UVWA)
1435+
if (inSize >= 4 && outSize >= 4)
1436+
{
1437+
auto sPtr = static_cast<const uint32_t*>(pSource);
1438+
auto dPtr = static_cast<uint32_t*>(pDestination);
1439+
1440+
for (size_t ocount = 0, icount = 0; ((icount < (inSize - 3)) && (ocount < (outSize - 3))); icount += 4, ocount += 4)
1441+
{
1442+
const uint32_t t = *(sPtr++);
1443+
1444+
// Converts 2-bit unsigned / 10-bit signed / 10-bit signed / 10-bit signed to 2:10:10:10 unsigned
1445+
constexpr int m = 1U << 9;
1446+
uint32_t w = ((((t >> 20) & 0x3ff) ^ m) - m) + 512;
1447+
uint32_t v = ((((t >> 10) & 0x3ff) ^ m) - m) + 512;
1448+
uint32_t u = (((t & 0x3ff) ^ m) - m) + 512;
1449+
1450+
uint32_t t1 = u;
1451+
uint32_t t2 = v << 10;
1452+
uint32_t t3 = w << 20;
1453+
uint32_t ta = (tflags & TEXP_SCANLINE_SETALPHA) ? 0xC0000000 : (t & 0xC0000000);
1454+
1455+
*(dPtr++) = t1 | t2 | t3 | ta;
1456+
}
1457+
return true;
1458+
}
1459+
return false;
1460+
1461+
default:
1462+
return false;
1463+
}
1464+
}
13361465

13371466
//-------------------------------------------------------------------------------------
13381467
// Converts or copies image data from pPixels into scratch image data
@@ -1356,7 +1485,7 @@ namespace
13561485
{
13571486
if (convFlags & CONV_FLAGS_888)
13581487
cpFlags |= CP_FLAGS_24BPP;
1359-
else if (convFlags & (CONV_FLAGS_565 | CONV_FLAGS_5551 | CONV_FLAGS_4444 | CONV_FLAGS_8332 | CONV_FLAGS_A8P8 | CONV_FLAGS_L16 | CONV_FLAGS_A8L8))
1488+
else if (convFlags & (CONV_FLAGS_565 | CONV_FLAGS_5551 | CONV_FLAGS_4444 | CONV_FLAGS_8332 | CONV_FLAGS_A8P8 | CONV_FLAGS_L16 | CONV_FLAGS_A8L8 | CONV_FLAGS_L6V5U5))
13601489
cpFlags |= CP_FLAGS_16BPP;
13611490
else if (convFlags & (CONV_FLAGS_44 | CONV_FLAGS_332 | CONV_FLAGS_PAL8 | CONV_FLAGS_L8))
13621491
cpFlags |= CP_FLAGS_8BPP;
@@ -1502,13 +1631,18 @@ namespace
15021631
}
15031632
else if (convFlags & CONV_FLAGS_SWIZZLE)
15041633
{
1505-
SwizzleScanline(pDest, dpitch, pSrc, spitch,
1506-
metadata.format, tflags);
1634+
SwizzleScanline(pDest, dpitch, pSrc, spitch, metadata.format, tflags);
1635+
}
1636+
else if (convFlags & (CONV_FLAGS_L8U8V8 | CONV_FLAGS_WUV10))
1637+
{
1638+
const TEXP_LEGACY_FORMAT lformat = FindLegacyFormat(convFlags);
1639+
if (!LegacyConvertScanline(pDest, dpitch, metadata.format,
1640+
pSrc, spitch, lformat, tflags))
1641+
return E_FAIL;
15071642
}
15081643
else
15091644
{
1510-
CopyScanline(pDest, dpitch, pSrc, spitch,
1511-
metadata.format, tflags);
1645+
CopyScanline(pDest, dpitch, pSrc, spitch, metadata.format, tflags);
15121646
}
15131647

15141648
pSrc += spitch;
@@ -1605,6 +1739,13 @@ namespace
16051739
{
16061740
SwizzleScanline(pDest, dpitch, pSrc, spitch, metadata.format, tflags);
16071741
}
1742+
else if (convFlags & (CONV_FLAGS_L8U8V8 | CONV_FLAGS_WUV10))
1743+
{
1744+
const TEXP_LEGACY_FORMAT lformat = FindLegacyFormat(convFlags);
1745+
if (!LegacyConvertScanline(pDest, dpitch, metadata.format,
1746+
pSrc, spitch, lformat, tflags))
1747+
return E_FAIL;
1748+
}
16081749
else
16091750
{
16101751
CopyScanline(pDest, dpitch, pSrc, spitch, metadata.format, tflags);
@@ -1662,6 +1803,14 @@ namespace
16621803
{
16631804
SwizzleScanline(pPixels, rowPitch, pPixels, rowPitch, metadata.format, tflags);
16641805
}
1806+
else if (convFlags & (CONV_FLAGS_L8U8V8 | CONV_FLAGS_WUV10))
1807+
{
1808+
const TEXP_LEGACY_FORMAT lformat = FindLegacyFormat(convFlags);
1809+
if (!LegacyConvertScanline(pPixels, rowPitch, metadata.format, pPixels, rowPitch, lformat, tflags))
1810+
{
1811+
return E_UNEXPECTED;
1812+
}
1813+
}
16651814
else
16661815
{
16671816
CopyScanline(pPixels, rowPitch, pPixels, rowPitch, metadata.format, tflags);
@@ -2133,7 +2282,7 @@ HRESULT DirectX::LoadFromDDSFileEx(
21332282
}
21342283
#endif
21352284

2136-
if (convFlags & (CONV_FLAGS_SWIZZLE | CONV_FLAGS_NOALPHA))
2285+
if (convFlags & (CONV_FLAGS_SWIZZLE | CONV_FLAGS_NOALPHA | CONV_FLAGS_L8U8V8 | CONV_FLAGS_WUV10))
21372286
{
21382287
// Swizzle/copy image in place
21392288
hr = CopyImageInPlace(convFlags, image);

0 commit comments

Comments
 (0)