]> git.cworth.org Git - apitrace/blob - thirdparty/directxtex/DirectXTex/DirectXTexDDS.cpp
directxtex: Fix include filename case.
[apitrace] / thirdparty / directxtex / DirectXTex / DirectXTexDDS.cpp
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 };
43
44 struct LegacyDDS
45 {
46     DXGI_FORMAT     format;
47     DWORD           convFlags;
48     DDS_PIXELFORMAT ddpf;
49 };
50
51 const LegacyDDS g_LegacyDDSMap[] = 
52 {
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
56
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)
59
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 },
64
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 } },
67
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
70
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
76
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)
79
80     { DXGI_FORMAT_R8G8B8A8_UNORM,     CONV_FLAGS_EXPAND
81                                       | CONV_FLAGS_NOALPHA
82                                       | CONV_FLAGS_888,       DDSPF_R8G8B8 }, // D3DFMT_R8G8B8
83
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
88      
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
93   
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
97
98     { DXGI_FORMAT_A8_UNORM,           CONV_FLAGS_NONE,        DDSPF_A8   }, // D3DFMT_A8
99
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
108
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)
110
111     { DXGI_FORMAT_R8G8B8A8_UNORM,     CONV_FLAGS_EXPAND
112                                       | CONV_FLAGS_PAL8
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
116
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
127                                       | CONV_FLAGS_NOALPHA
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
131 #endif
132 };
133
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
139
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
148
149 static DXGI_FORMAT _GetDXGIFormat( const DDS_PIXELFORMAT& ddpf, DWORD flags, _Inout_opt_ DWORD* convFlags )
150 {
151     const size_t MAP_SIZE = sizeof(g_LegacyDDSMap) / sizeof(LegacyDDS);
152     size_t index = 0;
153     for( index = 0; index < MAP_SIZE; ++index )
154     {
155         const LegacyDDS* entry = &g_LegacyDDSMap[index];
156
157         if ( ddpf.dwFlags & entry->ddpf.dwFlags )
158         {
159             if ( entry->ddpf.dwFlags & DDS_FOURCC )
160             {
161                 if ( ddpf.dwFourCC == entry->ddpf.dwFourCC )
162                     break;
163             }
164             else if ( entry->ddpf.dwFlags & DDS_PAL8 )
165             {
166                 if (  ddpf.dwRGBBitCount == entry->ddpf.dwRGBBitCount )
167                     break;
168             }
169             else if ( ddpf.dwRGBBitCount == entry->ddpf.dwRGBBitCount )
170             {
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 )
176                     break;
177             }
178         }
179     }
180
181     if ( index >= MAP_SIZE )
182         return DXGI_FORMAT_UNKNOWN;
183
184     DWORD cflags = g_LegacyDDSMap[index].convFlags;
185     DXGI_FORMAT format = g_LegacyDDSMap[index].format;
186
187     if ( (cflags & CONV_FLAGS_EXPAND) && (flags & DDS_FLAGS_NO_LEGACY_EXPANSION) )
188         return DXGI_FORMAT_UNKNOWN;
189
190     if ( (format == DXGI_FORMAT_R10G10B10A2_UNORM) && (flags & DDS_FLAGS_NO_R10B10G10A2_FIXUP) )
191     {
192         cflags ^= CONV_FLAGS_SWIZZLE;
193     }
194
195     if ( convFlags )
196         *convFlags = cflags;
197
198     return format;
199 }
200
201
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 )
207 {
208     if ( !pSource )
209         return E_INVALIDARG;
210
211     memset( &metadata, 0, sizeof(TexMetadata) );
212
213     if ( size < (sizeof(DDS_HEADER) + sizeof(uint32_t)) )
214     {
215         return HRESULT_FROM_WIN32( ERROR_INVALID_DATA );
216     }
217
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 )
221     {
222         return E_FAIL;
223     }
224
225     const DDS_HEADER* pHeader = reinterpret_cast<const DDS_HEADER*>( (const uint8_t*)pSource + sizeof( uint32_t ) );
226     assert( pHeader );
227
228     // Verify header to validate DDS file
229     if ( pHeader->dwSize != sizeof(DDS_HEADER)
230          || pHeader->ddspf.dwSize != sizeof(DDS_PIXELFORMAT) )
231     {
232         return E_FAIL;
233     }
234
235     metadata.mipLevels = pHeader->dwMipMapCount;
236     if ( metadata.mipLevels == 0 )
237         metadata.mipLevels = 1;
238
239     // Check for DX10 extension
240     if ( (pHeader->ddspf.dwFlags & DDS_FOURCC)
241          && (MAKEFOURCC( 'D', 'X', '1', '0' ) == pHeader->ddspf.dwFourCC) )
242     {
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)) )
245         {
246             return E_FAIL;
247         }
248
249         const DDS_HEADER_DXT10* d3d10ext = reinterpret_cast<const DDS_HEADER_DXT10*>( (const uint8_t*)pSource + sizeof( uint32_t ) + sizeof(DDS_HEADER) );
250         if ( convFlags )
251             *convFlags |= CONV_FLAGS_DX10;
252
253         metadata.arraySize = d3d10ext->arraySize;
254         if ( metadata.arraySize == 0 )
255         {
256             return HRESULT_FROM_WIN32( ERROR_INVALID_DATA );
257         }
258
259         metadata.format = d3d10ext->dxgiFormat;
260         if ( !IsValid( metadata.format ) )
261         {
262             HRESULT_FROM_WIN32( ERROR_INVALID_DATA );
263         }
264
265         switch ( d3d10ext->resourceDimension )
266         {
267         case DDS_DIMENSION_TEXTURE1D:
268
269             // D3DX writes 1D textures with a fixed Height of 1
270             if ( (pHeader->dwFlags & DDS_HEIGHT) && pHeader->dwHeight != 1 )
271             {
272                 return HRESULT_FROM_WIN32( ERROR_INVALID_DATA );
273             }
274
275             metadata.width = pHeader->dwWidth;
276             metadata.height = 1;
277             metadata.depth = 1;
278             metadata.dimension = TEX_DIMENSION_TEXTURE1D;
279             break;
280
281         case DDS_DIMENSION_TEXTURE2D:
282             if ( d3d10ext->miscFlag & DDS_RESOURCE_MISC_TEXTURECUBE )
283             {
284                 metadata.miscFlags |= TEX_MISC_TEXTURECUBE;
285                 metadata.arraySize *= 6;
286             }
287
288             metadata.width = pHeader->dwWidth;
289             metadata.height = pHeader->dwHeight;
290             metadata.depth = 1;
291             metadata.dimension = TEX_DIMENSION_TEXTURE2D;
292             break;
293
294         case DDS_DIMENSION_TEXTURE3D:
295             if ( !(pHeader->dwFlags & DDS_HEADER_FLAGS_VOLUME) )
296             {
297                 return HRESULT_FROM_WIN32( ERROR_INVALID_DATA );
298             }
299
300             if ( metadata.arraySize > 1 )
301                 return HRESULT_FROM_WIN32( ERROR_NOT_SUPPORTED );
302
303             metadata.width = pHeader->dwWidth;
304             metadata.height = pHeader->dwHeight;
305             metadata.depth = pHeader->dwDepth;
306             metadata.dimension = TEX_DIMENSION_TEXTURE3D;
307             break;
308
309         default:
310             return HRESULT_FROM_WIN32( ERROR_INVALID_DATA );
311         }
312     }
313     else
314     {
315         metadata.arraySize = 1;
316
317         if ( pHeader->dwFlags & DDS_HEADER_FLAGS_VOLUME )
318         {
319             metadata.width = pHeader->dwWidth;
320             metadata.height = pHeader->dwHeight;
321             metadata.depth = pHeader->dwDepth;
322             metadata.dimension = TEX_DIMENSION_TEXTURE3D;
323         }
324         else 
325         {
326             if ( pHeader->dwCaps2 & DDS_CUBEMAP )
327             {
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 );
331
332                 metadata.arraySize = 6;
333                 metadata.miscFlags |= TEX_MISC_TEXTURECUBE;
334             }
335
336             metadata.width = pHeader->dwWidth;
337             metadata.height = pHeader->dwHeight;
338             metadata.depth = 1;
339             metadata.dimension = TEX_DIMENSION_TEXTURE2D;
340
341             // Note there's no way for a legacy Direct3D 9 DDS to express a '1D' texture
342         }
343
344         metadata.format = _GetDXGIFormat( pHeader->ddspf, flags, convFlags );
345
346         if ( metadata.format == DXGI_FORMAT_UNKNOWN )
347             return HRESULT_FROM_WIN32( ERROR_NOT_SUPPORTED );
348     }
349
350     // Special flag for handling BGR DXGI 1.1 formats
351     if (flags & DDS_FLAGS_FORCE_RGB)
352     {
353         switch ( metadata.format )
354         {
355         case DXGI_FORMAT_B8G8R8A8_UNORM:
356             metadata.format = DXGI_FORMAT_R8G8B8A8_UNORM;
357             if ( convFlags )
358                 *convFlags |= CONV_FLAGS_SWIZZLE;
359             break;
360
361         case DXGI_FORMAT_B8G8R8X8_UNORM:
362             metadata.format = DXGI_FORMAT_R8G8B8A8_UNORM;
363             if ( convFlags )
364                 *convFlags |= CONV_FLAGS_SWIZZLE | CONV_FLAGS_NOALPHA;
365             break;
366
367         case DXGI_FORMAT_B8G8R8A8_TYPELESS:
368             metadata.format = DXGI_FORMAT_R8G8B8A8_TYPELESS;
369             if ( convFlags )
370                 *convFlags |= CONV_FLAGS_SWIZZLE;
371             break;
372
373         case DXGI_FORMAT_B8G8R8A8_UNORM_SRGB:
374             metadata.format = DXGI_FORMAT_R8G8B8A8_UNORM_SRGB;
375             if ( convFlags )
376                 *convFlags |= CONV_FLAGS_SWIZZLE;
377             break;
378
379         case DXGI_FORMAT_B8G8R8X8_TYPELESS:
380             metadata.format = DXGI_FORMAT_R8G8B8A8_TYPELESS;
381             if ( convFlags )
382                 *convFlags |= CONV_FLAGS_SWIZZLE | CONV_FLAGS_NOALPHA;
383             break;
384
385         case DXGI_FORMAT_B8G8R8X8_UNORM_SRGB:
386             metadata.format = DXGI_FORMAT_R8G8B8A8_UNORM_SRGB;
387             if ( convFlags )
388                 *convFlags |= CONV_FLAGS_SWIZZLE | CONV_FLAGS_NOALPHA;
389             break;
390         }
391     }
392
393     // Special flag for handling 16bpp formats
394     if (flags & DDS_FLAGS_NO_16BPP)
395     {
396         switch ( metadata.format )
397         {
398         case DXGI_FORMAT_B5G6R5_UNORM:
399         case DXGI_FORMAT_B5G5R5A1_UNORM:
400 #ifdef DXGI_1_2_FORMATS
401         case DXGI_FORMAT_B4G4R4A4_UNORM:
402 #endif
403             metadata.format = DXGI_FORMAT_R8G8B8A8_UNORM;
404             if ( convFlags )
405             {
406                 *convFlags |= CONV_FLAGS_EXPAND;
407                 if ( metadata.format == DXGI_FORMAT_B5G6R5_UNORM )
408                     *convFlags |= CONV_FLAGS_NOALPHA;
409             }
410         }
411     }
412
413     return S_OK;
414 }
415
416
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 )
422 {
423     assert( IsValid( metadata.format ) && !IsVideo( metadata.format ) );
424
425     if ( metadata.arraySize > 1 )
426     {
427         if ( (metadata.arraySize != 6) || (metadata.dimension != TEX_DIMENSION_TEXTURE2D) || !(metadata.miscFlags & TEX_MISC_TEXTURECUBE) )
428         {
429             flags |= DDS_FLAGS_FORCE_DX10_EXT;
430         }
431     }
432
433     DDS_PIXELFORMAT ddpf = { 0 };
434     if ( !(flags & DDS_FLAGS_FORCE_DX10_EXT) )
435     {
436         switch( metadata.format )
437         {
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
457
458 #ifdef DXGI_1_2_FORMATS
459         case DXGI_FORMAT_B4G4R4A4_UNORM:        memcpy_s( &ddpf, sizeof(ddpf), &DDSPF_A4R4G4B4, sizeof(DDS_PIXELFORMAT) ); break;
460 #endif
461
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
465             break;
466         case DXGI_FORMAT_R16G16B16A16_FLOAT:
467             ddpf.dwSize = sizeof(DDS_PIXELFORMAT); ddpf.dwFlags = DDS_FOURCC; ddpf.dwFourCC = 113;  // D3DFMT_A16B16G16R16F
468             break;
469         case DXGI_FORMAT_R16G16B16A16_UNORM:
470             ddpf.dwSize = sizeof(DDS_PIXELFORMAT); ddpf.dwFlags = DDS_FOURCC; ddpf.dwFourCC = 36;  // D3DFMT_A16B16G16R16
471             break;
472         case DXGI_FORMAT_R16G16B16A16_SNORM:
473             ddpf.dwSize = sizeof(DDS_PIXELFORMAT); ddpf.dwFlags = DDS_FOURCC; ddpf.dwFourCC = 110;  // D3DFMT_Q16W16V16U16
474             break;
475         case DXGI_FORMAT_R32G32_FLOAT:
476             ddpf.dwSize = sizeof(DDS_PIXELFORMAT); ddpf.dwFlags = DDS_FOURCC; ddpf.dwFourCC = 115;  // D3DFMT_G32R32F
477             break;
478        case DXGI_FORMAT_R16G16_FLOAT:
479             ddpf.dwSize = sizeof(DDS_PIXELFORMAT); ddpf.dwFlags = DDS_FOURCC; ddpf.dwFourCC = 112;  // D3DFMT_G16R16F
480             break;
481         case DXGI_FORMAT_R32_FLOAT:
482             ddpf.dwSize = sizeof(DDS_PIXELFORMAT); ddpf.dwFlags = DDS_FOURCC; ddpf.dwFourCC = 114;  // D3DFMT_R32F
483             break;
484         case DXGI_FORMAT_R16_FLOAT:
485             ddpf.dwSize = sizeof(DDS_PIXELFORMAT); ddpf.dwFlags = DDS_FOURCC; ddpf.dwFourCC = 111;  // D3DFMT_R16F
486             break;
487         }
488     }
489
490     required = sizeof(uint32_t) + sizeof(DDS_HEADER);
491
492     if ( ddpf.dwSize == 0 )
493         required += sizeof(DDS_HEADER_DXT10);
494
495     if ( !pDestination )
496         return S_OK;
497
498     if ( maxsize < required )
499         return E_NOT_SUFFICIENT_BUFFER;
500
501     *reinterpret_cast<uint32_t*>(pDestination) = DDS_MAGIC;
502
503     DDS_HEADER* header = reinterpret_cast<DDS_HEADER*>( reinterpret_cast<uint8_t*>(pDestination) + sizeof(uint32_t) );
504     assert( header );
505
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;
510
511     if (metadata.mipLevels > 0)
512     {
513         header->dwFlags |= DDS_HEADER_FLAGS_MIPMAP;
514
515 #ifdef _AMD64_
516         if ( metadata.mipLevels > 0xFFFFFFFF )
517             return E_INVALIDARG;
518 #endif
519
520         header->dwMipMapCount = static_cast<uint32_t>( metadata.mipLevels );
521
522         if ( header->dwMipMapCount > 1 )
523             header->dwCaps |= DDS_SURFACE_FLAGS_MIPMAP;
524     }
525
526     switch( metadata.dimension )
527     {
528     case TEX_DIMENSION_TEXTURE1D:
529 #ifdef _AMD64_
530         if ( metadata.height > 0xFFFFFFFF )
531             return E_INVALIDARG;
532 #endif
533
534         header->dwWidth = static_cast<uint32_t>( metadata.width ); 
535         header->dwHeight = header->dwDepth = 1;
536         break;
537
538     case TEX_DIMENSION_TEXTURE2D:
539 #ifdef _AMD64_
540         if ( metadata.height > 0xFFFFFFFF
541              || metadata.width > 0xFFFFFFFF)
542             return E_INVALIDARG;
543 #endif
544
545         header->dwHeight = static_cast<uint32_t>( metadata.height ); 
546         header->dwWidth = static_cast<uint32_t>( metadata.width );
547         header->dwDepth = 1;
548
549         if ( metadata.miscFlags & TEX_MISC_TEXTURECUBE )
550         {
551             header->dwCaps |= DDS_SURFACE_FLAGS_CUBEMAP;
552             header->dwCaps2 |= DDS_CUBEMAP_ALLFACES;
553         }
554         break;
555
556     case TEX_DIMENSION_TEXTURE3D:
557 #ifdef _AMD64_
558         if ( metadata.height > 0xFFFFFFFF
559              || metadata.width > 0xFFFFFFFF
560              || metadata.depth > 0xFFFFFFFF )
561             return E_INVALIDARG;
562 #endif
563
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 );
569         break;
570
571     default:
572         return E_FAIL;
573     }
574
575     size_t rowPitch, slicePitch;
576     ComputePitch( metadata.format, metadata.width, metadata.height, rowPitch, slicePitch, CP_FLAGS_NONE );
577
578 #ifdef _AMD64_
579     if ( slicePitch > 0xFFFFFFFF
580          || rowPitch > 0xFFFFFFFF )
581         return E_FAIL;
582 #endif
583
584     if ( IsCompressed( metadata.format ) )
585     {
586         header->dwFlags |= DDS_HEADER_FLAGS_LINEARSIZE;
587         header->dwPitchOrLinearSize = static_cast<uint32_t>( slicePitch );
588     }
589     else
590     {
591         header->dwFlags |= DDS_HEADER_FLAGS_PITCH;
592         header->dwPitchOrLinearSize = static_cast<uint32_t>( rowPitch );
593     }
594
595     if ( ddpf.dwSize == 0 )
596     {
597         memcpy_s( &header->ddspf, sizeof(header->ddspf), &DDSPF_DX10, sizeof(DDS_PIXELFORMAT) );
598
599         DDS_HEADER_DXT10* ext = reinterpret_cast<DDS_HEADER_DXT10*>( reinterpret_cast<uint8_t*>(header) + sizeof(DDS_HEADER) );
600         assert( ext );
601
602         memset( ext, 0, sizeof(DDS_HEADER_DXT10) );
603         ext->dxgiFormat = metadata.format;
604         ext->resourceDimension = metadata.dimension;
605
606 #ifdef _AMD64_
607         if ( metadata.arraySize > 0xFFFFFFFF )
608             return E_INVALIDARG;
609 #endif
610
611         if ( metadata.miscFlags & TEX_MISC_TEXTURECUBE )
612         {
613             ext->miscFlag |= TEX_MISC_TEXTURECUBE;
614             assert( (metadata.arraySize % 6) == 0 );
615             ext->arraySize = static_cast<UINT>( metadata.arraySize / 6 );
616         }
617         else
618         {
619             ext->arraySize = static_cast<UINT>( metadata.arraySize );
620         }
621     }
622     else
623     {
624         memcpy_s( &header->ddspf, sizeof(header->ddspf), &ddpf, sizeof(ddpf) );
625     }
626
627     return S_OK;
628 }
629
630
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
636 {
637     TEXP_LEGACY_UNKNOWN     = 0,
638     TEXP_LEGACY_R8G8B8,
639     TEXP_LEGACY_R3G3B2,
640     TEXP_LEGACY_A8R3G3B2,
641     TEXP_LEGACY_P8,
642     TEXP_LEGACY_A8P8,
643     TEXP_LEGACY_A4L4,
644     TEXP_LEGACY_B4G4R4A4,
645 };
646
647 inline static TEXP_LEGACY_FORMAT _FindLegacyFormat( DWORD flags )
648 {
649     TEXP_LEGACY_FORMAT lformat = TEXP_LEGACY_UNKNOWN;
650
651     if ( flags & CONV_FLAGS_PAL8 )
652     {
653         lformat = ( flags & CONV_FLAGS_A8P8 ) ? TEXP_LEGACY_A8P8 : TEXP_LEGACY_P8;
654     }
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;
666 #endif
667
668     return lformat;
669 }
670
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 )
674 {
675     assert( pDestination && outSize > 0 );
676     assert( pSource && inSize > 0 );
677     assert( IsValid(outFormat) && !IsVideo(outFormat) );
678
679     switch( inFormat )
680     {
681     case TEXP_LEGACY_R8G8B8:
682         if ( outFormat != DXGI_FORMAT_R8G8B8A8_UNORM )
683             return false;
684
685         // D3DFMT_R8G8B8 -> DXGI_FORMAT_R8G8B8A8_UNORM
686         {
687             const uint8_t * __restrict sPtr = reinterpret_cast<const uint8_t*>(pSource);
688             uint32_t * __restrict dPtr = reinterpret_cast<uint32_t*>(pDestination);
689
690             for( size_t ocount = 0, icount = 0; ((icount < inSize) && (ocount < outSize)); icount += 3, ocount += 4 )
691             {
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);
696
697                 *(dPtr++) = t1 | t2 | t3 | 0xff000000;
698                 sPtr += 3;
699             }
700         }
701         return true;
702
703     case TEXP_LEGACY_R3G3B2:
704         switch( outFormat )
705         {
706         case DXGI_FORMAT_R8G8B8A8_UNORM:
707             // D3DFMT_R3G3B2 -> DXGI_FORMAT_R8G8B8A8_UNORM
708             {
709                 const uint8_t* __restrict sPtr = reinterpret_cast<const uint8_t*>(pSource);
710                 uint32_t * __restrict dPtr = reinterpret_cast<uint32_t*>(pDestination);
711
712                 for( size_t ocount = 0, icount = 0; ((icount < inSize) && (ocount < outSize)); ++icount, ocount += 4 )
713                 {
714                     uint8_t t = *(sPtr++);
715
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);
719
720                     *(dPtr++) = t1 | t2 | t3 | 0xff000000;
721                 }
722             }
723             return true;
724
725         case DXGI_FORMAT_B5G6R5_UNORM:
726             // D3DFMT_R3G3B2 -> DXGI_FORMAT_B5G6R5_UNORM
727             {
728                 const uint8_t* __restrict sPtr = reinterpret_cast<const uint8_t*>(pSource);
729                 uint16_t * __restrict dPtr = reinterpret_cast<uint16_t*>(pDestination);
730
731                 for( size_t ocount = 0, icount = 0; ((icount < inSize) && (ocount < outSize)); ++icount, ocount += 2 )
732                 {
733                     uint8_t t = *(sPtr++);
734
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);
738
739                     *(dPtr++) = t1 | t2 | t3;
740                 }
741             }
742             return true;
743         }
744         break;
745
746     case TEXP_LEGACY_A8R3G3B2:
747         if ( outFormat != DXGI_FORMAT_R8G8B8A8_UNORM )
748             return false;
749
750         // D3DFMT_A8R3G3B2 -> DXGI_FORMAT_R8G8B8A8_UNORM
751         {
752             const uint16_t* __restrict sPtr = reinterpret_cast<const uint16_t*>(pSource);
753             uint32_t * __restrict dPtr = reinterpret_cast<uint32_t*>(pDestination);
754
755             for( size_t ocount = 0, icount = 0; ((icount < inSize) && (ocount < outSize)); icount += 2, ocount += 4 )
756             {
757                 uint16_t t = *(sPtr++);
758
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);
763
764                 *(dPtr++) = t1 | t2 | t3 | ta;
765             }
766         }
767         return true;
768
769     case TEXP_LEGACY_P8:
770         if ( (outFormat != DXGI_FORMAT_R8G8B8A8_UNORM) || !pal8 )
771             return false;
772
773         // D3DFMT_P8 -> DXGI_FORMAT_R8G8B8A8_UNORM
774         {
775             const uint8_t* __restrict sPtr = reinterpret_cast<const uint8_t*>(pSource);
776             uint32_t * __restrict dPtr = reinterpret_cast<uint32_t*>(pDestination);
777
778             for( size_t ocount = 0, icount = 0; ((icount < inSize) && (ocount < outSize)); ++icount, ocount += 4 )
779             {
780                 uint8_t t = *(sPtr++);
781
782                 *(dPtr++) = pal8[ t ];
783             }
784         }
785         return true;
786
787     case TEXP_LEGACY_A8P8:
788         if ( (outFormat != DXGI_FORMAT_R8G8B8A8_UNORM) || !pal8 )
789             return false;
790
791         // D3DFMT_A8P8 -> DXGI_FORMAT_R8G8B8A8_UNORM
792         {
793             const uint16_t* __restrict sPtr = reinterpret_cast<const uint16_t*>(pSource);
794             uint32_t * __restrict dPtr = reinterpret_cast<uint32_t*>(pDestination);
795
796             for( size_t ocount = 0, icount = 0; ((icount < inSize) && (ocount < outSize)); icount += 2, ocount += 4 )
797             {
798                 uint16_t t = *(sPtr++);
799
800                 uint32_t t1 = pal8[ t & 0xff ];
801                 uint32_t ta = ( flags & TEXP_SCANLINE_SETALPHA ) ? 0xff000000 : ((t & 0xff00) << 16);
802
803                 *(dPtr++) = t1 | ta;
804             }
805         }
806         return true;
807
808     case TEXP_LEGACY_A4L4:
809         switch( outFormat )
810         {
811 #ifdef DXGI_1_2_FORMATS
812         case DXGI_FORMAT_B4G4R4A4_UNORM :
813             // D3DFMT_A4L4 -> DXGI_FORMAT_B4G4R4A4_UNORM 
814             {
815                 const uint8_t * __restrict sPtr = reinterpret_cast<const uint8_t*>(pSource);
816                 uint16_t * __restrict dPtr = reinterpret_cast<uint16_t*>(pDestination);
817
818                 for( size_t ocount = 0, icount = 0; ((icount < inSize) && (ocount < outSize)); ++icount, ocount += 2 )
819                 {
820                     uint8_t t = *(sPtr++);
821
822                     uint16_t t1 = (t & 0x0f);
823                     uint16_t ta = ( flags & TEXP_SCANLINE_SETALPHA ) ? 0xf000 : ((t & 0xf0) << 8);
824
825                     *(dPtr++) = t1 | (t1 << 4) | (t1 << 8) | ta;
826                 }
827             }
828             return true;
829 #endif // DXGI_1_2_FORMATS
830
831         case DXGI_FORMAT_R8G8B8A8_UNORM:
832             // D3DFMT_A4L4 -> DXGI_FORMAT_R8G8B8A8_UNORM
833             {
834                 const uint8_t * __restrict sPtr = reinterpret_cast<const uint8_t*>(pSource);
835                 uint32_t * __restrict dPtr = reinterpret_cast<uint32_t*>(pDestination);
836
837                 for( size_t ocount = 0, icount = 0; ((icount < inSize) && (ocount < outSize)); ++icount, ocount += 4 )
838                 {
839                     uint8_t t = *(sPtr++);
840
841                     uint32_t t1 = ((t & 0x0f) << 4) | (t & 0x0f);
842                     uint32_t ta = ( flags & TEXP_SCANLINE_SETALPHA ) ? 0xff000000 : (((t & 0xf0) << 24) | ((t & 0xf0) << 20));
843
844                     *(dPtr++) = t1 | (t1 << 8) | (t1 << 16) | ta;
845                 }
846             }
847             return true;
848         }
849         break;
850
851 #ifndef DXGI_1_2_FORMATS
852     case TEXP_LEGACY_B4G4R4A4:
853         if (outFormat != DXGI_FORMAT_R8G8B8A8_UNORM)
854             return false;
855
856         // D3DFMT_A4R4G4B4 -> DXGI_FORMAT_R8G8B8A8_UNORM
857         {
858             const uint16_t * __restrict sPtr = reinterpret_cast<const uint16_t*>(pSource);
859             uint32_t * __restrict dPtr = reinterpret_cast<uint32_t*>(pDestination);
860
861             for( size_t ocount = 0, icount = 0; ((icount < inSize) && (ocount < outSize)); icount += 2, ocount += 4 )
862             {
863                 uint16_t t = *(sPtr++);
864
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));
869
870                 *(dPtr++) = t1 | t2 | t3 | ta;
871             }
872         }
873         return true;
874 #endif
875     }
876
877     return false;
878 }
879
880
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 )
886 {
887     assert( pPixels );
888     assert( image.GetPixels() );
889
890     if ( !size )
891         return E_FAIL;
892
893     if ( convFlags & CONV_FLAGS_EXPAND )
894     {
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;
901     }
902
903     size_t pixelSize, nimages;
904     _DetermineImageArray( metadata, cpFlags, nimages, pixelSize );
905     if ( (nimages == 0) || (nimages != image.GetImageCount()) )
906     {
907         return E_FAIL;
908     }
909
910     assert( pixelSize <= size );
911
912     std::unique_ptr<Image[]> timages( new Image[nimages] );
913     if ( !_SetupImageArray( (uint8_t*)pPixels, size, metadata, cpFlags, timages.get(), nimages ) )
914     {
915         return E_FAIL;
916     }
917
918     if ( nimages != image.GetImageCount() )
919     {
920         return E_FAIL;
921     }
922
923     const Image* images = image.GetImages();
924     if ( !images )
925     {
926         return E_FAIL;
927     }
928
929     DWORD tflags = (convFlags & CONV_FLAGS_NOALPHA) ? TEXP_SCANLINE_SETALPHA : 0;
930     if ( convFlags & CONV_FLAGS_SWIZZLE )
931         tflags |= TEXP_SCANLINE_LEGACY;
932
933     switch (metadata.dimension)
934     {
935     case TEX_DIMENSION_TEXTURE1D:
936     case TEX_DIMENSION_TEXTURE2D:
937         {
938             size_t index = 0;
939             for( size_t item = 0; item < metadata.arraySize; ++item )
940             {
941                 for( size_t level = 0; level < metadata.mipLevels; ++level, ++index )
942                 {
943                     if ( index >= nimages )
944                         return E_FAIL;
945
946                     if ( images[ index ].height != timages[ index ].height )
947                         return E_FAIL;
948
949                     size_t dpitch = images[ index ].rowPitch;
950                     size_t spitch = timages[ index ].rowPitch;
951
952                     const uint8_t *pSrc = const_cast<const uint8_t*>( timages[ index ].pixels );
953                     if ( !pSrc )
954                         return E_POINTER;
955
956                     uint8_t *pDest = images[ index ].pixels;
957                     if ( !pDest )
958                         return E_POINTER;
959
960                     if ( IsCompressed( metadata.format ) )
961                     {
962                         size_t csize = std::min<size_t>( images[ index ].slicePitch, timages[ index ].slicePitch );
963                         memcpy_s( pDest, images[ index ].slicePitch, pSrc, csize );
964                     }
965                     else
966                     {
967                         for( size_t h = 0; h < images[ index ].height; ++h )
968                         {
969                             if ( convFlags & CONV_FLAGS_EXPAND )
970                             {
971 #ifdef DXGI_1_2_FORMATS
972                                 if ( convFlags & (CONV_FLAGS_565|CONV_FLAGS_5551|CONV_FLAGS_4444) )
973 #else
974                                 if ( convFlags & (CONV_FLAGS_565|CONV_FLAGS_5551) )
975 #endif
976                                 {
977                                     if ( !_ExpandScanline( pDest, dpitch, DXGI_FORMAT_R8G8B8A8_UNORM,
978                                                            pSrc, spitch,
979                                                            (convFlags & CONV_FLAGS_565) ? DXGI_FORMAT_B5G6R5_UNORM : DXGI_FORMAT_B5G5R5A1_UNORM,
980                                                            tflags ) )
981                                         return E_FAIL;
982                                 }
983                                 else
984                                 {
985                                     TEXP_LEGACY_FORMAT lformat = _FindLegacyFormat( convFlags );
986                                     if ( !_LegacyExpandScanline( pDest, dpitch, metadata.format,
987                                                                  pSrc, spitch, lformat, pal8,
988                                                                  tflags ) )
989                                         return E_FAIL;
990                                 }
991                             }
992                             else if ( convFlags & CONV_FLAGS_SWIZZLE )
993                             {
994                                 _SwizzleScanline( pDest, dpitch, pSrc, spitch,
995                                                   metadata.format, tflags );
996                             }
997                             else
998                             {
999                                 _CopyScanline( pDest, dpitch, pSrc, spitch,
1000                                                metadata.format, tflags );
1001                             }
1002
1003                             pSrc += spitch;
1004                             pDest += dpitch;
1005                         }
1006                     }
1007                 }
1008             }
1009         }
1010         break;
1011
1012     case TEX_DIMENSION_TEXTURE3D:
1013         {
1014             size_t index = 0;
1015             size_t d = metadata.depth;
1016
1017             for( size_t level = 0; level < metadata.mipLevels; ++level )
1018             {
1019                 for( size_t slice = 0; slice < d; ++slice, ++index )
1020                 {
1021                     if ( index >= nimages )
1022                         return E_FAIL;
1023
1024                     if ( images[ index ].height != timages[ index ].height )
1025                         return E_FAIL;
1026
1027                     size_t dpitch = images[ index ].rowPitch;
1028                     size_t spitch = timages[ index ].rowPitch;
1029
1030                     const uint8_t *pSrc = const_cast<const uint8_t*>( timages[ index ].pixels );
1031                     if ( !pSrc )
1032                         return E_POINTER;
1033
1034                     uint8_t *pDest = images[ index ].pixels;
1035                     if ( !pDest )
1036                         return E_POINTER;
1037
1038                     if ( IsCompressed( metadata.format ) )
1039                     {
1040                         size_t csize = std::min<size_t>( images[ index ].slicePitch, timages[ index ].slicePitch );
1041                         memcpy_s( pDest, images[ index ].slicePitch, pSrc, csize );
1042                     }
1043                     else
1044                     {
1045                         for( size_t h = 0; h < images[ index ].height; ++h )
1046                         {
1047                             if ( convFlags & CONV_FLAGS_EXPAND )
1048                             {
1049 #ifdef DXGI_1_2_FORMATS
1050                                 if ( convFlags & (CONV_FLAGS_565|CONV_FLAGS_5551|CONV_FLAGS_4444) )
1051 #else
1052                                 if ( convFlags & (CONV_FLAGS_565|CONV_FLAGS_5551) )
1053 #endif
1054                                 {
1055                                     if ( !_ExpandScanline( pDest, dpitch, DXGI_FORMAT_R8G8B8A8_UNORM,
1056                                                            pSrc, spitch,
1057                                                            (convFlags & CONV_FLAGS_565) ? DXGI_FORMAT_B5G6R5_UNORM : DXGI_FORMAT_B5G5R5A1_UNORM,
1058                                                            tflags ) )
1059                                         return E_FAIL;
1060                                 }
1061                                 else
1062                                 {
1063                                     TEXP_LEGACY_FORMAT lformat = _FindLegacyFormat( convFlags );
1064                                     if ( !_LegacyExpandScanline( pDest, dpitch, metadata.format,
1065                                                                  pSrc, spitch, lformat, pal8,
1066                                                                  tflags ) )
1067                                         return E_FAIL;
1068                                 }
1069                             }
1070                             else if ( convFlags & CONV_FLAGS_SWIZZLE )
1071                             {
1072                                 _SwizzleScanline( pDest, dpitch, pSrc, spitch, metadata.format, tflags );
1073                             }
1074                             else
1075                             {
1076                                 _CopyScanline( pDest, dpitch, pSrc, spitch, metadata.format, tflags );
1077                             }
1078
1079                             pSrc += spitch;
1080                             pDest += dpitch;
1081                         }
1082                     }
1083                 }
1084
1085                 if ( d > 1 )
1086                     d >>= 1;
1087             }
1088         }
1089         break;
1090
1091     default:
1092         return E_FAIL;
1093     }
1094
1095     return S_OK;
1096 }
1097
1098 static HRESULT _CopyImageInPlace( DWORD convFlags, _In_ const ScratchImage& image )
1099 {
1100     if ( !image.GetPixels() )
1101         return E_FAIL;
1102
1103     const Image* images = image.GetImages();
1104     if ( !images )
1105         return E_FAIL;
1106
1107     const TexMetadata& metadata = image.GetMetadata();
1108
1109     DWORD tflags = (convFlags & CONV_FLAGS_NOALPHA) ? TEXP_SCANLINE_SETALPHA : 0;
1110     if ( convFlags & CONV_FLAGS_SWIZZLE )
1111         tflags |= TEXP_SCANLINE_LEGACY;
1112
1113     for( size_t i = 0; i < image.GetImageCount(); ++i )
1114     {
1115         const Image* img = &images[ i ];
1116         uint8_t *pPixels = img->pixels;
1117         if ( !pPixels )
1118             return E_POINTER;
1119
1120         size_t rowPitch = img->rowPitch;
1121
1122         for( size_t h = 0; h < img->height; ++h )
1123         {
1124             if ( convFlags & CONV_FLAGS_SWIZZLE )
1125             {
1126                 _SwizzleScanline( pPixels, rowPitch, pPixels, rowPitch, metadata.format, tflags );
1127             }
1128             else
1129             {
1130                 _CopyScanline( pPixels, rowPitch, pPixels, rowPitch, metadata.format, tflags );
1131             }
1132
1133             pPixels += rowPitch;
1134         }
1135     }
1136
1137     return S_OK;
1138 }
1139
1140
1141 //=====================================================================================
1142 // Entry-points
1143 //=====================================================================================
1144
1145 //-------------------------------------------------------------------------------------
1146 // Obtain metadata from DDS file in memory/on disk
1147 //-------------------------------------------------------------------------------------
1148
1149 HRESULT GetMetadataFromDDSMemory( LPCVOID pSource, size_t size, DWORD flags, TexMetadata& metadata )
1150 {
1151     if ( !pSource || size == 0 )
1152         return E_INVALIDARG;
1153
1154     return _DecodeDDSHeader( pSource, size, flags, metadata, 0 );
1155 }
1156
1157 HRESULT GetMetadataFromDDSFile( LPCWSTR szFile, DWORD flags, TexMetadata& metadata )
1158 {
1159     if ( !szFile )
1160         return E_INVALIDARG;
1161
1162 #if (_WIN32_WINNT >= 0x0602 /*_WIN32_WINNT_WIN8*/)
1163     ScopedHandle hFile( safe_handle( CreateFile2( szFile, GENERIC_READ, FILE_SHARE_READ, OPEN_EXISTING, 0 ) ) );
1164 #else
1165     ScopedHandle hFile( safe_handle( CreateFileW( szFile, GENERIC_READ, FILE_SHARE_READ, 0, OPEN_EXISTING,
1166                                                   FILE_FLAG_SEQUENTIAL_SCAN, 0 ) ) );
1167 #endif
1168     if ( !hFile )
1169     {
1170         return HRESULT_FROM_WIN32( GetLastError() );
1171     }
1172
1173     // Get the file size
1174     LARGE_INTEGER fileSize = {0};
1175
1176 #if (_WIN32_WINNT >= _WIN32_WINNT_VISTA)
1177     FILE_STANDARD_INFO fileInfo;
1178     if ( !GetFileInformationByHandleEx( hFile.get(), FileStandardInfo, &fileInfo, sizeof(fileInfo) ) )
1179     {
1180         return HRESULT_FROM_WIN32( GetLastError() );
1181     }
1182     fileSize = fileInfo.EndOfFile;
1183 #else
1184     if ( !GetFileSizeEx( hFile.get(), &fileSize ) )
1185     {
1186         return HRESULT_FROM_WIN32( GetLastError() );
1187     }
1188 #endif
1189
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 )
1192     {
1193         return HRESULT_FROM_WIN32( ERROR_FILE_TOO_LARGE );
1194     }
1195
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) ) )
1198     {
1199         return E_FAIL;
1200     }
1201
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];
1205
1206     DWORD bytesRead = 0;
1207     if ( !ReadFile( hFile.get(), header, MAX_HEADER_SIZE, &bytesRead, 0 ) )
1208     {
1209         return HRESULT_FROM_WIN32( GetLastError() );
1210     }
1211
1212     return _DecodeDDSHeader( header, bytesRead, flags, metadata, 0 );
1213 }
1214
1215
1216 //-------------------------------------------------------------------------------------
1217 // Load a DDS file in memory
1218 //-------------------------------------------------------------------------------------
1219 HRESULT LoadFromDDSMemory( LPCVOID pSource, size_t size, DWORD flags, TexMetadata* metadata, ScratchImage& image )
1220 {
1221     if ( !pSource || size == 0 )
1222         return E_INVALIDARG;
1223
1224     image.Release();
1225
1226     DWORD convFlags = 0;
1227     TexMetadata mdata;
1228     HRESULT hr = _DecodeDDSHeader( pSource, size, flags, mdata, &convFlags );  
1229     if ( FAILED(hr) )
1230         return hr;
1231
1232     size_t offset = sizeof(uint32_t) + sizeof(DDS_HEADER);
1233     if ( convFlags & CONV_FLAGS_DX10 )
1234         offset += sizeof(DDS_HEADER_DXT10);
1235
1236     assert( offset <= size );
1237
1238     const uint32_t *pal8 = nullptr;
1239     if ( convFlags & CONV_FLAGS_PAL8 )
1240     {
1241         pal8 = reinterpret_cast<const uint32_t*>( reinterpret_cast<const uint8_t*>(pSource) + offset );
1242         assert( pal8 );
1243         offset += ( 256 * sizeof(uint32_t) );
1244         if ( size < offset )
1245             return E_FAIL;
1246     }
1247
1248     hr = image.Initialize( mdata );
1249     if ( FAILED(hr) )
1250         return hr;
1251
1252     LPCVOID pPixels = reinterpret_cast<LPCVOID>( reinterpret_cast<const uint8_t*>(pSource) + offset );
1253     assert( pPixels );
1254     hr = _CopyImage( pPixels, size - offset, mdata,
1255                      (flags & DDS_FLAGS_LEGACY_DWORD) ? CP_FLAGS_LEGACY_DWORD : CP_FLAGS_NONE, convFlags, pal8, image );
1256     if ( FAILED(hr) )
1257     {
1258         image.Release();
1259         return hr;
1260     }
1261     if ( metadata )
1262         memcpy( metadata, &mdata, sizeof(TexMetadata) );
1263
1264     return S_OK;
1265 }
1266
1267
1268 //-------------------------------------------------------------------------------------
1269 // Load a DDS file from disk
1270 //-------------------------------------------------------------------------------------
1271 HRESULT LoadFromDDSFile( LPCWSTR szFile, DWORD flags, TexMetadata* metadata, ScratchImage& image )
1272 {
1273     if ( !szFile )
1274         return E_INVALIDARG;
1275
1276     image.Release();
1277
1278 #if (_WIN32_WINNT >= 0x0602 /*_WIN32_WINNT_WIN8*/)
1279     ScopedHandle hFile( safe_handle ( CreateFile2( szFile, GENERIC_READ, FILE_SHARE_READ, OPEN_EXISTING, 0 ) ) );
1280 #else
1281     ScopedHandle hFile( safe_handle ( CreateFileW( szFile, GENERIC_READ, FILE_SHARE_READ, 0, OPEN_EXISTING,
1282                                                    FILE_FLAG_SEQUENTIAL_SCAN, 0 ) ) );
1283 #endif
1284
1285     if ( !hFile )
1286     {
1287         return HRESULT_FROM_WIN32( GetLastError() );
1288     }
1289
1290     // Get the file size
1291     LARGE_INTEGER fileSize = {0};
1292
1293 #if (_WIN32_WINNT >= _WIN32_WINNT_VISTA)
1294     FILE_STANDARD_INFO fileInfo;
1295     if ( !GetFileInformationByHandleEx( hFile.get(), FileStandardInfo, &fileInfo, sizeof(fileInfo) ) )
1296     {
1297         return HRESULT_FROM_WIN32( GetLastError() );
1298     }
1299     fileSize = fileInfo.EndOfFile;
1300 #else
1301     if ( !GetFileSizeEx( hFile.get(), &fileSize ) )
1302     {
1303         return HRESULT_FROM_WIN32( GetLastError() );
1304     }
1305 #endif
1306
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 )
1309     {
1310         return HRESULT_FROM_WIN32( ERROR_FILE_TOO_LARGE );
1311     }
1312
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) ) )
1315     {
1316         return E_FAIL;
1317     }
1318
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];
1322
1323     DWORD bytesRead = 0;
1324     if ( !ReadFile( hFile.get(), header, MAX_HEADER_SIZE, &bytesRead, 0 ) )
1325     {
1326         return HRESULT_FROM_WIN32( GetLastError() );
1327     }
1328
1329     DWORD convFlags = 0;
1330     TexMetadata mdata;
1331     HRESULT hr = _DecodeDDSHeader( header, bytesRead, flags, mdata, &convFlags );
1332     if ( FAILED(hr) )
1333         return hr;
1334
1335     DWORD offset = MAX_HEADER_SIZE;
1336
1337     if ( !(convFlags & CONV_FLAGS_DX10) )
1338     {
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 ) )
1342         {
1343             return HRESULT_FROM_WIN32( GetLastError() );
1344         }
1345
1346         offset = sizeof(uint32_t) + sizeof(DDS_HEADER);
1347     }
1348
1349     std::unique_ptr<uint32_t[]> pal8;
1350     if ( convFlags & CONV_FLAGS_PAL8 )
1351     {
1352         pal8.reset( new uint32_t[256] );
1353         if ( !pal8 )
1354         {
1355             return E_OUTOFMEMORY;
1356         }
1357
1358         if ( !ReadFile( hFile.get(), pal8.get(), 256 * sizeof(uint32_t), &bytesRead, 0 ) )
1359         {
1360             return HRESULT_FROM_WIN32( GetLastError() );
1361         }
1362
1363         if ( bytesRead != (256 * sizeof(uint32_t)) )
1364         {
1365             return E_FAIL;
1366         }
1367
1368         offset += ( 256 * sizeof(uint32_t) );
1369     }
1370
1371     DWORD remaining = fileSize.LowPart - offset;
1372     if ( remaining == 0 )
1373         return E_FAIL;
1374
1375     hr = image.Initialize( mdata );
1376     if ( FAILED(hr) )
1377         return hr;
1378
1379     if ( (convFlags & CONV_FLAGS_EXPAND) || (flags & DDS_FLAGS_LEGACY_DWORD) )
1380     {
1381         std::unique_ptr<uint8_t[]> temp( new uint8_t[ remaining ] );
1382         if ( !temp )
1383         {
1384             image.Release();
1385             return E_OUTOFMEMORY;
1386         }
1387
1388         if ( !ReadFile( hFile.get(), temp.get(), remaining, &bytesRead, 0 ) )
1389         {
1390             image.Release();
1391             return HRESULT_FROM_WIN32( GetLastError() );
1392         }
1393
1394         if ( bytesRead != remaining )
1395         {
1396             image.Release();
1397             return E_FAIL;
1398         }
1399
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 );
1403         if ( FAILED(hr) )
1404         {
1405             image.Release();
1406             return hr;
1407         }
1408     }
1409     else
1410     {
1411         if ( remaining > image.GetPixelsSize() )
1412         {
1413             image.Release();
1414             return E_FAIL;
1415         }
1416
1417         if ( !ReadFile( hFile.get(), image.GetPixels(), static_cast<DWORD>( image.GetPixelsSize() ), &bytesRead, 0 ) )
1418         {
1419             image.Release();
1420             return HRESULT_FROM_WIN32( GetLastError() );
1421         }
1422
1423         if ( convFlags & (CONV_FLAGS_SWIZZLE|CONV_FLAGS_NOALPHA) )
1424         {
1425             // Swizzle/copy image in place
1426             hr = _CopyImageInPlace( convFlags, image );
1427             if ( FAILED(hr) )
1428             {
1429                 image.Release();
1430                 return hr;
1431             }
1432         }
1433     }
1434
1435     if ( metadata )
1436         memcpy( metadata, &mdata, sizeof(TexMetadata) );
1437
1438     return S_OK;
1439 }
1440
1441
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 )
1446 {
1447     if ( !images || (nimages == 0) )
1448         return E_INVALIDARG;
1449
1450     // Determine memory required
1451     size_t required = 0;
1452     HRESULT hr = _EncodeDDSHeader( metadata, flags, 0, 0, required );
1453     if ( FAILED(hr) )
1454         return hr;
1455
1456     for( size_t i = 0; i < nimages; ++i )
1457     {
1458         required += images[ i ].slicePitch;
1459         if ( !images[ i ].pixels )
1460             return E_POINTER;
1461     }
1462
1463     assert( required > 0 );
1464
1465     blob.Release();
1466
1467     hr = blob.Initialize( required );
1468     if ( FAILED(hr) )
1469         return hr;
1470
1471     uint8_t* pDestination = reinterpret_cast<uint8_t*>( blob.GetBufferPointer() );
1472     assert( pDestination );
1473
1474     hr = _EncodeDDSHeader( metadata, flags, pDestination, blob.GetBufferSize(), required );
1475     if ( FAILED(hr) )
1476     {
1477         blob.Release();
1478         return hr;
1479     }
1480
1481     size_t remaining = blob.GetBufferSize() - required;
1482     pDestination += required;
1483
1484     if ( !remaining )
1485     {
1486         blob.Release();
1487         return E_FAIL;
1488     }
1489
1490     switch( metadata.dimension )
1491     {
1492     case DDS_DIMENSION_TEXTURE1D:
1493     case DDS_DIMENSION_TEXTURE2D:
1494         {
1495             size_t index = 0;
1496             for( size_t item = 0; item < metadata.arraySize; ++item )
1497             {
1498                 for( size_t level = 0; level < metadata.mipLevels; ++level )
1499                 {
1500                     if ( index >= nimages )
1501                     {
1502                         blob.Release();
1503                         return E_FAIL;
1504                     }
1505
1506                     size_t pixsize = images[ index ].slicePitch;
1507                     if ( memcpy_s( pDestination, remaining, images[ index ].pixels, pixsize ) )
1508                     {
1509                         blob.Release();
1510                         return E_FAIL;
1511                     }
1512                     pDestination += pixsize;
1513                     remaining -= pixsize;
1514
1515                     ++index;
1516                 }
1517             }
1518         }
1519         break;
1520
1521     case DDS_DIMENSION_TEXTURE3D:
1522         {
1523             if ( metadata.arraySize != 1 )
1524             {
1525                 blob.Release();
1526                 return E_FAIL;
1527             }
1528
1529             size_t d = metadata.depth;
1530
1531             size_t index = 0;
1532             for( size_t level = 0; level < metadata.mipLevels; ++level )
1533             {
1534                 for( size_t slice = 0; slice < d; ++slice )
1535                 {
1536                     if ( index >= nimages )
1537                     {
1538                         blob.Release();
1539                         return E_FAIL;
1540                     }
1541
1542                     size_t pixsize = images[ index ].slicePitch;
1543                     if ( memcpy_s( pDestination, remaining, images[ index ].pixels, pixsize ) )
1544                     {
1545                         blob.Release();
1546                         return E_FAIL;
1547                     }
1548                     pDestination += pixsize;
1549                     remaining -= pixsize;
1550
1551                     ++index;
1552                 }
1553
1554                 if ( d > 1 )
1555                     d >>= 1;
1556             }
1557         }
1558         break;
1559
1560     default:
1561         blob.Release();
1562         return E_FAIL;
1563     }
1564
1565     return S_OK;
1566 }
1567
1568
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 )
1573 {
1574     if ( !szFile )
1575         return E_INVALIDARG;
1576
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];
1580     size_t required;
1581     HRESULT hr = _EncodeDDSHeader( metadata, flags, header, MAX_HEADER_SIZE, required );
1582     if ( FAILED(hr) )
1583         return hr;
1584
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 ) ) );
1588 #else
1589     ScopedHandle hFile( safe_handle( CreateFileW( szFile, GENERIC_WRITE, 0, 0, CREATE_ALWAYS, 0, 0 ) ) );
1590 #endif
1591     if ( !hFile )
1592     {
1593         return HRESULT_FROM_WIN32( GetLastError() );
1594     }
1595
1596     DWORD bytesWritten;
1597     if ( !WriteFile( hFile.get(), header, static_cast<DWORD>( required ), &bytesWritten, 0 ) )
1598     {
1599         return HRESULT_FROM_WIN32( GetLastError() );
1600     }
1601
1602     if ( bytesWritten != required )
1603     {
1604         return E_FAIL;
1605     }
1606
1607     // Write images
1608     switch( metadata.dimension )
1609     {
1610     case DDS_DIMENSION_TEXTURE1D:
1611     case DDS_DIMENSION_TEXTURE2D:
1612         {
1613             size_t index = 0;
1614             for( size_t item = 0; item < metadata.arraySize; ++item )
1615             {
1616                 for( size_t level = 0; level < metadata.mipLevels; ++level, ++index )
1617                 {
1618                     if ( index >= nimages )
1619                         return E_FAIL;
1620
1621                     if ( !images[ index ].pixels )
1622                         return E_POINTER;
1623
1624                     size_t pixsize = images[ index ].slicePitch;
1625
1626                     if ( !WriteFile( hFile.get(), images[ index ].pixels, static_cast<DWORD>( pixsize ), &bytesWritten, 0 ) )
1627                     {
1628                         return HRESULT_FROM_WIN32( GetLastError() );
1629                     }
1630
1631                     if ( bytesWritten != pixsize )
1632                     {
1633                         return E_FAIL;
1634                     }
1635                 }
1636             }
1637         }
1638         break;
1639
1640     case DDS_DIMENSION_TEXTURE3D:
1641         {
1642             if ( metadata.arraySize != 1 )
1643                 return E_FAIL;
1644
1645             size_t d = metadata.depth;
1646
1647             size_t index = 0;
1648             for( size_t level = 0; level < metadata.mipLevels; ++level )
1649             {
1650                 for( size_t slice = 0; slice < d; ++slice, ++index )
1651                 {
1652                     if ( index >= nimages )
1653                         return E_FAIL;
1654
1655                     if ( !images[ index ].pixels )
1656                         return E_POINTER;
1657
1658                     size_t pixsize = images[ index ].slicePitch;
1659
1660                     if ( !WriteFile( hFile.get(), images[ index ].pixels, static_cast<DWORD>( pixsize ), &bytesWritten, 0 ) )
1661                     {
1662                         return HRESULT_FROM_WIN32( GetLastError() );
1663                     }
1664
1665                     if ( bytesWritten != pixsize )
1666                     {
1667                         return E_FAIL;
1668                     }
1669                 }
1670
1671                 if ( d > 1 )
1672                     d >>= 1;
1673             }
1674         }
1675         break;
1676
1677     default:
1678         return E_FAIL;
1679     }
1680
1681     return S_OK;
1682 }
1683
1684 }; // namespace