1 //-------------------------------------------------------------------------------------
2 // DirectXTexCompress.cpp
4 // DirectX Texture Library - Texture compression
6 // THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
7 // ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
8 // THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
11 // Copyright (c) Microsoft Corporation. All rights reserved.
13 // http://go.microsoft.com/fwlink/?LinkId=248926
14 //-------------------------------------------------------------------------------------
16 #include "DirectXTexP.h"
20 #pragma warning(disable : 4616 6001 6993)
28 inline static DWORD _GetBCFlags( _In_ DWORD compress )
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) );
38 //-------------------------------------------------------------------------------------
39 static HRESULT _CompressBC( _In_ const Image& image, _In_ const Image& result, _In_ DWORD bcflags,
40 _In_ float alphaRef, _In_ bool degenerate )
42 if ( !image.pixels || !result.pixels )
45 assert( image.width == result.width );
46 assert( image.height == result.height );
48 const DXGI_FORMAT format = image.format;
49 size_t sbpp = BitsPerPixel( format );
55 // We don't support compressing from monochrome (DXGI_FORMAT_R1_UNORM)
56 return HRESULT_FROM_WIN32( ERROR_NOT_SUPPORTED );
60 sbpp = ( sbpp + 7 ) / 8;
62 uint8_t *pDest = result.pixels;
64 // Determine BC format encoder
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;
84 return HRESULT_FROM_WIN32( ERROR_NOT_SUPPORTED );
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 )
92 const uint8_t *sptr = pSrc;
93 uint8_t* dptr = pDest;
94 for( size_t count = 0; count < rowPitch; count += sbpp*4 )
96 if ( !_LoadScanline( &temp[0], 4, sptr, rowPitch, format ) )
99 if ( image.height > 1 )
101 if ( !_LoadScanline( &temp[4], 4, sptr + rowPitch, rowPitch, format ) )
104 if ( image.height > 2 )
106 if ( !_LoadScanline( &temp[8], 4, sptr + rowPitch*2, rowPitch, format ) )
109 if ( !_LoadScanline( &temp[12], 4, sptr + rowPitch*3, rowPitch, format ) )
116 assert( image.width < 4 || image.height < 4 );
117 const size_t uSrc[] = { 0, 0, 0, 1 };
119 if ( image.width < 4 )
121 for( size_t t=0; t < image.height && t < 4; ++t )
123 for( size_t s = image.width; s < 4; ++s )
125 temp[ t*4 + s ] = temp[ t*4 + uSrc[s] ];
130 if ( image.height < 4 )
132 for( size_t t=image.height; t < 4; ++t )
134 for( size_t s =0; s < 4; ++s )
136 temp[ t*4 + s ] = temp[ uSrc[t]*4 + s ];
142 _ConvertScanline( temp, 16, result.format, format, 0 );
145 pfEncode( dptr, temp, bcflags );
147 D3DXEncodeBC1( dptr, temp, alphaRef, bcflags );
154 pDest += result.rowPitch;
161 //-------------------------------------------------------------------------------------
163 static HRESULT _CompressBC_Parallel( _In_ const Image& image, _In_ const Image& result, _In_ DWORD bcflags,
164 _In_ float alphaRef )
166 if ( !image.pixels || !result.pixels )
169 // Parallel version doesn't support degenerate case
170 assert( ((image.width % 4) == 0) && ((image.height % 4) == 0 ) );
172 assert( image.width == result.width );
173 assert( image.height == result.height );
175 const DXGI_FORMAT format = image.format;
176 size_t sbpp = BitsPerPixel( format );
182 // We don't support compressing from monochrome (DXGI_FORMAT_R1_UNORM)
183 return HRESULT_FROM_WIN32( ERROR_NOT_SUPPORTED );
187 sbpp = ( sbpp + 7 ) / 8;
189 // Determine BC format encoder
192 switch(result.format)
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;
209 return HRESULT_FROM_WIN32( ERROR_NOT_SUPPORTED );
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);
217 #pragma omp parallel for
218 for( int nb=0; nb < static_cast<int>( nBlocks ); ++nb )
220 const size_t nbWidth = std::max<size_t>(1, image.width / 4);
222 const size_t y = nb / nbWidth;
223 const size_t x = nb - (y*nbWidth);
225 assert( x < image.width && y < image.height );
227 size_t rowPitch = image.rowPitch;
228 const uint8_t *pSrc = image.pixels + (y*4*rowPitch) + (x*4*sbpp);
230 uint8_t *pDest = result.pixels + (nb*blocksize);
233 if ( !_LoadScanline( &temp[0], 4, pSrc, rowPitch, format ) )
236 if ( !_LoadScanline( &temp[4], 4, pSrc + rowPitch, rowPitch, format ) )
239 if ( !_LoadScanline( &temp[8], 4, pSrc + rowPitch*2, rowPitch, format ) )
242 if ( !_LoadScanline( &temp[12], 4, pSrc + rowPitch*3, rowPitch, format ) )
245 _ConvertScanline( temp, 16, result.format, format, 0 );
248 pfEncode( pDest, temp, bcflags );
250 D3DXEncodeBC1( pDest, temp, alphaRef, bcflags );
253 return (fail) ? E_FAIL : S_OK;
259 //-------------------------------------------------------------------------------------
260 static DXGI_FORMAT _DefaultDecompress( _In_ DXGI_FORMAT format )
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;
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;
280 case DXGI_FORMAT_BC4_TYPELESS:
281 case DXGI_FORMAT_BC4_UNORM:
282 return DXGI_FORMAT_R8_UNORM;
284 case DXGI_FORMAT_BC4_SNORM:
285 return DXGI_FORMAT_R8_SNORM;
287 case DXGI_FORMAT_BC5_TYPELESS:
288 case DXGI_FORMAT_BC5_UNORM:
289 return DXGI_FORMAT_R8G8_UNORM;
291 case DXGI_FORMAT_BC5_SNORM:
292 return DXGI_FORMAT_R8G8_SNORM;
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;
302 return DXGI_FORMAT_UNKNOWN;
307 //-------------------------------------------------------------------------------------
308 static HRESULT _DecompressBC( _In_ const Image& cImage, _In_ const Image& result )
310 if ( !cImage.pixels || !result.pixels )
313 assert( cImage.width == result.width );
314 assert( cImage.height == result.height );
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 )
320 if ( width != 1 && width != 2 )
324 size_t height = cImage.height;
325 if ( (height % 4) != 0 )
327 if ( height != 1 && height != 2 )
331 const DXGI_FORMAT format = result.format;
332 size_t dbpp = BitsPerPixel( format );
338 // We don't support decompressing to monochrome (DXGI_FORMAT_R1_UNORM)
339 return HRESULT_FROM_WIN32( ERROR_NOT_SUPPORTED );
343 dbpp = ( dbpp + 7 ) / 8;
345 uint8_t *pDest = result.pixels;
349 // Promote "typeless" BC formats
351 switch( cImage.format )
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;
363 // Determine BC format decoder
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;
383 return HRESULT_FROM_WIN32( ERROR_NOT_SUPPORTED );
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 )
391 const uint8_t *sptr = pSrc;
392 uint8_t* dptr = pDest;
393 for( size_t count = 0; count < cImage.rowPitch; count += sbpp )
395 pfDecode( temp, sptr );
396 _ConvertScanline( temp, 16, format, cformat, 0 );
398 if ( !_StoreScanline( dptr, rowPitch, format, &temp[0], 4 ) )
401 if ( result.height > 1 )
403 if ( !_StoreScanline( dptr + rowPitch, rowPitch, format, &temp[4], 4 ) )
406 if ( result.height > 2 )
408 if ( !_StoreScanline( dptr + rowPitch*2, rowPitch, format, &temp[8], 4 ) )
411 if ( !_StoreScanline( dptr + rowPitch*3, rowPitch, format, &temp[12], 4 ) )
420 pSrc += cImage.rowPitch;
428 //=====================================================================================
430 //=====================================================================================
432 //-------------------------------------------------------------------------------------
434 //-------------------------------------------------------------------------------------
435 HRESULT Compress( const Image& srcImage, DXGI_FORMAT format, DWORD compress, float alphaRef, ScratchImage& image )
437 if ( IsCompressed(srcImage.format) || !IsCompressed(format) || IsTypeless(format) )
440 // Image size must be a multiple of 4 (degenerate cases for mipmaps are allowed)
441 bool degenerate = false;
443 size_t width = srcImage.width;
444 if ( (width % 4) != 0 )
446 if ( width != 1 && width != 2 )
452 size_t height = srcImage.height;
453 if ( (height % 4) != 0 )
455 if ( height != 1 && height != 2 )
461 // Create compressed image
462 HRESULT hr = image.Initialize2D( format, width, height, 1, 1 );
466 const Image *img = image.GetImage( 0, 0, 0 );
473 // Compress single image
474 if ( (compress & TEX_COMPRESS_PARALLEL) && !degenerate )
479 hr = _CompressBC_Parallel( srcImage, *img, _GetBCFlags( compress ), alphaRef );
484 hr = _CompressBC( srcImage, *img, _GetBCFlags( compress ), alphaRef, degenerate );
493 HRESULT Compress( const Image* srcImages, size_t nimages, const TexMetadata& metadata,
494 DXGI_FORMAT format, DWORD compress, float alphaRef, ScratchImage& cImages )
496 if ( !srcImages || !nimages )
499 if ( !IsCompressed(format) || IsTypeless(format) )
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 )
506 if ( width != 1 && width != 2 )
510 size_t height = srcImages[0].height;
511 if ( (height % 4) != 0 )
513 if ( height != 1 && height != 2 )
519 TexMetadata mdata2 = metadata;
520 mdata2.format = format;
521 HRESULT hr = cImages.Initialize( mdata2 );
525 if ( nimages != cImages.GetImageCount() )
531 const Image* dest = cImages.GetImages();
538 for( size_t index=0; index < nimages; ++index )
540 assert( dest[ index ].format == format );
542 const Image& src = srcImages[ index ];
546 if ( width != dest[ index ].width || height != dest[ index ].height )
552 bool degenerate = ((height < 4) || (width < 4)) != 0;
554 if ( (compress & TEX_COMPRESS_PARALLEL) && !degenerate)
559 if ( compress & TEX_COMPRESS_PARALLEL )
561 hr = _CompressBC_Parallel( src, dest[ index ], _GetBCFlags( compress ), alphaRef );
572 hr = _CompressBC( src, dest[ index ], _GetBCFlags( compress ), alphaRef, degenerate );
585 //-------------------------------------------------------------------------------------
587 //-------------------------------------------------------------------------------------
588 HRESULT Decompress( const Image& cImage, DXGI_FORMAT format, ScratchImage& image )
590 if ( IsCompressed(format) || IsTypeless(format) )
593 if ( format == DXGI_FORMAT_UNKNOWN )
595 // Pick a default decompressed format based on BC input format
596 format = _DefaultDecompress( cImage.format );
597 if ( format == DXGI_FORMAT_UNKNOWN )
599 // Input is not a compressed format
603 else if ( !IsCompressed(cImage.format) || !IsValid(format) )
606 // Create decompressed image
607 HRESULT hr = image.Initialize2D( format, cImage.width, cImage.height, 1, 1 );
611 const Image *img = image.GetImage( 0, 0, 0 );
618 // Decompress single image
619 hr = _DecompressBC( cImage, *img );
626 HRESULT Decompress( const Image* cImages, size_t nimages, const TexMetadata& metadata,
627 DXGI_FORMAT format, ScratchImage& images )
629 if ( !cImages || !nimages )
632 if ( IsCompressed(format) || IsTypeless(format) )
635 if ( format == DXGI_FORMAT_UNKNOWN )
637 // Pick a default decompressed format based on BC input format
638 format = _DefaultDecompress( cImages[0].format );
639 if ( format == DXGI_FORMAT_UNKNOWN )
641 // Input is not a compressed format
645 else if ( !IsValid(format) )
650 TexMetadata mdata2 = metadata;
651 mdata2.format = format;
652 HRESULT hr = images.Initialize( mdata2 );
656 if ( nimages != images.GetImageCount() )
662 const Image* dest = images.GetImages();
669 for( size_t index=0; index < nimages; ++index )
671 assert( dest[ index ].format == format );
673 const Image& src = cImages[ index ];
674 if ( !IsCompressed( src.format ) )
680 if ( src.width != dest[ index ].width || src.height != dest[ index ].height )
686 hr = _DecompressBC( src, dest[ index ] );