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);
112 static HRESULT _EnsureWicBitmapPixelFormat( _In_ IWICImagingFactory* pWIC, _In_ IWICBitmap* src, _In_ DWORD filter,
113 _In_ const WICPixelFormatGUID& desiredPixelFormat,
114 _Deref_out_ IWICBitmap** dest )
116 if ( !pWIC || !src || !dest )
121 WICPixelFormatGUID actualPixelFormat;
122 HRESULT hr = src->GetPixelFormat( &actualPixelFormat );
126 if ( memcmp( &actualPixelFormat, &desiredPixelFormat, sizeof(WICPixelFormatGUID) ) == 0 )
133 ScopedObject<IWICFormatConverter> converter;
134 hr = pWIC->CreateFormatConverter( &converter );
137 hr = converter->Initialize( src, desiredPixelFormat, _GetWICDither(filter), 0, 0, WICBitmapPaletteTypeCustom );
142 hr = pWIC->CreateBitmapFromSource( converter.Get(), WICBitmapCacheOnDemand, dest );
150 HRESULT _ResizeSeparateColorAndAlpha( _In_ IWICImagingFactory* pWIC, _In_ IWICBitmap* original,
151 _In_ size_t newWidth, _In_ size_t newHeight, _In_ DWORD filter, _Inout_ const Image* img )
153 if ( !pWIC || !original || !img )
156 const WICBitmapInterpolationMode interpolationMode = _GetWICInterp(filter);
158 WICPixelFormatGUID desiredPixelFormat = GUID_WICPixelFormatUndefined;
159 HRESULT hr = original->GetPixelFormat( &desiredPixelFormat );
161 size_t colorBytesInPixel = 0;
162 size_t colorBytesPerPixel = 0;
163 size_t colorWithAlphaBytesPerPixel = 0;
164 WICPixelFormatGUID colorPixelFormat = GUID_WICPixelFormatUndefined;
165 WICPixelFormatGUID colorWithAlphaPixelFormat = GUID_WICPixelFormatUndefined;
169 ScopedObject<IWICComponentInfo> componentInfo;
170 hr = pWIC->CreateComponentInfo( desiredPixelFormat, &componentInfo );
172 ScopedObject<IWICPixelFormatInfo> pixelFormatInfo;
175 hr = componentInfo->QueryInterface( __uuidof(IWICPixelFormatInfo), (void**)&pixelFormatInfo );
178 UINT bitsPerPixel = 0;
181 hr = pixelFormatInfo->GetBitsPerPixel( &bitsPerPixel );
186 if ( bitsPerPixel <= 32 )
188 colorBytesInPixel = colorBytesPerPixel = 3;
189 colorPixelFormat = GUID_WICPixelFormat24bppBGR;
191 colorWithAlphaBytesPerPixel = 4;
192 colorWithAlphaPixelFormat = GUID_WICPixelFormat32bppBGRA;
196 #if(_WIN32_WINNT >= 0x0602 /*_WIN32_WINNT_WIN8*/) || defined(_WIN7_PLATFORM_UPDATE)
199 colorBytesInPixel = colorBytesPerPixel = 12;
200 colorPixelFormat = GUID_WICPixelFormat96bppRGBFloat;
205 colorBytesInPixel = 12;
206 colorBytesPerPixel = 16;
207 colorPixelFormat = GUID_WICPixelFormat128bppRGBFloat;
210 colorWithAlphaBytesPerPixel = 16;
211 colorWithAlphaPixelFormat = GUID_WICPixelFormat128bppRGBAFloat;
216 // Resize color only image (no alpha channel)
217 ScopedObject<IWICBitmap> resizedColor;
220 ScopedObject<IWICBitmapScaler> colorScaler;
222 hr = pWIC->CreateBitmapScaler(&colorScaler);
225 ScopedObject<IWICBitmap> converted;
227 hr = _EnsureWicBitmapPixelFormat( pWIC, original, filter, colorPixelFormat, &converted );
230 hr = colorScaler->Initialize( converted.Get(), static_cast<UINT>(newWidth), static_cast<UINT>(newHeight), interpolationMode );
236 ScopedObject<IWICBitmap> resized;
238 hr = pWIC->CreateBitmapFromSource( colorScaler.Get(), WICBitmapCacheOnDemand, &resized );
241 hr = _EnsureWicBitmapPixelFormat( pWIC, resized.Get(), filter, colorPixelFormat, &resizedColor );
246 // Resize color+alpha image
247 ScopedObject<IWICBitmap> resizedColorWithAlpha;
250 ScopedObject<IWICBitmapScaler> colorWithAlphaScaler;
252 hr = pWIC->CreateBitmapScaler( &colorWithAlphaScaler );
255 ScopedObject<IWICBitmap> converted;
257 hr = _EnsureWicBitmapPixelFormat( pWIC, original, filter, colorWithAlphaPixelFormat, &converted );
260 hr = colorWithAlphaScaler->Initialize( converted.Get(), static_cast<UINT>(newWidth), static_cast<UINT>(newHeight), interpolationMode );
266 ScopedObject<IWICBitmap> resized;
268 hr = pWIC->CreateBitmapFromSource( colorWithAlphaScaler.Get(), WICBitmapCacheOnDemand, &resized );
271 hr = _EnsureWicBitmapPixelFormat( pWIC, resized.Get(), filter, colorWithAlphaPixelFormat, &resizedColorWithAlpha );
276 // Merge pixels (copying color channels from color only image to color+alpha image)
279 ScopedObject<IWICBitmapLock> colorLock;
280 ScopedObject<IWICBitmapLock> colorWithAlphaLock;
282 hr = resizedColor->Lock( nullptr, WICBitmapLockRead, &colorLock );
285 hr = resizedColorWithAlpha->Lock( nullptr, WICBitmapLockWrite, &colorWithAlphaLock );
290 WICInProcPointer colorWithAlphaData = nullptr;
291 UINT colorWithAlphaSizeInBytes = 0;
292 UINT colorWithAlphaStride = 0;
294 hr = colorWithAlphaLock->GetDataPointer( &colorWithAlphaSizeInBytes, &colorWithAlphaData );
297 if ( !colorWithAlphaData )
303 hr = colorWithAlphaLock->GetStride( &colorWithAlphaStride );
307 WICInProcPointer colorData = nullptr;
308 UINT colorSizeInBytes = 0;
309 UINT colorStride = 0;
312 hr = colorLock->GetDataPointer( &colorSizeInBytes, &colorData );
321 hr = colorLock->GetStride( &colorStride );
326 for ( size_t j = 0; SUCCEEDED(hr) && j < newHeight; j++ )
328 for ( size_t i = 0; SUCCEEDED(hr) && i < newWidth; i++ )
330 size_t colorWithAlphaIndex = (j * colorWithAlphaStride) + (i * colorWithAlphaBytesPerPixel);
331 size_t colorIndex = (j * colorStride) + (i * colorBytesPerPixel);
333 if ( ((colorWithAlphaIndex + colorBytesInPixel) > colorWithAlphaSizeInBytes)
334 || ( (colorIndex + colorBytesPerPixel) > colorSizeInBytes) )
340 memcpy_s( colorWithAlphaData + colorWithAlphaIndex, colorWithAlphaBytesPerPixel, colorData + colorIndex, colorBytesInPixel );
349 ScopedObject<IWICBitmap> wicBitmap;
350 hr = _EnsureWicBitmapPixelFormat( pWIC, resizedColorWithAlpha.Get(), filter, desiredPixelFormat, &wicBitmap );
353 hr = wicBitmap->CopyPixels( nullptr, static_cast<UINT>(img->rowPitch), static_cast<UINT>(img->slicePitch), img->pixels );
361 //-------------------------------------------------------------------------------------
362 // Generate a (2D) mip-map chain from a base image using WIC's image scaler
363 //-------------------------------------------------------------------------------------
364 static HRESULT _GenerateMipMapsUsingWIC( _In_ const Image& baseImage, _In_ DWORD filter, _In_ size_t levels,
365 _In_ const WICPixelFormatGUID& pfGUID, _In_ const ScratchImage& mipChain, _In_ size_t item )
367 assert( levels > 1 );
369 if ( !baseImage.pixels || !mipChain.GetPixels() )
372 IWICImagingFactory* pWIC = _GetWIC();
374 return E_NOINTERFACE;
376 size_t width = baseImage.width;
377 size_t height = baseImage.height;
379 ScopedObject<IWICBitmap> source;
380 HRESULT hr = pWIC->CreateBitmapFromMemory( static_cast<UINT>( width ), static_cast<UINT>( height ), pfGUID,
381 static_cast<UINT>( baseImage.rowPitch ), static_cast<UINT>( baseImage.slicePitch ),
382 baseImage.pixels, &source );
386 // Copy base image to top miplevel
387 const Image *img0 = mipChain.GetImage( 0, item, 0 );
391 uint8_t* pDest = img0->pixels;
395 const uint8_t *pSrc = baseImage.pixels;
396 for( size_t h=0; h < height; ++h )
398 size_t msize = std::min<size_t>( img0->rowPitch, baseImage.rowPitch );
399 memcpy_s( pDest, img0->rowPitch, pSrc, msize );
400 pSrc += baseImage.rowPitch;
401 pDest += img0->rowPitch;
404 ScopedObject<IWICComponentInfo> componentInfo;
405 hr = pWIC->CreateComponentInfo( pfGUID, &componentInfo );
409 ScopedObject<IWICPixelFormatInfo2> pixelFormatInfo;
410 hr = componentInfo->QueryInterface( __uuidof(IWICPixelFormatInfo2), (void**)&pixelFormatInfo );
414 BOOL supportsTransparency = FALSE;
415 hr = pixelFormatInfo->SupportsTransparency( &supportsTransparency );
419 // Resize base image to each target mip level
420 for( size_t level = 1; level < levels; ++level )
422 const Image *img = mipChain.GetImage( level, item, 0 );
432 assert( img->width == width && img->height == height && img->format == baseImage.format );
434 if ( (filter & TEX_FILTER_SEPARATE_ALPHA) && supportsTransparency )
436 hr = _ResizeSeparateColorAndAlpha( pWIC, source.Get(), width, height, filter, img );
442 ScopedObject<IWICBitmapScaler> scaler;
443 hr = pWIC->CreateBitmapScaler( &scaler );
447 hr = scaler->Initialize( source.Get(), static_cast<UINT>( width ), static_cast<UINT>( height ), _GetWICInterp( filter ) );
451 WICPixelFormatGUID pfScaler;
452 hr = scaler->GetPixelFormat( &pfScaler );
456 if ( memcmp( &pfScaler, &pfGUID, sizeof(WICPixelFormatGUID) ) == 0 )
458 hr = scaler->CopyPixels( 0, static_cast<UINT>( img->rowPitch ), static_cast<UINT>( img->slicePitch ), img->pixels );
464 // The WIC bitmap scaler is free to return a different pixel format than the source image, so here we
466 ScopedObject<IWICFormatConverter> FC;
467 hr = pWIC->CreateFormatConverter( &FC );
471 hr = FC->Initialize( scaler.Get(), pfGUID, _GetWICDither( filter ), 0, 0, WICBitmapPaletteTypeCustom );
475 hr = FC->CopyPixels( 0, static_cast<UINT>( img->rowPitch ), static_cast<UINT>( img->slicePitch ), img->pixels );
486 //-------------------------------------------------------------------------------------
487 // Generate volume mip-map helpers
488 //-------------------------------------------------------------------------------------
489 static HRESULT _Setup3DMips( _In_count_(depth) const Image* baseImages, _In_ size_t depth, size_t levels,
490 _Out_ ScratchImage& mipChain )
492 if ( !baseImages || !depth )
495 assert( levels > 1 );
497 size_t width = baseImages[0].width;
498 size_t height = baseImages[0].height;
500 HRESULT hr = mipChain.Initialize3D( baseImages[0].format, width, height, depth, levels );
504 // Copy base images to top slice
505 for( size_t slice=0; slice < depth; ++slice )
507 const Image& src = baseImages[slice];
509 const Image *dest = mipChain.GetImage( 0, 0, slice );
516 assert( src.format == dest->format );
518 uint8_t* pDest = dest->pixels;
525 const uint8_t *pSrc = src.pixels;
526 size_t rowPitch = src.rowPitch;
527 for( size_t h=0; h < height; ++h )
529 size_t msize = std::min<size_t>( dest->rowPitch, rowPitch );
530 memcpy_s( pDest, dest->rowPitch, pSrc, msize );
532 pDest += dest->rowPitch;
539 static HRESULT _Generate3DMipsPointFilter( _In_ size_t depth, _In_ size_t levels, _In_ const ScratchImage& mipChain )
541 if ( !depth || !mipChain.GetImages() )
544 // This assumes that the base images are already placed into the mipChain at the top level... (see _Setup3DMips)
546 assert( levels > 1 );
548 size_t width = mipChain.GetMetadata().width;
549 size_t height = mipChain.GetMetadata().height;
551 assert( ispow2(width) && ispow2(height) && ispow2(depth) );
553 // Allocate temporary space (2 scanlines)
554 ScopedAlignedArrayXMVECTOR scanline( reinterpret_cast<XMVECTOR*>( _aligned_malloc( (sizeof(XMVECTOR)*width*2), 16 ) ) );
556 return E_OUTOFMEMORY;
558 XMVECTOR* target = scanline.get();
560 XMVECTOR* row = target + width;
562 // Resize base image to each target mip level
563 for( size_t level=1; level < levels; ++level )
568 for( size_t slice=0; slice < depth; slice += 2 )
570 const Image* src = mipChain.GetImage( level-1, 0, slice );
571 const Image* dest = mipChain.GetImage( level, 0, slice >> 1 );
576 const uint8_t* pSrc = src->pixels;
577 uint8_t* pDest = dest->pixels;
579 size_t rowPitch = src->rowPitch;
581 size_t nheight = height >> 1;
583 for( size_t y = 0; y < nheight; ++y )
585 if ( !_LoadScanline( row, width, pSrc, rowPitch, src->format ) )
589 size_t nwidth = width >> 1;
591 for( size_t x = 0; x < nwidth; ++x )
593 target[ x ] = row[ x*2 ];
596 if ( !_StoreScanline( pDest, dest->rowPitch, dest->format, target, nwidth ) )
598 pDest += dest->rowPitch;
605 const Image* src = mipChain.GetImage( level-1, 0, 0 );
606 const Image* dest = mipChain.GetImage( level, 0, 0 );
611 const uint8_t* pSrc = src->pixels;
612 uint8_t* pDest = dest->pixels;
614 size_t rowPitch = src->rowPitch;
616 size_t nheight = height >> 1;
618 for( size_t y = 0; y < nheight; ++y )
620 if ( !_LoadScanline( row, width, pSrc, rowPitch, src->format ) )
624 size_t nwidth = width >> 1;
626 for( size_t x = 0; x < nwidth; ++x )
628 target[ x ] = row[ x*2 ];
631 if ( !_StoreScanline( pDest, dest->rowPitch, dest->format, target, nwidth ) )
633 pDest += dest->rowPitch;
647 assert( height == 1 && width == 1 && depth == 1 );
652 static HRESULT _Generate3DMipsBoxFilter( _In_ size_t depth, _In_ size_t levels, _In_ const ScratchImage& mipChain )
654 if ( !depth || !mipChain.GetImages() )
657 // This assumes that the base images are already placed into the mipChain at the top level... (see _Setup3DMips)
659 assert( levels > 1 );
661 size_t width = mipChain.GetMetadata().width;
662 size_t height = mipChain.GetMetadata().height;
664 assert( ispow2(width) && ispow2(height) && ispow2(depth) );
666 // Allocate temporary space (5 scanlines)
667 ScopedAlignedArrayXMVECTOR scanline( reinterpret_cast<XMVECTOR*>( _aligned_malloc( (sizeof(XMVECTOR)*width*5), 16 ) ) );
669 return E_OUTOFMEMORY;
671 XMVECTOR* target = scanline.get();
673 XMVECTOR* urow0 = target + width;
674 XMVECTOR* urow1 = target + width*2;
675 XMVECTOR* vrow0 = target + width*3;
676 XMVECTOR* vrow1 = target + width*4;
678 const XMVECTOR* urow2 = urow0 + 1;
679 const XMVECTOR* urow3 = urow1 + 1;
680 const XMVECTOR* vrow2 = vrow0 + 1;
681 const XMVECTOR* vrow3 = vrow1 + 1;
683 // Resize base image to each target mip level
684 for( size_t level=1; level < levels; ++level )
703 for( size_t slice=0; slice < depth; slice += 2 )
705 const Image* srca = mipChain.GetImage( level-1, 0, slice );
706 const Image* srcb = mipChain.GetImage( level-1, 0, slice+1 );
707 const Image* dest = mipChain.GetImage( level, 0, slice >> 1 );
709 if ( !srca || !srcb || !dest )
712 const uint8_t* pSrc1 = srca->pixels;
713 const uint8_t* pSrc2 = srcb->pixels;
714 uint8_t* pDest = dest->pixels;
716 size_t aRowPitch = srca->rowPitch;
717 size_t bRowPitch = srcb->rowPitch;
719 size_t nheight = height >> 1;
721 for( size_t y = 0; y < nheight; ++y )
723 if ( !_LoadScanline( urow0, width, pSrc1, aRowPitch, srca->format ) )
727 if ( urow0 != urow1 )
729 if ( !_LoadScanline( urow1, width, pSrc1, aRowPitch, srca->format ) )
734 if ( urow0 != vrow0 )
736 if ( !_LoadScanline( vrow0, width, pSrc2, bRowPitch, srcb->format ) )
741 if ( urow0 != vrow1 && vrow0 != vrow1 )
743 if ( !_LoadScanline( vrow1, width, pSrc2, bRowPitch, srcb->format ) )
748 size_t nwidth = width >> 1;
750 for( size_t x = 0; x < nwidth; ++x )
754 // Box filter: Average 2x2x2 pixels
755 XMVECTOR v = XMVectorAdd( urow0[ x2 ], urow1[ x2 ] );
756 v = XMVectorAdd( v, urow2[ x2 ] );
757 v = XMVectorAdd( v, urow3[ x2 ] );
758 v = XMVectorAdd( v, vrow0[ x2 ] );
759 v = XMVectorAdd( v, vrow1[ x2 ] );
760 v = XMVectorAdd( v, vrow2[ x2 ] );
761 v = XMVectorAdd( v, vrow3[ x2 ] );
763 target[ x ] = XMVectorMultiply( v, s_boxScale3D );
766 if ( !_StoreScanline( pDest, dest->rowPitch, dest->format, target, nwidth ) )
768 pDest += dest->rowPitch;
775 const Image* src = mipChain.GetImage( level-1, 0, 0 );
776 const Image* dest = mipChain.GetImage( level, 0, 0 );
781 const uint8_t* pSrc = src->pixels;
782 uint8_t* pDest = dest->pixels;
784 size_t rowPitch = src->rowPitch;
786 size_t nheight = height >> 1;
788 for( size_t y = 0; y < nheight; ++y )
790 if ( !_LoadScanline( urow0, width, pSrc, rowPitch, src->format ) )
794 if ( urow0 != urow1 )
796 if ( !_LoadScanline( urow1, width, pSrc, rowPitch, src->format ) )
801 size_t nwidth = width >> 1;
803 for( size_t x = 0; x < nwidth; ++x )
807 // Box filter: Average 2x2 pixels
808 XMVECTOR v = XMVectorAdd( urow0[ x2 ], urow1[ x2 ] );
809 v = XMVectorAdd( v, urow2[ x2 ] );
810 v = XMVectorAdd( v, urow3[ x2 ] );
812 target[ x ] = XMVectorMultiply( v, s_boxScale );
815 if ( !_StoreScanline( pDest, dest->rowPitch, dest->format, target, nwidth ) )
817 pDest += dest->rowPitch;
831 assert( height == 1 && width == 1 && depth == 1 );
837 //=====================================================================================
839 //=====================================================================================
841 //-------------------------------------------------------------------------------------
842 // Generate mipmap chain
843 //-------------------------------------------------------------------------------------
844 HRESULT GenerateMipMaps( const Image& baseImage, DWORD filter, size_t levels, ScratchImage& mipChain, bool allow1D )
846 if ( !IsValid( baseImage.format ) )
849 if ( !baseImage.pixels )
852 if ( !_CalculateMipLevels(baseImage.width, baseImage.height, levels) )
855 if ( IsCompressed( baseImage.format ) || IsVideo( baseImage.format ) )
857 return HRESULT_FROM_WIN32( ERROR_NOT_SUPPORTED );
860 static_assert( TEX_FILTER_POINT == 0x100000, "TEX_FILTER_ flag values don't match TEX_FILTER_MASK" );
861 switch(filter & TEX_FILTER_MASK)
864 case TEX_FILTER_POINT:
865 case TEX_FILTER_FANT: // Equivalent to Box filter
866 case TEX_FILTER_LINEAR:
867 case TEX_FILTER_CUBIC:
869 WICPixelFormatGUID pfGUID;
870 if ( _DXGIToWIC( baseImage.format, pfGUID ) )
872 // Case 1: Base image format is supported by Windows Imaging Component
873 HRESULT hr = (baseImage.height > 1 || !allow1D)
874 ? mipChain.Initialize2D( baseImage.format, baseImage.width, baseImage.height, 1, levels )
875 : mipChain.Initialize1D( baseImage.format, baseImage.width, 1, levels );
879 return _GenerateMipMapsUsingWIC( baseImage, filter, levels, pfGUID, mipChain, 0 );
883 // Case 2: Base image format is not supported by WIC, so we have to convert, generate, and convert back
884 assert( baseImage.format != DXGI_FORMAT_R32G32B32A32_FLOAT );
886 HRESULT hr = _ConvertToR32G32B32A32( baseImage, temp );
890 const Image *timg = temp.GetImage( 0, 0, 0 );
894 ScratchImage tMipChain;
895 hr = _GenerateMipMapsUsingWIC( *timg, filter, levels, GUID_WICPixelFormat128bppRGBAFloat, tMipChain, 0 );
901 return _ConvertFromR32G32B32A32( tMipChain.GetImages(), tMipChain.GetImageCount(), tMipChain.GetMetadata(), baseImage.format, mipChain );
907 return HRESULT_FROM_WIN32( ERROR_NOT_SUPPORTED );
911 HRESULT GenerateMipMaps( const Image* srcImages, size_t nimages, const TexMetadata& metadata,
912 DWORD filter, size_t levels, ScratchImage& mipChain )
914 if ( !srcImages || !nimages || !IsValid(metadata.format) )
917 if ( metadata.dimension == TEX_DIMENSION_TEXTURE3D
918 || IsCompressed( metadata.format ) || IsVideo( metadata.format ) )
919 return HRESULT_FROM_WIN32( ERROR_NOT_SUPPORTED );
921 if ( !_CalculateMipLevels(metadata.width, metadata.height, levels) )
924 static_assert( TEX_FILTER_POINT == 0x100000, "TEX_FILTER_ flag values don't match TEX_FILTER_MASK" );
925 switch(filter & TEX_FILTER_MASK)
928 case TEX_FILTER_POINT:
929 case TEX_FILTER_FANT: // Equivalent to Box filter
930 case TEX_FILTER_LINEAR:
931 case TEX_FILTER_CUBIC:
933 WICPixelFormatGUID pfGUID;
934 if ( _DXGIToWIC( metadata.format, pfGUID ) )
936 // Case 1: Base image format is supported by Windows Imaging Component
937 TexMetadata mdata2 = metadata;
938 mdata2.mipLevels = levels;
939 HRESULT hr = mipChain.Initialize( mdata2 );
943 for( size_t item = 0; item < metadata.arraySize; ++item )
945 size_t index = metadata.ComputeIndex( 0, item, 0 );
946 if ( index >= nimages )
952 const Image& baseImage = srcImages[ index ];
954 hr = _GenerateMipMapsUsingWIC( baseImage, filter, levels, pfGUID, mipChain, item );
966 // Case 2: Base image format is not supported by WIC, so we have to convert, generate, and convert back
967 assert( metadata.format != DXGI_FORMAT_R32G32B32A32_FLOAT );
969 TexMetadata mdata2 = metadata;
970 mdata2.mipLevels = levels;
971 mdata2.format = DXGI_FORMAT_R32G32B32A32_FLOAT;
972 ScratchImage tMipChain;
973 HRESULT hr = tMipChain.Initialize( mdata2 );
977 for( size_t item = 0; item < metadata.arraySize; ++item )
979 size_t index = metadata.ComputeIndex( 0, item, 0 );
980 if ( index >= nimages )
983 const Image& baseImage = srcImages[ index ];
986 hr = _ConvertToR32G32B32A32( baseImage, temp );
990 const Image *timg = temp.GetImage( 0, 0, 0 );
994 hr = _GenerateMipMapsUsingWIC( *timg, filter, levels, GUID_WICPixelFormat128bppRGBAFloat, tMipChain, item );
999 return _ConvertFromR32G32B32A32( tMipChain.GetImages(), tMipChain.GetImageCount(), tMipChain.GetMetadata(), metadata.format, mipChain );
1005 return HRESULT_FROM_WIN32( ERROR_NOT_SUPPORTED );;
1010 //-------------------------------------------------------------------------------------
1011 // Generate mipmap chain for volume texture
1012 //-------------------------------------------------------------------------------------
1013 HRESULT GenerateMipMaps3D( const Image* baseImages, size_t depth, DWORD filter, size_t levels, ScratchImage& mipChain )
1015 if ( !baseImages || !depth )
1016 return E_INVALIDARG;
1018 DXGI_FORMAT format = baseImages[0].format;
1019 size_t width = baseImages[0].width;
1020 size_t height = baseImages[0].height;
1022 if ( !ispow2(width) || !ispow2(height) || !ispow2(depth) )
1023 return E_INVALIDARG;
1025 if ( !_CalculateMipLevels3D(width, height, depth, levels) )
1026 return E_INVALIDARG;
1028 for( size_t slice=0; slice < depth; ++slice )
1030 if ( !baseImages[slice].pixels )
1033 if ( baseImages[slice].format != format || baseImages[slice].width != width || baseImages[slice].height != height )
1035 // All base images must be the same format, width, and height
1040 if ( IsCompressed( format ) )
1042 // We don't support generating mipmaps from compressed images, as those should be generated before compression
1043 return HRESULT_FROM_WIN32( ERROR_NOT_SUPPORTED );
1048 static_assert( TEX_FILTER_POINT == 0x100000, "TEX_FILTER_ flag values don't match TEX_FILTER_MASK" );
1049 switch( filter & TEX_FILTER_MASK )
1052 case TEX_FILTER_FANT:
1053 hr = _Setup3DMips( baseImages, depth, levels, mipChain );
1057 // For decimation, Fant is equivalent to a Box filter
1058 hr = _Generate3DMipsBoxFilter( depth, levels, mipChain );
1063 case WIC_FLAGS_FILTER_POINT:
1064 hr = _Setup3DMips( baseImages, depth, levels, mipChain );
1068 hr = _Generate3DMipsPointFilter( depth, levels, mipChain );
1073 case WIC_FLAGS_FILTER_LINEAR:
1074 // Need to implement a 3D bi-linear filter (2x2x2)
1077 case WIC_FLAGS_FILTER_CUBIC:
1078 // Need to implement a 3D bi-cubic filter (3x3x3)
1082 return HRESULT_FROM_WIN32( ERROR_NOT_SUPPORTED );;
1086 HRESULT GenerateMipMaps3D( const Image* srcImages, size_t nimages, const TexMetadata& metadata,
1087 DWORD filter, size_t levels, ScratchImage& mipChain )
1089 if ( !srcImages || !nimages || !IsValid(metadata.format)
1090 || !ispow2(metadata.width) || !ispow2(metadata.height) || !ispow2(metadata.depth) )
1091 return E_INVALIDARG;
1093 if ( metadata.dimension != TEX_DIMENSION_TEXTURE3D
1094 || IsCompressed( metadata.format ) || IsVideo( metadata.format ) )
1095 return HRESULT_FROM_WIN32( ERROR_NOT_SUPPORTED );
1097 if ( !_CalculateMipLevels3D(metadata.width, metadata.height, metadata.depth, levels) )
1098 return E_INVALIDARG;
1100 std::vector<const Image> baseImages;
1101 baseImages.reserve( metadata.depth );
1102 for( size_t slice=0; slice < metadata.depth; ++slice )
1104 size_t index = metadata.ComputeIndex( 0, 0, slice );
1105 if ( index >= nimages )
1108 const Image& src = srcImages[ index ];
1112 if ( src.format != metadata.format || src.width != metadata.width || src.height != metadata.height )
1114 // All base images must be the same format, width, and height
1118 baseImages.push_back( src );
1121 assert( baseImages.size() == metadata.depth );
1125 static_assert( TEX_FILTER_POINT == 0x100000, "TEX_FILTER_ flag values don't match TEX_FILTER_MASK" );
1126 switch( filter & TEX_FILTER_MASK )
1129 case TEX_FILTER_FANT:
1130 hr = _Setup3DMips( &baseImages[0], metadata.depth, levels, mipChain );
1134 // For decimation, Fant is equivalent to a Box filter
1135 hr = _Generate3DMipsBoxFilter( metadata.depth, levels, mipChain );
1140 case WIC_FLAGS_FILTER_POINT:
1141 hr = _Setup3DMips( &baseImages[0], metadata.depth, levels, mipChain );
1145 hr = _Generate3DMipsPointFilter( metadata.depth, levels, mipChain );
1150 case WIC_FLAGS_FILTER_LINEAR:
1151 // Need to implement a 3D bi-linear filter (2x2x2)
1154 case WIC_FLAGS_FILTER_CUBIC:
1155 // Need to implement a 3D bi-cubic filter (3x3x3)
1159 return HRESULT_FROM_WIN32( ERROR_NOT_SUPPORTED );;