1 //-------------------------------------------------------------------------------------
2 // DirectXTexDDS.cpp
3 //
4 // DirectX Texture Library - Microsoft DirectDraw Surface (DDS) file format reader/writer
5 //
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
9 // PARTICULAR PURPOSE.
10 //
11 // Copyright (c) Microsoft Corporation. All rights reserved.
12 //
13 // http://go.microsoft.com/fwlink/?LinkId=248926
14 //-------------------------------------------------------------------------------------
15
16 #include "DirectXTexP.h"
17
18 #include "DDS.h"
19
20 namespace DirectX
21 {
22
23 //-------------------------------------------------------------------------------------
24 // Legacy format mapping table (used for DDS files without 'DX10' extended header)
25 //-------------------------------------------------------------------------------------
26 enum CONVERSION_FLAGS
27 {
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
42 CONV_FLAGS_PMALPHA = 0x20000, // Contains premultiplied alpha data
43 CONV_FLAGS_L8 = 0x40000, // Source is a 8 luminance format
44 CONV_FLAGS_L16 = 0x80000, // Source is a 16 luminance format
45 CONV_FLAGS_A8L8 = 0x100000, // Source is a 8:8 luminance format
46 };
47
48 struct LegacyDDS
49 {
50 DXGI_FORMAT format;
51 DWORD convFlags;
52 DDS_PIXELFORMAT ddpf;
53 };
54
55 const LegacyDDS g_LegacyDDSMap[] =
56 {
57 { DXGI_FORMAT_BC1_UNORM, CONV_FLAGS_NONE, DDSPF_DXT1 }, // D3DFMT_DXT1
58 { DXGI_FORMAT_BC2_UNORM, CONV_FLAGS_NONE, DDSPF_DXT3 }, // D3DFMT_DXT3
59 { DXGI_FORMAT_BC3_UNORM, CONV_FLAGS_NONE, DDSPF_DXT5 }, // D3DFMT_DXT5
60
61 { DXGI_FORMAT_BC2_UNORM, CONV_FLAGS_PMALPHA, DDSPF_DXT2 }, // D3DFMT_DXT2
62 { DXGI_FORMAT_BC3_UNORM, CONV_FLAGS_PMALPHA, DDSPF_DXT4 }, // D3DFMT_DXT4
63
64 { DXGI_FORMAT_BC4_UNORM, CONV_FLAGS_NONE, DDSPF_BC4_UNORM },
65 { DXGI_FORMAT_BC4_SNORM, CONV_FLAGS_NONE, DDSPF_BC4_SNORM },
66 { DXGI_FORMAT_BC5_UNORM, CONV_FLAGS_NONE, DDSPF_BC5_UNORM },
67 { DXGI_FORMAT_BC5_SNORM, CONV_FLAGS_NONE, DDSPF_BC5_SNORM },
68
69 { DXGI_FORMAT_BC4_UNORM, CONV_FLAGS_NONE, { sizeof(DDS_PIXELFORMAT), DDS_FOURCC, MAKEFOURCC( 'A', 'T', 'I', '1' ), 0, 0, 0, 0, 0 } },
70 { DXGI_FORMAT_BC5_UNORM, CONV_FLAGS_NONE, { sizeof(DDS_PIXELFORMAT), DDS_FOURCC, MAKEFOURCC( 'A', 'T', 'I', '2' ), 0, 0, 0, 0, 0 } },
71
72 { DXGI_FORMAT_R8G8_B8G8_UNORM, CONV_FLAGS_NONE, DDSPF_R8G8_B8G8 }, // D3DFMT_R8G8_B8G8
73 { DXGI_FORMAT_G8R8_G8B8_UNORM, CONV_FLAGS_NONE, DDSPF_G8R8_G8B8 }, // D3DFMT_G8R8_G8B8
74
75 { DXGI_FORMAT_B8G8R8A8_UNORM, CONV_FLAGS_NONE, DDSPF_A8R8G8B8 }, // D3DFMT_A8R8G8B8 (uses DXGI 1.1 format)
76 { DXGI_FORMAT_B8G8R8X8_UNORM, CONV_FLAGS_NONE, DDSPF_X8R8G8B8 }, // D3DFMT_X8R8G8B8 (uses DXGI 1.1 format)
77 { DXGI_FORMAT_R8G8B8A8_UNORM, CONV_FLAGS_NONE, DDSPF_A8B8G8R8 }, // D3DFMT_A8B8G8R8
78 { DXGI_FORMAT_R8G8B8A8_UNORM, CONV_FLAGS_NOALPHA, DDSPF_X8B8G8R8 }, // D3DFMT_X8B8G8R8
79 { DXGI_FORMAT_R16G16_UNORM, CONV_FLAGS_NONE, DDSPF_G16R16 }, // D3DFMT_G16R16
80
81 { DXGI_FORMAT_R10G10B10A2_UNORM, CONV_FLAGS_SWIZZLE, { sizeof(DDS_PIXELFORMAT), DDS_RGB, 0, 32, 0x000003ff, 0x000ffc00, 0x3ff00000, 0xc0000000 } }, // D3DFMT_A2R10G10B10 (D3DX reversal issue workaround)
82 { DXGI_FORMAT_R10G10B10A2_UNORM, CONV_FLAGS_NONE, { sizeof(DDS_PIXELFORMAT), DDS_RGB, 0, 32, 0x3ff00000, 0x000ffc00, 0x000003ff, 0xc0000000 } }, // D3DFMT_A2B10G10R10 (D3DX reversal issue workaround)
83
84 { DXGI_FORMAT_R8G8B8A8_UNORM, CONV_FLAGS_EXPAND
85 | CONV_FLAGS_NOALPHA
86 | CONV_FLAGS_888, DDSPF_R8G8B8 }, // D3DFMT_R8G8B8
87
88 { DXGI_FORMAT_B5G6R5_UNORM, CONV_FLAGS_565, DDSPF_R5G6B5 }, // D3DFMT_R5G6B5
89 { DXGI_FORMAT_B5G5R5A1_UNORM, CONV_FLAGS_5551, DDSPF_A1R5G5B5 }, // D3DFMT_A1R5G5B5
90 { DXGI_FORMAT_B5G5R5A1_UNORM, CONV_FLAGS_5551
91 | CONV_FLAGS_NOALPHA, { sizeof(DDS_PIXELFORMAT), DDS_RGB, 0, 16, 0x7c00, 0x03e0, 0x001f, 0x0000 } }, // D3DFMT_X1R5G5B5
92
93 { DXGI_FORMAT_R8G8B8A8_UNORM, CONV_FLAGS_EXPAND
94 | CONV_FLAGS_8332, { sizeof(DDS_PIXELFORMAT), DDS_RGB, 0, 16, 0x00e0, 0x001c, 0x0003, 0xff00 } }, // D3DFMT_A8R3G3B2
95 { DXGI_FORMAT_B5G6R5_UNORM, CONV_FLAGS_EXPAND
96 | CONV_FLAGS_332, { sizeof(DDS_PIXELFORMAT), DDS_RGB, 0, 8, 0xe0, 0x1c, 0x03, 0x00 } }, // D3DFMT_R3G3B2
97
98 { DXGI_FORMAT_R8_UNORM, CONV_FLAGS_NONE, DDSPF_L8 }, // D3DFMT_L8
99 { DXGI_FORMAT_R16_UNORM, CONV_FLAGS_NONE, DDSPF_L16 }, // D3DFMT_L16
100 { DXGI_FORMAT_R8G8_UNORM, CONV_FLAGS_NONE, DDSPF_A8L8 }, // D3DFMT_A8L8
101
102 { DXGI_FORMAT_A8_UNORM, CONV_FLAGS_NONE, DDSPF_A8 }, // D3DFMT_A8
103
104 { DXGI_FORMAT_R16G16B16A16_UNORM, CONV_FLAGS_NONE, { sizeof(DDS_PIXELFORMAT), DDS_FOURCC, 36, 0, 0, 0, 0, 0 } }, // D3DFMT_A16B16G16R16
105 { DXGI_FORMAT_R16G16B16A16_SNORM, CONV_FLAGS_NONE, { sizeof(DDS_PIXELFORMAT), DDS_FOURCC, 110, 0, 0, 0, 0, 0 } }, // D3DFMT_Q16W16V16U16
106 { DXGI_FORMAT_R16_FLOAT, CONV_FLAGS_NONE, { sizeof(DDS_PIXELFORMAT), DDS_FOURCC, 111, 0, 0, 0, 0, 0 } }, // D3DFMT_R16F
107 { DXGI_FORMAT_R16G16_FLOAT, CONV_FLAGS_NONE, { sizeof(DDS_PIXELFORMAT), DDS_FOURCC, 112, 0, 0, 0, 0, 0 } }, // D3DFMT_G16R16F
108 { DXGI_FORMAT_R16G16B16A16_FLOAT, CONV_FLAGS_NONE, { sizeof(DDS_PIXELFORMAT), DDS_FOURCC, 113, 0, 0, 0, 0, 0 } }, // D3DFMT_A16B16G16R16F
109 { DXGI_FORMAT_R32_FLOAT, CONV_FLAGS_NONE, { sizeof(DDS_PIXELFORMAT), DDS_FOURCC, 114, 0, 0, 0, 0, 0 } }, // D3DFMT_R32F
110 { DXGI_FORMAT_R32G32_FLOAT, CONV_FLAGS_NONE, { sizeof(DDS_PIXELFORMAT), DDS_FOURCC, 115, 0, 0, 0, 0, 0 } }, // D3DFMT_G32R32F
111 { DXGI_FORMAT_R32G32B32A32_FLOAT, CONV_FLAGS_NONE, { sizeof(DDS_PIXELFORMAT), DDS_FOURCC, 116, 0, 0, 0, 0, 0 } }, // D3DFMT_A32B32G32R32F
112
113 { 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)
114
115 { DXGI_FORMAT_R8G8B8A8_UNORM, CONV_FLAGS_EXPAND
116 | CONV_FLAGS_PAL8
117 | CONV_FLAGS_A8P8, { sizeof(DDS_PIXELFORMAT), DDS_PAL8, 0, 16, 0, 0, 0, 0 } }, // D3DFMT_A8P8
118 { DXGI_FORMAT_R8G8B8A8_UNORM, CONV_FLAGS_EXPAND
119 | CONV_FLAGS_PAL8, { sizeof(DDS_PIXELFORMAT), DDS_PAL8, 0, 8, 0, 0, 0, 0 } }, // D3DFMT_P8
120
121 { DXGI_FORMAT_B4G4R4A4_UNORM, CONV_FLAGS_4444, DDSPF_A4R4G4B4 }, // D3DFMT_A4R4G4B4 (uses DXGI 1.2 format)
122 { DXGI_FORMAT_B4G4R4A4_UNORM, CONV_FLAGS_NOALPHA
123 | CONV_FLAGS_4444, { sizeof(DDS_PIXELFORMAT), DDS_RGB, 0, 16, 0x0f00, 0x00f0, 0x000f, 0x0000 } }, // D3DFMT_X4R4G4B4 (uses DXGI 1.2 format)
124 { DXGI_FORMAT_B4G4R4A4_UNORM, CONV_FLAGS_EXPAND
125 | CONV_FLAGS_44, { sizeof(DDS_PIXELFORMAT), DDS_LUMINANCE, 0, 8, 0x0f, 0x00, 0x00, 0xf0 } }, // D3DFMT_A4L4 (uses DXGI 1.2 format)
126
127 { DXGI_FORMAT_YUY2, CONV_FLAGS_NONE, DDSPF_YUY2 }, // D3DFMT_YUY2 (uses DXGI 1.2 format)
128 { DXGI_FORMAT_YUY2, CONV_FLAGS_SWIZZLE, { sizeof(DDS_PIXELFORMAT), DDS_FOURCC, MAKEFOURCC('U','Y','V','Y'), 0, 0, 0, 0, 0 } }, // D3DFMT_UYVY (uses DXGI 1.2 format)
129 };
130
131 // Note that many common DDS reader/writers (including D3DX) swap the
132 // the RED/BLUE masks for 10:10:10:2 formats. We assumme
133 // below that the 'backwards' header mask is being used since it is most
134 // likely written by D3DX. The more robust solution is to use the 'DX10'
135 // header extension and specify the DXGI_FORMAT_R10G10B10A2_UNORM format directly
136
137 // We do not support the following legacy Direct3D 9 formats:
138 // BumpDuDv D3DFMT_V8U8, D3DFMT_Q8W8V8U8, D3DFMT_V16U16, D3DFMT_A2W10V10U10
139 // BumpLuminance D3DFMT_L6V5U5, D3DFMT_X8L8V8U8
140 // FourCC 117 D3DFMT_CxV8U8
141 // ZBuffer D3DFMT_D16_LOCKABLE
142 // FourCC 82 D3DFMT_D32F_LOCKABLE
143
_GetDXGIFormat(const DDS_PIXELFORMAT & ddpf,DWORD flags,_Inout_ DWORD & convFlags)144 static DXGI_FORMAT _GetDXGIFormat( const DDS_PIXELFORMAT& ddpf, DWORD flags, _Inout_ DWORD& convFlags )
145 {
146 const size_t MAP_SIZE = sizeof(g_LegacyDDSMap) / sizeof(LegacyDDS);
147 size_t index = 0;
148 for( index = 0; index < MAP_SIZE; ++index )
149 {
150 const LegacyDDS* entry = &g_LegacyDDSMap[index];
151
152 if ( ddpf.dwFlags & entry->ddpf.dwFlags )
153 {
154 if ( entry->ddpf.dwFlags & DDS_FOURCC )
155 {
156 if ( ddpf.dwFourCC == entry->ddpf.dwFourCC )
157 break;
158 }
159 else if ( entry->ddpf.dwFlags & DDS_PAL8 )
160 {
161 if ( ddpf.dwRGBBitCount == entry->ddpf.dwRGBBitCount )
162 break;
163 }
164 else if ( ddpf.dwRGBBitCount == entry->ddpf.dwRGBBitCount )
165 {
166 // RGB, RGBA, ALPHA, LUMINANCE
167 if ( ddpf.dwRBitMask == entry->ddpf.dwRBitMask
168 && ddpf.dwGBitMask == entry->ddpf.dwGBitMask
169 && ddpf.dwBBitMask == entry->ddpf.dwBBitMask
170 && ddpf.dwABitMask == entry->ddpf.dwABitMask )
171 break;
172 }
173 }
174 }
175
176 if ( index >= MAP_SIZE )
177 return DXGI_FORMAT_UNKNOWN;
178
179 DWORD cflags = g_LegacyDDSMap[index].convFlags;
180 DXGI_FORMAT format = g_LegacyDDSMap[index].format;
181
182 if ( (cflags & CONV_FLAGS_EXPAND) && (flags & DDS_FLAGS_NO_LEGACY_EXPANSION) )
183 return DXGI_FORMAT_UNKNOWN;
184
185 if ( (format == DXGI_FORMAT_R10G10B10A2_UNORM) && (flags & DDS_FLAGS_NO_R10B10G10A2_FIXUP) )
186 {
187 cflags ^= CONV_FLAGS_SWIZZLE;
188 }
189
190 convFlags = cflags;
191
192 return format;
193 }
194
195
196 //-------------------------------------------------------------------------------------
197 // Decodes DDS header including optional DX10 extended header
198 //-------------------------------------------------------------------------------------
_DecodeDDSHeader(_In_reads_bytes_ (size)LPCVOID pSource,size_t size,DWORD flags,_Out_ TexMetadata & metadata,_Inout_ DWORD & convFlags)199 static HRESULT _DecodeDDSHeader( _In_reads_bytes_(size) LPCVOID pSource, size_t size, DWORD flags, _Out_ TexMetadata& metadata,
200 _Inout_ DWORD& convFlags )
201 {
202 if ( !pSource )
203 return E_INVALIDARG;
204
205 memset( &metadata, 0, sizeof(TexMetadata) );
206
207 if ( size < (sizeof(DDS_HEADER) + sizeof(uint32_t)) )
208 {
209 return HRESULT_FROM_WIN32( ERROR_INVALID_DATA );
210 }
211
212 // DDS files always start with the same magic number ("DDS ")
213 uint32_t dwMagicNumber = *reinterpret_cast<const uint32_t*>(pSource);
214 if ( dwMagicNumber != DDS_MAGIC )
215 {
216 return E_FAIL;
217 }
218
219 auto pHeader = reinterpret_cast<const DDS_HEADER*>( (const uint8_t*)pSource + sizeof( uint32_t ) );
220
221 // Verify header to validate DDS file
222 if ( pHeader->dwSize != sizeof(DDS_HEADER)
223 || pHeader->ddspf.dwSize != sizeof(DDS_PIXELFORMAT) )
224 {
225 return E_FAIL;
226 }
227
228 metadata.mipLevels = pHeader->dwMipMapCount;
229 if ( metadata.mipLevels == 0 )
230 metadata.mipLevels = 1;
231
232 // Check for DX10 extension
233 if ( (pHeader->ddspf.dwFlags & DDS_FOURCC)
234 && (MAKEFOURCC( 'D', 'X', '1', '0' ) == pHeader->ddspf.dwFourCC) )
235 {
236 // Buffer must be big enough for both headers and magic value
237 if ( size < ( sizeof(DDS_HEADER) + sizeof(uint32_t) + sizeof(DDS_HEADER_DXT10) ) )
238 {
239 return E_FAIL;
240 }
241
242 auto d3d10ext = reinterpret_cast<const DDS_HEADER_DXT10*>( (const uint8_t*)pSource + sizeof( uint32_t ) + sizeof(DDS_HEADER) );
243 convFlags |= CONV_FLAGS_DX10;
244
245 metadata.arraySize = d3d10ext->arraySize;
246 if ( metadata.arraySize == 0 )
247 {
248 return HRESULT_FROM_WIN32( ERROR_INVALID_DATA );
249 }
250
251 metadata.format = d3d10ext->dxgiFormat;
252 if ( !IsValid( metadata.format ) || IsPalettized( metadata.format ) )
253 {
254 return HRESULT_FROM_WIN32( ERROR_NOT_SUPPORTED );
255 }
256
257 static_assert( TEX_MISC_TEXTURECUBE == DDS_RESOURCE_MISC_TEXTURECUBE, "DDS header mismatch");
258
259 metadata.miscFlags = d3d10ext->miscFlag & ~TEX_MISC_TEXTURECUBE;
260
261 switch ( d3d10ext->resourceDimension )
262 {
263 case DDS_DIMENSION_TEXTURE1D:
264
265 // D3DX writes 1D textures with a fixed Height of 1
266 if ( (pHeader->dwFlags & DDS_HEIGHT) && pHeader->dwHeight != 1 )
267 {
268 return HRESULT_FROM_WIN32( ERROR_INVALID_DATA );
269 }
270
271 metadata.width = pHeader->dwWidth;
272 metadata.height = 1;
273 metadata.depth = 1;
274 metadata.dimension = TEX_DIMENSION_TEXTURE1D;
275 break;
276
277 case DDS_DIMENSION_TEXTURE2D:
278 if ( d3d10ext->miscFlag & DDS_RESOURCE_MISC_TEXTURECUBE )
279 {
280 metadata.miscFlags |= TEX_MISC_TEXTURECUBE;
281 metadata.arraySize *= 6;
282 }
283
284 metadata.width = pHeader->dwWidth;
285 metadata.height = pHeader->dwHeight;
286 metadata.depth = 1;
287 metadata.dimension = TEX_DIMENSION_TEXTURE2D;
288 break;
289
290 case DDS_DIMENSION_TEXTURE3D:
291 if ( !(pHeader->dwFlags & DDS_HEADER_FLAGS_VOLUME) )
292 {
293 return HRESULT_FROM_WIN32( ERROR_INVALID_DATA );
294 }
295
296 if ( metadata.arraySize > 1 )
297 return HRESULT_FROM_WIN32( ERROR_NOT_SUPPORTED );
298
299 metadata.width = pHeader->dwWidth;
300 metadata.height = pHeader->dwHeight;
301 metadata.depth = pHeader->dwDepth;
302 metadata.dimension = TEX_DIMENSION_TEXTURE3D;
303 break;
304
305 default:
306 return HRESULT_FROM_WIN32( ERROR_INVALID_DATA );
307 }
308
309 static_assert( TEX_MISC2_ALPHA_MODE_MASK == DDS_MISC_FLAGS2_ALPHA_MODE_MASK, "DDS header mismatch");
310
311 static_assert( TEX_ALPHA_MODE_UNKNOWN == DDS_ALPHA_MODE_UNKNOWN, "DDS header mismatch");
312 static_assert( TEX_ALPHA_MODE_STRAIGHT == DDS_ALPHA_MODE_STRAIGHT, "DDS header mismatch");
313 static_assert( TEX_ALPHA_MODE_PREMULTIPLIED == DDS_ALPHA_MODE_PREMULTIPLIED, "DDS header mismatch");
314 static_assert( TEX_ALPHA_MODE_OPAQUE == DDS_ALPHA_MODE_OPAQUE, "DDS header mismatch");
315 static_assert( TEX_ALPHA_MODE_CUSTOM == DDS_ALPHA_MODE_CUSTOM, "DDS header mismatch");
316
317 metadata.miscFlags2 = d3d10ext->miscFlags2;
318 }
319 else
320 {
321 metadata.arraySize = 1;
322
323 if ( pHeader->dwFlags & DDS_HEADER_FLAGS_VOLUME )
324 {
325 metadata.width = pHeader->dwWidth;
326 metadata.height = pHeader->dwHeight;
327 metadata.depth = pHeader->dwDepth;
328 metadata.dimension = TEX_DIMENSION_TEXTURE3D;
329 }
330 else
331 {
332 if ( pHeader->dwCaps2 & DDS_CUBEMAP )
333 {
334 // We require all six faces to be defined
335 if ( (pHeader->dwCaps2 & DDS_CUBEMAP_ALLFACES ) != DDS_CUBEMAP_ALLFACES )
336 return HRESULT_FROM_WIN32( ERROR_NOT_SUPPORTED );
337
338 metadata.arraySize = 6;
339 metadata.miscFlags |= TEX_MISC_TEXTURECUBE;
340 }
341
342 metadata.width = pHeader->dwWidth;
343 metadata.height = pHeader->dwHeight;
344 metadata.depth = 1;
345 metadata.dimension = TEX_DIMENSION_TEXTURE2D;
346
347 // Note there's no way for a legacy Direct3D 9 DDS to express a '1D' texture
348 }
349
350 metadata.format = _GetDXGIFormat( pHeader->ddspf, flags, convFlags );
351
352 if ( metadata.format == DXGI_FORMAT_UNKNOWN )
353 return HRESULT_FROM_WIN32( ERROR_NOT_SUPPORTED );
354
355 if ( convFlags & CONV_FLAGS_PMALPHA )
356 metadata.miscFlags2 |= TEX_ALPHA_MODE_PREMULTIPLIED;
357
358 // Special flag for handling LUMINANCE legacy formats
359 if ( flags & DDS_FLAGS_EXPAND_LUMINANCE )
360 {
361 switch ( metadata.format )
362 {
363 case DXGI_FORMAT_R8_UNORM:
364 metadata.format = DXGI_FORMAT_R8G8B8A8_UNORM;
365 convFlags |= CONV_FLAGS_L8 | CONV_FLAGS_EXPAND;
366 break;
367
368 case DXGI_FORMAT_R8G8_UNORM:
369 metadata.format = DXGI_FORMAT_R8G8B8A8_UNORM;
370 convFlags |= CONV_FLAGS_A8L8 | CONV_FLAGS_EXPAND;
371 break;
372
373 case DXGI_FORMAT_R16_UNORM:
374 metadata.format = DXGI_FORMAT_R16G16B16A16_UNORM;
375 convFlags |= CONV_FLAGS_L16 | CONV_FLAGS_EXPAND;
376 break;
377 }
378 }
379 }
380
381 // Special flag for handling BGR DXGI 1.1 formats
382 if (flags & DDS_FLAGS_FORCE_RGB)
383 {
384 switch ( metadata.format )
385 {
386 case DXGI_FORMAT_B8G8R8A8_UNORM:
387 metadata.format = DXGI_FORMAT_R8G8B8A8_UNORM;
388 convFlags |= CONV_FLAGS_SWIZZLE;
389 break;
390
391 case DXGI_FORMAT_B8G8R8X8_UNORM:
392 metadata.format = DXGI_FORMAT_R8G8B8A8_UNORM;
393 convFlags |= CONV_FLAGS_SWIZZLE | CONV_FLAGS_NOALPHA;
394 break;
395
396 case DXGI_FORMAT_B8G8R8A8_TYPELESS:
397 metadata.format = DXGI_FORMAT_R8G8B8A8_TYPELESS;
398 convFlags |= CONV_FLAGS_SWIZZLE;
399 break;
400
401 case DXGI_FORMAT_B8G8R8A8_UNORM_SRGB:
402 metadata.format = DXGI_FORMAT_R8G8B8A8_UNORM_SRGB;
403 convFlags |= CONV_FLAGS_SWIZZLE;
404 break;
405
406 case DXGI_FORMAT_B8G8R8X8_TYPELESS:
407 metadata.format = DXGI_FORMAT_R8G8B8A8_TYPELESS;
408 convFlags |= CONV_FLAGS_SWIZZLE | CONV_FLAGS_NOALPHA;
409 break;
410
411 case DXGI_FORMAT_B8G8R8X8_UNORM_SRGB:
412 metadata.format = DXGI_FORMAT_R8G8B8A8_UNORM_SRGB;
413 convFlags |= CONV_FLAGS_SWIZZLE | CONV_FLAGS_NOALPHA;
414 break;
415 }
416 }
417
418 // Special flag for handling 16bpp formats
419 if (flags & DDS_FLAGS_NO_16BPP)
420 {
421 switch ( metadata.format )
422 {
423 case DXGI_FORMAT_B5G6R5_UNORM:
424 case DXGI_FORMAT_B5G5R5A1_UNORM:
425 case DXGI_FORMAT_B4G4R4A4_UNORM:
426 metadata.format = DXGI_FORMAT_R8G8B8A8_UNORM;
427 convFlags |= CONV_FLAGS_EXPAND;
428 if ( metadata.format == DXGI_FORMAT_B5G6R5_UNORM )
429 convFlags |= CONV_FLAGS_NOALPHA;
430 }
431 }
432
433 return S_OK;
434 }
435
436
437 //-------------------------------------------------------------------------------------
438 // Encodes DDS file header (magic value, header, optional DX10 extended header)
439 //-------------------------------------------------------------------------------------
440 _Use_decl_annotations_
_EncodeDDSHeader(const TexMetadata & metadata,DWORD flags,LPVOID pDestination,size_t maxsize,size_t & required)441 HRESULT _EncodeDDSHeader( const TexMetadata& metadata, DWORD flags,
442 LPVOID pDestination, size_t maxsize, size_t& required )
443 {
444 if ( !IsValid( metadata.format ) )
445 return E_INVALIDARG;
446
447 if ( IsPalettized( metadata.format ) )
448 return HRESULT_FROM_WIN32( ERROR_NOT_SUPPORTED );
449
450 if ( metadata.arraySize > 1 )
451 {
452 if ( (metadata.arraySize != 6) || (metadata.dimension != TEX_DIMENSION_TEXTURE2D) || !(metadata.IsCubemap()) )
453 {
454 // Texture1D arrays, Texture2D arrays, and Cubemap arrays must be stored using 'DX10' extended header
455 flags |= DDS_FLAGS_FORCE_DX10_EXT;
456 }
457 }
458
459 if ( flags & DDS_FLAGS_FORCE_DX10_EXT_MISC2 )
460 {
461 flags |= DDS_FLAGS_FORCE_DX10_EXT;
462 }
463
464 DDS_PIXELFORMAT ddpf = { 0 };
465 if ( !(flags & DDS_FLAGS_FORCE_DX10_EXT) )
466 {
467 switch( metadata.format )
468 {
469 case DXGI_FORMAT_R8G8B8A8_UNORM: memcpy_s( &ddpf, sizeof(ddpf), &DDSPF_A8B8G8R8, sizeof(DDS_PIXELFORMAT) ); break;
470 case DXGI_FORMAT_R16G16_UNORM: memcpy_s( &ddpf, sizeof(ddpf), &DDSPF_G16R16, sizeof(DDS_PIXELFORMAT) ); break;
471 case DXGI_FORMAT_R8G8_UNORM: memcpy_s( &ddpf, sizeof(ddpf), &DDSPF_A8L8, sizeof(DDS_PIXELFORMAT) ); break;
472 case DXGI_FORMAT_R16_UNORM: memcpy_s( &ddpf, sizeof(ddpf), &DDSPF_L16, sizeof(DDS_PIXELFORMAT) ); break;
473 case DXGI_FORMAT_R8_UNORM: memcpy_s( &ddpf, sizeof(ddpf), &DDSPF_L8, sizeof(DDS_PIXELFORMAT) ); break;
474 case DXGI_FORMAT_A8_UNORM: memcpy_s( &ddpf, sizeof(ddpf), &DDSPF_A8, sizeof(DDS_PIXELFORMAT) ); break;
475 case DXGI_FORMAT_R8G8_B8G8_UNORM: memcpy_s( &ddpf, sizeof(ddpf), &DDSPF_R8G8_B8G8, sizeof(DDS_PIXELFORMAT) ); break;
476 case DXGI_FORMAT_G8R8_G8B8_UNORM: memcpy_s( &ddpf, sizeof(ddpf), &DDSPF_G8R8_G8B8, sizeof(DDS_PIXELFORMAT) ); break;
477 case DXGI_FORMAT_BC1_UNORM: memcpy_s( &ddpf, sizeof(ddpf), &DDSPF_DXT1, sizeof(DDS_PIXELFORMAT) ); break;
478 case DXGI_FORMAT_BC2_UNORM: memcpy_s( &ddpf, sizeof(ddpf), metadata.IsPMAlpha() ? (&DDSPF_DXT2) : (&DDSPF_DXT3), sizeof(DDS_PIXELFORMAT) ); break;
479 case DXGI_FORMAT_BC3_UNORM: memcpy_s( &ddpf, sizeof(ddpf), metadata.IsPMAlpha() ? (&DDSPF_DXT4) : (&DDSPF_DXT5), sizeof(DDS_PIXELFORMAT) ); break;
480 case DXGI_FORMAT_BC4_UNORM: memcpy_s( &ddpf, sizeof(ddpf), &DDSPF_BC4_UNORM, sizeof(DDS_PIXELFORMAT) ); break;
481 case DXGI_FORMAT_BC4_SNORM: memcpy_s( &ddpf, sizeof(ddpf), &DDSPF_BC4_SNORM, sizeof(DDS_PIXELFORMAT) ); break;
482 case DXGI_FORMAT_BC5_UNORM: memcpy_s( &ddpf, sizeof(ddpf), &DDSPF_BC5_UNORM, sizeof(DDS_PIXELFORMAT) ); break;
483 case DXGI_FORMAT_BC5_SNORM: memcpy_s( &ddpf, sizeof(ddpf), &DDSPF_BC5_SNORM, sizeof(DDS_PIXELFORMAT) ); break;
484 case DXGI_FORMAT_B5G6R5_UNORM: memcpy_s( &ddpf, sizeof(ddpf), &DDSPF_R5G6B5, sizeof(DDS_PIXELFORMAT) ); break;
485 case DXGI_FORMAT_B5G5R5A1_UNORM: memcpy_s( &ddpf, sizeof(ddpf), &DDSPF_A1R5G5B5, sizeof(DDS_PIXELFORMAT) ); break;
486 case DXGI_FORMAT_B8G8R8A8_UNORM: memcpy_s( &ddpf, sizeof(ddpf), &DDSPF_A8R8G8B8, sizeof(DDS_PIXELFORMAT) ); break; // DXGI 1.1
487 case DXGI_FORMAT_B8G8R8X8_UNORM: memcpy_s( &ddpf, sizeof(ddpf), &DDSPF_X8R8G8B8, sizeof(DDS_PIXELFORMAT) ); break; // DXGI 1.1
488 case DXGI_FORMAT_B4G4R4A4_UNORM: memcpy_s( &ddpf, sizeof(ddpf), &DDSPF_A4R4G4B4, sizeof(DDS_PIXELFORMAT) ); break; // DXGI 1.2
489 case DXGI_FORMAT_YUY2: memcpy_s( &ddpf, sizeof(ddpf), &DDSPF_YUY2, sizeof(DDS_PIXELFORMAT) ); break; // DXGI 1.2
490
491 // Legacy D3DX formats using D3DFMT enum value as FourCC
492 case DXGI_FORMAT_R32G32B32A32_FLOAT:
493 ddpf.dwSize = sizeof(DDS_PIXELFORMAT); ddpf.dwFlags = DDS_FOURCC; ddpf.dwFourCC = 116; // D3DFMT_A32B32G32R32F
494 break;
495 case DXGI_FORMAT_R16G16B16A16_FLOAT:
496 ddpf.dwSize = sizeof(DDS_PIXELFORMAT); ddpf.dwFlags = DDS_FOURCC; ddpf.dwFourCC = 113; // D3DFMT_A16B16G16R16F
497 break;
498 case DXGI_FORMAT_R16G16B16A16_UNORM:
499 ddpf.dwSize = sizeof(DDS_PIXELFORMAT); ddpf.dwFlags = DDS_FOURCC; ddpf.dwFourCC = 36; // D3DFMT_A16B16G16R16
500 break;
501 case DXGI_FORMAT_R16G16B16A16_SNORM:
502 ddpf.dwSize = sizeof(DDS_PIXELFORMAT); ddpf.dwFlags = DDS_FOURCC; ddpf.dwFourCC = 110; // D3DFMT_Q16W16V16U16
503 break;
504 case DXGI_FORMAT_R32G32_FLOAT:
505 ddpf.dwSize = sizeof(DDS_PIXELFORMAT); ddpf.dwFlags = DDS_FOURCC; ddpf.dwFourCC = 115; // D3DFMT_G32R32F
506 break;
507 case DXGI_FORMAT_R16G16_FLOAT:
508 ddpf.dwSize = sizeof(DDS_PIXELFORMAT); ddpf.dwFlags = DDS_FOURCC; ddpf.dwFourCC = 112; // D3DFMT_G16R16F
509 break;
510 case DXGI_FORMAT_R32_FLOAT:
511 ddpf.dwSize = sizeof(DDS_PIXELFORMAT); ddpf.dwFlags = DDS_FOURCC; ddpf.dwFourCC = 114; // D3DFMT_R32F
512 break;
513 case DXGI_FORMAT_R16_FLOAT:
514 ddpf.dwSize = sizeof(DDS_PIXELFORMAT); ddpf.dwFlags = DDS_FOURCC; ddpf.dwFourCC = 111; // D3DFMT_R16F
515 break;
516 }
517 }
518
519 required = sizeof(uint32_t) + sizeof(DDS_HEADER);
520
521 if ( ddpf.dwSize == 0 )
522 required += sizeof(DDS_HEADER_DXT10);
523
524 if ( !pDestination )
525 return S_OK;
526
527 if ( maxsize < required )
528 return E_NOT_SUFFICIENT_BUFFER;
529
530 *reinterpret_cast<uint32_t*>(pDestination) = DDS_MAGIC;
531
532 auto header = reinterpret_cast<DDS_HEADER*>( reinterpret_cast<uint8_t*>(pDestination) + sizeof(uint32_t) );
533 assert( header );
534
535 memset( header, 0, sizeof(DDS_HEADER ) );
536 header->dwSize = sizeof( DDS_HEADER );
537 header->dwFlags = DDS_HEADER_FLAGS_TEXTURE;
538 header->dwCaps = DDS_SURFACE_FLAGS_TEXTURE;
539
540 if (metadata.mipLevels > 0)
541 {
542 header->dwFlags |= DDS_HEADER_FLAGS_MIPMAP;
543
544 #ifdef _M_X64
545 if ( metadata.mipLevels > 0xFFFFFFFF )
546 return E_INVALIDARG;
547 #endif
548
549 header->dwMipMapCount = static_cast<uint32_t>( metadata.mipLevels );
550
551 if ( header->dwMipMapCount > 1 )
552 header->dwCaps |= DDS_SURFACE_FLAGS_MIPMAP;
553 }
554
555 switch( metadata.dimension )
556 {
557 case TEX_DIMENSION_TEXTURE1D:
558 #ifdef _M_X64
559 if ( metadata.width > 0xFFFFFFFF )
560 return E_INVALIDARG;
561 #endif
562
563 header->dwWidth = static_cast<uint32_t>( metadata.width );
564 header->dwHeight = header->dwDepth = 1;
565 break;
566
567 case TEX_DIMENSION_TEXTURE2D:
568 #ifdef _M_X64
569 if ( metadata.height > 0xFFFFFFFF
570 || metadata.width > 0xFFFFFFFF)
571 return E_INVALIDARG;
572 #endif
573
574 header->dwHeight = static_cast<uint32_t>( metadata.height );
575 header->dwWidth = static_cast<uint32_t>( metadata.width );
576 header->dwDepth = 1;
577
578 if ( metadata.IsCubemap() )
579 {
580 header->dwCaps |= DDS_SURFACE_FLAGS_CUBEMAP;
581 header->dwCaps2 |= DDS_CUBEMAP_ALLFACES;
582 }
583 break;
584
585 case TEX_DIMENSION_TEXTURE3D:
586 #ifdef _M_X64
587 if ( metadata.height > 0xFFFFFFFF
588 || metadata.width > 0xFFFFFFFF
589 || metadata.depth > 0xFFFFFFFF )
590 return E_INVALIDARG;
591 #endif
592
593 header->dwFlags |= DDS_HEADER_FLAGS_VOLUME;
594 header->dwCaps2 |= DDS_FLAGS_VOLUME;
595 header->dwHeight = static_cast<uint32_t>( metadata.height );
596 header->dwWidth = static_cast<uint32_t>( metadata.width );
597 header->dwDepth = static_cast<uint32_t>( metadata.depth );
598 break;
599
600 default:
601 return E_FAIL;
602 }
603
604 size_t rowPitch, slicePitch;
605 ComputePitch( metadata.format, metadata.width, metadata.height, rowPitch, slicePitch, CP_FLAGS_NONE );
606
607 #ifdef _M_X64
608 if ( slicePitch > 0xFFFFFFFF
609 || rowPitch > 0xFFFFFFFF )
610 return E_FAIL;
611 #endif
612
613 if ( IsCompressed( metadata.format ) )
614 {
615 header->dwFlags |= DDS_HEADER_FLAGS_LINEARSIZE;
616 header->dwPitchOrLinearSize = static_cast<uint32_t>( slicePitch );
617 }
618 else
619 {
620 header->dwFlags |= DDS_HEADER_FLAGS_PITCH;
621 header->dwPitchOrLinearSize = static_cast<uint32_t>( rowPitch );
622 }
623
624 if ( ddpf.dwSize == 0 )
625 {
626 memcpy_s( &header->ddspf, sizeof(header->ddspf), &DDSPF_DX10, sizeof(DDS_PIXELFORMAT) );
627
628 auto ext = reinterpret_cast<DDS_HEADER_DXT10*>( reinterpret_cast<uint8_t*>(header) + sizeof(DDS_HEADER) );
629 assert( ext );
630
631 memset( ext, 0, sizeof(DDS_HEADER_DXT10) );
632 ext->dxgiFormat = metadata.format;
633 ext->resourceDimension = metadata.dimension;
634
635 #ifdef _M_X64
636 if ( metadata.arraySize > 0xFFFFFFFF )
637 return E_INVALIDARG;
638 #endif
639
640 static_assert( TEX_MISC_TEXTURECUBE == DDS_RESOURCE_MISC_TEXTURECUBE, "DDS header mismatch");
641
642 ext->miscFlag = metadata.miscFlags & ~TEX_MISC_TEXTURECUBE;
643
644 if ( metadata.miscFlags & TEX_MISC_TEXTURECUBE )
645 {
646 ext->miscFlag |= TEX_MISC_TEXTURECUBE;
647 assert( (metadata.arraySize % 6) == 0 );
648 ext->arraySize = static_cast<UINT>( metadata.arraySize / 6 );
649 }
650 else
651 {
652 ext->arraySize = static_cast<UINT>( metadata.arraySize );
653 }
654
655 static_assert( TEX_MISC2_ALPHA_MODE_MASK == DDS_MISC_FLAGS2_ALPHA_MODE_MASK, "DDS header mismatch");
656
657 static_assert( TEX_ALPHA_MODE_UNKNOWN == DDS_ALPHA_MODE_UNKNOWN, "DDS header mismatch");
658 static_assert( TEX_ALPHA_MODE_STRAIGHT == DDS_ALPHA_MODE_STRAIGHT, "DDS header mismatch");
659 static_assert( TEX_ALPHA_MODE_PREMULTIPLIED == DDS_ALPHA_MODE_PREMULTIPLIED, "DDS header mismatch");
660 static_assert( TEX_ALPHA_MODE_OPAQUE == DDS_ALPHA_MODE_OPAQUE, "DDS header mismatch");
661 static_assert( TEX_ALPHA_MODE_CUSTOM == DDS_ALPHA_MODE_CUSTOM, "DDS header mismatch");
662
663 if ( flags & DDS_FLAGS_FORCE_DX10_EXT_MISC2 )
664 {
665 // This was formerly 'reserved'. D3DX10 and D3DX11 will fail if this value is anything other than 0
666 ext->miscFlags2 = metadata.miscFlags2;
667 }
668 }
669 else
670 {
671 memcpy_s( &header->ddspf, sizeof(header->ddspf), &ddpf, sizeof(ddpf) );
672 }
673
674 return S_OK;
675 }
676
677
678 //-------------------------------------------------------------------------------------
679 // Converts an image row with optional clearing of alpha value to 1.0
680 // Returns true if supported, false if expansion case not supported
681 //-------------------------------------------------------------------------------------
682 enum TEXP_LEGACY_FORMAT
683 {
684 TEXP_LEGACY_UNKNOWN = 0,
685 TEXP_LEGACY_R8G8B8,
686 TEXP_LEGACY_R3G3B2,
687 TEXP_LEGACY_A8R3G3B2,
688 TEXP_LEGACY_P8,
689 TEXP_LEGACY_A8P8,
690 TEXP_LEGACY_A4L4,
691 TEXP_LEGACY_B4G4R4A4,
692 TEXP_LEGACY_L8,
693 TEXP_LEGACY_L16,
694 TEXP_LEGACY_A8L8
695 };
696
_FindLegacyFormat(DWORD flags)697 inline static TEXP_LEGACY_FORMAT _FindLegacyFormat( DWORD flags )
698 {
699 TEXP_LEGACY_FORMAT lformat = TEXP_LEGACY_UNKNOWN;
700
701 if ( flags & CONV_FLAGS_PAL8 )
702 {
703 lformat = ( flags & CONV_FLAGS_A8P8 ) ? TEXP_LEGACY_A8P8 : TEXP_LEGACY_P8;
704 }
705 else if ( flags & CONV_FLAGS_888 )
706 lformat = TEXP_LEGACY_R8G8B8;
707 else if ( flags & CONV_FLAGS_332 )
708 lformat = TEXP_LEGACY_R3G3B2;
709 else if ( flags & CONV_FLAGS_8332 )
710 lformat = TEXP_LEGACY_A8R3G3B2;
711 else if ( flags & CONV_FLAGS_44 )
712 lformat = TEXP_LEGACY_A4L4;
713 else if ( flags & CONV_FLAGS_4444 )
714 lformat = TEXP_LEGACY_B4G4R4A4;
715 else if ( flags & CONV_FLAGS_L8 )
716 lformat = TEXP_LEGACY_L8;
717 else if ( flags & CONV_FLAGS_L16 )
718 lformat = TEXP_LEGACY_L16;
719 else if ( flags & CONV_FLAGS_A8L8 )
720 lformat = TEXP_LEGACY_A8L8;
721
722 return lformat;
723 }
724
725 _Success_(return != false)
_Out_writes_bytes_(outSize)726 static bool _LegacyExpandScanline( _Out_writes_bytes_(outSize) LPVOID pDestination, size_t outSize, _In_ DXGI_FORMAT outFormat,
727 _In_reads_bytes_(inSize) LPCVOID pSource, size_t inSize, _In_ TEXP_LEGACY_FORMAT inFormat,
728 _In_reads_opt_(256) const uint32_t* pal8, _In_ DWORD flags )
729 {
730 assert( pDestination && outSize > 0 );
731 assert( pSource && inSize > 0 );
732 assert( IsValid(outFormat) && !IsPlanar(outFormat) && !IsPalettized(outFormat) );
733
734 switch( inFormat )
735 {
736 case TEXP_LEGACY_R8G8B8:
737 if ( outFormat != DXGI_FORMAT_R8G8B8A8_UNORM )
738 return false;
739
740 // D3DFMT_R8G8B8 -> DXGI_FORMAT_R8G8B8A8_UNORM
741 if ( inSize >= 3 && outSize >= 4 )
742 {
743 const uint8_t * __restrict sPtr = reinterpret_cast<const uint8_t*>(pSource);
744 uint32_t * __restrict dPtr = reinterpret_cast<uint32_t*>(pDestination);
745
746 for( size_t ocount = 0, icount = 0; ( ( icount < ( inSize - 2 ) ) && ( ocount < ( outSize - 3 ) ) ); icount += 3, ocount += 4 )
747 {
748 // 24bpp Direct3D 9 files are actually BGR, so need to swizzle as well
749 uint32_t t1 = ( *(sPtr) << 16 );
750 uint32_t t2 = ( *(sPtr+1) << 8 );
751 uint32_t t3 = *(sPtr+2);
752
753 *(dPtr++) = t1 | t2 | t3 | 0xff000000;
754 sPtr += 3;
755 }
756 return true;
757 }
758 return false;
759
760 case TEXP_LEGACY_R3G3B2:
761 switch( outFormat )
762 {
763 case DXGI_FORMAT_R8G8B8A8_UNORM:
764 // D3DFMT_R3G3B2 -> DXGI_FORMAT_R8G8B8A8_UNORM
765 if ( inSize >= 1 && outSize >= 4 )
766 {
767 const uint8_t* __restrict sPtr = reinterpret_cast<const uint8_t*>(pSource);
768 uint32_t * __restrict dPtr = reinterpret_cast<uint32_t*>(pDestination);
769
770 for( size_t ocount = 0, icount = 0; ( ( icount < inSize ) && ( ocount < ( outSize - 3 ) ) ); ++icount, ocount += 4 )
771 {
772 uint8_t t = *(sPtr++);
773
774 uint32_t t1 = (t & 0xe0) | ((t & 0xe0) >> 3) | ((t & 0xc0) >> 6);
775 uint32_t t2 = ((t & 0x1c) << 11) | ((t & 0x1c) << 8) | ((t & 0x18) << 5);
776 uint32_t t3 = ((t & 0x03) << 22) | ((t & 0x03) << 20) | ((t & 0x03) << 18) | ((t & 0x03) << 16);
777
778 *(dPtr++) = t1 | t2 | t3 | 0xff000000;
779 }
780 return true;
781 }
782 return false;
783
784 case DXGI_FORMAT_B5G6R5_UNORM:
785 // D3DFMT_R3G3B2 -> DXGI_FORMAT_B5G6R5_UNORM
786 if ( inSize >= 1 && outSize >= 2 )
787 {
788 const uint8_t* __restrict sPtr = reinterpret_cast<const uint8_t*>(pSource);
789 uint16_t * __restrict dPtr = reinterpret_cast<uint16_t*>(pDestination);
790
791 for( size_t ocount = 0, icount = 0; ( ( icount < inSize ) && ( ocount < ( outSize - 1 ) ) ); ++icount, ocount += 2 )
792 {
793 uint8_t t = *(sPtr++);
794
795 uint16_t t1 = ((t & 0xe0) << 8) | ((t & 0xc0) << 5);
796 uint16_t t2 = ((t & 0x1c) << 6) | ((t & 0x1c) << 3);
797 uint16_t t3 = ((t & 0x03) << 3) | ((t & 0x03) << 1) | ((t & 0x02) >> 1);
798
799 *(dPtr++) = t1 | t2 | t3;
800 }
801 return true;
802 }
803 return false;
804 }
805 break;
806
807 case TEXP_LEGACY_A8R3G3B2:
808 if ( outFormat != DXGI_FORMAT_R8G8B8A8_UNORM )
809 return false;
810
811 // D3DFMT_A8R3G3B2 -> DXGI_FORMAT_R8G8B8A8_UNORM
812 if ( inSize >= 2 && outSize >= 4 )
813 {
814 const uint16_t* __restrict sPtr = reinterpret_cast<const uint16_t*>(pSource);
815 uint32_t * __restrict dPtr = reinterpret_cast<uint32_t*>(pDestination);
816
817 for( size_t ocount = 0, icount = 0; ( ( icount < ( inSize - 1 ) ) && ( ocount < ( outSize - 3 ) ) ); icount += 2, ocount += 4 )
818 {
819 uint16_t t = *(sPtr++);
820
821 uint32_t t1 = (t & 0x00e0) | ((t & 0x00e0) >> 3) | ((t & 0x00c0) >> 6);
822 uint32_t t2 = ((t & 0x001c) << 11) | ((t & 0x001c) << 8) | ((t & 0x0018) << 5);
823 uint32_t t3 = ((t & 0x0003) << 22) | ((t & 0x0003) << 20) | ((t & 0x0003) << 18) | ((t & 0x0003) << 16);
824 uint32_t ta = ( flags & TEXP_SCANLINE_SETALPHA ) ? 0xff000000 : ((t & 0xff00) << 16);
825
826 *(dPtr++) = t1 | t2 | t3 | ta;
827 }
828 return true;
829 }
830 return false;
831
832 case TEXP_LEGACY_P8:
833 if ( (outFormat != DXGI_FORMAT_R8G8B8A8_UNORM) || !pal8 )
834 return false;
835
836 // D3DFMT_P8 -> DXGI_FORMAT_R8G8B8A8_UNORM
837 if ( inSize >= 1 && outSize >= 4 )
838 {
839 const uint8_t* __restrict sPtr = reinterpret_cast<const uint8_t*>(pSource);
840 uint32_t * __restrict dPtr = reinterpret_cast<uint32_t*>(pDestination);
841
842 for( size_t ocount = 0, icount = 0; ( ( icount < inSize ) && ( ocount < ( outSize - 3 ) ) ); ++icount, ocount += 4 )
843 {
844 uint8_t t = *(sPtr++);
845
846 *(dPtr++) = pal8[ t ];
847 }
848 return true;
849 }
850 return false;
851
852 case TEXP_LEGACY_A8P8:
853 if ( (outFormat != DXGI_FORMAT_R8G8B8A8_UNORM) || !pal8 )
854 return false;
855
856 // D3DFMT_A8P8 -> DXGI_FORMAT_R8G8B8A8_UNORM
857 if ( inSize >= 2 && outSize >= 4 )
858 {
859 const uint16_t* __restrict sPtr = reinterpret_cast<const uint16_t*>(pSource);
860 uint32_t * __restrict dPtr = reinterpret_cast<uint32_t*>(pDestination);
861
862 for( size_t ocount = 0, icount = 0; ( ( icount < ( inSize - 1 ) ) && ( ocount < ( outSize - 3 ) ) ); icount += 2, ocount += 4 )
863 {
864 uint16_t t = *(sPtr++);
865
866 uint32_t t1 = pal8[ t & 0xff ];
867 uint32_t ta = ( flags & TEXP_SCANLINE_SETALPHA ) ? 0xff000000 : ((t & 0xff00) << 16);
868
869 *(dPtr++) = t1 | ta;
870 }
871 return true;
872 }
873 return false;
874
875 case TEXP_LEGACY_A4L4:
876 switch( outFormat )
877 {
878 case DXGI_FORMAT_B4G4R4A4_UNORM :
879 // D3DFMT_A4L4 -> DXGI_FORMAT_B4G4R4A4_UNORM
880 if ( inSize >= 1 && outSize >= 2 )
881 {
882 const uint8_t * __restrict sPtr = reinterpret_cast<const uint8_t*>(pSource);
883 uint16_t * __restrict dPtr = reinterpret_cast<uint16_t*>(pDestination);
884
885 for( size_t ocount = 0, icount = 0; ( ( icount < inSize ) && ( ocount < ( outSize - 1 ) ) ); ++icount, ocount += 2 )
886 {
887 uint8_t t = *(sPtr++);
888
889 uint16_t t1 = (t & 0x0f);
890 uint16_t ta = ( flags & TEXP_SCANLINE_SETALPHA ) ? 0xf000 : ((t & 0xf0) << 8);
891
892 *(dPtr++) = t1 | (t1 << 4) | (t1 << 8) | ta;
893 }
894 return true;
895 }
896 return false;
897
898 case DXGI_FORMAT_R8G8B8A8_UNORM:
899 // D3DFMT_A4L4 -> DXGI_FORMAT_R8G8B8A8_UNORM
900 if ( inSize >= 1 && outSize >= 4 )
901 {
902 const uint8_t * __restrict sPtr = reinterpret_cast<const uint8_t*>(pSource);
903 uint32_t * __restrict dPtr = reinterpret_cast<uint32_t*>(pDestination);
904
905 for( size_t ocount = 0, icount = 0; ( ( icount < inSize ) && ( ocount < ( outSize - 3 ) ) ); ++icount, ocount += 4 )
906 {
907 uint8_t t = *(sPtr++);
908
909 uint32_t t1 = ((t & 0x0f) << 4) | (t & 0x0f);
910 uint32_t ta = ( flags & TEXP_SCANLINE_SETALPHA ) ? 0xff000000 : (((t & 0xf0) << 24) | ((t & 0xf0) << 20));
911
912 *(dPtr++) = t1 | (t1 << 8) | (t1 << 16) | ta;
913 }
914 return true;
915 }
916 return false;
917 }
918 break;
919
920 case TEXP_LEGACY_B4G4R4A4:
921 if (outFormat != DXGI_FORMAT_R8G8B8A8_UNORM)
922 return false;
923
924 // D3DFMT_A4R4G4B4 -> DXGI_FORMAT_R8G8B8A8_UNORM
925 if ( inSize >= 2 && outSize >= 4 )
926 {
927 const uint16_t * __restrict sPtr = reinterpret_cast<const uint16_t*>(pSource);
928 uint32_t * __restrict dPtr = reinterpret_cast<uint32_t*>(pDestination);
929
930 for( size_t ocount = 0, icount = 0; ( ( icount < ( inSize - 1 ) ) && ( ocount < ( outSize - 3 ) ) ); icount += 2, ocount += 4 )
931 {
932 uint16_t t = *(sPtr++);
933
934 uint32_t t1 = ((t & 0x0f00) >> 4) | ((t & 0x0f00) >> 8);
935 uint32_t t2 = ((t & 0x00f0) << 8) | ((t & 0x00f0) << 4);
936 uint32_t t3 = ((t & 0x000f) << 20) | ((t & 0x000f) << 16);
937 uint32_t ta = ( flags & TEXP_SCANLINE_SETALPHA ) ? 0xff000000 : (((t & 0xf000) << 16) | ((t & 0xf000) << 12));
938
939 *(dPtr++) = t1 | t2 | t3 | ta;
940 }
941 return true;
942 }
943 return false;
944
945 case TEXP_LEGACY_L8:
946 if (outFormat != DXGI_FORMAT_R8G8B8A8_UNORM)
947 return false;
948
949 // D3DFMT_L8 -> DXGI_FORMAT_R8G8B8A8_UNORM
950 if ( inSize >= 1 && outSize >= 4 )
951 {
952 const uint8_t * __restrict sPtr = reinterpret_cast<const uint8_t*>(pSource);
953 uint32_t * __restrict dPtr = reinterpret_cast<uint32_t*>(pDestination);
954
955 for( size_t ocount = 0, icount = 0; ( ( icount < inSize ) && ( ocount < ( outSize - 3 ) ) ); ++icount, ocount += 4 )
956 {
957 uint32_t t1 = *(sPtr++);
958 uint32_t t2 = (t1 << 8);
959 uint32_t t3 = (t1 << 16);
960
961 *(dPtr++) = t1 | t2 | t3 | 0xff000000;
962 }
963 return true;
964 }
965 return false;
966
967 case TEXP_LEGACY_L16:
968 if (outFormat != DXGI_FORMAT_R16G16B16A16_UNORM)
969 return false;
970
971 // D3DFMT_L16 -> DXGI_FORMAT_R16G16B16A16_UNORM
972 if ( inSize >= 2 && outSize >= 8 )
973 {
974 const uint16_t* __restrict sPtr = reinterpret_cast<const uint16_t*>(pSource);
975 uint64_t * __restrict dPtr = reinterpret_cast<uint64_t*>(pDestination);
976
977 for( size_t ocount = 0, icount = 0; ( ( icount < ( inSize - 1 ) ) && ( ocount < ( outSize - 7 ) ) ); icount += 2, ocount += 8 )
978 {
979 uint16_t t = *(sPtr++);
980
981 uint64_t t1 = t;
982 uint64_t t2 = (t1 << 16);
983 uint64_t t3 = (t1 << 32);
984
985 *(dPtr++) = t1 | t2 | t3 | 0xffff000000000000;
986 }
987 return true;
988 }
989 return false;
990
991 case TEXP_LEGACY_A8L8:
992 if (outFormat != DXGI_FORMAT_R8G8B8A8_UNORM)
993 return false;
994
995 // D3DFMT_A8L8 -> DXGI_FORMAT_R8G8B8A8_UNORM
996 if ( inSize >= 2 && outSize >= 4 )
997 {
998 const uint16_t* __restrict sPtr = reinterpret_cast<const uint16_t*>(pSource);
999 uint32_t * __restrict dPtr = reinterpret_cast<uint32_t*>(pDestination);
1000
1001 for( size_t ocount = 0, icount = 0; ( ( icount < ( inSize - 1 ) ) && ( ocount < ( outSize - 3 ) ) ); icount += 2, ocount += 4 )
1002 {
1003 uint16_t t = *(sPtr++);
1004
1005 uint32_t t1 = (t & 0xff);
1006 uint32_t t2 = (t1 << 8);
1007 uint32_t t3 = (t1 << 16);
1008 uint32_t ta = ( flags & TEXP_SCANLINE_SETALPHA ) ? 0xff000000 : ((t & 0xff00) << 16);
1009
1010 *(dPtr++) = t1 | t2 | t3 | ta;
1011 }
1012 return true;
1013 }
1014 return false;
1015 }
1016
1017 return false;
1018 }
1019
1020
1021 //-------------------------------------------------------------------------------------
1022 // Converts or copies image data from pPixels into scratch image data
1023 //-------------------------------------------------------------------------------------
_In_reads_bytes_(size) const1024 static HRESULT _CopyImage( _In_reads_bytes_(size) const void* pPixels, _In_ size_t size,
1025 _In_ const TexMetadata& metadata, _In_ DWORD cpFlags, _In_ DWORD convFlags, _In_reads_opt_(256) const uint32_t *pal8, _In_ const ScratchImage& image )
1026 {
1027 assert( pPixels );
1028 assert( image.GetPixels() );
1029
1030 if ( !size )
1031 return E_FAIL;
1032
1033 if ( convFlags & CONV_FLAGS_EXPAND )
1034 {
1035 if ( convFlags & CONV_FLAGS_888 )
1036 cpFlags |= CP_FLAGS_24BPP;
1037 else if ( convFlags & (CONV_FLAGS_565 | CONV_FLAGS_5551 | CONV_FLAGS_4444 | CONV_FLAGS_8332 | CONV_FLAGS_A8P8 | CONV_FLAGS_L16 | CONV_FLAGS_A8L8) )
1038 cpFlags |= CP_FLAGS_16BPP;
1039 else if ( convFlags & (CONV_FLAGS_44 | CONV_FLAGS_332 | CONV_FLAGS_PAL8 | CONV_FLAGS_L8) )
1040 cpFlags |= CP_FLAGS_8BPP;
1041 }
1042
1043 size_t pixelSize, nimages;
1044 _DetermineImageArray( metadata, cpFlags, nimages, pixelSize );
1045 if ( (nimages == 0) || (nimages != image.GetImageCount()) )
1046 {
1047 return E_FAIL;
1048 }
1049
1050 assert( pixelSize <= size );
1051
1052 std::unique_ptr<Image[]> timages( new (std::nothrow) Image[nimages] );
1053 if ( !timages )
1054 {
1055 return E_OUTOFMEMORY;
1056 }
1057
1058 if ( !_SetupImageArray( (uint8_t*)pPixels, size, metadata, cpFlags, timages.get(), nimages ) )
1059 {
1060 return E_FAIL;
1061 }
1062
1063 if ( nimages != image.GetImageCount() )
1064 {
1065 return E_FAIL;
1066 }
1067
1068 const Image* images = image.GetImages();
1069 if ( !images )
1070 {
1071 return E_FAIL;
1072 }
1073
1074 DWORD tflags = (convFlags & CONV_FLAGS_NOALPHA) ? TEXP_SCANLINE_SETALPHA : 0;
1075 if ( convFlags & CONV_FLAGS_SWIZZLE )
1076 tflags |= TEXP_SCANLINE_LEGACY;
1077
1078 switch (metadata.dimension)
1079 {
1080 case TEX_DIMENSION_TEXTURE1D:
1081 case TEX_DIMENSION_TEXTURE2D:
1082 {
1083 size_t index = 0;
1084 for( size_t item = 0; item < metadata.arraySize; ++item )
1085 {
1086 for( size_t level = 0; level < metadata.mipLevels; ++level, ++index )
1087 {
1088 if ( index >= nimages )
1089 return E_FAIL;
1090
1091 if ( images[ index ].height != timages[ index ].height )
1092 return E_FAIL;
1093
1094 size_t dpitch = images[ index ].rowPitch;
1095 size_t spitch = timages[ index ].rowPitch;
1096
1097 const uint8_t *pSrc = const_cast<const uint8_t*>( timages[ index ].pixels );
1098 if ( !pSrc )
1099 return E_POINTER;
1100
1101 uint8_t *pDest = images[ index ].pixels;
1102 if ( !pDest )
1103 return E_POINTER;
1104
1105 if ( IsCompressed( metadata.format ) )
1106 {
1107 size_t csize = std::min<size_t>( images[ index ].slicePitch, timages[ index ].slicePitch );
1108 memcpy_s( pDest, images[ index ].slicePitch, pSrc, csize );
1109 }
1110 else if ( IsPlanar( metadata.format ) )
1111 {
1112 size_t count = ComputeScanlines( metadata.format, images[ index ].height );
1113 if ( !count )
1114 return E_UNEXPECTED;
1115
1116 size_t csize = std::min<size_t>( dpitch, spitch );
1117 for( size_t h = 0; h < count; ++h )
1118 {
1119 memcpy_s( pDest, dpitch, pSrc, csize );
1120 pSrc += spitch;
1121 pDest += dpitch;
1122 }
1123 }
1124 else
1125 {
1126 for( size_t h = 0; h < images[ index ].height; ++h )
1127 {
1128 if ( convFlags & CONV_FLAGS_EXPAND )
1129 {
1130 if ( convFlags & (CONV_FLAGS_565|CONV_FLAGS_5551|CONV_FLAGS_4444) )
1131 {
1132 if ( !_ExpandScanline( pDest, dpitch, DXGI_FORMAT_R8G8B8A8_UNORM,
1133 pSrc, spitch,
1134 (convFlags & CONV_FLAGS_565) ? DXGI_FORMAT_B5G6R5_UNORM : DXGI_FORMAT_B5G5R5A1_UNORM,
1135 tflags ) )
1136 return E_FAIL;
1137 }
1138 else
1139 {
1140 TEXP_LEGACY_FORMAT lformat = _FindLegacyFormat( convFlags );
1141 if ( !_LegacyExpandScanline( pDest, dpitch, metadata.format,
1142 pSrc, spitch, lformat, pal8,
1143 tflags ) )
1144 return E_FAIL;
1145 }
1146 }
1147 else if ( convFlags & CONV_FLAGS_SWIZZLE )
1148 {
1149 _SwizzleScanline( pDest, dpitch, pSrc, spitch,
1150 metadata.format, tflags );
1151 }
1152 else
1153 {
1154 _CopyScanline( pDest, dpitch, pSrc, spitch,
1155 metadata.format, tflags );
1156 }
1157
1158 pSrc += spitch;
1159 pDest += dpitch;
1160 }
1161 }
1162 }
1163 }
1164 }
1165 break;
1166
1167 case TEX_DIMENSION_TEXTURE3D:
1168 {
1169 size_t index = 0;
1170 size_t d = metadata.depth;
1171
1172 for( size_t level = 0; level < metadata.mipLevels; ++level )
1173 {
1174 for( size_t slice = 0; slice < d; ++slice, ++index )
1175 {
1176 if ( index >= nimages )
1177 return E_FAIL;
1178
1179 if ( images[ index ].height != timages[ index ].height )
1180 return E_FAIL;
1181
1182 size_t dpitch = images[ index ].rowPitch;
1183 size_t spitch = timages[ index ].rowPitch;
1184
1185 const uint8_t *pSrc = const_cast<const uint8_t*>( timages[ index ].pixels );
1186 if ( !pSrc )
1187 return E_POINTER;
1188
1189 uint8_t *pDest = images[ index ].pixels;
1190 if ( !pDest )
1191 return E_POINTER;
1192
1193 if ( IsCompressed( metadata.format ) )
1194 {
1195 size_t csize = std::min<size_t>( images[ index ].slicePitch, timages[ index ].slicePitch );
1196 memcpy_s( pDest, images[ index ].slicePitch, pSrc, csize );
1197 }
1198 else if ( IsPlanar( metadata.format ) )
1199 {
1200 // Direct3D does not support any planar formats for Texture3D
1201 return HRESULT_FROM_WIN32( ERROR_NOT_SUPPORTED );
1202 }
1203 else
1204 {
1205 for( size_t h = 0; h < images[ index ].height; ++h )
1206 {
1207 if ( convFlags & CONV_FLAGS_EXPAND )
1208 {
1209 if ( convFlags & (CONV_FLAGS_565|CONV_FLAGS_5551|CONV_FLAGS_4444) )
1210 {
1211 if ( !_ExpandScanline( pDest, dpitch, DXGI_FORMAT_R8G8B8A8_UNORM,
1212 pSrc, spitch,
1213 (convFlags & CONV_FLAGS_565) ? DXGI_FORMAT_B5G6R5_UNORM : DXGI_FORMAT_B5G5R5A1_UNORM,
1214 tflags ) )
1215 return E_FAIL;
1216 }
1217 else
1218 {
1219 TEXP_LEGACY_FORMAT lformat = _FindLegacyFormat( convFlags );
1220 if ( !_LegacyExpandScanline( pDest, dpitch, metadata.format,
1221 pSrc, spitch, lformat, pal8,
1222 tflags ) )
1223 return E_FAIL;
1224 }
1225 }
1226 else if ( convFlags & CONV_FLAGS_SWIZZLE )
1227 {
1228 _SwizzleScanline( pDest, dpitch, pSrc, spitch, metadata.format, tflags );
1229 }
1230 else
1231 {
1232 _CopyScanline( pDest, dpitch, pSrc, spitch, metadata.format, tflags );
1233 }
1234
1235 pSrc += spitch;
1236 pDest += dpitch;
1237 }
1238 }
1239 }
1240
1241 if ( d > 1 )
1242 d >>= 1;
1243 }
1244 }
1245 break;
1246
1247 default:
1248 return E_FAIL;
1249 }
1250
1251 return S_OK;
1252 }
1253
_CopyImageInPlace(DWORD convFlags,_In_ const ScratchImage & image)1254 static HRESULT _CopyImageInPlace( DWORD convFlags, _In_ const ScratchImage& image )
1255 {
1256 if ( !image.GetPixels() )
1257 return E_FAIL;
1258
1259 const Image* images = image.GetImages();
1260 if ( !images )
1261 return E_FAIL;
1262
1263 const TexMetadata& metadata = image.GetMetadata();
1264
1265 if ( IsPlanar( metadata.format ) )
1266 return HRESULT_FROM_WIN32( ERROR_NOT_SUPPORTED );
1267
1268 DWORD tflags = (convFlags & CONV_FLAGS_NOALPHA) ? TEXP_SCANLINE_SETALPHA : 0;
1269 if ( convFlags & CONV_FLAGS_SWIZZLE )
1270 tflags |= TEXP_SCANLINE_LEGACY;
1271
1272 for( size_t i = 0; i < image.GetImageCount(); ++i )
1273 {
1274 const Image* img = &images[ i ];
1275 uint8_t *pPixels = img->pixels;
1276 if ( !pPixels )
1277 return E_POINTER;
1278
1279 size_t rowPitch = img->rowPitch;
1280
1281 for( size_t h = 0; h < img->height; ++h )
1282 {
1283 if ( convFlags & CONV_FLAGS_SWIZZLE )
1284 {
1285 _SwizzleScanline( pPixels, rowPitch, pPixels, rowPitch, metadata.format, tflags );
1286 }
1287 else
1288 {
1289 _CopyScanline( pPixels, rowPitch, pPixels, rowPitch, metadata.format, tflags );
1290 }
1291
1292 pPixels += rowPitch;
1293 }
1294 }
1295
1296 return S_OK;
1297 }
1298
1299
1300 //=====================================================================================
1301 // Entry-points
1302 //=====================================================================================
1303
1304 //-------------------------------------------------------------------------------------
1305 // Obtain metadata from DDS file in memory/on disk
1306 //-------------------------------------------------------------------------------------
1307
1308 _Use_decl_annotations_
GetMetadataFromDDSMemory(LPCVOID pSource,size_t size,DWORD flags,TexMetadata & metadata)1309 HRESULT GetMetadataFromDDSMemory( LPCVOID pSource, size_t size, DWORD flags, TexMetadata& metadata )
1310 {
1311 if ( !pSource || size == 0 )
1312 return E_INVALIDARG;
1313
1314 DWORD convFlags = 0;
1315 return _DecodeDDSHeader( pSource, size, flags, metadata, convFlags );
1316 }
1317
1318 _Use_decl_annotations_
GetMetadataFromDDSFile(LPCWSTR szFile,DWORD flags,TexMetadata & metadata)1319 HRESULT GetMetadataFromDDSFile( LPCWSTR szFile, DWORD flags, TexMetadata& metadata )
1320 {
1321 if ( !szFile )
1322 return E_INVALIDARG;
1323
1324 #if (_WIN32_WINNT >= _WIN32_WINNT_WIN8)
1325 ScopedHandle hFile( safe_handle( CreateFile2( szFile, GENERIC_READ, FILE_SHARE_READ, OPEN_EXISTING, 0 ) ) );
1326 #else
1327 ScopedHandle hFile( safe_handle( CreateFileW( szFile, GENERIC_READ, FILE_SHARE_READ, 0, OPEN_EXISTING,
1328 FILE_FLAG_SEQUENTIAL_SCAN, 0 ) ) );
1329 #endif
1330 if ( !hFile )
1331 {
1332 return HRESULT_FROM_WIN32( GetLastError() );
1333 }
1334
1335 // Get the file size
1336 LARGE_INTEGER fileSize = {0};
1337
1338 #if (_WIN32_WINNT >= _WIN32_WINNT_VISTA)
1339 FILE_STANDARD_INFO fileInfo;
1340 if ( !GetFileInformationByHandleEx( hFile.get(), FileStandardInfo, &fileInfo, sizeof(fileInfo) ) )
1341 {
1342 return HRESULT_FROM_WIN32( GetLastError() );
1343 }
1344 fileSize = fileInfo.EndOfFile;
1345 #else
1346 if ( !GetFileSizeEx( hFile.get(), &fileSize ) )
1347 {
1348 return HRESULT_FROM_WIN32( GetLastError() );
1349 }
1350 #endif
1351
1352 // File is too big for 32-bit allocation, so reject read (4 GB should be plenty large enough for a valid DDS file)
1353 if ( fileSize.HighPart > 0 )
1354 {
1355 return HRESULT_FROM_WIN32( ERROR_FILE_TOO_LARGE );
1356 }
1357
1358 // Need at least enough data to fill the standard header and magic number to be a valid DDS
1359 if ( fileSize.LowPart < ( sizeof(DDS_HEADER) + sizeof(uint32_t) ) )
1360 {
1361 return E_FAIL;
1362 }
1363
1364 // Read the header in (including extended header if present)
1365 const size_t MAX_HEADER_SIZE = sizeof(uint32_t) + sizeof(DDS_HEADER) + sizeof(DDS_HEADER_DXT10);
1366 uint8_t header[MAX_HEADER_SIZE];
1367
1368 DWORD bytesRead = 0;
1369 if ( !ReadFile( hFile.get(), header, MAX_HEADER_SIZE, &bytesRead, 0 ) )
1370 {
1371 return HRESULT_FROM_WIN32( GetLastError() );
1372 }
1373
1374 DWORD convFlags = 0;
1375 return _DecodeDDSHeader( header, bytesRead, flags, metadata, convFlags );
1376 }
1377
1378
1379 //-------------------------------------------------------------------------------------
1380 // Load a DDS file in memory
1381 //-------------------------------------------------------------------------------------
1382 _Use_decl_annotations_
LoadFromDDSMemory(LPCVOID pSource,size_t size,DWORD flags,TexMetadata * metadata,ScratchImage & image)1383 HRESULT LoadFromDDSMemory( LPCVOID pSource, size_t size, DWORD flags, TexMetadata* metadata, ScratchImage& image )
1384 {
1385 if ( !pSource || size == 0 )
1386 return E_INVALIDARG;
1387
1388 image.Release();
1389
1390 DWORD convFlags = 0;
1391 TexMetadata mdata;
1392 HRESULT hr = _DecodeDDSHeader( pSource, size, flags, mdata, convFlags );
1393 if ( FAILED(hr) )
1394 return hr;
1395
1396 size_t offset = sizeof(uint32_t) + sizeof(DDS_HEADER);
1397 if ( convFlags & CONV_FLAGS_DX10 )
1398 offset += sizeof(DDS_HEADER_DXT10);
1399
1400 assert( offset <= size );
1401
1402 const uint32_t *pal8 = nullptr;
1403 if ( convFlags & CONV_FLAGS_PAL8 )
1404 {
1405 pal8 = reinterpret_cast<const uint32_t*>( reinterpret_cast<const uint8_t*>(pSource) + offset );
1406 assert( pal8 );
1407 offset += ( 256 * sizeof(uint32_t) );
1408 if ( size < offset )
1409 return E_FAIL;
1410 }
1411
1412 hr = image.Initialize( mdata );
1413 if ( FAILED(hr) )
1414 return hr;
1415
1416 auto pPixels = reinterpret_cast<LPCVOID>( reinterpret_cast<const uint8_t*>(pSource) + offset );
1417 assert( pPixels );
1418 hr = _CopyImage( pPixels, size - offset, mdata,
1419 (flags & DDS_FLAGS_LEGACY_DWORD) ? CP_FLAGS_LEGACY_DWORD : CP_FLAGS_NONE, convFlags, pal8, image );
1420 if ( FAILED(hr) )
1421 {
1422 image.Release();
1423 return hr;
1424 }
1425 if ( metadata )
1426 memcpy( metadata, &mdata, sizeof(TexMetadata) );
1427
1428 return S_OK;
1429 }
1430
1431
1432 //-------------------------------------------------------------------------------------
1433 // Load a DDS file from disk
1434 //-------------------------------------------------------------------------------------
1435 _Use_decl_annotations_
LoadFromDDSFile(LPCWSTR szFile,DWORD flags,TexMetadata * metadata,ScratchImage & image)1436 HRESULT LoadFromDDSFile( LPCWSTR szFile, DWORD flags, TexMetadata* metadata, ScratchImage& image )
1437 {
1438 if ( !szFile )
1439 return E_INVALIDARG;
1440
1441 image.Release();
1442
1443 #if (_WIN32_WINNT >= _WIN32_WINNT_WIN8)
1444 ScopedHandle hFile( safe_handle ( CreateFile2( szFile, GENERIC_READ, FILE_SHARE_READ, OPEN_EXISTING, 0 ) ) );
1445 #else
1446 ScopedHandle hFile( safe_handle ( CreateFileW( szFile, GENERIC_READ, FILE_SHARE_READ, 0, OPEN_EXISTING,
1447 FILE_FLAG_SEQUENTIAL_SCAN, 0 ) ) );
1448 #endif
1449
1450 if ( !hFile )
1451 {
1452 return HRESULT_FROM_WIN32( GetLastError() );
1453 }
1454
1455 // Get the file size
1456 LARGE_INTEGER fileSize = {0};
1457
1458 #if (_WIN32_WINNT >= _WIN32_WINNT_VISTA)
1459 FILE_STANDARD_INFO fileInfo;
1460 if ( !GetFileInformationByHandleEx( hFile.get(), FileStandardInfo, &fileInfo, sizeof(fileInfo) ) )
1461 {
1462 return HRESULT_FROM_WIN32( GetLastError() );
1463 }
1464 fileSize = fileInfo.EndOfFile;
1465 #else
1466 if ( !GetFileSizeEx( hFile.get(), &fileSize ) )
1467 {
1468 return HRESULT_FROM_WIN32( GetLastError() );
1469 }
1470 #endif
1471
1472 // File is too big for 32-bit allocation, so reject read (4 GB should be plenty large enough for a valid DDS file)
1473 if ( fileSize.HighPart > 0 )
1474 {
1475 return HRESULT_FROM_WIN32( ERROR_FILE_TOO_LARGE );
1476 }
1477
1478 // Need at least enough data to fill the standard header and magic number to be a valid DDS
1479 if ( fileSize.LowPart < ( sizeof(DDS_HEADER) + sizeof(uint32_t) ) )
1480 {
1481 return E_FAIL;
1482 }
1483
1484 // Read the header in (including extended header if present)
1485 const size_t MAX_HEADER_SIZE = sizeof(uint32_t) + sizeof(DDS_HEADER) + sizeof(DDS_HEADER_DXT10);
1486 uint8_t header[MAX_HEADER_SIZE];
1487
1488 DWORD bytesRead = 0;
1489 if ( !ReadFile( hFile.get(), header, MAX_HEADER_SIZE, &bytesRead, 0 ) )
1490 {
1491 return HRESULT_FROM_WIN32( GetLastError() );
1492 }
1493
1494 DWORD convFlags = 0;
1495 TexMetadata mdata;
1496 HRESULT hr = _DecodeDDSHeader( header, bytesRead, flags, mdata, convFlags );
1497 if ( FAILED(hr) )
1498 return hr;
1499
1500 DWORD offset = MAX_HEADER_SIZE;
1501
1502 if ( !(convFlags & CONV_FLAGS_DX10) )
1503 {
1504 // Must reset file position since we read more than the standard header above
1505 LARGE_INTEGER filePos = { sizeof(uint32_t) + sizeof(DDS_HEADER), 0};
1506 if ( !SetFilePointerEx( hFile.get(), filePos, 0, FILE_BEGIN ) )
1507 {
1508 return HRESULT_FROM_WIN32( GetLastError() );
1509 }
1510
1511 offset = sizeof(uint32_t) + sizeof(DDS_HEADER);
1512 }
1513
1514 std::unique_ptr<uint32_t[]> pal8;
1515 if ( convFlags & CONV_FLAGS_PAL8 )
1516 {
1517 pal8.reset( new (std::nothrow) uint32_t[256] );
1518 if ( !pal8 )
1519 {
1520 return E_OUTOFMEMORY;
1521 }
1522
1523 if ( !ReadFile( hFile.get(), pal8.get(), 256 * sizeof(uint32_t), &bytesRead, 0 ) )
1524 {
1525 return HRESULT_FROM_WIN32( GetLastError() );
1526 }
1527
1528 if ( bytesRead != (256 * sizeof(uint32_t)) )
1529 {
1530 return E_FAIL;
1531 }
1532
1533 offset += ( 256 * sizeof(uint32_t) );
1534 }
1535
1536 DWORD remaining = fileSize.LowPart - offset;
1537 if ( remaining == 0 )
1538 return E_FAIL;
1539
1540 hr = image.Initialize( mdata );
1541 if ( FAILED(hr) )
1542 return hr;
1543
1544 if ( (convFlags & CONV_FLAGS_EXPAND) || (flags & DDS_FLAGS_LEGACY_DWORD) )
1545 {
1546 std::unique_ptr<uint8_t[]> temp( new (std::nothrow) uint8_t[ remaining ] );
1547 if ( !temp )
1548 {
1549 image.Release();
1550 return E_OUTOFMEMORY;
1551 }
1552
1553 if ( !ReadFile( hFile.get(), temp.get(), remaining, &bytesRead, 0 ) )
1554 {
1555 image.Release();
1556 return HRESULT_FROM_WIN32( GetLastError() );
1557 }
1558
1559 if ( bytesRead != remaining )
1560 {
1561 image.Release();
1562 return E_FAIL;
1563 }
1564
1565 hr = _CopyImage( temp.get(), remaining, mdata,
1566 (flags & DDS_FLAGS_LEGACY_DWORD) ? CP_FLAGS_LEGACY_DWORD : CP_FLAGS_NONE,
1567 convFlags, pal8.get(), image );
1568 if ( FAILED(hr) )
1569 {
1570 image.Release();
1571 return hr;
1572 }
1573 }
1574 else
1575 {
1576 if ( remaining < image.GetPixelsSize() )
1577 {
1578 image.Release();
1579 return E_FAIL;
1580 }
1581
1582 if ( !ReadFile( hFile.get(), image.GetPixels(), static_cast<DWORD>( image.GetPixelsSize() ), &bytesRead, 0 ) )
1583 {
1584 image.Release();
1585 return HRESULT_FROM_WIN32( GetLastError() );
1586 }
1587
1588 if ( convFlags & (CONV_FLAGS_SWIZZLE|CONV_FLAGS_NOALPHA) )
1589 {
1590 // Swizzle/copy image in place
1591 hr = _CopyImageInPlace( convFlags, image );
1592 if ( FAILED(hr) )
1593 {
1594 image.Release();
1595 return hr;
1596 }
1597 }
1598 }
1599
1600 if ( metadata )
1601 memcpy( metadata, &mdata, sizeof(TexMetadata) );
1602
1603 return S_OK;
1604 }
1605
1606
1607 //-------------------------------------------------------------------------------------
1608 // Save a DDS file to memory
1609 //-------------------------------------------------------------------------------------
1610 _Use_decl_annotations_
SaveToDDSMemory(const Image * images,size_t nimages,const TexMetadata & metadata,DWORD flags,Blob & blob)1611 HRESULT SaveToDDSMemory( const Image* images, size_t nimages, const TexMetadata& metadata, DWORD flags, Blob& blob )
1612 {
1613 if ( !images || (nimages == 0) )
1614 return E_INVALIDARG;
1615
1616 // Determine memory required
1617 size_t required = 0;
1618 HRESULT hr = _EncodeDDSHeader( metadata, flags, 0, 0, required );
1619 if ( FAILED(hr) )
1620 return hr;
1621
1622 bool fastpath = true;
1623
1624 for( size_t i = 0; i < nimages; ++i )
1625 {
1626 if ( !images[ i ].pixels )
1627 return E_POINTER;
1628
1629 if ( images[ i ].format != metadata.format )
1630 return E_FAIL;
1631
1632 size_t ddsRowPitch, ddsSlicePitch;
1633 ComputePitch( metadata.format, images[ i ].width, images[ i ].height, ddsRowPitch, ddsSlicePitch, CP_FLAGS_NONE );
1634
1635 assert( images[ i ].rowPitch > 0 );
1636 assert( images[ i ].slicePitch > 0 );
1637
1638 if ( ( images[ i ].rowPitch != ddsRowPitch ) || ( images[ i ].slicePitch != ddsSlicePitch ) )
1639 {
1640 fastpath = false;
1641 }
1642
1643 required += ddsSlicePitch;
1644 }
1645
1646 assert( required > 0 );
1647
1648 blob.Release();
1649
1650 hr = blob.Initialize( required );
1651 if ( FAILED(hr) )
1652 return hr;
1653
1654 auto pDestination = reinterpret_cast<uint8_t*>( blob.GetBufferPointer() );
1655 assert( pDestination );
1656
1657 hr = _EncodeDDSHeader( metadata, flags, pDestination, blob.GetBufferSize(), required );
1658 if ( FAILED(hr) )
1659 {
1660 blob.Release();
1661 return hr;
1662 }
1663
1664 size_t remaining = blob.GetBufferSize() - required;
1665 pDestination += required;
1666
1667 if ( !remaining )
1668 {
1669 blob.Release();
1670 return E_FAIL;
1671 }
1672
1673 switch( metadata.dimension )
1674 {
1675 case DDS_DIMENSION_TEXTURE1D:
1676 case DDS_DIMENSION_TEXTURE2D:
1677 {
1678 size_t index = 0;
1679 for( size_t item = 0; item < metadata.arraySize; ++item )
1680 {
1681 for( size_t level = 0; level < metadata.mipLevels; ++level )
1682 {
1683 if ( index >= nimages )
1684 {
1685 blob.Release();
1686 return E_FAIL;
1687 }
1688
1689 if ( fastpath )
1690 {
1691 size_t pixsize = images[ index ].slicePitch;
1692 if ( memcpy_s( pDestination, remaining, images[ index ].pixels, pixsize ) )
1693 {
1694 blob.Release();
1695 return E_FAIL;
1696 }
1697
1698 pDestination += pixsize;
1699 remaining -= pixsize;
1700 }
1701 else
1702 {
1703 size_t ddsRowPitch, ddsSlicePitch;
1704 ComputePitch( metadata.format, images[ index ].width, images[ index ].height, ddsRowPitch, ddsSlicePitch, CP_FLAGS_NONE );
1705
1706 size_t rowPitch = images[ index ].rowPitch;
1707
1708 const uint8_t * __restrict sPtr = reinterpret_cast<const uint8_t*>(images[ index ].pixels);
1709 uint8_t * __restrict dPtr = reinterpret_cast<uint8_t*>(pDestination);
1710
1711 size_t lines = ComputeScanlines( metadata.format, images[ index ].height );
1712 size_t csize = std::min<size_t>( rowPitch, ddsRowPitch );
1713 size_t tremaining = remaining;
1714 for( size_t j = 0; j < lines; ++j )
1715 {
1716 if ( memcpy_s( dPtr, tremaining, sPtr, csize ) )
1717 {
1718 blob.Release();
1719 return E_FAIL;
1720 }
1721
1722 sPtr += rowPitch;
1723 dPtr += ddsRowPitch;
1724 tremaining -= ddsRowPitch;
1725 }
1726
1727 pDestination += ddsSlicePitch;
1728 remaining -= ddsSlicePitch;
1729 }
1730
1731 ++index;
1732 }
1733 }
1734 }
1735 break;
1736
1737 case DDS_DIMENSION_TEXTURE3D:
1738 {
1739 if ( metadata.arraySize != 1 )
1740 {
1741 blob.Release();
1742 return E_FAIL;
1743 }
1744
1745 size_t d = metadata.depth;
1746
1747 size_t index = 0;
1748 for( size_t level = 0; level < metadata.mipLevels; ++level )
1749 {
1750 for( size_t slice = 0; slice < d; ++slice )
1751 {
1752 if ( index >= nimages )
1753 {
1754 blob.Release();
1755 return E_FAIL;
1756 }
1757
1758 if ( fastpath )
1759 {
1760 size_t pixsize = images[ index ].slicePitch;
1761 if ( memcpy_s( pDestination, remaining, images[ index ].pixels, pixsize ) )
1762 {
1763 blob.Release();
1764 return E_FAIL;
1765 }
1766
1767 pDestination += pixsize;
1768 remaining -= pixsize;
1769 }
1770 else
1771 {
1772 size_t ddsRowPitch, ddsSlicePitch;
1773 ComputePitch( metadata.format, images[ index ].width, images[ index ].height, ddsRowPitch, ddsSlicePitch, CP_FLAGS_NONE );
1774
1775 size_t rowPitch = images[ index ].rowPitch;
1776
1777 const uint8_t * __restrict sPtr = reinterpret_cast<const uint8_t*>(images[ index ].pixels);
1778 uint8_t * __restrict dPtr = reinterpret_cast<uint8_t*>(pDestination);
1779
1780 size_t lines = ComputeScanlines( metadata.format, images[ index ].height );
1781 size_t csize = std::min<size_t>( rowPitch, ddsRowPitch );
1782 size_t tremaining = remaining;
1783 for( size_t j = 0; j < lines; ++j )
1784 {
1785 if ( memcpy_s( dPtr, tremaining, sPtr, csize ) )
1786 {
1787 blob.Release();
1788 return E_FAIL;
1789 }
1790
1791 sPtr += rowPitch;
1792 dPtr += ddsRowPitch;
1793 tremaining -= ddsRowPitch;
1794 }
1795
1796 pDestination += ddsSlicePitch;
1797 remaining -= ddsSlicePitch;
1798 }
1799
1800 ++index;
1801 }
1802
1803 if ( d > 1 )
1804 d >>= 1;
1805 }
1806 }
1807 break;
1808
1809 default:
1810 blob.Release();
1811 return E_FAIL;
1812 }
1813
1814 return S_OK;
1815 }
1816
1817
1818 //-------------------------------------------------------------------------------------
1819 // Save a DDS file to disk
1820 //-------------------------------------------------------------------------------------
1821 _Use_decl_annotations_
SaveToDDSFile(const Image * images,size_t nimages,const TexMetadata & metadata,DWORD flags,LPCWSTR szFile)1822 HRESULT SaveToDDSFile( const Image* images, size_t nimages, const TexMetadata& metadata, DWORD flags, LPCWSTR szFile )
1823 {
1824 if ( !szFile )
1825 return E_INVALIDARG;
1826
1827 // Create DDS Header
1828 const size_t MAX_HEADER_SIZE = sizeof(uint32_t) + sizeof(DDS_HEADER) + sizeof(DDS_HEADER_DXT10);
1829 uint8_t header[MAX_HEADER_SIZE];
1830 size_t required;
1831 HRESULT hr = _EncodeDDSHeader( metadata, flags, header, MAX_HEADER_SIZE, required );
1832 if ( FAILED(hr) )
1833 return hr;
1834
1835 // Create file and write header
1836 #if (_WIN32_WINNT >= _WIN32_WINNT_WIN8)
1837 ScopedHandle hFile( safe_handle( CreateFile2( szFile, GENERIC_WRITE, 0, CREATE_ALWAYS, 0 ) ) );
1838 #else
1839 ScopedHandle hFile( safe_handle( CreateFileW( szFile, GENERIC_WRITE, 0, 0, CREATE_ALWAYS, 0, 0 ) ) );
1840 #endif
1841 if ( !hFile )
1842 {
1843 return HRESULT_FROM_WIN32( GetLastError() );
1844 }
1845
1846 DWORD bytesWritten;
1847 if ( !WriteFile( hFile.get(), header, static_cast<DWORD>( required ), &bytesWritten, 0 ) )
1848 {
1849 return HRESULT_FROM_WIN32( GetLastError() );
1850 }
1851
1852 if ( bytesWritten != required )
1853 {
1854 return E_FAIL;
1855 }
1856
1857 // Write images
1858 switch( metadata.dimension )
1859 {
1860 case DDS_DIMENSION_TEXTURE1D:
1861 case DDS_DIMENSION_TEXTURE2D:
1862 {
1863 size_t index = 0;
1864 for( size_t item = 0; item < metadata.arraySize; ++item )
1865 {
1866 for( size_t level = 0; level < metadata.mipLevels; ++level, ++index )
1867 {
1868 if ( index >= nimages )
1869 return E_FAIL;
1870
1871 if ( !images[ index ].pixels )
1872 return E_POINTER;
1873
1874 assert( images[ index ].rowPitch > 0 );
1875 assert( images[ index ].slicePitch > 0 );
1876
1877 size_t ddsRowPitch, ddsSlicePitch;
1878 ComputePitch( metadata.format, images[ index ].width, images[ index ].height, ddsRowPitch, ddsSlicePitch, CP_FLAGS_NONE );
1879
1880 if ( images[ index ].slicePitch == ddsSlicePitch )
1881 {
1882 if ( !WriteFile( hFile.get(), images[ index ].pixels, static_cast<DWORD>( ddsSlicePitch ), &bytesWritten, 0 ) )
1883 {
1884 return HRESULT_FROM_WIN32( GetLastError() );
1885 }
1886
1887 if ( bytesWritten != ddsSlicePitch )
1888 {
1889 return E_FAIL;
1890 }
1891 }
1892 else
1893 {
1894 size_t rowPitch = images[ index ].rowPitch;
1895 if ( rowPitch < ddsRowPitch )
1896 {
1897 // DDS uses 1-byte alignment, so if this is happening then the input pitch isn't actually a full line of data
1898 return E_FAIL;
1899 }
1900
1901 const uint8_t * __restrict sPtr = reinterpret_cast<const uint8_t*>(images[ index ].pixels);
1902
1903 size_t lines = ComputeScanlines( metadata.format, images[ index ].height );
1904 for( size_t j = 0; j < lines; ++j )
1905 {
1906 if ( !WriteFile( hFile.get(), sPtr, static_cast<DWORD>( ddsRowPitch ), &bytesWritten, 0 ) )
1907 {
1908 return HRESULT_FROM_WIN32( GetLastError() );
1909 }
1910
1911 if ( bytesWritten != ddsRowPitch )
1912 {
1913 return E_FAIL;
1914 }
1915
1916 sPtr += rowPitch;
1917 }
1918 }
1919 }
1920 }
1921 }
1922 break;
1923
1924 case DDS_DIMENSION_TEXTURE3D:
1925 {
1926 if ( metadata.arraySize != 1 )
1927 return E_FAIL;
1928
1929 size_t d = metadata.depth;
1930
1931 size_t index = 0;
1932 for( size_t level = 0; level < metadata.mipLevels; ++level )
1933 {
1934 for( size_t slice = 0; slice < d; ++slice, ++index )
1935 {
1936 if ( index >= nimages )
1937 return E_FAIL;
1938
1939 if ( !images[ index ].pixels )
1940 return E_POINTER;
1941
1942 assert( images[ index ].rowPitch > 0 );
1943 assert( images[ index ].slicePitch > 0 );
1944
1945 size_t ddsRowPitch, ddsSlicePitch;
1946 ComputePitch( metadata.format, images[ index ].width, images[ index ].height, ddsRowPitch, ddsSlicePitch, CP_FLAGS_NONE );
1947
1948 if ( images[ index ].slicePitch == ddsSlicePitch )
1949 {
1950 if ( !WriteFile( hFile.get(), images[ index ].pixels, static_cast<DWORD>( ddsSlicePitch ), &bytesWritten, 0 ) )
1951 {
1952 return HRESULT_FROM_WIN32( GetLastError() );
1953 }
1954
1955 if ( bytesWritten != ddsSlicePitch )
1956 {
1957 return E_FAIL;
1958 }
1959 }
1960 else
1961 {
1962 size_t rowPitch = images[ index ].rowPitch;
1963 if ( rowPitch < ddsRowPitch )
1964 {
1965 // DDS uses 1-byte alignment, so if this is happening then the input pitch isn't actually a full line of data
1966 return E_FAIL;
1967 }
1968
1969 const uint8_t * __restrict sPtr = reinterpret_cast<const uint8_t*>(images[ index ].pixels);
1970
1971 size_t lines = ComputeScanlines( metadata.format, images[ index ].height );
1972 for( size_t j = 0; j < lines; ++j )
1973 {
1974 if ( !WriteFile( hFile.get(), sPtr, static_cast<DWORD>( ddsRowPitch ), &bytesWritten, 0 ) )
1975 {
1976 return HRESULT_FROM_WIN32( GetLastError() );
1977 }
1978
1979 if ( bytesWritten != ddsRowPitch )
1980 {
1981 return E_FAIL;
1982 }
1983
1984 sPtr += rowPitch;
1985 }
1986 }
1987 }
1988
1989 if ( d > 1 )
1990 d >>= 1;
1991 }
1992 }
1993 break;
1994
1995 default:
1996 return E_FAIL;
1997 }
1998
1999 return S_OK;
2000 }
2001
2002 }; // namespace
2003