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