]> git.cworth.org Git - apitrace/blob - thirdparty/directxtex/DirectXTex/DirectXTexCompress.cpp
thirdparty/directxtex: Import DirectXTex library.
[apitrace] / thirdparty / directxtex / DirectXTex / DirectXTexCompress.cpp
1 //-------------------------------------------------------------------------------------
2 // DirectXTexCompress.cpp
3 //  
4 // DirectX Texture Library - Texture compression
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 #ifdef _OPENMP
19 #include <omp.h>
20 #pragma warning(disable : 4616 6001 6993)
21 #endif
22
23 #include "bc.h"
24
25 namespace DirectX
26 {
27
28 inline static DWORD _GetBCFlags( _In_ DWORD compress )
29 {
30     static_assert( TEX_COMPRESS_RGB_DITHER == BC_FLAGS_DITHER_RGB, "TEX_COMPRESS_* flags should match BC_FLAGS_*" );
31     static_assert( TEX_COMPRESS_A_DITHER == BC_FLAGS_DITHER_A, "TEX_COMPRESS_* flags should match BC_FLAGS_*"  );
32     static_assert( TEX_COMPRESS_DITHER == (BC_FLAGS_DITHER_RGB | BC_FLAGS_DITHER_A), "TEX_COMPRESS_* flags should match BC_FLAGS_*"  );
33     static_assert( TEX_COMPRESS_UNIFORM == BC_FLAGS_UNIFORM, "TEX_COMPRESS_* flags should match BC_FLAGS_*"  );
34     return ( compress & (BC_FLAGS_DITHER_RGB|BC_FLAGS_DITHER_A|BC_FLAGS_UNIFORM) );
35 }
36
37
38 //-------------------------------------------------------------------------------------
39 static HRESULT _CompressBC( _In_ const Image& image, _In_ const Image& result, _In_ DWORD bcflags,
40                             _In_ float alphaRef, _In_ bool degenerate )
41 {
42     if ( !image.pixels || !result.pixels )
43         return E_POINTER;
44
45     assert( image.width == result.width );
46     assert( image.height == result.height );
47
48     const DXGI_FORMAT format = image.format;
49     size_t sbpp = BitsPerPixel( format );
50     if ( !sbpp )
51         return E_FAIL;
52
53     if ( sbpp < 8 )
54     {
55         // We don't support compressing from monochrome (DXGI_FORMAT_R1_UNORM)
56         return HRESULT_FROM_WIN32( ERROR_NOT_SUPPORTED );
57     }
58
59     // Round to bytes
60     sbpp = ( sbpp + 7 ) / 8;
61
62     uint8_t *pDest = result.pixels;
63
64     // Determine BC format encoder
65     BC_ENCODE pfEncode;
66     size_t blocksize;
67     switch(result.format)
68     {
69     case DXGI_FORMAT_BC1_UNORM:
70     case DXGI_FORMAT_BC1_UNORM_SRGB:    pfEncode = nullptr;         blocksize = 8;   break;
71     case DXGI_FORMAT_BC2_UNORM:
72     case DXGI_FORMAT_BC2_UNORM_SRGB:    pfEncode = D3DXEncodeBC2;   blocksize = 16;  break;
73     case DXGI_FORMAT_BC3_UNORM:
74     case DXGI_FORMAT_BC3_UNORM_SRGB:    pfEncode = D3DXEncodeBC3;   blocksize = 16;  break;
75     case DXGI_FORMAT_BC4_UNORM:         pfEncode = D3DXEncodeBC4U;  blocksize = 8;   break;
76     case DXGI_FORMAT_BC4_SNORM:         pfEncode = D3DXEncodeBC4S;  blocksize = 8;   break;
77     case DXGI_FORMAT_BC5_UNORM:         pfEncode = D3DXEncodeBC5U;  blocksize = 16;  break;
78     case DXGI_FORMAT_BC5_SNORM:         pfEncode = D3DXEncodeBC5S;  blocksize = 16;  break;
79     case DXGI_FORMAT_BC6H_UF16:         pfEncode = D3DXEncodeBC6HU; blocksize = 16;  break;
80     case DXGI_FORMAT_BC6H_SF16:         pfEncode = D3DXEncodeBC6HS; blocksize = 16;  break;
81     case DXGI_FORMAT_BC7_UNORM:
82     case DXGI_FORMAT_BC7_UNORM_SRGB:    pfEncode = D3DXEncodeBC7;   blocksize = 16;  break;
83     default:
84         return HRESULT_FROM_WIN32( ERROR_NOT_SUPPORTED );
85     }
86
87     XMVECTOR temp[16];
88     const uint8_t *pSrc = image.pixels;
89     const size_t rowPitch = image.rowPitch;
90     for( size_t h=0; h < image.height; h += 4 )
91     {
92         const uint8_t *sptr = pSrc;
93         uint8_t* dptr = pDest;
94         for( size_t count = 0; count < rowPitch; count += sbpp*4 )
95         {
96             if ( !_LoadScanline( &temp[0], 4, sptr, rowPitch, format ) )
97                 return E_FAIL;
98
99             if ( image.height > 1 )
100             {
101                 if ( !_LoadScanline( &temp[4], 4, sptr + rowPitch, rowPitch, format ) )
102                     return E_FAIL;
103
104                 if ( image.height > 2 )
105                 {
106                     if ( !_LoadScanline( &temp[8], 4, sptr + rowPitch*2, rowPitch, format ) )
107                         return E_FAIL;
108
109                     if ( !_LoadScanline( &temp[12], 4, sptr + rowPitch*3, rowPitch, format ) )
110                         return E_FAIL;
111                 }
112             }
113
114             if ( degenerate )
115             {
116                 assert( image.width < 4 || image.height < 4 );
117                 const size_t uSrc[] = { 0, 0, 0, 1 };
118
119                 if ( image.width < 4 )
120                 {
121                     for( size_t t=0; t < image.height && t < 4; ++t )
122                     {
123                         for( size_t s = image.width; s < 4; ++s )
124                         {
125                             temp[ t*4 + s ] = temp[ t*4 + uSrc[s] ]; 
126                         }
127                     }
128                 }
129
130                 if ( image.height < 4 )
131                 {
132                     for( size_t t=image.height; t < 4; ++t )
133                     {
134                         for( size_t s =0; s < 4; ++s )
135                         {
136                             temp[ t*4 + s ] = temp[ uSrc[t]*4 + s ]; 
137                         }
138                     }
139                 }
140             }
141
142             _ConvertScanline( temp, 16, result.format, format, 0 );
143             
144             if ( pfEncode )
145                 pfEncode( dptr, temp, bcflags );
146             else
147                 D3DXEncodeBC1( dptr, temp, alphaRef, bcflags );
148
149             sptr += sbpp*4;
150             dptr += blocksize;
151         }
152
153         pSrc += rowPitch*4;
154         pDest += result.rowPitch;
155     }
156
157     return S_OK;
158 }
159
160
161 //-------------------------------------------------------------------------------------
162 #ifdef _OPENMP
163 static HRESULT _CompressBC_Parallel( _In_ const Image& image, _In_ const Image& result, _In_ DWORD bcflags,
164                                      _In_ float alphaRef )
165 {
166     if ( !image.pixels || !result.pixels )
167         return E_POINTER;
168
169     // Parallel version doesn't support degenerate case
170     assert( ((image.width % 4) == 0) && ((image.height % 4) == 0 ) );
171
172     assert( image.width == result.width );
173     assert( image.height == result.height );
174
175     const DXGI_FORMAT format = image.format;
176     size_t sbpp = BitsPerPixel( format );
177     if ( !sbpp )
178         return E_FAIL;
179
180     if ( sbpp < 8 )
181     {
182         // We don't support compressing from monochrome (DXGI_FORMAT_R1_UNORM)
183         return HRESULT_FROM_WIN32( ERROR_NOT_SUPPORTED );
184     }
185
186     // Round to bytes
187     sbpp = ( sbpp + 7 ) / 8;
188
189     // Determine BC format encoder
190     BC_ENCODE pfEncode;
191     size_t blocksize;
192     switch(result.format)
193     {
194     case DXGI_FORMAT_BC1_UNORM:
195     case DXGI_FORMAT_BC1_UNORM_SRGB:    pfEncode = nullptr;         blocksize = 8;   break;
196     case DXGI_FORMAT_BC2_UNORM:
197     case DXGI_FORMAT_BC2_UNORM_SRGB:    pfEncode = D3DXEncodeBC2;   blocksize = 16;  break;
198     case DXGI_FORMAT_BC3_UNORM:
199     case DXGI_FORMAT_BC3_UNORM_SRGB:    pfEncode = D3DXEncodeBC3;   blocksize = 16;  break;
200     case DXGI_FORMAT_BC4_UNORM:         pfEncode = D3DXEncodeBC4U;  blocksize = 8;   break;
201     case DXGI_FORMAT_BC4_SNORM:         pfEncode = D3DXEncodeBC4S;  blocksize = 8;   break;
202     case DXGI_FORMAT_BC5_UNORM:         pfEncode = D3DXEncodeBC5U;  blocksize = 16;  break;
203     case DXGI_FORMAT_BC5_SNORM:         pfEncode = D3DXEncodeBC5S;  blocksize = 16;  break;
204     case DXGI_FORMAT_BC6H_UF16:         pfEncode = D3DXEncodeBC6HU; blocksize = 16;  break;
205     case DXGI_FORMAT_BC6H_SF16:         pfEncode = D3DXEncodeBC6HS; blocksize = 16;  break;
206     case DXGI_FORMAT_BC7_UNORM:
207     case DXGI_FORMAT_BC7_UNORM_SRGB:    pfEncode = D3DXEncodeBC7;   blocksize = 16;  break;
208     default:
209         return HRESULT_FROM_WIN32( ERROR_NOT_SUPPORTED );
210     }
211
212     // Refactored version of loop to support parallel independance
213     const size_t nBlocks = std::max<size_t>(1, image.width / 4) * std::max<size_t>(1, image.height / 4);
214
215     bool fail = false;
216
217 #pragma omp parallel for
218     for( int nb=0; nb < static_cast<int>( nBlocks ); ++nb )
219     {
220         const size_t nbWidth = std::max<size_t>(1, image.width / 4);
221
222         const size_t y = nb / nbWidth;
223         const size_t x = nb - (y*nbWidth);
224
225         assert( x < image.width && y < image.height );
226
227         size_t rowPitch = image.rowPitch;
228         const uint8_t *pSrc = image.pixels + (y*4*rowPitch) + (x*4*sbpp);
229
230         uint8_t *pDest = result.pixels + (nb*blocksize);
231
232         XMVECTOR temp[16];
233         if ( !_LoadScanline( &temp[0], 4, pSrc, rowPitch, format ) )
234             fail = true;
235
236         if ( !_LoadScanline( &temp[4], 4, pSrc + rowPitch, rowPitch, format ) )
237             fail = true;
238
239         if ( !_LoadScanline( &temp[8], 4, pSrc + rowPitch*2, rowPitch, format ) )
240             fail = true;
241
242         if ( !_LoadScanline( &temp[12], 4, pSrc + rowPitch*3, rowPitch, format ) )
243             fail = true;
244
245         _ConvertScanline( temp, 16, result.format, format, 0 );
246             
247         if ( pfEncode )
248             pfEncode( pDest, temp, bcflags );
249         else
250             D3DXEncodeBC1( pDest, temp, alphaRef, bcflags );
251     }
252
253     return (fail) ? E_FAIL : S_OK;
254 }
255
256 #endif // _OPENMP
257
258
259 //-------------------------------------------------------------------------------------
260 static DXGI_FORMAT _DefaultDecompress( _In_ DXGI_FORMAT format )
261 {
262     switch( format )
263     {
264     case DXGI_FORMAT_BC1_TYPELESS:
265     case DXGI_FORMAT_BC1_UNORM:
266     case DXGI_FORMAT_BC2_TYPELESS:
267     case DXGI_FORMAT_BC2_UNORM:
268     case DXGI_FORMAT_BC3_TYPELESS:
269     case DXGI_FORMAT_BC3_UNORM:
270     case DXGI_FORMAT_BC7_TYPELESS:
271     case DXGI_FORMAT_BC7_UNORM:
272         return DXGI_FORMAT_R8G8B8A8_UNORM;
273
274     case DXGI_FORMAT_BC1_UNORM_SRGB:
275     case DXGI_FORMAT_BC2_UNORM_SRGB:
276     case DXGI_FORMAT_BC3_UNORM_SRGB:
277     case DXGI_FORMAT_BC7_UNORM_SRGB:
278         return DXGI_FORMAT_R8G8B8A8_UNORM_SRGB;
279
280     case DXGI_FORMAT_BC4_TYPELESS:
281     case DXGI_FORMAT_BC4_UNORM:
282         return DXGI_FORMAT_R8_UNORM;
283
284     case DXGI_FORMAT_BC4_SNORM:
285         return DXGI_FORMAT_R8_SNORM;
286
287     case DXGI_FORMAT_BC5_TYPELESS:
288     case DXGI_FORMAT_BC5_UNORM:
289         return DXGI_FORMAT_R8G8_UNORM;
290
291     case DXGI_FORMAT_BC5_SNORM:
292         return DXGI_FORMAT_R8G8_SNORM;
293
294     case DXGI_FORMAT_BC6H_TYPELESS:
295     case DXGI_FORMAT_BC6H_UF16:
296     case DXGI_FORMAT_BC6H_SF16:
297         // We could use DXGI_FORMAT_R32G32B32_FLOAT here since BC6H is always Alpha 1.0,
298         // but this format is more supported by viewers
299         return DXGI_FORMAT_R32G32B32A32_FLOAT;
300
301     default:
302         return DXGI_FORMAT_UNKNOWN;
303     }
304 }
305
306
307 //-------------------------------------------------------------------------------------
308 static HRESULT _DecompressBC( _In_ const Image& cImage, _In_ const Image& result )
309 {
310     if ( !cImage.pixels || !result.pixels )
311         return E_POINTER;
312
313     assert( cImage.width == result.width );
314     assert( cImage.height == result.height );
315
316     // Image must be a multiple of 4 (degenerate cases of 1x1, 1x2, 2x1, and 2x2 are allowed)
317     size_t width = cImage.width;
318     if ( (width % 4) != 0 )
319     {
320         if ( width != 1 && width != 2 )
321             return E_INVALIDARG;
322     }
323
324     size_t height = cImage.height;
325     if ( (height % 4) != 0 )
326     {
327         if ( height != 1 && height != 2 )
328             return E_INVALIDARG;
329     }
330
331     const DXGI_FORMAT format = result.format;
332     size_t dbpp = BitsPerPixel( format );
333     if ( !dbpp )
334         return E_FAIL;
335
336     if ( dbpp < 8 )
337     {
338         // We don't support decompressing to monochrome (DXGI_FORMAT_R1_UNORM)
339         return HRESULT_FROM_WIN32( ERROR_NOT_SUPPORTED );
340     }
341
342     // Round to bytes
343     dbpp = ( dbpp + 7 ) / 8;
344
345     uint8_t *pDest = result.pixels;
346     if ( !pDest )
347         return E_POINTER;
348
349     // Promote "typeless" BC formats
350     DXGI_FORMAT cformat;
351     switch( cImage.format )
352     {
353     case DXGI_FORMAT_BC1_TYPELESS:  cformat = DXGI_FORMAT_BC1_UNORM; break;
354     case DXGI_FORMAT_BC2_TYPELESS:  cformat = DXGI_FORMAT_BC2_UNORM; break;
355     case DXGI_FORMAT_BC3_TYPELESS:  cformat = DXGI_FORMAT_BC3_UNORM; break;
356     case DXGI_FORMAT_BC4_TYPELESS:  cformat = DXGI_FORMAT_BC4_UNORM; break;
357     case DXGI_FORMAT_BC5_TYPELESS:  cformat = DXGI_FORMAT_BC5_UNORM; break;
358     case DXGI_FORMAT_BC6H_TYPELESS: cformat = DXGI_FORMAT_BC6H_UF16; break;
359     case DXGI_FORMAT_BC7_TYPELESS:  cformat = DXGI_FORMAT_BC7_UNORM; break;
360     default:                        cformat = cImage.format;         break;
361     }
362
363     // Determine BC format decoder
364     BC_DECODE pfDecode;
365     size_t sbpp;
366     switch(cformat)
367     {
368     case DXGI_FORMAT_BC1_UNORM:
369     case DXGI_FORMAT_BC1_UNORM_SRGB:    pfDecode = D3DXDecodeBC1;   sbpp = 8;   break;
370     case DXGI_FORMAT_BC2_UNORM:
371     case DXGI_FORMAT_BC2_UNORM_SRGB:    pfDecode = D3DXDecodeBC2;   sbpp = 16;  break;
372     case DXGI_FORMAT_BC3_UNORM:
373     case DXGI_FORMAT_BC3_UNORM_SRGB:    pfDecode = D3DXDecodeBC3;   sbpp = 16;  break;
374     case DXGI_FORMAT_BC4_UNORM:         pfDecode = D3DXDecodeBC4U;  sbpp = 8;   break;
375     case DXGI_FORMAT_BC4_SNORM:         pfDecode = D3DXDecodeBC4S;  sbpp = 8;   break;
376     case DXGI_FORMAT_BC5_UNORM:         pfDecode = D3DXDecodeBC5U;  sbpp = 16;  break;
377     case DXGI_FORMAT_BC5_SNORM:         pfDecode = D3DXDecodeBC5S;  sbpp = 16;  break;
378     case DXGI_FORMAT_BC6H_UF16:         pfDecode = D3DXDecodeBC6HU; sbpp = 16;  break;
379     case DXGI_FORMAT_BC6H_SF16:         pfDecode = D3DXDecodeBC6HS; sbpp = 16;  break;
380     case DXGI_FORMAT_BC7_UNORM:
381     case DXGI_FORMAT_BC7_UNORM_SRGB:    pfDecode = D3DXDecodeBC7;   sbpp = 16;  break;
382     default:
383         return HRESULT_FROM_WIN32( ERROR_NOT_SUPPORTED );
384     }
385
386     XMVECTOR temp[16];
387     const uint8_t *pSrc = cImage.pixels;
388     const size_t rowPitch = result.rowPitch;
389     for( size_t h=0; h < cImage.height; h += 4 )
390     {
391         const uint8_t *sptr = pSrc;
392         uint8_t* dptr = pDest;
393         for( size_t count = 0; count < cImage.rowPitch; count += sbpp )
394         {
395             pfDecode( temp, sptr );
396             _ConvertScanline( temp, 16, format, cformat, 0 );
397
398             if ( !_StoreScanline( dptr, rowPitch, format, &temp[0], 4 ) )
399                 return E_FAIL;
400
401             if ( result.height > 1 )
402             {
403                 if ( !_StoreScanline( dptr + rowPitch, rowPitch, format, &temp[4], 4 ) )
404                     return E_FAIL;
405
406                 if ( result.height > 2 )
407                 {
408                     if ( !_StoreScanline( dptr + rowPitch*2, rowPitch, format, &temp[8], 4 ) )
409                         return E_FAIL;
410
411                     if ( !_StoreScanline( dptr + rowPitch*3, rowPitch, format, &temp[12], 4 ) )
412                         return E_FAIL;
413                 }
414             }
415
416             sptr += sbpp;
417             dptr += dbpp*4;
418         }
419
420         pSrc += cImage.rowPitch;
421         pDest += rowPitch*4;
422     }
423
424     return S_OK;
425 }
426
427
428 //=====================================================================================
429 // Entry-points
430 //=====================================================================================
431
432 //-------------------------------------------------------------------------------------
433 // Compression
434 //-------------------------------------------------------------------------------------
435 HRESULT Compress( const Image& srcImage, DXGI_FORMAT format, DWORD compress, float alphaRef, ScratchImage& image )
436 {
437     if ( IsCompressed(srcImage.format) || !IsCompressed(format) || IsTypeless(format) )
438         return E_INVALIDARG;
439
440     // Image size must be a multiple of 4 (degenerate cases for mipmaps are allowed)
441     bool degenerate = false;
442
443     size_t width = srcImage.width;
444     if ( (width % 4) != 0 )
445     {
446         if ( width != 1 && width != 2 )
447             return E_INVALIDARG;
448
449         degenerate = true;
450     }
451
452     size_t height = srcImage.height;
453     if ( (height % 4) != 0 )
454     {
455         if ( height != 1 && height != 2 )
456             return E_INVALIDARG;
457
458         degenerate = true;
459     }
460
461     // Create compressed image
462     HRESULT hr = image.Initialize2D( format, width, height, 1, 1 );
463     if ( FAILED(hr) )
464         return hr;
465
466     const Image *img = image.GetImage( 0, 0, 0 );
467     if ( !img )
468     {
469         image.Release();
470         return E_POINTER;
471     }
472
473     // Compress single image
474     if ( (compress & TEX_COMPRESS_PARALLEL) && !degenerate )
475     {
476 #ifndef _OPENMP
477         return E_NOTIMPL;
478 #else
479         hr = _CompressBC_Parallel( srcImage, *img, _GetBCFlags( compress ), alphaRef );
480 #endif // _OPENMP
481     }
482     else
483     {
484         hr = _CompressBC( srcImage, *img, _GetBCFlags( compress ), alphaRef, degenerate );
485     }
486
487     if ( FAILED(hr) )
488         image.Release();
489
490     return hr;
491 }
492
493 HRESULT Compress( const Image* srcImages, size_t nimages, const TexMetadata& metadata,
494                   DXGI_FORMAT format, DWORD compress, float alphaRef, ScratchImage& cImages )
495 {
496     if ( !srcImages || !nimages )
497         return E_INVALIDARG;
498
499     if ( !IsCompressed(format) || IsTypeless(format) )
500         return E_INVALIDARG;
501
502     // Image size must be a multiple of 4 (degenerate cases for mipmaps are allowed)
503     size_t width = srcImages[0].width;
504     if ( (width % 4) != 0 )
505     {
506         if ( width != 1 && width != 2 )
507             return E_INVALIDARG;
508     }
509
510     size_t height = srcImages[0].height;
511     if ( (height % 4) != 0 )
512     {
513         if ( height != 1 && height != 2 )
514             return E_INVALIDARG;
515     }
516
517     cImages.Release();
518
519     TexMetadata mdata2 = metadata;
520     mdata2.format = format;
521     HRESULT hr = cImages.Initialize( mdata2 );
522     if ( FAILED(hr) )
523         return hr;
524
525     if ( nimages != cImages.GetImageCount() )
526     {
527         cImages.Release();
528         return E_FAIL;
529     }
530
531     const Image* dest = cImages.GetImages();
532     if ( !dest  )
533     {
534         cImages.Release();
535         return E_POINTER;
536     }
537
538     for( size_t index=0; index < nimages; ++index )
539     {
540         assert( dest[ index ].format == format );
541
542         const Image& src = srcImages[ index ];
543
544         height = src.height;
545         width = src.width;
546         if ( width != dest[ index ].width || height != dest[ index ].height )
547         {
548             cImages.Release();
549             return E_FAIL;
550         }
551
552         bool degenerate = ((height < 4) || (width < 4)) != 0;
553
554         if ( (compress & TEX_COMPRESS_PARALLEL) && !degenerate)
555         {
556 #ifndef _OPENMP
557             return E_NOTIMPL;
558 #else
559             if ( compress & TEX_COMPRESS_PARALLEL )
560             {
561                 hr = _CompressBC_Parallel( src, dest[ index ], _GetBCFlags( compress ), alphaRef );
562                 if ( FAILED(hr) )
563                 {
564                     cImages.Release();
565                     return  hr;
566                 }
567             }
568 #endif // _OPENMP
569         }
570         else
571         {
572             hr = _CompressBC( src, dest[ index ], _GetBCFlags( compress ), alphaRef, degenerate );
573             if ( FAILED(hr) )
574             {
575                 cImages.Release();
576                 return hr;
577             }
578         }
579     }
580
581     return S_OK;
582 }
583
584
585 //-------------------------------------------------------------------------------------
586 // Decompression
587 //-------------------------------------------------------------------------------------
588 HRESULT Decompress( const Image& cImage, DXGI_FORMAT format, ScratchImage& image )
589 {
590     if ( IsCompressed(format) || IsTypeless(format) )
591         return E_INVALIDARG;
592
593     if ( format == DXGI_FORMAT_UNKNOWN )
594     {
595         // Pick a default decompressed format based on BC input format
596         format = _DefaultDecompress( cImage.format );
597         if ( format == DXGI_FORMAT_UNKNOWN )
598         {
599             // Input is not a compressed format
600             return E_INVALIDARG;
601         }
602     }
603     else if ( !IsCompressed(cImage.format) || !IsValid(format) )
604         return E_INVALIDARG;
605
606     // Create decompressed image
607     HRESULT hr = image.Initialize2D( format, cImage.width, cImage.height, 1, 1 );
608     if ( FAILED(hr) )
609         return hr;
610
611     const Image *img = image.GetImage( 0, 0, 0 );
612     if ( !img )
613     {
614         image.Release();
615         return E_POINTER;
616     }
617
618     // Decompress single image
619     hr = _DecompressBC( cImage, *img );
620     if ( FAILED(hr) )
621         image.Release();
622
623     return hr;
624 }
625
626 HRESULT Decompress( const Image* cImages, size_t nimages, const TexMetadata& metadata,
627                     DXGI_FORMAT format, ScratchImage& images )
628 {
629     if ( !cImages || !nimages )
630         return E_INVALIDARG;
631
632     if ( IsCompressed(format) || IsTypeless(format) )
633         return E_INVALIDARG;
634
635     if ( format == DXGI_FORMAT_UNKNOWN )
636     {
637         // Pick a default decompressed format based on BC input format
638         format = _DefaultDecompress( cImages[0].format );
639         if ( format == DXGI_FORMAT_UNKNOWN )
640         {
641             // Input is not a compressed format
642             return E_FAIL;
643         }
644     }
645     else if ( !IsValid(format) )
646         return E_INVALIDARG;
647
648     images.Release();
649
650     TexMetadata mdata2 = metadata;
651     mdata2.format = format;
652     HRESULT hr = images.Initialize( mdata2 );
653     if ( FAILED(hr) )
654         return hr;
655
656     if ( nimages != images.GetImageCount() )
657     {
658         images.Release();
659         return E_FAIL;
660     }
661
662     const Image* dest = images.GetImages();
663     if ( !dest )
664     {
665         images.Release();
666         return E_POINTER;
667     }
668
669     for( size_t index=0; index < nimages; ++index )
670     {
671         assert( dest[ index ].format == format );
672
673         const Image& src = cImages[ index ];
674         if ( !IsCompressed( src.format ) )
675         {
676             images.Release();
677             return E_FAIL;
678         }
679
680         if ( src.width != dest[ index ].width || src.height != dest[ index ].height )
681         {
682             images.Release();
683             return E_FAIL;
684         }
685
686         hr = _DecompressBC( src, dest[ index ] );
687         if ( FAILED(hr) )
688         {
689             images.Release();
690             return hr;
691         }
692     }
693
694     return S_OK;
695 }
696
697 }; // namespace