1 //-------------------------------------------------------------------------------------
4 // DirectX Texture Library - Microsoft DirectDraw Surface (DDS) file format reader/writer
6 // THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
7 // ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
8 // THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
11 // Copyright (c) Microsoft Corporation. All rights reserved.
13 // http://go.microsoft.com/fwlink/?LinkId=248926
14 //-------------------------------------------------------------------------------------
16 #include "directxtexp.h"
23 //-------------------------------------------------------------------------------------
24 // Legacy format mapping table (used for DDS files without 'DX10' extended header)
25 //-------------------------------------------------------------------------------------
28 CONV_FLAGS_NONE = 0x0,
29 CONV_FLAGS_EXPAND = 0x1, // Conversion requires expanded pixel size
30 CONV_FLAGS_NOALPHA = 0x2, // Conversion requires setting alpha to known value
31 CONV_FLAGS_SWIZZLE = 0x4, // BGR/RGB order swizzling required
32 CONV_FLAGS_PAL8 = 0x8, // Has an 8-bit palette
33 CONV_FLAGS_888 = 0x10, // Source is an 8:8:8 (24bpp) format
34 CONV_FLAGS_565 = 0x20, // Source is a 5:6:5 (16bpp) format
35 CONV_FLAGS_5551 = 0x40, // Source is a 5:5:5:1 (16bpp) format
36 CONV_FLAGS_4444 = 0x80, // Source is a 4:4:4:4 (16bpp) format
37 CONV_FLAGS_44 = 0x100, // Source is a 4:4 (8bpp) format
38 CONV_FLAGS_332 = 0x200, // Source is a 3:3:2 (8bpp) format
39 CONV_FLAGS_8332 = 0x400, // Source is a 8:3:3:2 (16bpp) format
40 CONV_FLAGS_A8P8 = 0x800, // Has an 8-bit palette with an alpha channel
41 CONV_FLAGS_DX10 = 0x10000, // Has the 'DX10' extension header
51 const LegacyDDS g_LegacyDDSMap[] =
53 { DXGI_FORMAT_BC1_UNORM, CONV_FLAGS_NONE, DDSPF_DXT1 }, // D3DFMT_DXT1
54 { DXGI_FORMAT_BC2_UNORM, CONV_FLAGS_NONE, DDSPF_DXT3 }, // D3DFMT_DXT3
55 { DXGI_FORMAT_BC3_UNORM, CONV_FLAGS_NONE, DDSPF_DXT5 }, // D3DFMT_DXT5
57 { DXGI_FORMAT_BC2_UNORM, CONV_FLAGS_NONE, DDSPF_DXT2 }, // D3DFMT_DXT2 (ignore premultiply)
58 { DXGI_FORMAT_BC3_UNORM, CONV_FLAGS_NONE, DDSPF_DXT4 }, // D3DFMT_DXT4 (ignore premultiply)
60 { DXGI_FORMAT_BC4_UNORM, CONV_FLAGS_NONE, DDSPF_BC4_UNORM },
61 { DXGI_FORMAT_BC4_SNORM, CONV_FLAGS_NONE, DDSPF_BC4_SNORM },
62 { DXGI_FORMAT_BC5_UNORM, CONV_FLAGS_NONE, DDSPF_BC5_UNORM },
63 { DXGI_FORMAT_BC5_SNORM, CONV_FLAGS_NONE, DDSPF_BC5_SNORM },
65 { DXGI_FORMAT_BC4_UNORM, CONV_FLAGS_NONE, { sizeof(DDS_PIXELFORMAT), DDS_FOURCC, MAKEFOURCC( 'A', 'T', 'I', '1' ), 0, 0, 0, 0, 0 } },
66 { DXGI_FORMAT_BC5_UNORM, CONV_FLAGS_NONE, { sizeof(DDS_PIXELFORMAT), DDS_FOURCC, MAKEFOURCC( 'A', 'T', 'I', '2' ), 0, 0, 0, 0, 0 } },
68 { DXGI_FORMAT_R8G8_B8G8_UNORM, CONV_FLAGS_NONE, DDSPF_R8G8_B8G8 }, // D3DFMT_R8G8_B8G8
69 { DXGI_FORMAT_G8R8_G8B8_UNORM, CONV_FLAGS_NONE, DDSPF_G8R8_G8B8 }, // D3DFMT_G8R8_G8B8
71 { DXGI_FORMAT_B8G8R8A8_UNORM, CONV_FLAGS_NONE, DDSPF_A8R8G8B8 }, // D3DFMT_A8R8G8B8 (uses DXGI 1.1 format)
72 { DXGI_FORMAT_B8G8R8X8_UNORM, CONV_FLAGS_NONE, DDSPF_X8R8G8B8 }, // D3DFMT_X8R8G8B8 (uses DXGI 1.1 format)
73 { DXGI_FORMAT_R8G8B8A8_UNORM, CONV_FLAGS_NONE, DDSPF_A8B8G8R8 }, // D3DFMT_A8B8G8R8
74 { DXGI_FORMAT_R8G8B8A8_UNORM, CONV_FLAGS_NOALPHA, DDSPF_X8B8G8R8 }, // D3DFMT_X8B8G8R8
75 { DXGI_FORMAT_R16G16_UNORM, CONV_FLAGS_NONE, DDSPF_G16R16 }, // D3DFMT_G16R16
77 { DXGI_FORMAT_R10G10B10A2_UNORM, CONV_FLAGS_SWIZZLE, { sizeof(DDS_PIXELFORMAT), DDS_RGB, 0, 32, 0x000003ff, 0x000ffc00, 0x3ff00000, 0xc0000000 } }, // D3DFMT_A2R10G10B10 (D3DX reversal issue workaround)
78 { DXGI_FORMAT_R10G10B10A2_UNORM, CONV_FLAGS_NONE, { sizeof(DDS_PIXELFORMAT), DDS_RGB, 0, 32, 0x3ff00000, 0x000ffc00, 0x000003ff, 0xc0000000 } }, // D3DFMT_A2B10G10R10 (D3DX reversal issue workaround)
80 { DXGI_FORMAT_R8G8B8A8_UNORM, CONV_FLAGS_EXPAND
82 | CONV_FLAGS_888, DDSPF_R8G8B8 }, // D3DFMT_R8G8B8
84 { DXGI_FORMAT_B5G6R5_UNORM, CONV_FLAGS_565, DDSPF_R5G6B5 }, // D3DFMT_R5G6B5
85 { DXGI_FORMAT_B5G5R5A1_UNORM, CONV_FLAGS_5551, DDSPF_A1R5G5B5 }, // D3DFMT_A1R5G5B5
86 { DXGI_FORMAT_B5G5R5A1_UNORM, CONV_FLAGS_5551
87 | CONV_FLAGS_NOALPHA, { sizeof(DDS_PIXELFORMAT), DDS_RGB, 0, 16, 0x7c00, 0x03e0, 0x001f, 0x0000 } }, // D3DFMT_X1R5G5B5
89 { DXGI_FORMAT_R8G8B8A8_UNORM, CONV_FLAGS_EXPAND
90 | CONV_FLAGS_8332, { sizeof(DDS_PIXELFORMAT), DDS_RGB, 0, 16, 0x00e0, 0x001c, 0x0003, 0xff00 } }, // D3DFMT_A8R3G3B2
91 { DXGI_FORMAT_B5G6R5_UNORM, CONV_FLAGS_EXPAND
92 | CONV_FLAGS_332, { sizeof(DDS_PIXELFORMAT), DDS_RGB, 0, 8, 0xe0, 0x1c, 0x03, 0x00 } }, // D3DFMT_R3G3B2
94 { DXGI_FORMAT_R8_UNORM, CONV_FLAGS_NONE, DDSPF_L8 }, // D3DFMT_L8
95 { DXGI_FORMAT_R16_UNORM, CONV_FLAGS_NONE, DDSPF_L16 }, // D3DFMT_L16
96 { DXGI_FORMAT_R8G8_UNORM, CONV_FLAGS_NONE, DDSPF_A8L8 }, // D3DFMT_A8L8
98 { DXGI_FORMAT_A8_UNORM, CONV_FLAGS_NONE, DDSPF_A8 }, // D3DFMT_A8
100 { DXGI_FORMAT_R16G16B16A16_UNORM, CONV_FLAGS_NONE, { sizeof(DDS_PIXELFORMAT), DDS_FOURCC, 36, 0, 0, 0, 0, 0 } }, // D3DFMT_A16B16G16R16
101 { DXGI_FORMAT_R16G16B16A16_SNORM, CONV_FLAGS_NONE, { sizeof(DDS_PIXELFORMAT), DDS_FOURCC, 110, 0, 0, 0, 0, 0 } }, // D3DFMT_Q16W16V16U16
102 { DXGI_FORMAT_R16_FLOAT, CONV_FLAGS_NONE, { sizeof(DDS_PIXELFORMAT), DDS_FOURCC, 111, 0, 0, 0, 0, 0 } }, // D3DFMT_R16F
103 { DXGI_FORMAT_R16G16_FLOAT, CONV_FLAGS_NONE, { sizeof(DDS_PIXELFORMAT), DDS_FOURCC, 112, 0, 0, 0, 0, 0 } }, // D3DFMT_G16R16F
104 { DXGI_FORMAT_R16G16B16A16_FLOAT, CONV_FLAGS_NONE, { sizeof(DDS_PIXELFORMAT), DDS_FOURCC, 113, 0, 0, 0, 0, 0 } }, // D3DFMT_A16B16G16R16F
105 { DXGI_FORMAT_R32_FLOAT, CONV_FLAGS_NONE, { sizeof(DDS_PIXELFORMAT), DDS_FOURCC, 114, 0, 0, 0, 0, 0 } }, // D3DFMT_R32F
106 { DXGI_FORMAT_R32G32_FLOAT, CONV_FLAGS_NONE, { sizeof(DDS_PIXELFORMAT), DDS_FOURCC, 115, 0, 0, 0, 0, 0 } }, // D3DFMT_G32R32F
107 { DXGI_FORMAT_R32G32B32A32_FLOAT, CONV_FLAGS_NONE, { sizeof(DDS_PIXELFORMAT), DDS_FOURCC, 116, 0, 0, 0, 0, 0 } }, // D3DFMT_A32B32G32R32F
109 { DXGI_FORMAT_R32_FLOAT, CONV_FLAGS_NONE, { sizeof(DDS_PIXELFORMAT), DDS_RGB, 0, 32, 0xffffffff, 0x00000000, 0x00000000, 0x00000000 } }, // D3DFMT_R32F (D3DX uses FourCC 114 instead)
111 { DXGI_FORMAT_R8G8B8A8_UNORM, CONV_FLAGS_EXPAND
113 | CONV_FLAGS_A8P8, { sizeof(DDS_PIXELFORMAT), DDS_PAL8, 0, 16, 0, 0, 0, 0 } }, // D3DFMT_A8P8
114 { DXGI_FORMAT_R8G8B8A8_UNORM, CONV_FLAGS_EXPAND
115 | CONV_FLAGS_PAL8, { sizeof(DDS_PIXELFORMAT), DDS_PAL8, 0, 8, 0, 0, 0, 0 } }, // D3DFMT_P8
117 #ifdef DXGI_1_2_FORMATS
118 { DXGI_FORMAT_B4G4R4A4_UNORM, CONV_FLAGS_4444, DDSPF_A4R4G4B4 }, // D3DFMT_A4R4G4B4 (uses DXGI 1.2 format)
119 { DXGI_FORMAT_B4G4R4A4_UNORM, CONV_FLAGS_NOALPHA
120 | CONV_FLAGS_4444, { sizeof(DDS_PIXELFORMAT), DDS_RGB, 0, 16, 0x0f00, 0x00f0, 0x000f, 0x0000 } }, // D3DFMT_X4R4G4B4 (uses DXGI 1.2 format)
121 { DXGI_FORMAT_B4G4R4A4_UNORM, CONV_FLAGS_EXPAND
122 | CONV_FLAGS_44, { sizeof(DDS_PIXELFORMAT), DDS_LUMINANCE, 0, 8, 0x0f, 0x00, 0x00, 0xf0 } }, // D3DFMT_A4L4 (uses DXGI 1.2 format)
123 #else // !DXGI_1_2_FORMATS
124 { DXGI_FORMAT_R8G8B8A8_UNORM, CONV_FLAGS_EXPAND
125 | CONV_FLAGS_4444, DDSPF_A4R4G4B4 }, // D3DFMT_A4R4G4B4
126 { DXGI_FORMAT_R8G8B8A8_UNORM, CONV_FLAGS_EXPAND
128 | CONV_FLAGS_4444, { sizeof(DDS_PIXELFORMAT), DDS_RGB, 0, 16, 0x0f00, 0x00f0, 0x000f, 0x0000 } }, // D3DFMT_X4R4G4B4
129 { DXGI_FORMAT_R8G8B8A8_UNORM, CONV_FLAGS_EXPAND
130 | CONV_FLAGS_44, { sizeof(DDS_PIXELFORMAT), DDS_LUMINANCE, 0, 8, 0x0f, 0x00, 0x00, 0xf0 } }, // D3DFMT_A4L4
134 // Note that many common DDS reader/writers (including D3DX) swap the
135 // the RED/BLUE masks for 10:10:10:2 formats. We assumme
136 // below that the 'backwards' header mask is being used since it is most
137 // likely written by D3DX. The more robust solution is to use the 'DX10'
138 // header extension and specify the DXGI_FORMAT_R10G10B10A2_UNORM format directly
140 // We do not support the following legacy Direct3D 9 formats:
141 // BumpDuDv D3DFMT_V8U8, D3DFMT_Q8W8V8U8, D3DFMT_V16U16, D3DFMT_A2W10V10U10
142 // BumpLuminance D3DFMT_L6V5U5, D3DFMT_X8L8V8U8
143 // FourCC "UYVY" D3DFMT_UYVY
144 // FourCC "YUY2" D3DFMT_YUY2
145 // FourCC 117 D3DFMT_CxV8U8
146 // ZBuffer D3DFMT_D16_LOCKABLE
147 // FourCC 82 D3DFMT_D32F_LOCKABLE
149 static DXGI_FORMAT _GetDXGIFormat( const DDS_PIXELFORMAT& ddpf, DWORD flags, _Inout_opt_ DWORD* convFlags )
151 const size_t MAP_SIZE = sizeof(g_LegacyDDSMap) / sizeof(LegacyDDS);
153 for( index = 0; index < MAP_SIZE; ++index )
155 const LegacyDDS* entry = &g_LegacyDDSMap[index];
157 if ( ddpf.dwFlags & entry->ddpf.dwFlags )
159 if ( entry->ddpf.dwFlags & DDS_FOURCC )
161 if ( ddpf.dwFourCC == entry->ddpf.dwFourCC )
164 else if ( entry->ddpf.dwFlags & DDS_PAL8 )
166 if ( ddpf.dwRGBBitCount == entry->ddpf.dwRGBBitCount )
169 else if ( ddpf.dwRGBBitCount == entry->ddpf.dwRGBBitCount )
171 // RGB, RGBA, ALPHA, LUMINANCE
172 if ( ddpf.dwRBitMask == entry->ddpf.dwRBitMask
173 && ddpf.dwGBitMask == entry->ddpf.dwGBitMask
174 && ddpf.dwBBitMask == entry->ddpf.dwBBitMask
175 && ddpf.dwABitMask == entry->ddpf.dwABitMask )
181 if ( index >= MAP_SIZE )
182 return DXGI_FORMAT_UNKNOWN;
184 DWORD cflags = g_LegacyDDSMap[index].convFlags;
185 DXGI_FORMAT format = g_LegacyDDSMap[index].format;
187 if ( (cflags & CONV_FLAGS_EXPAND) && (flags & DDS_FLAGS_NO_LEGACY_EXPANSION) )
188 return DXGI_FORMAT_UNKNOWN;
190 if ( (format == DXGI_FORMAT_R10G10B10A2_UNORM) && (flags & DDS_FLAGS_NO_R10B10G10A2_FIXUP) )
192 cflags ^= CONV_FLAGS_SWIZZLE;
202 //-------------------------------------------------------------------------------------
203 // Decodes DDS header including optional DX10 extended header
204 //-------------------------------------------------------------------------------------
205 static HRESULT _DecodeDDSHeader( _In_bytecount_(size) LPCVOID pSource, size_t size, DWORD flags, _Out_ TexMetadata& metadata,
206 _Inout_opt_ DWORD* convFlags )
211 memset( &metadata, 0, sizeof(TexMetadata) );
213 if ( size < (sizeof(DDS_HEADER) + sizeof(uint32_t)) )
215 return HRESULT_FROM_WIN32( ERROR_INVALID_DATA );
218 // DDS files always start with the same magic number ("DDS ")
219 uint32_t dwMagicNumber = *reinterpret_cast<const uint32_t*>(pSource);
220 if ( dwMagicNumber != DDS_MAGIC )
225 const DDS_HEADER* pHeader = reinterpret_cast<const DDS_HEADER*>( (const uint8_t*)pSource + sizeof( uint32_t ) );
228 // Verify header to validate DDS file
229 if ( pHeader->dwSize != sizeof(DDS_HEADER)
230 || pHeader->ddspf.dwSize != sizeof(DDS_PIXELFORMAT) )
235 metadata.mipLevels = pHeader->dwMipMapCount;
236 if ( metadata.mipLevels == 0 )
237 metadata.mipLevels = 1;
239 // Check for DX10 extension
240 if ( (pHeader->ddspf.dwFlags & DDS_FOURCC)
241 && (MAKEFOURCC( 'D', 'X', '1', '0' ) == pHeader->ddspf.dwFourCC) )
243 // Buffer must be big enough for both headers and magic value
244 if ( size < (sizeof(DDS_HEADER)+sizeof(uint32_t)+sizeof(DDS_HEADER_DXT10)) )
249 const DDS_HEADER_DXT10* d3d10ext = reinterpret_cast<const DDS_HEADER_DXT10*>( (const uint8_t*)pSource + sizeof( uint32_t ) + sizeof(DDS_HEADER) );
251 *convFlags |= CONV_FLAGS_DX10;
253 metadata.arraySize = d3d10ext->arraySize;
254 if ( metadata.arraySize == 0 )
256 return HRESULT_FROM_WIN32( ERROR_INVALID_DATA );
259 metadata.format = d3d10ext->dxgiFormat;
260 if ( !IsValid( metadata.format ) )
262 HRESULT_FROM_WIN32( ERROR_INVALID_DATA );
265 switch ( d3d10ext->resourceDimension )
267 case DDS_DIMENSION_TEXTURE1D:
269 // D3DX writes 1D textures with a fixed Height of 1
270 if ( (pHeader->dwFlags & DDS_HEIGHT) && pHeader->dwHeight != 1 )
272 return HRESULT_FROM_WIN32( ERROR_INVALID_DATA );
275 metadata.width = pHeader->dwWidth;
278 metadata.dimension = TEX_DIMENSION_TEXTURE1D;
281 case DDS_DIMENSION_TEXTURE2D:
282 if ( d3d10ext->miscFlag & DDS_RESOURCE_MISC_TEXTURECUBE )
284 metadata.miscFlags |= TEX_MISC_TEXTURECUBE;
285 metadata.arraySize *= 6;
288 metadata.width = pHeader->dwWidth;
289 metadata.height = pHeader->dwHeight;
291 metadata.dimension = TEX_DIMENSION_TEXTURE2D;
294 case DDS_DIMENSION_TEXTURE3D:
295 if ( !(pHeader->dwFlags & DDS_HEADER_FLAGS_VOLUME) )
297 return HRESULT_FROM_WIN32( ERROR_INVALID_DATA );
300 if ( metadata.arraySize > 1 )
301 return HRESULT_FROM_WIN32( ERROR_NOT_SUPPORTED );
303 metadata.width = pHeader->dwWidth;
304 metadata.height = pHeader->dwHeight;
305 metadata.depth = pHeader->dwDepth;
306 metadata.dimension = TEX_DIMENSION_TEXTURE3D;
310 return HRESULT_FROM_WIN32( ERROR_INVALID_DATA );
315 metadata.arraySize = 1;
317 if ( pHeader->dwFlags & DDS_HEADER_FLAGS_VOLUME )
319 metadata.width = pHeader->dwWidth;
320 metadata.height = pHeader->dwHeight;
321 metadata.depth = pHeader->dwDepth;
322 metadata.dimension = TEX_DIMENSION_TEXTURE3D;
326 if ( pHeader->dwCaps2 & DDS_CUBEMAP )
328 // We require all six faces to be defined
329 if ( (pHeader->dwCaps2 & DDS_CUBEMAP_ALLFACES ) != DDS_CUBEMAP_ALLFACES )
330 return HRESULT_FROM_WIN32( ERROR_NOT_SUPPORTED );
332 metadata.arraySize = 6;
333 metadata.miscFlags |= TEX_MISC_TEXTURECUBE;
336 metadata.width = pHeader->dwWidth;
337 metadata.height = pHeader->dwHeight;
339 metadata.dimension = TEX_DIMENSION_TEXTURE2D;
341 // Note there's no way for a legacy Direct3D 9 DDS to express a '1D' texture
344 metadata.format = _GetDXGIFormat( pHeader->ddspf, flags, convFlags );
346 if ( metadata.format == DXGI_FORMAT_UNKNOWN )
347 return HRESULT_FROM_WIN32( ERROR_NOT_SUPPORTED );
350 // Special flag for handling BGR DXGI 1.1 formats
351 if (flags & DDS_FLAGS_FORCE_RGB)
353 switch ( metadata.format )
355 case DXGI_FORMAT_B8G8R8A8_UNORM:
356 metadata.format = DXGI_FORMAT_R8G8B8A8_UNORM;
358 *convFlags |= CONV_FLAGS_SWIZZLE;
361 case DXGI_FORMAT_B8G8R8X8_UNORM:
362 metadata.format = DXGI_FORMAT_R8G8B8A8_UNORM;
364 *convFlags |= CONV_FLAGS_SWIZZLE | CONV_FLAGS_NOALPHA;
367 case DXGI_FORMAT_B8G8R8A8_TYPELESS:
368 metadata.format = DXGI_FORMAT_R8G8B8A8_TYPELESS;
370 *convFlags |= CONV_FLAGS_SWIZZLE;
373 case DXGI_FORMAT_B8G8R8A8_UNORM_SRGB:
374 metadata.format = DXGI_FORMAT_R8G8B8A8_UNORM_SRGB;
376 *convFlags |= CONV_FLAGS_SWIZZLE;
379 case DXGI_FORMAT_B8G8R8X8_TYPELESS:
380 metadata.format = DXGI_FORMAT_R8G8B8A8_TYPELESS;
382 *convFlags |= CONV_FLAGS_SWIZZLE | CONV_FLAGS_NOALPHA;
385 case DXGI_FORMAT_B8G8R8X8_UNORM_SRGB:
386 metadata.format = DXGI_FORMAT_R8G8B8A8_UNORM_SRGB;
388 *convFlags |= CONV_FLAGS_SWIZZLE | CONV_FLAGS_NOALPHA;
393 // Special flag for handling 16bpp formats
394 if (flags & DDS_FLAGS_NO_16BPP)
396 switch ( metadata.format )
398 case DXGI_FORMAT_B5G6R5_UNORM:
399 case DXGI_FORMAT_B5G5R5A1_UNORM:
400 #ifdef DXGI_1_2_FORMATS
401 case DXGI_FORMAT_B4G4R4A4_UNORM:
403 metadata.format = DXGI_FORMAT_R8G8B8A8_UNORM;
406 *convFlags |= CONV_FLAGS_EXPAND;
407 if ( metadata.format == DXGI_FORMAT_B5G6R5_UNORM )
408 *convFlags |= CONV_FLAGS_NOALPHA;
417 //-------------------------------------------------------------------------------------
418 // Encodes DDS file header (magic value, header, optional DX10 extended header)
419 //-------------------------------------------------------------------------------------
420 HRESULT _EncodeDDSHeader( _In_ const TexMetadata& metadata, DWORD flags,
421 _Out_opt_cap_x_(maxsize) LPVOID pDestination, _In_ size_t maxsize, _Out_ size_t& required )
423 assert( IsValid( metadata.format ) && !IsVideo( metadata.format ) );
425 if ( metadata.arraySize > 1 )
427 if ( (metadata.arraySize != 6) || (metadata.dimension != TEX_DIMENSION_TEXTURE2D) || !(metadata.miscFlags & TEX_MISC_TEXTURECUBE) )
429 flags |= DDS_FLAGS_FORCE_DX10_EXT;
433 DDS_PIXELFORMAT ddpf = { 0 };
434 if ( !(flags & DDS_FLAGS_FORCE_DX10_EXT) )
436 switch( metadata.format )
438 case DXGI_FORMAT_R8G8B8A8_UNORM: memcpy_s( &ddpf, sizeof(ddpf), &DDSPF_A8B8G8R8, sizeof(DDS_PIXELFORMAT) ); break;
439 case DXGI_FORMAT_R16G16_UNORM: memcpy_s( &ddpf, sizeof(ddpf), &DDSPF_G16R16, sizeof(DDS_PIXELFORMAT) ); break;
440 case DXGI_FORMAT_R8G8_UNORM: memcpy_s( &ddpf, sizeof(ddpf), &DDSPF_A8L8, sizeof(DDS_PIXELFORMAT) ); break;
441 case DXGI_FORMAT_R16_UNORM: memcpy_s( &ddpf, sizeof(ddpf), &DDSPF_L16, sizeof(DDS_PIXELFORMAT) ); break;
442 case DXGI_FORMAT_R8_UNORM: memcpy_s( &ddpf, sizeof(ddpf), &DDSPF_L8, sizeof(DDS_PIXELFORMAT) ); break;
443 case DXGI_FORMAT_A8_UNORM: memcpy_s( &ddpf, sizeof(ddpf), &DDSPF_A8, sizeof(DDS_PIXELFORMAT) ); break;
444 case DXGI_FORMAT_R8G8_B8G8_UNORM: memcpy_s( &ddpf, sizeof(ddpf), &DDSPF_R8G8_B8G8, sizeof(DDS_PIXELFORMAT) ); break;
445 case DXGI_FORMAT_G8R8_G8B8_UNORM: memcpy_s( &ddpf, sizeof(ddpf), &DDSPF_G8R8_G8B8, sizeof(DDS_PIXELFORMAT) ); break;
446 case DXGI_FORMAT_BC1_UNORM: memcpy_s( &ddpf, sizeof(ddpf), &DDSPF_DXT1, sizeof(DDS_PIXELFORMAT) ); break;
447 case DXGI_FORMAT_BC2_UNORM: memcpy_s( &ddpf, sizeof(ddpf), &DDSPF_DXT3, sizeof(DDS_PIXELFORMAT) ); break;
448 case DXGI_FORMAT_BC3_UNORM: memcpy_s( &ddpf, sizeof(ddpf), &DDSPF_DXT5, sizeof(DDS_PIXELFORMAT) ); break;
449 case DXGI_FORMAT_BC4_UNORM: memcpy_s( &ddpf, sizeof(ddpf), &DDSPF_BC4_UNORM, sizeof(DDS_PIXELFORMAT) ); break;
450 case DXGI_FORMAT_BC4_SNORM: memcpy_s( &ddpf, sizeof(ddpf), &DDSPF_BC4_SNORM, sizeof(DDS_PIXELFORMAT) ); break;
451 case DXGI_FORMAT_BC5_UNORM: memcpy_s( &ddpf, sizeof(ddpf), &DDSPF_BC5_UNORM, sizeof(DDS_PIXELFORMAT) ); break;
452 case DXGI_FORMAT_BC5_SNORM: memcpy_s( &ddpf, sizeof(ddpf), &DDSPF_BC5_SNORM, sizeof(DDS_PIXELFORMAT) ); break;
453 case DXGI_FORMAT_B5G6R5_UNORM: memcpy_s( &ddpf, sizeof(ddpf), &DDSPF_R5G6B5, sizeof(DDS_PIXELFORMAT) ); break;
454 case DXGI_FORMAT_B5G5R5A1_UNORM: memcpy_s( &ddpf, sizeof(ddpf), &DDSPF_A1R5G5B5, sizeof(DDS_PIXELFORMAT) ); break;
455 case DXGI_FORMAT_B8G8R8A8_UNORM: memcpy_s( &ddpf, sizeof(ddpf), &DDSPF_A8R8G8B8, sizeof(DDS_PIXELFORMAT) ); break; // DXGI 1.1
456 case DXGI_FORMAT_B8G8R8X8_UNORM: memcpy_s( &ddpf, sizeof(ddpf), &DDSPF_X8R8G8B8, sizeof(DDS_PIXELFORMAT) ); break; // DXGI 1.1
458 #ifdef DXGI_1_2_FORMATS
459 case DXGI_FORMAT_B4G4R4A4_UNORM: memcpy_s( &ddpf, sizeof(ddpf), &DDSPF_A4R4G4B4, sizeof(DDS_PIXELFORMAT) ); break;
462 // Legacy D3DX formats using D3DFMT enum value as FourCC
463 case DXGI_FORMAT_R32G32B32A32_FLOAT:
464 ddpf.dwSize = sizeof(DDS_PIXELFORMAT); ddpf.dwFlags = DDS_FOURCC; ddpf.dwFourCC = 116; // D3DFMT_A32B32G32R32F
466 case DXGI_FORMAT_R16G16B16A16_FLOAT:
467 ddpf.dwSize = sizeof(DDS_PIXELFORMAT); ddpf.dwFlags = DDS_FOURCC; ddpf.dwFourCC = 113; // D3DFMT_A16B16G16R16F
469 case DXGI_FORMAT_R16G16B16A16_UNORM:
470 ddpf.dwSize = sizeof(DDS_PIXELFORMAT); ddpf.dwFlags = DDS_FOURCC; ddpf.dwFourCC = 36; // D3DFMT_A16B16G16R16
472 case DXGI_FORMAT_R16G16B16A16_SNORM:
473 ddpf.dwSize = sizeof(DDS_PIXELFORMAT); ddpf.dwFlags = DDS_FOURCC; ddpf.dwFourCC = 110; // D3DFMT_Q16W16V16U16
475 case DXGI_FORMAT_R32G32_FLOAT:
476 ddpf.dwSize = sizeof(DDS_PIXELFORMAT); ddpf.dwFlags = DDS_FOURCC; ddpf.dwFourCC = 115; // D3DFMT_G32R32F
478 case DXGI_FORMAT_R16G16_FLOAT:
479 ddpf.dwSize = sizeof(DDS_PIXELFORMAT); ddpf.dwFlags = DDS_FOURCC; ddpf.dwFourCC = 112; // D3DFMT_G16R16F
481 case DXGI_FORMAT_R32_FLOAT:
482 ddpf.dwSize = sizeof(DDS_PIXELFORMAT); ddpf.dwFlags = DDS_FOURCC; ddpf.dwFourCC = 114; // D3DFMT_R32F
484 case DXGI_FORMAT_R16_FLOAT:
485 ddpf.dwSize = sizeof(DDS_PIXELFORMAT); ddpf.dwFlags = DDS_FOURCC; ddpf.dwFourCC = 111; // D3DFMT_R16F
490 required = sizeof(uint32_t) + sizeof(DDS_HEADER);
492 if ( ddpf.dwSize == 0 )
493 required += sizeof(DDS_HEADER_DXT10);
498 if ( maxsize < required )
499 return E_NOT_SUFFICIENT_BUFFER;
501 *reinterpret_cast<uint32_t*>(pDestination) = DDS_MAGIC;
503 DDS_HEADER* header = reinterpret_cast<DDS_HEADER*>( reinterpret_cast<uint8_t*>(pDestination) + sizeof(uint32_t) );
506 memset( header, 0, sizeof(DDS_HEADER ) );
507 header->dwSize = sizeof( DDS_HEADER );
508 header->dwFlags = DDS_HEADER_FLAGS_TEXTURE;
509 header->dwCaps = DDS_SURFACE_FLAGS_TEXTURE;
511 if (metadata.mipLevels > 0)
513 header->dwFlags |= DDS_HEADER_FLAGS_MIPMAP;
516 if ( metadata.mipLevels > 0xFFFFFFFF )
520 header->dwMipMapCount = static_cast<uint32_t>( metadata.mipLevels );
522 if ( header->dwMipMapCount > 1 )
523 header->dwCaps |= DDS_SURFACE_FLAGS_MIPMAP;
526 switch( metadata.dimension )
528 case TEX_DIMENSION_TEXTURE1D:
530 if ( metadata.height > 0xFFFFFFFF )
534 header->dwWidth = static_cast<uint32_t>( metadata.width );
535 header->dwHeight = header->dwDepth = 1;
538 case TEX_DIMENSION_TEXTURE2D:
540 if ( metadata.height > 0xFFFFFFFF
541 || metadata.width > 0xFFFFFFFF)
545 header->dwHeight = static_cast<uint32_t>( metadata.height );
546 header->dwWidth = static_cast<uint32_t>( metadata.width );
549 if ( metadata.miscFlags & TEX_MISC_TEXTURECUBE )
551 header->dwCaps |= DDS_SURFACE_FLAGS_CUBEMAP;
552 header->dwCaps2 |= DDS_CUBEMAP_ALLFACES;
556 case TEX_DIMENSION_TEXTURE3D:
558 if ( metadata.height > 0xFFFFFFFF
559 || metadata.width > 0xFFFFFFFF
560 || metadata.depth > 0xFFFFFFFF )
564 header->dwFlags |= DDS_HEADER_FLAGS_VOLUME;
565 header->dwCaps2 |= DDS_FLAGS_VOLUME;
566 header->dwHeight = static_cast<uint32_t>( metadata.height );
567 header->dwWidth = static_cast<uint32_t>( metadata.width );
568 header->dwDepth = static_cast<uint32_t>( metadata.depth );
575 size_t rowPitch, slicePitch;
576 ComputePitch( metadata.format, metadata.width, metadata.height, rowPitch, slicePitch, CP_FLAGS_NONE );
579 if ( slicePitch > 0xFFFFFFFF
580 || rowPitch > 0xFFFFFFFF )
584 if ( IsCompressed( metadata.format ) )
586 header->dwFlags |= DDS_HEADER_FLAGS_LINEARSIZE;
587 header->dwPitchOrLinearSize = static_cast<uint32_t>( slicePitch );
591 header->dwFlags |= DDS_HEADER_FLAGS_PITCH;
592 header->dwPitchOrLinearSize = static_cast<uint32_t>( rowPitch );
595 if ( ddpf.dwSize == 0 )
597 memcpy_s( &header->ddspf, sizeof(header->ddspf), &DDSPF_DX10, sizeof(DDS_PIXELFORMAT) );
599 DDS_HEADER_DXT10* ext = reinterpret_cast<DDS_HEADER_DXT10*>( reinterpret_cast<uint8_t*>(header) + sizeof(DDS_HEADER) );
602 memset( ext, 0, sizeof(DDS_HEADER_DXT10) );
603 ext->dxgiFormat = metadata.format;
604 ext->resourceDimension = metadata.dimension;
607 if ( metadata.arraySize > 0xFFFFFFFF )
611 if ( metadata.miscFlags & TEX_MISC_TEXTURECUBE )
613 ext->miscFlag |= TEX_MISC_TEXTURECUBE;
614 assert( (metadata.arraySize % 6) == 0 );
615 ext->arraySize = static_cast<UINT>( metadata.arraySize / 6 );
619 ext->arraySize = static_cast<UINT>( metadata.arraySize );
624 memcpy_s( &header->ddspf, sizeof(header->ddspf), &ddpf, sizeof(ddpf) );
631 //-------------------------------------------------------------------------------------
632 // Converts an image row with optional clearing of alpha value to 1.0
633 // Returns true if supported, false if expansion case not supported
634 //-------------------------------------------------------------------------------------
635 enum TEXP_LEGACY_FORMAT
637 TEXP_LEGACY_UNKNOWN = 0,
640 TEXP_LEGACY_A8R3G3B2,
644 TEXP_LEGACY_B4G4R4A4,
647 inline static TEXP_LEGACY_FORMAT _FindLegacyFormat( DWORD flags )
649 TEXP_LEGACY_FORMAT lformat = TEXP_LEGACY_UNKNOWN;
651 if ( flags & CONV_FLAGS_PAL8 )
653 lformat = ( flags & CONV_FLAGS_A8P8 ) ? TEXP_LEGACY_A8P8 : TEXP_LEGACY_P8;
655 else if ( flags & CONV_FLAGS_888 )
656 lformat = TEXP_LEGACY_R8G8B8;
657 else if ( flags & CONV_FLAGS_332 )
658 lformat = TEXP_LEGACY_R3G3B2;
659 else if ( flags & CONV_FLAGS_8332 )
660 lformat = TEXP_LEGACY_A8R3G3B2;
661 else if ( flags & CONV_FLAGS_44 )
662 lformat = TEXP_LEGACY_A4L4;
663 #ifndef DXGI_1_2_FORMATS
664 else if ( flags & CONV_FLAGS_4444 )
665 lformat = TEXP_LEGACY_B4G4R4A4;
671 static bool _LegacyExpandScanline( _Out_bytecap_(outSize) LPVOID pDestination, size_t outSize, _In_ DXGI_FORMAT outFormat,
672 _In_bytecount_(inSize) LPCVOID pSource, size_t inSize, _In_ TEXP_LEGACY_FORMAT inFormat,
673 _In_opt_count_c_(256) const uint32_t* pal8, _In_ DWORD flags )
675 assert( pDestination && outSize > 0 );
676 assert( pSource && inSize > 0 );
677 assert( IsValid(outFormat) && !IsVideo(outFormat) );
681 case TEXP_LEGACY_R8G8B8:
682 if ( outFormat != DXGI_FORMAT_R8G8B8A8_UNORM )
685 // D3DFMT_R8G8B8 -> DXGI_FORMAT_R8G8B8A8_UNORM
687 const uint8_t * __restrict sPtr = reinterpret_cast<const uint8_t*>(pSource);
688 uint32_t * __restrict dPtr = reinterpret_cast<uint32_t*>(pDestination);
690 for( size_t ocount = 0, icount = 0; ((icount < inSize) && (ocount < outSize)); icount += 3, ocount += 4 )
692 // 24bpp Direct3D 9 files are actually BGR, so need to swizzle as well
693 uint32_t t1 = ( *(sPtr) << 16 );
694 uint32_t t2 = ( *(sPtr+1) << 8 );
695 uint32_t t3 = *(sPtr+2);
697 *(dPtr++) = t1 | t2 | t3 | 0xff000000;
703 case TEXP_LEGACY_R3G3B2:
706 case DXGI_FORMAT_R8G8B8A8_UNORM:
707 // D3DFMT_R3G3B2 -> DXGI_FORMAT_R8G8B8A8_UNORM
709 const uint8_t* __restrict sPtr = reinterpret_cast<const uint8_t*>(pSource);
710 uint32_t * __restrict dPtr = reinterpret_cast<uint32_t*>(pDestination);
712 for( size_t ocount = 0, icount = 0; ((icount < inSize) && (ocount < outSize)); ++icount, ocount += 4 )
714 uint8_t t = *(sPtr++);
716 uint32_t t1 = (t & 0xe0) | ((t & 0xe0) >> 3) | ((t & 0xc0) >> 6);
717 uint32_t t2 = ((t & 0x1c) << 11) | ((t & 0x1c) << 8) | ((t & 0x18) << 5);
718 uint32_t t3 = ((t & 0x03) << 22) | ((t & 0x03) << 20) | ((t & 0x03) << 18) | ((t & 0x03) << 16);
720 *(dPtr++) = t1 | t2 | t3 | 0xff000000;
725 case DXGI_FORMAT_B5G6R5_UNORM:
726 // D3DFMT_R3G3B2 -> DXGI_FORMAT_B5G6R5_UNORM
728 const uint8_t* __restrict sPtr = reinterpret_cast<const uint8_t*>(pSource);
729 uint16_t * __restrict dPtr = reinterpret_cast<uint16_t*>(pDestination);
731 for( size_t ocount = 0, icount = 0; ((icount < inSize) && (ocount < outSize)); ++icount, ocount += 2 )
733 uint8_t t = *(sPtr++);
735 uint16_t t1 = ((t & 0xe0) << 8) | ((t & 0xc0) << 5);
736 uint16_t t2 = ((t & 0x1c) << 6) | ((t & 0x1c) << 3);
737 uint16_t t3 = ((t & 0x03) << 3) | ((t & 0x03) << 1) | ((t & 0x02) >> 1);
739 *(dPtr++) = t1 | t2 | t3;
746 case TEXP_LEGACY_A8R3G3B2:
747 if ( outFormat != DXGI_FORMAT_R8G8B8A8_UNORM )
750 // D3DFMT_A8R3G3B2 -> DXGI_FORMAT_R8G8B8A8_UNORM
752 const uint16_t* __restrict sPtr = reinterpret_cast<const uint16_t*>(pSource);
753 uint32_t * __restrict dPtr = reinterpret_cast<uint32_t*>(pDestination);
755 for( size_t ocount = 0, icount = 0; ((icount < inSize) && (ocount < outSize)); icount += 2, ocount += 4 )
757 uint16_t t = *(sPtr++);
759 uint32_t t1 = (t & 0x00e0) | ((t & 0x00e0) >> 3) | ((t & 0x00c0) >> 6);
760 uint32_t t2 = ((t & 0x001c) << 11) | ((t & 0x001c) << 8) | ((t & 0x0018) << 5);
761 uint32_t t3 = ((t & 0x0003) << 22) | ((t & 0x0003) << 20) | ((t & 0x0003) << 18) | ((t & 0x0003) << 16);
762 uint32_t ta = ( flags & TEXP_SCANLINE_SETALPHA ) ? 0xff000000 : ((t & 0xff00) << 16);
764 *(dPtr++) = t1 | t2 | t3 | ta;
770 if ( (outFormat != DXGI_FORMAT_R8G8B8A8_UNORM) || !pal8 )
773 // D3DFMT_P8 -> DXGI_FORMAT_R8G8B8A8_UNORM
775 const uint8_t* __restrict sPtr = reinterpret_cast<const uint8_t*>(pSource);
776 uint32_t * __restrict dPtr = reinterpret_cast<uint32_t*>(pDestination);
778 for( size_t ocount = 0, icount = 0; ((icount < inSize) && (ocount < outSize)); ++icount, ocount += 4 )
780 uint8_t t = *(sPtr++);
782 *(dPtr++) = pal8[ t ];
787 case TEXP_LEGACY_A8P8:
788 if ( (outFormat != DXGI_FORMAT_R8G8B8A8_UNORM) || !pal8 )
791 // D3DFMT_A8P8 -> DXGI_FORMAT_R8G8B8A8_UNORM
793 const uint16_t* __restrict sPtr = reinterpret_cast<const uint16_t*>(pSource);
794 uint32_t * __restrict dPtr = reinterpret_cast<uint32_t*>(pDestination);
796 for( size_t ocount = 0, icount = 0; ((icount < inSize) && (ocount < outSize)); icount += 2, ocount += 4 )
798 uint16_t t = *(sPtr++);
800 uint32_t t1 = pal8[ t & 0xff ];
801 uint32_t ta = ( flags & TEXP_SCANLINE_SETALPHA ) ? 0xff000000 : ((t & 0xff00) << 16);
808 case TEXP_LEGACY_A4L4:
811 #ifdef DXGI_1_2_FORMATS
812 case DXGI_FORMAT_B4G4R4A4_UNORM :
813 // D3DFMT_A4L4 -> DXGI_FORMAT_B4G4R4A4_UNORM
815 const uint8_t * __restrict sPtr = reinterpret_cast<const uint8_t*>(pSource);
816 uint16_t * __restrict dPtr = reinterpret_cast<uint16_t*>(pDestination);
818 for( size_t ocount = 0, icount = 0; ((icount < inSize) && (ocount < outSize)); ++icount, ocount += 2 )
820 uint8_t t = *(sPtr++);
822 uint16_t t1 = (t & 0x0f);
823 uint16_t ta = ( flags & TEXP_SCANLINE_SETALPHA ) ? 0xf000 : ((t & 0xf0) << 8);
825 *(dPtr++) = t1 | (t1 << 4) | (t1 << 8) | ta;
829 #endif // DXGI_1_2_FORMATS
831 case DXGI_FORMAT_R8G8B8A8_UNORM:
832 // D3DFMT_A4L4 -> DXGI_FORMAT_R8G8B8A8_UNORM
834 const uint8_t * __restrict sPtr = reinterpret_cast<const uint8_t*>(pSource);
835 uint32_t * __restrict dPtr = reinterpret_cast<uint32_t*>(pDestination);
837 for( size_t ocount = 0, icount = 0; ((icount < inSize) && (ocount < outSize)); ++icount, ocount += 4 )
839 uint8_t t = *(sPtr++);
841 uint32_t t1 = ((t & 0x0f) << 4) | (t & 0x0f);
842 uint32_t ta = ( flags & TEXP_SCANLINE_SETALPHA ) ? 0xff000000 : (((t & 0xf0) << 24) | ((t & 0xf0) << 20));
844 *(dPtr++) = t1 | (t1 << 8) | (t1 << 16) | ta;
851 #ifndef DXGI_1_2_FORMATS
852 case TEXP_LEGACY_B4G4R4A4:
853 if (outFormat != DXGI_FORMAT_R8G8B8A8_UNORM)
856 // D3DFMT_A4R4G4B4 -> DXGI_FORMAT_R8G8B8A8_UNORM
858 const uint16_t * __restrict sPtr = reinterpret_cast<const uint16_t*>(pSource);
859 uint32_t * __restrict dPtr = reinterpret_cast<uint32_t*>(pDestination);
861 for( size_t ocount = 0, icount = 0; ((icount < inSize) && (ocount < outSize)); icount += 2, ocount += 4 )
863 uint16_t t = *(sPtr++);
865 uint32_t t1 = ((t & 0x0f00) >> 4) | ((t & 0x0f00) >> 8);
866 uint32_t t2 = ((t & 0x00f0) << 8) | ((t & 0x00f0) << 4);
867 uint32_t t3 = ((t & 0x000f) << 20) | ((t & 0x000f) << 16);
868 uint32_t ta = ( flags & TEXP_SCANLINE_SETALPHA ) ? 0xff000000 : (((t & 0xf000) << 16) | ((t & 0xf000) << 12));
870 *(dPtr++) = t1 | t2 | t3 | ta;
881 //-------------------------------------------------------------------------------------
882 // Converts or copies image data from pPixels into scratch image data
883 //-------------------------------------------------------------------------------------
884 static HRESULT _CopyImage( _In_bytecount_(size) const void* pPixels, _In_ size_t size,
885 _In_ const TexMetadata& metadata, _In_ DWORD cpFlags, _In_ DWORD convFlags, _In_opt_count_c_(256) const uint32_t *pal8, _In_ const ScratchImage& image )
888 assert( image.GetPixels() );
893 if ( convFlags & CONV_FLAGS_EXPAND )
895 if ( convFlags & CONV_FLAGS_888 )
896 cpFlags |= CP_FLAGS_24BPP;
897 else if ( convFlags & (CONV_FLAGS_565 | CONV_FLAGS_5551 | CONV_FLAGS_4444 | CONV_FLAGS_8332 | CONV_FLAGS_A8P8 ) )
898 cpFlags |= CP_FLAGS_16BPP;
899 else if ( convFlags & (CONV_FLAGS_44 | CONV_FLAGS_332 | CONV_FLAGS_PAL8) )
900 cpFlags |= CP_FLAGS_8BPP;
903 size_t pixelSize, nimages;
904 _DetermineImageArray( metadata, cpFlags, nimages, pixelSize );
905 if ( (nimages == 0) || (nimages != image.GetImageCount()) )
910 assert( pixelSize <= size );
912 std::unique_ptr<Image[]> timages( new Image[nimages] );
913 if ( !_SetupImageArray( (uint8_t*)pPixels, size, metadata, cpFlags, timages.get(), nimages ) )
918 if ( nimages != image.GetImageCount() )
923 const Image* images = image.GetImages();
929 DWORD tflags = (convFlags & CONV_FLAGS_NOALPHA) ? TEXP_SCANLINE_SETALPHA : 0;
930 if ( convFlags & CONV_FLAGS_SWIZZLE )
931 tflags |= TEXP_SCANLINE_LEGACY;
933 switch (metadata.dimension)
935 case TEX_DIMENSION_TEXTURE1D:
936 case TEX_DIMENSION_TEXTURE2D:
939 for( size_t item = 0; item < metadata.arraySize; ++item )
941 for( size_t level = 0; level < metadata.mipLevels; ++level, ++index )
943 if ( index >= nimages )
946 if ( images[ index ].height != timages[ index ].height )
949 size_t dpitch = images[ index ].rowPitch;
950 size_t spitch = timages[ index ].rowPitch;
952 const uint8_t *pSrc = const_cast<const uint8_t*>( timages[ index ].pixels );
956 uint8_t *pDest = images[ index ].pixels;
960 if ( IsCompressed( metadata.format ) )
962 size_t csize = std::min<size_t>( images[ index ].slicePitch, timages[ index ].slicePitch );
963 memcpy_s( pDest, images[ index ].slicePitch, pSrc, csize );
967 for( size_t h = 0; h < images[ index ].height; ++h )
969 if ( convFlags & CONV_FLAGS_EXPAND )
971 #ifdef DXGI_1_2_FORMATS
972 if ( convFlags & (CONV_FLAGS_565|CONV_FLAGS_5551|CONV_FLAGS_4444) )
974 if ( convFlags & (CONV_FLAGS_565|CONV_FLAGS_5551) )
977 if ( !_ExpandScanline( pDest, dpitch, DXGI_FORMAT_R8G8B8A8_UNORM,
979 (convFlags & CONV_FLAGS_565) ? DXGI_FORMAT_B5G6R5_UNORM : DXGI_FORMAT_B5G5R5A1_UNORM,
985 TEXP_LEGACY_FORMAT lformat = _FindLegacyFormat( convFlags );
986 if ( !_LegacyExpandScanline( pDest, dpitch, metadata.format,
987 pSrc, spitch, lformat, pal8,
992 else if ( convFlags & CONV_FLAGS_SWIZZLE )
994 _SwizzleScanline( pDest, dpitch, pSrc, spitch,
995 metadata.format, tflags );
999 _CopyScanline( pDest, dpitch, pSrc, spitch,
1000 metadata.format, tflags );
1012 case TEX_DIMENSION_TEXTURE3D:
1015 size_t d = metadata.depth;
1017 for( size_t level = 0; level < metadata.mipLevels; ++level )
1019 for( size_t slice = 0; slice < d; ++slice, ++index )
1021 if ( index >= nimages )
1024 if ( images[ index ].height != timages[ index ].height )
1027 size_t dpitch = images[ index ].rowPitch;
1028 size_t spitch = timages[ index ].rowPitch;
1030 const uint8_t *pSrc = const_cast<const uint8_t*>( timages[ index ].pixels );
1034 uint8_t *pDest = images[ index ].pixels;
1038 if ( IsCompressed( metadata.format ) )
1040 size_t csize = std::min<size_t>( images[ index ].slicePitch, timages[ index ].slicePitch );
1041 memcpy_s( pDest, images[ index ].slicePitch, pSrc, csize );
1045 for( size_t h = 0; h < images[ index ].height; ++h )
1047 if ( convFlags & CONV_FLAGS_EXPAND )
1049 #ifdef DXGI_1_2_FORMATS
1050 if ( convFlags & (CONV_FLAGS_565|CONV_FLAGS_5551|CONV_FLAGS_4444) )
1052 if ( convFlags & (CONV_FLAGS_565|CONV_FLAGS_5551) )
1055 if ( !_ExpandScanline( pDest, dpitch, DXGI_FORMAT_R8G8B8A8_UNORM,
1057 (convFlags & CONV_FLAGS_565) ? DXGI_FORMAT_B5G6R5_UNORM : DXGI_FORMAT_B5G5R5A1_UNORM,
1063 TEXP_LEGACY_FORMAT lformat = _FindLegacyFormat( convFlags );
1064 if ( !_LegacyExpandScanline( pDest, dpitch, metadata.format,
1065 pSrc, spitch, lformat, pal8,
1070 else if ( convFlags & CONV_FLAGS_SWIZZLE )
1072 _SwizzleScanline( pDest, dpitch, pSrc, spitch, metadata.format, tflags );
1076 _CopyScanline( pDest, dpitch, pSrc, spitch, metadata.format, tflags );
1098 static HRESULT _CopyImageInPlace( DWORD convFlags, _In_ const ScratchImage& image )
1100 if ( !image.GetPixels() )
1103 const Image* images = image.GetImages();
1107 const TexMetadata& metadata = image.GetMetadata();
1109 DWORD tflags = (convFlags & CONV_FLAGS_NOALPHA) ? TEXP_SCANLINE_SETALPHA : 0;
1110 if ( convFlags & CONV_FLAGS_SWIZZLE )
1111 tflags |= TEXP_SCANLINE_LEGACY;
1113 for( size_t i = 0; i < image.GetImageCount(); ++i )
1115 const Image* img = &images[ i ];
1116 uint8_t *pPixels = img->pixels;
1120 size_t rowPitch = img->rowPitch;
1122 for( size_t h = 0; h < img->height; ++h )
1124 if ( convFlags & CONV_FLAGS_SWIZZLE )
1126 _SwizzleScanline( pPixels, rowPitch, pPixels, rowPitch, metadata.format, tflags );
1130 _CopyScanline( pPixels, rowPitch, pPixels, rowPitch, metadata.format, tflags );
1133 pPixels += rowPitch;
1141 //=====================================================================================
1143 //=====================================================================================
1145 //-------------------------------------------------------------------------------------
1146 // Obtain metadata from DDS file in memory/on disk
1147 //-------------------------------------------------------------------------------------
1149 HRESULT GetMetadataFromDDSMemory( LPCVOID pSource, size_t size, DWORD flags, TexMetadata& metadata )
1151 if ( !pSource || size == 0 )
1152 return E_INVALIDARG;
1154 return _DecodeDDSHeader( pSource, size, flags, metadata, 0 );
1157 HRESULT GetMetadataFromDDSFile( LPCWSTR szFile, DWORD flags, TexMetadata& metadata )
1160 return E_INVALIDARG;
1162 #if (_WIN32_WINNT >= 0x0602 /*_WIN32_WINNT_WIN8*/)
1163 ScopedHandle hFile( safe_handle( CreateFile2( szFile, GENERIC_READ, FILE_SHARE_READ, OPEN_EXISTING, 0 ) ) );
1165 ScopedHandle hFile( safe_handle( CreateFileW( szFile, GENERIC_READ, FILE_SHARE_READ, 0, OPEN_EXISTING,
1166 FILE_FLAG_SEQUENTIAL_SCAN, 0 ) ) );
1170 return HRESULT_FROM_WIN32( GetLastError() );
1173 // Get the file size
1174 LARGE_INTEGER fileSize = {0};
1176 #if (_WIN32_WINNT >= _WIN32_WINNT_VISTA)
1177 FILE_STANDARD_INFO fileInfo;
1178 if ( !GetFileInformationByHandleEx( hFile.get(), FileStandardInfo, &fileInfo, sizeof(fileInfo) ) )
1180 return HRESULT_FROM_WIN32( GetLastError() );
1182 fileSize = fileInfo.EndOfFile;
1184 if ( !GetFileSizeEx( hFile.get(), &fileSize ) )
1186 return HRESULT_FROM_WIN32( GetLastError() );
1190 // File is too big for 32-bit allocation, so reject read (4 GB should be plenty large enough for a valid DDS file)
1191 if ( fileSize.HighPart > 0 )
1193 return HRESULT_FROM_WIN32( ERROR_FILE_TOO_LARGE );
1196 // Need at least enough data to fill the standard header and magic number to be a valid DDS
1197 if ( fileSize.LowPart < ( sizeof(DDS_HEADER) + sizeof(uint32_t) ) )
1202 // Read the header in (including extended header if present)
1203 const size_t MAX_HEADER_SIZE = sizeof(uint32_t) + sizeof(DDS_HEADER) + sizeof(DDS_HEADER_DXT10);
1204 uint8_t header[MAX_HEADER_SIZE];
1206 DWORD bytesRead = 0;
1207 if ( !ReadFile( hFile.get(), header, MAX_HEADER_SIZE, &bytesRead, 0 ) )
1209 return HRESULT_FROM_WIN32( GetLastError() );
1212 return _DecodeDDSHeader( header, bytesRead, flags, metadata, 0 );
1216 //-------------------------------------------------------------------------------------
1217 // Load a DDS file in memory
1218 //-------------------------------------------------------------------------------------
1219 HRESULT LoadFromDDSMemory( LPCVOID pSource, size_t size, DWORD flags, TexMetadata* metadata, ScratchImage& image )
1221 if ( !pSource || size == 0 )
1222 return E_INVALIDARG;
1226 DWORD convFlags = 0;
1228 HRESULT hr = _DecodeDDSHeader( pSource, size, flags, mdata, &convFlags );
1232 size_t offset = sizeof(uint32_t) + sizeof(DDS_HEADER);
1233 if ( convFlags & CONV_FLAGS_DX10 )
1234 offset += sizeof(DDS_HEADER_DXT10);
1236 assert( offset <= size );
1238 const uint32_t *pal8 = nullptr;
1239 if ( convFlags & CONV_FLAGS_PAL8 )
1241 pal8 = reinterpret_cast<const uint32_t*>( reinterpret_cast<const uint8_t*>(pSource) + offset );
1243 offset += ( 256 * sizeof(uint32_t) );
1244 if ( size < offset )
1248 hr = image.Initialize( mdata );
1252 LPCVOID pPixels = reinterpret_cast<LPCVOID>( reinterpret_cast<const uint8_t*>(pSource) + offset );
1254 hr = _CopyImage( pPixels, size - offset, mdata,
1255 (flags & DDS_FLAGS_LEGACY_DWORD) ? CP_FLAGS_LEGACY_DWORD : CP_FLAGS_NONE, convFlags, pal8, image );
1262 memcpy( metadata, &mdata, sizeof(TexMetadata) );
1268 //-------------------------------------------------------------------------------------
1269 // Load a DDS file from disk
1270 //-------------------------------------------------------------------------------------
1271 HRESULT LoadFromDDSFile( LPCWSTR szFile, DWORD flags, TexMetadata* metadata, ScratchImage& image )
1274 return E_INVALIDARG;
1278 #if (_WIN32_WINNT >= 0x0602 /*_WIN32_WINNT_WIN8*/)
1279 ScopedHandle hFile( safe_handle ( CreateFile2( szFile, GENERIC_READ, FILE_SHARE_READ, OPEN_EXISTING, 0 ) ) );
1281 ScopedHandle hFile( safe_handle ( CreateFileW( szFile, GENERIC_READ, FILE_SHARE_READ, 0, OPEN_EXISTING,
1282 FILE_FLAG_SEQUENTIAL_SCAN, 0 ) ) );
1287 return HRESULT_FROM_WIN32( GetLastError() );
1290 // Get the file size
1291 LARGE_INTEGER fileSize = {0};
1293 #if (_WIN32_WINNT >= _WIN32_WINNT_VISTA)
1294 FILE_STANDARD_INFO fileInfo;
1295 if ( !GetFileInformationByHandleEx( hFile.get(), FileStandardInfo, &fileInfo, sizeof(fileInfo) ) )
1297 return HRESULT_FROM_WIN32( GetLastError() );
1299 fileSize = fileInfo.EndOfFile;
1301 if ( !GetFileSizeEx( hFile.get(), &fileSize ) )
1303 return HRESULT_FROM_WIN32( GetLastError() );
1307 // File is too big for 32-bit allocation, so reject read (4 GB should be plenty large enough for a valid DDS file)
1308 if ( fileSize.HighPart > 0 )
1310 return HRESULT_FROM_WIN32( ERROR_FILE_TOO_LARGE );
1313 // Need at least enough data to fill the standard header and magic number to be a valid DDS
1314 if ( fileSize.LowPart < ( sizeof(DDS_HEADER) + sizeof(uint32_t) ) )
1319 // Read the header in (including extended header if present)
1320 const size_t MAX_HEADER_SIZE = sizeof(uint32_t) + sizeof(DDS_HEADER) + sizeof(DDS_HEADER_DXT10);
1321 uint8_t header[MAX_HEADER_SIZE];
1323 DWORD bytesRead = 0;
1324 if ( !ReadFile( hFile.get(), header, MAX_HEADER_SIZE, &bytesRead, 0 ) )
1326 return HRESULT_FROM_WIN32( GetLastError() );
1329 DWORD convFlags = 0;
1331 HRESULT hr = _DecodeDDSHeader( header, bytesRead, flags, mdata, &convFlags );
1335 DWORD offset = MAX_HEADER_SIZE;
1337 if ( !(convFlags & CONV_FLAGS_DX10) )
1339 // Must reset file position since we read more than the standard header above
1340 LARGE_INTEGER filePos = { sizeof(uint32_t) + sizeof(DDS_HEADER), 0};
1341 if ( !SetFilePointerEx( hFile.get(), filePos, 0, FILE_BEGIN ) )
1343 return HRESULT_FROM_WIN32( GetLastError() );
1346 offset = sizeof(uint32_t) + sizeof(DDS_HEADER);
1349 std::unique_ptr<uint32_t[]> pal8;
1350 if ( convFlags & CONV_FLAGS_PAL8 )
1352 pal8.reset( new uint32_t[256] );
1355 return E_OUTOFMEMORY;
1358 if ( !ReadFile( hFile.get(), pal8.get(), 256 * sizeof(uint32_t), &bytesRead, 0 ) )
1360 return HRESULT_FROM_WIN32( GetLastError() );
1363 if ( bytesRead != (256 * sizeof(uint32_t)) )
1368 offset += ( 256 * sizeof(uint32_t) );
1371 DWORD remaining = fileSize.LowPart - offset;
1372 if ( remaining == 0 )
1375 hr = image.Initialize( mdata );
1379 if ( (convFlags & CONV_FLAGS_EXPAND) || (flags & DDS_FLAGS_LEGACY_DWORD) )
1381 std::unique_ptr<uint8_t[]> temp( new uint8_t[ remaining ] );
1385 return E_OUTOFMEMORY;
1388 if ( !ReadFile( hFile.get(), temp.get(), remaining, &bytesRead, 0 ) )
1391 return HRESULT_FROM_WIN32( GetLastError() );
1394 if ( bytesRead != remaining )
1400 hr = _CopyImage( temp.get(), remaining, mdata,
1401 (flags & DDS_FLAGS_LEGACY_DWORD) ? CP_FLAGS_LEGACY_DWORD : CP_FLAGS_NONE,
1402 convFlags, pal8.get(), image );
1411 if ( remaining > image.GetPixelsSize() )
1417 if ( !ReadFile( hFile.get(), image.GetPixels(), static_cast<DWORD>( image.GetPixelsSize() ), &bytesRead, 0 ) )
1420 return HRESULT_FROM_WIN32( GetLastError() );
1423 if ( convFlags & (CONV_FLAGS_SWIZZLE|CONV_FLAGS_NOALPHA) )
1425 // Swizzle/copy image in place
1426 hr = _CopyImageInPlace( convFlags, image );
1436 memcpy( metadata, &mdata, sizeof(TexMetadata) );
1442 //-------------------------------------------------------------------------------------
1443 // Save a DDS file to memory
1444 //-------------------------------------------------------------------------------------
1445 HRESULT SaveToDDSMemory( const Image* images, size_t nimages, const TexMetadata& metadata, DWORD flags, Blob& blob )
1447 if ( !images || (nimages == 0) )
1448 return E_INVALIDARG;
1450 // Determine memory required
1451 size_t required = 0;
1452 HRESULT hr = _EncodeDDSHeader( metadata, flags, 0, 0, required );
1456 for( size_t i = 0; i < nimages; ++i )
1458 required += images[ i ].slicePitch;
1459 if ( !images[ i ].pixels )
1463 assert( required > 0 );
1467 hr = blob.Initialize( required );
1471 uint8_t* pDestination = reinterpret_cast<uint8_t*>( blob.GetBufferPointer() );
1472 assert( pDestination );
1474 hr = _EncodeDDSHeader( metadata, flags, pDestination, blob.GetBufferSize(), required );
1481 size_t remaining = blob.GetBufferSize() - required;
1482 pDestination += required;
1490 switch( metadata.dimension )
1492 case DDS_DIMENSION_TEXTURE1D:
1493 case DDS_DIMENSION_TEXTURE2D:
1496 for( size_t item = 0; item < metadata.arraySize; ++item )
1498 for( size_t level = 0; level < metadata.mipLevels; ++level )
1500 if ( index >= nimages )
1506 size_t pixsize = images[ index ].slicePitch;
1507 if ( memcpy_s( pDestination, remaining, images[ index ].pixels, pixsize ) )
1512 pDestination += pixsize;
1513 remaining -= pixsize;
1521 case DDS_DIMENSION_TEXTURE3D:
1523 if ( metadata.arraySize != 1 )
1529 size_t d = metadata.depth;
1532 for( size_t level = 0; level < metadata.mipLevels; ++level )
1534 for( size_t slice = 0; slice < d; ++slice )
1536 if ( index >= nimages )
1542 size_t pixsize = images[ index ].slicePitch;
1543 if ( memcpy_s( pDestination, remaining, images[ index ].pixels, pixsize ) )
1548 pDestination += pixsize;
1549 remaining -= pixsize;
1569 //-------------------------------------------------------------------------------------
1570 // Save a DDS file to disk
1571 //-------------------------------------------------------------------------------------
1572 HRESULT SaveToDDSFile( const Image* images, size_t nimages, const TexMetadata& metadata, DWORD flags, LPCWSTR szFile )
1575 return E_INVALIDARG;
1577 // Create DDS Header
1578 const size_t MAX_HEADER_SIZE = sizeof(uint32_t) + sizeof(DDS_HEADER) + sizeof(DDS_HEADER_DXT10);
1579 uint8_t header[MAX_HEADER_SIZE];
1581 HRESULT hr = _EncodeDDSHeader( metadata, flags, header, MAX_HEADER_SIZE, required );
1585 // Create file and write header
1586 #if (_WIN32_WINNT >= 0x0602 /*_WIN32_WINNT_WIN8*/)
1587 ScopedHandle hFile( safe_handle( CreateFile2( szFile, GENERIC_WRITE, 0, CREATE_ALWAYS, 0 ) ) );
1589 ScopedHandle hFile( safe_handle( CreateFileW( szFile, GENERIC_WRITE, 0, 0, CREATE_ALWAYS, 0, 0 ) ) );
1593 return HRESULT_FROM_WIN32( GetLastError() );
1597 if ( !WriteFile( hFile.get(), header, static_cast<DWORD>( required ), &bytesWritten, 0 ) )
1599 return HRESULT_FROM_WIN32( GetLastError() );
1602 if ( bytesWritten != required )
1608 switch( metadata.dimension )
1610 case DDS_DIMENSION_TEXTURE1D:
1611 case DDS_DIMENSION_TEXTURE2D:
1614 for( size_t item = 0; item < metadata.arraySize; ++item )
1616 for( size_t level = 0; level < metadata.mipLevels; ++level, ++index )
1618 if ( index >= nimages )
1621 if ( !images[ index ].pixels )
1624 size_t pixsize = images[ index ].slicePitch;
1626 if ( !WriteFile( hFile.get(), images[ index ].pixels, static_cast<DWORD>( pixsize ), &bytesWritten, 0 ) )
1628 return HRESULT_FROM_WIN32( GetLastError() );
1631 if ( bytesWritten != pixsize )
1640 case DDS_DIMENSION_TEXTURE3D:
1642 if ( metadata.arraySize != 1 )
1645 size_t d = metadata.depth;
1648 for( size_t level = 0; level < metadata.mipLevels; ++level )
1650 for( size_t slice = 0; slice < d; ++slice, ++index )
1652 if ( index >= nimages )
1655 if ( !images[ index ].pixels )
1658 size_t pixsize = images[ index ].slicePitch;
1660 if ( !WriteFile( hFile.get(), images[ index ].pixels, static_cast<DWORD>( pixsize ), &bytesWritten, 0 ) )
1662 return HRESULT_FROM_WIN32( GetLastError() );
1665 if ( bytesWritten != pixsize )