1 //-------------------------------------------------------------------------------------
2 // DirectXTexMipMaps.cpp
4 // DirectX Texture Library - Mip-map generation
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"
21 static const XMVECTORF32 s_boxScale = { 0.25f, 0.25f, 0.25f, 0.25f };
22 static const XMVECTORF32 s_boxScale3D = { 0.125f, 0.125f, 0.125f, 0.125f };
24 //-------------------------------------------------------------------------------------
25 // Mipmap helper functions
26 //-------------------------------------------------------------------------------------
27 inline static bool ispow2( _In_ size_t x )
29 return ((x != 0) && !(x & (x - 1)));
32 static size_t _CountMips( _In_ size_t width, _In_ size_t height)
36 while ( height > 1 || width > 1 )
50 bool _CalculateMipLevels( _In_ size_t width, _In_ size_t height, _Inout_ size_t& mipLevels )
54 size_t maxMips = _CountMips(width,height);
55 if ( mipLevels > maxMips )
58 else if ( mipLevels == 0 )
60 mipLevels = _CountMips(width,height);
69 static size_t _CountMips3D( _In_ size_t width, _In_ size_t height, _In_ size_t depth)
73 while ( height > 1 || width > 1 || depth > 1 )
90 bool _CalculateMipLevels3D( _In_ size_t width, _In_ size_t height, _In_ size_t depth, _Inout_ size_t& mipLevels )
94 if ( !ispow2(width) || !ispow2(height) || !ispow2(depth) )
97 size_t maxMips = _CountMips3D(width,height,depth);
98 if ( mipLevels > maxMips )
101 else if ( mipLevels == 0 && ispow2(width) && ispow2(height) && ispow2(depth) )
103 mipLevels = _CountMips3D(width,height,depth);
114 static HRESULT _EnsureWicBitmapPixelFormat( _In_ IWICImagingFactory* pWIC, _In_ IWICBitmap* src, _In_ DWORD filter,
115 _In_ const WICPixelFormatGUID& desiredPixelFormat,
116 _Deref_out_ IWICBitmap** dest )
118 if ( !pWIC || !src || !dest )
123 WICPixelFormatGUID actualPixelFormat;
124 HRESULT hr = src->GetPixelFormat( &actualPixelFormat );
128 if ( memcmp( &actualPixelFormat, &desiredPixelFormat, sizeof(WICPixelFormatGUID) ) == 0 )
135 ScopedObject<IWICFormatConverter> converter;
136 hr = pWIC->CreateFormatConverter( &converter );
139 hr = converter->Initialize( src, desiredPixelFormat, _GetWICDither(filter), 0, 0, WICBitmapPaletteTypeCustom );
144 hr = pWIC->CreateBitmapFromSource( converter.Get(), WICBitmapCacheOnDemand, dest );
152 HRESULT _ResizeSeparateColorAndAlpha( _In_ IWICImagingFactory* pWIC, _In_ IWICBitmap* original,
153 _In_ size_t newWidth, _In_ size_t newHeight, _In_ DWORD filter, _Inout_ const Image* img )
155 if ( !pWIC || !original || !img )
158 const WICBitmapInterpolationMode interpolationMode = _GetWICInterp(filter);
160 WICPixelFormatGUID desiredPixelFormat = GUID_WICPixelFormatUndefined;
161 HRESULT hr = original->GetPixelFormat( &desiredPixelFormat );
163 size_t colorBytesInPixel = 0;
164 size_t colorBytesPerPixel = 0;
165 size_t colorWithAlphaBytesPerPixel = 0;
166 WICPixelFormatGUID colorPixelFormat = GUID_WICPixelFormatUndefined;
167 WICPixelFormatGUID colorWithAlphaPixelFormat = GUID_WICPixelFormatUndefined;
171 ScopedObject<IWICComponentInfo> componentInfo;
172 hr = pWIC->CreateComponentInfo( desiredPixelFormat, &componentInfo );
174 ScopedObject<IWICPixelFormatInfo> pixelFormatInfo;
177 hr = componentInfo->QueryInterface( __uuidof(IWICPixelFormatInfo), (void**)&pixelFormatInfo );
180 UINT bitsPerPixel = 0;
183 hr = pixelFormatInfo->GetBitsPerPixel( &bitsPerPixel );
188 if ( bitsPerPixel <= 32 )
190 colorBytesInPixel = colorBytesPerPixel = 3;
191 colorPixelFormat = GUID_WICPixelFormat24bppBGR;
193 colorWithAlphaBytesPerPixel = 4;
194 colorWithAlphaPixelFormat = GUID_WICPixelFormat32bppBGRA;
198 #if(_WIN32_WINNT >= 0x0602 /*_WIN32_WINNT_WIN8*/) || defined(_WIN7_PLATFORM_UPDATE)
201 colorBytesInPixel = colorBytesPerPixel = 12;
202 colorPixelFormat = GUID_WICPixelFormat96bppRGBFloat;
207 colorBytesInPixel = 12;
208 colorBytesPerPixel = 16;
209 colorPixelFormat = GUID_WICPixelFormat128bppRGBFloat;
212 colorWithAlphaBytesPerPixel = 16;
213 colorWithAlphaPixelFormat = GUID_WICPixelFormat128bppRGBAFloat;
218 // Resize color only image (no alpha channel)
219 ScopedObject<IWICBitmap> resizedColor;
222 ScopedObject<IWICBitmapScaler> colorScaler;
224 hr = pWIC->CreateBitmapScaler(&colorScaler);
227 ScopedObject<IWICBitmap> converted;
229 hr = _EnsureWicBitmapPixelFormat( pWIC, original, filter, colorPixelFormat, &converted );
232 hr = colorScaler->Initialize( converted.Get(), static_cast<UINT>(newWidth), static_cast<UINT>(newHeight), interpolationMode );
238 ScopedObject<IWICBitmap> resized;
240 hr = pWIC->CreateBitmapFromSource( colorScaler.Get(), WICBitmapCacheOnDemand, &resized );
243 hr = _EnsureWicBitmapPixelFormat( pWIC, resized.Get(), filter, colorPixelFormat, &resizedColor );
248 // Resize color+alpha image
249 ScopedObject<IWICBitmap> resizedColorWithAlpha;
252 ScopedObject<IWICBitmapScaler> colorWithAlphaScaler;
254 hr = pWIC->CreateBitmapScaler( &colorWithAlphaScaler );
257 ScopedObject<IWICBitmap> converted;
259 hr = _EnsureWicBitmapPixelFormat( pWIC, original, filter, colorWithAlphaPixelFormat, &converted );
262 hr = colorWithAlphaScaler->Initialize( converted.Get(), static_cast<UINT>(newWidth), static_cast<UINT>(newHeight), interpolationMode );
268 ScopedObject<IWICBitmap> resized;
270 hr = pWIC->CreateBitmapFromSource( colorWithAlphaScaler.Get(), WICBitmapCacheOnDemand, &resized );
273 hr = _EnsureWicBitmapPixelFormat( pWIC, resized.Get(), filter, colorWithAlphaPixelFormat, &resizedColorWithAlpha );
278 // Merge pixels (copying color channels from color only image to color+alpha image)
281 ScopedObject<IWICBitmapLock> colorLock;
282 ScopedObject<IWICBitmapLock> colorWithAlphaLock;
284 hr = resizedColor->Lock( nullptr, WICBitmapLockRead, &colorLock );
287 hr = resizedColorWithAlpha->Lock( nullptr, WICBitmapLockWrite, &colorWithAlphaLock );
292 WICInProcPointer colorWithAlphaData = nullptr;
293 UINT colorWithAlphaSizeInBytes = 0;
294 UINT colorWithAlphaStride = 0;
296 hr = colorWithAlphaLock->GetDataPointer( &colorWithAlphaSizeInBytes, &colorWithAlphaData );
299 if ( !colorWithAlphaData )
305 hr = colorWithAlphaLock->GetStride( &colorWithAlphaStride );
309 WICInProcPointer colorData = nullptr;
310 UINT colorSizeInBytes = 0;
311 UINT colorStride = 0;
314 hr = colorLock->GetDataPointer( &colorSizeInBytes, &colorData );
323 hr = colorLock->GetStride( &colorStride );
328 for ( size_t j = 0; SUCCEEDED(hr) && j < newHeight; j++ )
330 for ( size_t i = 0; SUCCEEDED(hr) && i < newWidth; i++ )
332 size_t colorWithAlphaIndex = (j * colorWithAlphaStride) + (i * colorWithAlphaBytesPerPixel);
333 size_t colorIndex = (j * colorStride) + (i * colorBytesPerPixel);
335 if ( ((colorWithAlphaIndex + colorBytesInPixel) > colorWithAlphaSizeInBytes)
336 || ( (colorIndex + colorBytesPerPixel) > colorSizeInBytes) )
342 memcpy_s( colorWithAlphaData + colorWithAlphaIndex, colorWithAlphaBytesPerPixel, colorData + colorIndex, colorBytesInPixel );
351 ScopedObject<IWICBitmap> wicBitmap;
352 hr = _EnsureWicBitmapPixelFormat( pWIC, resizedColorWithAlpha.Get(), filter, desiredPixelFormat, &wicBitmap );
355 hr = wicBitmap->CopyPixels( nullptr, static_cast<UINT>(img->rowPitch), static_cast<UINT>(img->slicePitch), img->pixels );
363 //-------------------------------------------------------------------------------------
364 // Generate a (2D) mip-map chain from a base image using WIC's image scaler
365 //-------------------------------------------------------------------------------------
366 static HRESULT _GenerateMipMapsUsingWIC( _In_ const Image& baseImage, _In_ DWORD filter, _In_ size_t levels,
367 _In_ const WICPixelFormatGUID& pfGUID, _In_ const ScratchImage& mipChain, _In_ size_t item )
369 assert( levels > 1 );
371 if ( !baseImage.pixels || !mipChain.GetPixels() )
374 IWICImagingFactory* pWIC = _GetWIC();
376 return E_NOINTERFACE;
378 size_t width = baseImage.width;
379 size_t height = baseImage.height;
381 ScopedObject<IWICBitmap> source;
382 HRESULT hr = pWIC->CreateBitmapFromMemory( static_cast<UINT>( width ), static_cast<UINT>( height ), pfGUID,
383 static_cast<UINT>( baseImage.rowPitch ), static_cast<UINT>( baseImage.slicePitch ),
384 baseImage.pixels, &source );
388 // Copy base image to top miplevel
389 const Image *img0 = mipChain.GetImage( 0, item, 0 );
393 uint8_t* pDest = img0->pixels;
397 const uint8_t *pSrc = baseImage.pixels;
398 for( size_t h=0; h < height; ++h )
400 size_t msize = std::min<size_t>( img0->rowPitch, baseImage.rowPitch );
401 memcpy_s( pDest, img0->rowPitch, pSrc, msize );
402 pSrc += baseImage.rowPitch;
403 pDest += img0->rowPitch;
406 ScopedObject<IWICComponentInfo> componentInfo;
407 hr = pWIC->CreateComponentInfo( pfGUID, &componentInfo );
411 ScopedObject<IWICPixelFormatInfo2> pixelFormatInfo;
412 hr = componentInfo->QueryInterface( __uuidof(IWICPixelFormatInfo2), (void**)&pixelFormatInfo );
416 BOOL supportsTransparency = FALSE;
417 hr = pixelFormatInfo->SupportsTransparency( &supportsTransparency );
421 // Resize base image to each target mip level
422 for( size_t level = 1; level < levels; ++level )
424 const Image *img = mipChain.GetImage( level, item, 0 );
434 assert( img->width == width && img->height == height && img->format == baseImage.format );
436 if ( (filter & TEX_FILTER_SEPARATE_ALPHA) && supportsTransparency )
438 hr = _ResizeSeparateColorAndAlpha( pWIC, source.Get(), width, height, filter, img );
444 ScopedObject<IWICBitmapScaler> scaler;
445 hr = pWIC->CreateBitmapScaler( &scaler );
449 hr = scaler->Initialize( source.Get(), static_cast<UINT>( width ), static_cast<UINT>( height ), _GetWICInterp( filter ) );
453 WICPixelFormatGUID pfScaler;
454 hr = scaler->GetPixelFormat( &pfScaler );
458 if ( memcmp( &pfScaler, &pfGUID, sizeof(WICPixelFormatGUID) ) == 0 )
460 hr = scaler->CopyPixels( 0, static_cast<UINT>( img->rowPitch ), static_cast<UINT>( img->slicePitch ), img->pixels );
466 // The WIC bitmap scaler is free to return a different pixel format than the source image, so here we
468 ScopedObject<IWICFormatConverter> FC;
469 hr = pWIC->CreateFormatConverter( &FC );
473 hr = FC->Initialize( scaler.Get(), pfGUID, _GetWICDither( filter ), 0, 0, WICBitmapPaletteTypeCustom );
477 hr = FC->CopyPixels( 0, static_cast<UINT>( img->rowPitch ), static_cast<UINT>( img->slicePitch ), img->pixels );
488 //-------------------------------------------------------------------------------------
489 // Generate volume mip-map helpers
490 //-------------------------------------------------------------------------------------
491 static HRESULT _Setup3DMips( _In_count_(depth) const Image* baseImages, _In_ size_t depth, size_t levels,
492 _Out_ ScratchImage& mipChain )
494 if ( !baseImages || !depth )
497 assert( levels > 1 );
499 size_t width = baseImages[0].width;
500 size_t height = baseImages[0].height;
502 HRESULT hr = mipChain.Initialize3D( baseImages[0].format, width, height, depth, levels );
506 // Copy base images to top slice
507 for( size_t slice=0; slice < depth; ++slice )
509 const Image& src = baseImages[slice];
511 const Image *dest = mipChain.GetImage( 0, 0, slice );
518 assert( src.format == dest->format );
520 uint8_t* pDest = dest->pixels;
527 const uint8_t *pSrc = src.pixels;
528 size_t rowPitch = src.rowPitch;
529 for( size_t h=0; h < height; ++h )
531 size_t msize = std::min<size_t>( dest->rowPitch, rowPitch );
532 memcpy_s( pDest, dest->rowPitch, pSrc, msize );
534 pDest += dest->rowPitch;
541 static HRESULT _Generate3DMipsPointFilter( _In_ size_t depth, _In_ size_t levels, _In_ const ScratchImage& mipChain )
543 if ( !depth || !mipChain.GetImages() )
546 // This assumes that the base images are already placed into the mipChain at the top level... (see _Setup3DMips)
548 assert( levels > 1 );
550 size_t width = mipChain.GetMetadata().width;
551 size_t height = mipChain.GetMetadata().height;
553 assert( ispow2(width) && ispow2(height) && ispow2(depth) );
555 // Allocate temporary space (2 scanlines)
556 ScopedAlignedArrayXMVECTOR scanline( reinterpret_cast<XMVECTOR*>( _aligned_malloc( (sizeof(XMVECTOR)*width*2), 16 ) ) );
558 return E_OUTOFMEMORY;
560 XMVECTOR* target = scanline.get();
562 XMVECTOR* row = target + width;
564 // Resize base image to each target mip level
565 for( size_t level=1; level < levels; ++level )
570 for( size_t slice=0; slice < depth; slice += 2 )
572 const Image* src = mipChain.GetImage( level-1, 0, slice );
573 const Image* dest = mipChain.GetImage( level, 0, slice >> 1 );
578 const uint8_t* pSrc = src->pixels;
579 uint8_t* pDest = dest->pixels;
581 size_t rowPitch = src->rowPitch;
583 size_t nheight = height >> 1;
585 for( size_t y = 0; y < nheight; ++y )
587 if ( !_LoadScanline( row, width, pSrc, rowPitch, src->format ) )
591 size_t nwidth = width >> 1;
593 for( size_t x = 0; x < nwidth; ++x )
595 target[ x ] = row[ x*2 ];
598 if ( !_StoreScanline( pDest, dest->rowPitch, dest->format, target, nwidth ) )
600 pDest += dest->rowPitch;
607 const Image* src = mipChain.GetImage( level-1, 0, 0 );
608 const Image* dest = mipChain.GetImage( level, 0, 0 );
613 const uint8_t* pSrc = src->pixels;
614 uint8_t* pDest = dest->pixels;
616 size_t rowPitch = src->rowPitch;
618 size_t nheight = height >> 1;
620 for( size_t y = 0; y < nheight; ++y )
622 if ( !_LoadScanline( row, width, pSrc, rowPitch, src->format ) )
626 size_t nwidth = width >> 1;
628 for( size_t x = 0; x < nwidth; ++x )
630 target[ x ] = row[ x*2 ];
633 if ( !_StoreScanline( pDest, dest->rowPitch, dest->format, target, nwidth ) )
635 pDest += dest->rowPitch;
649 assert( height == 1 && width == 1 && depth == 1 );
654 static HRESULT _Generate3DMipsBoxFilter( _In_ size_t depth, _In_ size_t levels, _In_ const ScratchImage& mipChain )
656 if ( !depth || !mipChain.GetImages() )
659 // This assumes that the base images are already placed into the mipChain at the top level... (see _Setup3DMips)
661 assert( levels > 1 );
663 size_t width = mipChain.GetMetadata().width;
664 size_t height = mipChain.GetMetadata().height;
666 assert( ispow2(width) && ispow2(height) && ispow2(depth) );
668 // Allocate temporary space (5 scanlines)
669 ScopedAlignedArrayXMVECTOR scanline( reinterpret_cast<XMVECTOR*>( _aligned_malloc( (sizeof(XMVECTOR)*width*5), 16 ) ) );
671 return E_OUTOFMEMORY;
673 XMVECTOR* target = scanline.get();
675 XMVECTOR* urow0 = target + width;
676 XMVECTOR* urow1 = target + width*2;
677 XMVECTOR* vrow0 = target + width*3;
678 XMVECTOR* vrow1 = target + width*4;
680 const XMVECTOR* urow2 = urow0 + 1;
681 const XMVECTOR* urow3 = urow1 + 1;
682 const XMVECTOR* vrow2 = vrow0 + 1;
683 const XMVECTOR* vrow3 = vrow1 + 1;
685 // Resize base image to each target mip level
686 for( size_t level=1; level < levels; ++level )
705 for( size_t slice=0; slice < depth; slice += 2 )
707 const Image* srca = mipChain.GetImage( level-1, 0, slice );
708 const Image* srcb = mipChain.GetImage( level-1, 0, slice+1 );
709 const Image* dest = mipChain.GetImage( level, 0, slice >> 1 );
711 if ( !srca || !srcb || !dest )
714 const uint8_t* pSrc1 = srca->pixels;
715 const uint8_t* pSrc2 = srcb->pixels;
716 uint8_t* pDest = dest->pixels;
718 size_t aRowPitch = srca->rowPitch;
719 size_t bRowPitch = srcb->rowPitch;
721 size_t nheight = height >> 1;
723 for( size_t y = 0; y < nheight; ++y )
725 if ( !_LoadScanline( urow0, width, pSrc1, aRowPitch, srca->format ) )
729 if ( urow0 != urow1 )
731 if ( !_LoadScanline( urow1, width, pSrc1, aRowPitch, srca->format ) )
736 if ( urow0 != vrow0 )
738 if ( !_LoadScanline( vrow0, width, pSrc2, bRowPitch, srcb->format ) )
743 if ( urow0 != vrow1 && vrow0 != vrow1 )
745 if ( !_LoadScanline( vrow1, width, pSrc2, bRowPitch, srcb->format ) )
750 size_t nwidth = width >> 1;
752 for( size_t x = 0; x < nwidth; ++x )
756 // Box filter: Average 2x2x2 pixels
757 XMVECTOR v = XMVectorAdd( urow0[ x2 ], urow1[ x2 ] );
758 v = XMVectorAdd( v, urow2[ x2 ] );
759 v = XMVectorAdd( v, urow3[ x2 ] );
760 v = XMVectorAdd( v, vrow0[ x2 ] );
761 v = XMVectorAdd( v, vrow1[ x2 ] );
762 v = XMVectorAdd( v, vrow2[ x2 ] );
763 v = XMVectorAdd( v, vrow3[ x2 ] );
765 target[ x ] = XMVectorMultiply( v, s_boxScale3D );
768 if ( !_StoreScanline( pDest, dest->rowPitch, dest->format, target, nwidth ) )
770 pDest += dest->rowPitch;
777 const Image* src = mipChain.GetImage( level-1, 0, 0 );
778 const Image* dest = mipChain.GetImage( level, 0, 0 );
783 const uint8_t* pSrc = src->pixels;
784 uint8_t* pDest = dest->pixels;
786 size_t rowPitch = src->rowPitch;
788 size_t nheight = height >> 1;
790 for( size_t y = 0; y < nheight; ++y )
792 if ( !_LoadScanline( urow0, width, pSrc, rowPitch, src->format ) )
796 if ( urow0 != urow1 )
798 if ( !_LoadScanline( urow1, width, pSrc, rowPitch, src->format ) )
803 size_t nwidth = width >> 1;
805 for( size_t x = 0; x < nwidth; ++x )
809 // Box filter: Average 2x2 pixels
810 XMVECTOR v = XMVectorAdd( urow0[ x2 ], urow1[ x2 ] );
811 v = XMVectorAdd( v, urow2[ x2 ] );
812 v = XMVectorAdd( v, urow3[ x2 ] );
814 target[ x ] = XMVectorMultiply( v, s_boxScale );
817 if ( !_StoreScanline( pDest, dest->rowPitch, dest->format, target, nwidth ) )
819 pDest += dest->rowPitch;
833 assert( height == 1 && width == 1 && depth == 1 );
839 //=====================================================================================
841 //=====================================================================================
843 //-------------------------------------------------------------------------------------
844 // Generate mipmap chain
845 //-------------------------------------------------------------------------------------
846 HRESULT GenerateMipMaps( const Image& baseImage, DWORD filter, size_t levels, ScratchImage& mipChain, bool allow1D )
848 if ( !IsValid( baseImage.format ) )
851 if ( !baseImage.pixels )
854 if ( !_CalculateMipLevels(baseImage.width, baseImage.height, levels) )
857 if ( IsCompressed( baseImage.format ) || IsVideo( baseImage.format ) )
859 return HRESULT_FROM_WIN32( ERROR_NOT_SUPPORTED );
862 static_assert( TEX_FILTER_POINT == 0x100000, "TEX_FILTER_ flag values don't match TEX_FILTER_MASK" );
863 switch(filter & TEX_FILTER_MASK)
866 case TEX_FILTER_POINT:
867 case TEX_FILTER_FANT: // Equivalent to Box filter
868 case TEX_FILTER_LINEAR:
869 case TEX_FILTER_CUBIC:
871 WICPixelFormatGUID pfGUID;
872 if ( _DXGIToWIC( baseImage.format, pfGUID ) )
874 // Case 1: Base image format is supported by Windows Imaging Component
875 HRESULT hr = (baseImage.height > 1 || !allow1D)
876 ? mipChain.Initialize2D( baseImage.format, baseImage.width, baseImage.height, 1, levels )
877 : mipChain.Initialize1D( baseImage.format, baseImage.width, 1, levels );
881 return _GenerateMipMapsUsingWIC( baseImage, filter, levels, pfGUID, mipChain, 0 );
885 // Case 2: Base image format is not supported by WIC, so we have to convert, generate, and convert back
886 assert( baseImage.format != DXGI_FORMAT_R32G32B32A32_FLOAT );
888 HRESULT hr = _ConvertToR32G32B32A32( baseImage, temp );
892 const Image *timg = temp.GetImage( 0, 0, 0 );
896 ScratchImage tMipChain;
897 hr = _GenerateMipMapsUsingWIC( *timg, filter, levels, GUID_WICPixelFormat128bppRGBAFloat, tMipChain, 0 );
903 return _ConvertFromR32G32B32A32( tMipChain.GetImages(), tMipChain.GetImageCount(), tMipChain.GetMetadata(), baseImage.format, mipChain );
909 return HRESULT_FROM_WIN32( ERROR_NOT_SUPPORTED );
913 HRESULT GenerateMipMaps( const Image* srcImages, size_t nimages, const TexMetadata& metadata,
914 DWORD filter, size_t levels, ScratchImage& mipChain )
916 if ( !srcImages || !nimages || !IsValid(metadata.format) )
919 if ( metadata.dimension == TEX_DIMENSION_TEXTURE3D
920 || IsCompressed( metadata.format ) || IsVideo( metadata.format ) )
921 return HRESULT_FROM_WIN32( ERROR_NOT_SUPPORTED );
923 if ( !_CalculateMipLevels(metadata.width, metadata.height, levels) )
926 static_assert( TEX_FILTER_POINT == 0x100000, "TEX_FILTER_ flag values don't match TEX_FILTER_MASK" );
927 switch(filter & TEX_FILTER_MASK)
930 case TEX_FILTER_POINT:
931 case TEX_FILTER_FANT: // Equivalent to Box filter
932 case TEX_FILTER_LINEAR:
933 case TEX_FILTER_CUBIC:
935 WICPixelFormatGUID pfGUID;
936 if ( _DXGIToWIC( metadata.format, pfGUID ) )
938 // Case 1: Base image format is supported by Windows Imaging Component
939 TexMetadata mdata2 = metadata;
940 mdata2.mipLevels = levels;
941 HRESULT hr = mipChain.Initialize( mdata2 );
945 for( size_t item = 0; item < metadata.arraySize; ++item )
947 size_t index = metadata.ComputeIndex( 0, item, 0 );
948 if ( index >= nimages )
954 const Image& baseImage = srcImages[ index ];
956 hr = _GenerateMipMapsUsingWIC( baseImage, filter, levels, pfGUID, mipChain, item );
968 // Case 2: Base image format is not supported by WIC, so we have to convert, generate, and convert back
969 assert( metadata.format != DXGI_FORMAT_R32G32B32A32_FLOAT );
971 TexMetadata mdata2 = metadata;
972 mdata2.mipLevels = levels;
973 mdata2.format = DXGI_FORMAT_R32G32B32A32_FLOAT;
974 ScratchImage tMipChain;
975 HRESULT hr = tMipChain.Initialize( mdata2 );
979 for( size_t item = 0; item < metadata.arraySize; ++item )
981 size_t index = metadata.ComputeIndex( 0, item, 0 );
982 if ( index >= nimages )
985 const Image& baseImage = srcImages[ index ];
988 hr = _ConvertToR32G32B32A32( baseImage, temp );
992 const Image *timg = temp.GetImage( 0, 0, 0 );
996 hr = _GenerateMipMapsUsingWIC( *timg, filter, levels, GUID_WICPixelFormat128bppRGBAFloat, tMipChain, item );
1001 return _ConvertFromR32G32B32A32( tMipChain.GetImages(), tMipChain.GetImageCount(), tMipChain.GetMetadata(), metadata.format, mipChain );
1007 return HRESULT_FROM_WIN32( ERROR_NOT_SUPPORTED );;
1012 //-------------------------------------------------------------------------------------
1013 // Generate mipmap chain for volume texture
1014 //-------------------------------------------------------------------------------------
1015 HRESULT GenerateMipMaps3D( const Image* baseImages, size_t depth, DWORD filter, size_t levels, ScratchImage& mipChain )
1017 if ( !baseImages || !depth )
1018 return E_INVALIDARG;
1020 DXGI_FORMAT format = baseImages[0].format;
1021 size_t width = baseImages[0].width;
1022 size_t height = baseImages[0].height;
1024 if ( !ispow2(width) || !ispow2(height) || !ispow2(depth) )
1025 return E_INVALIDARG;
1027 if ( !_CalculateMipLevels3D(width, height, depth, levels) )
1028 return E_INVALIDARG;
1030 for( size_t slice=0; slice < depth; ++slice )
1032 if ( !baseImages[slice].pixels )
1035 if ( baseImages[slice].format != format || baseImages[slice].width != width || baseImages[slice].height != height )
1037 // All base images must be the same format, width, and height
1042 if ( IsCompressed( format ) )
1044 // We don't support generating mipmaps from compressed images, as those should be generated before compression
1045 return HRESULT_FROM_WIN32( ERROR_NOT_SUPPORTED );
1050 static_assert( TEX_FILTER_POINT == 0x100000, "TEX_FILTER_ flag values don't match TEX_FILTER_MASK" );
1051 switch( filter & TEX_FILTER_MASK )
1054 case TEX_FILTER_FANT:
1055 hr = _Setup3DMips( baseImages, depth, levels, mipChain );
1059 // For decimation, Fant is equivalent to a Box filter
1060 hr = _Generate3DMipsBoxFilter( depth, levels, mipChain );
1065 case WIC_FLAGS_FILTER_POINT:
1066 hr = _Setup3DMips( baseImages, depth, levels, mipChain );
1070 hr = _Generate3DMipsPointFilter( depth, levels, mipChain );
1075 case WIC_FLAGS_FILTER_LINEAR:
1076 // Need to implement a 3D bi-linear filter (2x2x2)
1079 case WIC_FLAGS_FILTER_CUBIC:
1080 // Need to implement a 3D bi-cubic filter (3x3x3)
1084 return HRESULT_FROM_WIN32( ERROR_NOT_SUPPORTED );;
1088 HRESULT GenerateMipMaps3D( const Image* srcImages, size_t nimages, const TexMetadata& metadata,
1089 DWORD filter, size_t levels, ScratchImage& mipChain )
1091 if ( !srcImages || !nimages || !IsValid(metadata.format)
1092 || !ispow2(metadata.width) || !ispow2(metadata.height) || !ispow2(metadata.depth) )
1093 return E_INVALIDARG;
1095 if ( metadata.dimension != TEX_DIMENSION_TEXTURE3D
1096 || IsCompressed( metadata.format ) || IsVideo( metadata.format ) )
1097 return HRESULT_FROM_WIN32( ERROR_NOT_SUPPORTED );
1099 if ( !_CalculateMipLevels3D(metadata.width, metadata.height, metadata.depth, levels) )
1100 return E_INVALIDARG;
1102 std::vector<const Image> baseImages;
1103 baseImages.reserve( metadata.depth );
1104 for( size_t slice=0; slice < metadata.depth; ++slice )
1106 size_t index = metadata.ComputeIndex( 0, 0, slice );
1107 if ( index >= nimages )
1110 const Image& src = srcImages[ index ];
1114 if ( src.format != metadata.format || src.width != metadata.width || src.height != metadata.height )
1116 // All base images must be the same format, width, and height
1120 baseImages.push_back( src );
1123 assert( baseImages.size() == metadata.depth );
1127 static_assert( TEX_FILTER_POINT == 0x100000, "TEX_FILTER_ flag values don't match TEX_FILTER_MASK" );
1128 switch( filter & TEX_FILTER_MASK )
1131 case TEX_FILTER_FANT:
1132 hr = _Setup3DMips( &baseImages[0], metadata.depth, levels, mipChain );
1136 // For decimation, Fant is equivalent to a Box filter
1137 hr = _Generate3DMipsBoxFilter( metadata.depth, levels, mipChain );
1142 case WIC_FLAGS_FILTER_POINT:
1143 hr = _Setup3DMips( &baseImages[0], metadata.depth, levels, mipChain );
1147 hr = _Generate3DMipsPointFilter( metadata.depth, levels, mipChain );
1152 case WIC_FLAGS_FILTER_LINEAR:
1153 // Need to implement a 3D bi-linear filter (2x2x2)
1156 case WIC_FLAGS_FILTER_CUBIC:
1157 // Need to implement a 3D bi-cubic filter (3x3x3)
1161 return HRESULT_FROM_WIN32( ERROR_NOT_SUPPORTED );;
1165 #endif /* !__MINGW32__ */