]> git.cworth.org Git - apitrace/blob - thirdparty/directxtex/DirectXTex/DirectXTexMipmaps.cpp
cb1c4c2736c3ba40fabbc3df67a6a8c24c6f3157
[apitrace] / thirdparty / directxtex / DirectXTex / DirectXTexMipmaps.cpp
1 //-------------------------------------------------------------------------------------
2 // DirectXTexMipMaps.cpp
3 //  
4 // DirectX Texture Library - Mip-map generation
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 namespace DirectX
19 {
20
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 };
23
24 //-------------------------------------------------------------------------------------
25 // Mipmap helper functions
26 //-------------------------------------------------------------------------------------
27 inline static bool ispow2( _In_ size_t x )
28 {
29     return ((x != 0) && !(x & (x - 1)));
30 }
31
32 static size_t _CountMips( _In_ size_t width, _In_ size_t height)
33 {
34     size_t mipLevels = 1;
35
36     while ( height > 1 || width > 1 )
37     {
38         if ( height > 1 )
39             height >>= 1;
40
41         if ( width > 1 )
42             width >>= 1;
43
44         ++mipLevels;
45     }
46     
47     return mipLevels;
48 }
49
50 bool _CalculateMipLevels( _In_ size_t width, _In_ size_t height, _Inout_ size_t& mipLevels )
51 {
52     if ( mipLevels > 1 )
53     {
54         size_t maxMips = _CountMips(width,height);
55         if ( mipLevels > maxMips )
56             return false;
57     }
58     else if ( mipLevels == 0 )
59     {
60         mipLevels = _CountMips(width,height);
61     }
62     else
63     {
64         mipLevels = 1;
65     }
66     return true;
67 }
68
69 static size_t _CountMips3D( _In_ size_t width, _In_ size_t height, _In_ size_t depth)
70 {
71     size_t mipLevels = 1;
72
73     while ( height > 1 || width > 1 || depth > 1 )
74     {
75         if ( height > 1 )
76             height >>= 1;
77
78         if ( width > 1 )
79             width >>= 1;
80
81         if ( depth > 1 )
82             depth >>= 1;
83
84         ++mipLevels;
85     }
86     
87     return mipLevels;
88 }
89
90 bool _CalculateMipLevels3D( _In_ size_t width, _In_ size_t height, _In_ size_t depth, _Inout_ size_t& mipLevels )
91 {
92     if ( mipLevels > 1 )
93     {
94         if ( !ispow2(width) || !ispow2(height) || !ispow2(depth) )
95             return false;
96
97         size_t maxMips = _CountMips3D(width,height,depth);
98         if ( mipLevels > maxMips )
99             return false;
100     }
101     else if ( mipLevels == 0 && ispow2(width) && ispow2(height) && ispow2(depth) )
102     {
103         mipLevels = _CountMips3D(width,height,depth);
104     }
105     else
106     {
107         mipLevels = 1;
108     }
109     return true;
110 }
111
112 static HRESULT _EnsureWicBitmapPixelFormat( _In_ IWICImagingFactory* pWIC, _In_ IWICBitmap* src, _In_ DWORD filter,
113                                             _In_ const WICPixelFormatGUID& desiredPixelFormat,
114                                             _Deref_out_ IWICBitmap** dest )
115 {
116     if ( !pWIC || !src || !dest )
117         return E_POINTER;
118
119     *dest = nullptr;
120     
121     WICPixelFormatGUID actualPixelFormat;
122     HRESULT hr = src->GetPixelFormat( &actualPixelFormat );
123     
124     if ( SUCCEEDED(hr) )
125     {
126         if ( memcmp( &actualPixelFormat, &desiredPixelFormat, sizeof(WICPixelFormatGUID) ) == 0 )
127         {
128             src->AddRef();
129             *dest = src;
130         }
131         else
132         {
133             ScopedObject<IWICFormatConverter> converter;
134             hr = pWIC->CreateFormatConverter( &converter );
135             if ( SUCCEEDED(hr) )
136             {
137                 hr = converter->Initialize( src, desiredPixelFormat, _GetWICDither(filter), 0, 0, WICBitmapPaletteTypeCustom );
138             }
139
140             if ( SUCCEEDED(hr) )
141             {
142                 hr = pWIC->CreateBitmapFromSource( converter.Get(), WICBitmapCacheOnDemand, dest );
143             }
144         }
145     }
146
147     return hr;
148 }
149
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 )
152 {
153     if ( !pWIC || !original || !img )
154         return E_POINTER;
155
156     const WICBitmapInterpolationMode interpolationMode = _GetWICInterp(filter);
157
158     WICPixelFormatGUID desiredPixelFormat = GUID_WICPixelFormatUndefined;
159     HRESULT hr = original->GetPixelFormat( &desiredPixelFormat );
160     
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;
166
167     if ( SUCCEEDED(hr) )
168     {
169         ScopedObject<IWICComponentInfo> componentInfo;
170         hr = pWIC->CreateComponentInfo( desiredPixelFormat, &componentInfo );
171
172         ScopedObject<IWICPixelFormatInfo> pixelFormatInfo;
173         if ( SUCCEEDED(hr) )
174         {
175             hr = componentInfo->QueryInterface( __uuidof(IWICPixelFormatInfo), (void**)&pixelFormatInfo );
176         }
177
178         UINT bitsPerPixel = 0;
179         if ( SUCCEEDED(hr) )
180         {
181             hr = pixelFormatInfo->GetBitsPerPixel( &bitsPerPixel );
182         }
183
184         if ( SUCCEEDED(hr) )
185         {
186             if ( bitsPerPixel <= 32 )
187             {
188                 colorBytesInPixel = colorBytesPerPixel = 3;
189                 colorPixelFormat = GUID_WICPixelFormat24bppBGR;
190
191                 colorWithAlphaBytesPerPixel = 4;
192                 colorWithAlphaPixelFormat = GUID_WICPixelFormat32bppBGRA;
193             }
194             else
195             {
196 #if(_WIN32_WINNT >= 0x0602 /*_WIN32_WINNT_WIN8*/) || defined(_WIN7_PLATFORM_UPDATE)
197                 if ( _IsWIC2() )
198                 {
199                     colorBytesInPixel = colorBytesPerPixel = 12;
200                     colorPixelFormat = GUID_WICPixelFormat96bppRGBFloat;
201                 }
202                 else
203 #endif
204                 {
205                     colorBytesInPixel = 12;
206                     colorBytesPerPixel = 16;
207                     colorPixelFormat = GUID_WICPixelFormat128bppRGBFloat;
208                 }
209
210                 colorWithAlphaBytesPerPixel = 16;
211                 colorWithAlphaPixelFormat = GUID_WICPixelFormat128bppRGBAFloat;
212             }
213         }
214     }
215     
216     // Resize color only image (no alpha channel)
217     ScopedObject<IWICBitmap> resizedColor;
218     if ( SUCCEEDED(hr) )
219     { 
220         ScopedObject<IWICBitmapScaler> colorScaler;
221         
222         hr = pWIC->CreateBitmapScaler(&colorScaler);
223         if ( SUCCEEDED(hr) )
224         {
225             ScopedObject<IWICBitmap> converted;
226
227             hr = _EnsureWicBitmapPixelFormat( pWIC, original, filter, colorPixelFormat, &converted );
228             if ( SUCCEEDED(hr) )
229             {
230                 hr = colorScaler->Initialize( converted.Get(), static_cast<UINT>(newWidth), static_cast<UINT>(newHeight), interpolationMode );
231             }
232         }
233             
234         if ( SUCCEEDED(hr) )
235         {
236             ScopedObject<IWICBitmap> resized;
237
238             hr = pWIC->CreateBitmapFromSource( colorScaler.Get(), WICBitmapCacheOnDemand, &resized );
239             if ( SUCCEEDED(hr) )
240             {
241                 hr = _EnsureWicBitmapPixelFormat( pWIC, resized.Get(), filter, colorPixelFormat, &resizedColor );
242             }
243         }
244     }
245     
246     // Resize color+alpha image
247     ScopedObject<IWICBitmap> resizedColorWithAlpha;
248     if ( SUCCEEDED(hr) )
249     {
250         ScopedObject<IWICBitmapScaler> colorWithAlphaScaler;
251         
252         hr = pWIC->CreateBitmapScaler( &colorWithAlphaScaler );
253         if ( SUCCEEDED(hr) )
254         {
255             ScopedObject<IWICBitmap> converted;
256
257             hr = _EnsureWicBitmapPixelFormat( pWIC, original, filter, colorWithAlphaPixelFormat, &converted );
258             if ( SUCCEEDED(hr) )
259             {
260                 hr = colorWithAlphaScaler->Initialize( converted.Get(), static_cast<UINT>(newWidth), static_cast<UINT>(newHeight), interpolationMode );
261             }
262         }
263             
264         if ( SUCCEEDED(hr) )
265         {
266             ScopedObject<IWICBitmap> resized;
267
268             hr = pWIC->CreateBitmapFromSource( colorWithAlphaScaler.Get(), WICBitmapCacheOnDemand, &resized );
269             if ( SUCCEEDED(hr) )
270             {
271                 hr = _EnsureWicBitmapPixelFormat( pWIC, resized.Get(), filter, colorWithAlphaPixelFormat, &resizedColorWithAlpha );
272             }
273         }
274     }
275
276     // Merge pixels (copying color channels from color only image to color+alpha image)
277     if ( SUCCEEDED(hr) )
278     {
279         ScopedObject<IWICBitmapLock> colorLock;
280         ScopedObject<IWICBitmapLock> colorWithAlphaLock;
281       
282         hr = resizedColor->Lock( nullptr, WICBitmapLockRead, &colorLock );
283         if ( SUCCEEDED(hr) )
284         {
285             hr = resizedColorWithAlpha->Lock( nullptr, WICBitmapLockWrite, &colorWithAlphaLock );
286         }
287         
288         if ( SUCCEEDED(hr) )
289         {
290             WICInProcPointer colorWithAlphaData = nullptr;
291             UINT colorWithAlphaSizeInBytes = 0;
292             UINT colorWithAlphaStride = 0;
293             
294             hr = colorWithAlphaLock->GetDataPointer( &colorWithAlphaSizeInBytes, &colorWithAlphaData );
295             if ( SUCCEEDED(hr) )
296             {
297                 if ( !colorWithAlphaData )
298                 {
299                     hr = E_POINTER;
300                 }
301                 else
302                 {
303                     hr = colorWithAlphaLock->GetStride( &colorWithAlphaStride );
304                 }
305             }
306
307             WICInProcPointer colorData = nullptr;
308             UINT colorSizeInBytes = 0;
309             UINT colorStride = 0;
310             if ( SUCCEEDED(hr) )
311             {
312                 hr = colorLock->GetDataPointer( &colorSizeInBytes, &colorData );
313                 if ( SUCCEEDED(hr) )
314                 {
315                     if ( !colorData )
316                     {
317                         hr = E_POINTER;
318                     }
319                     else
320                     {
321                         hr = colorLock->GetStride( &colorStride );
322                     }
323                 }
324             }
325             
326             for ( size_t j = 0; SUCCEEDED(hr) && j < newHeight; j++ )
327             {
328                 for ( size_t i = 0; SUCCEEDED(hr) && i < newWidth; i++ )
329                 {
330                     size_t colorWithAlphaIndex = (j * colorWithAlphaStride) + (i * colorWithAlphaBytesPerPixel);
331                     size_t colorIndex = (j * colorStride) + (i * colorBytesPerPixel);
332                     
333                     if ( ((colorWithAlphaIndex + colorBytesInPixel) > colorWithAlphaSizeInBytes)
334                          || ( (colorIndex + colorBytesPerPixel) > colorSizeInBytes) )
335                     {
336                         hr = E_INVALIDARG;
337                     }
338                     else
339                     {
340                         memcpy_s( colorWithAlphaData + colorWithAlphaIndex, colorWithAlphaBytesPerPixel, colorData + colorIndex, colorBytesInPixel );
341                     }
342                 }
343             }
344         }
345     }
346
347     if ( SUCCEEDED(hr) )
348     {
349         ScopedObject<IWICBitmap> wicBitmap;
350         hr = _EnsureWicBitmapPixelFormat( pWIC, resizedColorWithAlpha.Get(), filter, desiredPixelFormat, &wicBitmap );
351         if ( SUCCEEDED(hr) )
352         {
353             hr = wicBitmap->CopyPixels( nullptr, static_cast<UINT>(img->rowPitch), static_cast<UINT>(img->slicePitch), img->pixels );
354         }
355     }
356
357     return hr;
358 }
359
360
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 )
366 {
367     assert( levels > 1 );
368
369     if ( !baseImage.pixels || !mipChain.GetPixels() )
370         return E_POINTER;
371
372     IWICImagingFactory* pWIC = _GetWIC();
373     if ( !pWIC )
374         return E_NOINTERFACE;
375
376     size_t width = baseImage.width;
377     size_t height = baseImage.height;
378
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 );
383     if ( FAILED(hr) )
384         return hr;
385
386     // Copy base image to top miplevel
387     const Image *img0 = mipChain.GetImage( 0, item, 0 );
388     if ( !img0 )
389         return E_POINTER;
390
391     uint8_t* pDest = img0->pixels;
392     if ( !pDest )
393         return E_POINTER;
394
395     const uint8_t *pSrc = baseImage.pixels;
396     for( size_t h=0; h < height; ++h )
397     {
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;
402     }
403
404     ScopedObject<IWICComponentInfo> componentInfo;
405     hr = pWIC->CreateComponentInfo( pfGUID, &componentInfo );
406     if ( FAILED(hr) )
407         return hr;
408
409     ScopedObject<IWICPixelFormatInfo2> pixelFormatInfo;
410     hr = componentInfo->QueryInterface( __uuidof(IWICPixelFormatInfo2), (void**)&pixelFormatInfo );
411     if ( FAILED(hr) )
412         return hr;
413
414     BOOL supportsTransparency = FALSE;
415     hr = pixelFormatInfo->SupportsTransparency( &supportsTransparency );
416     if ( FAILED(hr) )
417         return hr;
418
419     // Resize base image to each target mip level
420     for( size_t level = 1; level < levels; ++level )
421     {
422         const Image *img = mipChain.GetImage( level, item, 0 );
423         if ( !img )
424             return E_POINTER;
425
426         if ( height > 1 )
427             height >>= 1;
428
429         if ( width > 1 )
430             width >>= 1;
431
432         assert( img->width == width && img->height == height && img->format == baseImage.format );
433
434         if ( (filter & TEX_FILTER_SEPARATE_ALPHA) && supportsTransparency )
435         {
436             hr = _ResizeSeparateColorAndAlpha( pWIC, source.Get(), width, height, filter, img );
437             if ( FAILED(hr) )
438                 return hr;
439         }
440         else
441         {
442             ScopedObject<IWICBitmapScaler> scaler;
443             hr = pWIC->CreateBitmapScaler( &scaler );
444             if ( FAILED(hr) )
445                 return hr;
446
447             hr = scaler->Initialize( source.Get(), static_cast<UINT>( width ), static_cast<UINT>( height ), _GetWICInterp( filter ) );
448             if ( FAILED(hr) )
449                 return hr;
450
451             WICPixelFormatGUID pfScaler;
452             hr = scaler->GetPixelFormat( &pfScaler );
453             if ( FAILED(hr) )
454                 return hr;
455
456             if ( memcmp( &pfScaler, &pfGUID, sizeof(WICPixelFormatGUID) ) == 0 )
457             {
458                 hr = scaler->CopyPixels( 0, static_cast<UINT>( img->rowPitch ), static_cast<UINT>( img->slicePitch ), img->pixels );
459                 if ( FAILED(hr) )
460                     return hr;
461             }
462             else
463             {
464                 // The WIC bitmap scaler is free to return a different pixel format than the source image, so here we
465                 // convert it back
466                 ScopedObject<IWICFormatConverter> FC;
467                 hr = pWIC->CreateFormatConverter( &FC );
468                 if ( FAILED(hr) )
469                     return hr;
470
471                 hr = FC->Initialize( scaler.Get(), pfGUID, _GetWICDither( filter ), 0, 0, WICBitmapPaletteTypeCustom );
472                 if ( FAILED(hr) )
473                     return hr;
474
475                 hr = FC->CopyPixels( 0, static_cast<UINT>( img->rowPitch ), static_cast<UINT>( img->slicePitch ), img->pixels );  
476                 if ( FAILED(hr) )
477                     return hr;
478             }
479         }
480     }
481
482     return S_OK;
483 }
484
485
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 )
491 {
492     if ( !baseImages || !depth )
493         return E_INVALIDARG;
494
495     assert( levels > 1 );
496
497     size_t width = baseImages[0].width;
498     size_t height = baseImages[0].height;
499
500     HRESULT hr = mipChain.Initialize3D( baseImages[0].format, width, height, depth, levels );
501     if ( FAILED(hr) )
502         return hr;
503
504     // Copy base images to top slice
505     for( size_t slice=0; slice < depth; ++slice )
506     {
507         const Image& src = baseImages[slice];
508
509         const Image *dest = mipChain.GetImage( 0, 0, slice );
510         if ( !dest )
511         {
512             mipChain.Release();
513             return E_POINTER;
514         }
515
516         assert( src.format == dest->format );
517
518         uint8_t* pDest = dest->pixels;
519         if ( !pDest )
520         {
521             mipChain.Release();
522             return E_POINTER;
523         }
524
525         const uint8_t *pSrc = src.pixels;
526         size_t rowPitch = src.rowPitch;
527         for( size_t h=0; h < height; ++h )
528         {
529             size_t msize = std::min<size_t>( dest->rowPitch, rowPitch );
530             memcpy_s( pDest, dest->rowPitch, pSrc, msize );  
531             pSrc += rowPitch;
532             pDest += dest->rowPitch;
533         }
534     }
535
536     return S_OK;
537 }
538
539 static HRESULT _Generate3DMipsPointFilter( _In_ size_t depth, _In_ size_t levels, _In_ const ScratchImage& mipChain )
540 {
541     if ( !depth || !mipChain.GetImages() )
542         return E_INVALIDARG;
543
544     // This assumes that the base images are already placed into the mipChain at the top level... (see _Setup3DMips)
545
546     assert( levels > 1 );
547
548     size_t width = mipChain.GetMetadata().width;
549     size_t height = mipChain.GetMetadata().height;
550
551     assert( ispow2(width) && ispow2(height) && ispow2(depth) );
552
553     // Allocate temporary space (2 scanlines)
554     ScopedAlignedArrayXMVECTOR scanline( reinterpret_cast<XMVECTOR*>( _aligned_malloc( (sizeof(XMVECTOR)*width*2), 16 ) ) );
555     if ( !scanline )
556         return E_OUTOFMEMORY;
557
558     XMVECTOR* target = scanline.get();
559
560     XMVECTOR* row = target + width;
561
562     // Resize base image to each target mip level
563     for( size_t level=1; level < levels; ++level )
564     {
565         if ( depth > 1 )
566         {
567             // 3D point filter
568             for( size_t slice=0; slice < depth; slice += 2 )
569             {
570                 const Image* src = mipChain.GetImage( level-1, 0, slice );
571                 const Image* dest = mipChain.GetImage( level, 0, slice >> 1 );
572
573                 if ( !src || !dest )
574                     return E_POINTER;
575
576                 const uint8_t* pSrc = src->pixels;
577                 uint8_t* pDest = dest->pixels;
578
579                 size_t rowPitch = src->rowPitch;
580
581                 size_t nheight = height >> 1;
582
583                 for( size_t y = 0; y < nheight; ++y )
584                 {
585                     if ( !_LoadScanline( row, width, pSrc, rowPitch, src->format ) )
586                         return E_FAIL;
587                     pSrc += rowPitch*2;
588
589                     size_t nwidth = width >> 1;
590
591                     for( size_t x = 0; x < nwidth; ++x )
592                     {
593                         target[ x ] = row[ x*2 ];
594                     }
595
596                     if ( !_StoreScanline( pDest, dest->rowPitch, dest->format, target, nwidth ) )
597                         return E_FAIL;
598                     pDest += dest->rowPitch;
599                 }
600             }
601         }
602         else
603         {
604             // 2D point filter
605             const Image* src = mipChain.GetImage( level-1, 0, 0 );
606             const Image* dest = mipChain.GetImage( level, 0, 0 );
607
608             if ( !src || !dest )
609                 return E_POINTER;
610
611             const uint8_t* pSrc = src->pixels;
612             uint8_t* pDest = dest->pixels;
613
614             size_t rowPitch = src->rowPitch;
615
616             size_t nheight = height >> 1;
617
618             for( size_t y = 0; y < nheight; ++y )
619             {
620                 if ( !_LoadScanline( row, width, pSrc, rowPitch, src->format ) )
621                     return E_FAIL;
622                 pSrc += rowPitch*2;
623
624                 size_t nwidth = width >> 1;
625
626                 for( size_t x = 0; x < nwidth; ++x )
627                 {
628                     target[ x ] = row[ x*2 ];
629                 }
630
631                 if ( !_StoreScanline( pDest, dest->rowPitch, dest->format, target, nwidth ) )
632                     return E_FAIL;
633                 pDest += dest->rowPitch;
634             }
635         }
636
637         if ( height > 1 )
638             height >>= 1;
639
640         if ( width > 1 )
641             width >>= 1;
642
643         if ( depth > 1 )
644             depth >>= 1;
645     }
646
647     assert( height == 1 && width == 1 && depth == 1 );
648
649     return S_OK;
650 }
651
652 static HRESULT _Generate3DMipsBoxFilter( _In_ size_t depth, _In_ size_t levels, _In_ const ScratchImage& mipChain )
653 {
654     if ( !depth || !mipChain.GetImages() )
655         return E_INVALIDARG;
656
657     // This assumes that the base images are already placed into the mipChain at the top level... (see _Setup3DMips)
658
659     assert( levels > 1 );
660
661     size_t width = mipChain.GetMetadata().width;
662     size_t height = mipChain.GetMetadata().height;
663
664     assert( ispow2(width) && ispow2(height) && ispow2(depth) );
665
666     // Allocate temporary space (5 scanlines)
667     ScopedAlignedArrayXMVECTOR scanline( reinterpret_cast<XMVECTOR*>( _aligned_malloc( (sizeof(XMVECTOR)*width*5), 16 ) ) );
668     if ( !scanline )
669         return E_OUTOFMEMORY;
670
671     XMVECTOR* target = scanline.get();
672
673     XMVECTOR* urow0 = target + width;
674     XMVECTOR* urow1 = target + width*2;
675     XMVECTOR* vrow0 = target + width*3;
676     XMVECTOR* vrow1 = target + width*4;
677
678     const XMVECTOR* urow2 = urow0 + 1;
679     const XMVECTOR* urow3 = urow1 + 1;
680     const XMVECTOR* vrow2 = vrow0 + 1;
681     const XMVECTOR* vrow3 = vrow1 + 1;
682
683     // Resize base image to each target mip level
684     for( size_t level=1; level < levels; ++level )
685     {
686         if ( height == 1)
687         {
688             urow0 = vrow0;
689             urow1 = vrow1;
690         }
691
692         if ( width == 1 )
693         {
694             urow2 = urow0;
695             urow3 = urow1;
696             vrow2 = vrow0;
697             vrow3 = vrow1;
698         }
699
700         if ( depth > 1 )
701         {
702             // 3D box filter
703             for( size_t slice=0; slice < depth; slice += 2 )
704             {
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 );
708
709                 if ( !srca || !srcb || !dest )
710                     return E_POINTER;
711
712                 const uint8_t* pSrc1 = srca->pixels;
713                 const uint8_t* pSrc2 = srcb->pixels;
714                 uint8_t* pDest = dest->pixels;
715
716                 size_t aRowPitch = srca->rowPitch;
717                 size_t bRowPitch = srcb->rowPitch;
718
719                 size_t nheight = height >> 1;
720
721                 for( size_t y = 0; y < nheight; ++y )
722                 {
723                     if ( !_LoadScanline( urow0, width, pSrc1, aRowPitch, srca->format ) )
724                         return E_FAIL;
725                     pSrc1 += aRowPitch;
726
727                     if ( urow0 != urow1 )
728                     {
729                         if ( !_LoadScanline( urow1, width, pSrc1, aRowPitch, srca->format ) )
730                             return E_FAIL;
731                         pSrc1 += aRowPitch;
732                     }
733
734                     if ( urow0 != vrow0 )
735                     {
736                         if ( !_LoadScanline( vrow0, width, pSrc2, bRowPitch, srcb->format ) )
737                             return E_FAIL;
738                         pSrc2 += bRowPitch;
739                     }
740
741                     if ( urow0 != vrow1 && vrow0 != vrow1 )
742                     {
743                         if ( !_LoadScanline( vrow1, width, pSrc2, bRowPitch, srcb->format ) )
744                             return E_FAIL;
745                         pSrc2 += bRowPitch;
746                     }
747
748                     size_t nwidth = width >> 1;
749
750                     for( size_t x = 0; x < nwidth; ++x )
751                     {
752                         size_t x2 = x*2;
753
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 ] );
762
763                         target[ x ] = XMVectorMultiply( v, s_boxScale3D );
764                     }
765
766                     if ( !_StoreScanline( pDest, dest->rowPitch, dest->format, target, nwidth ) )
767                         return E_FAIL;
768                     pDest += dest->rowPitch;
769                 }
770             }
771         }
772         else
773         {
774             // 2D box filter
775             const Image* src = mipChain.GetImage( level-1, 0, 0 );
776             const Image* dest = mipChain.GetImage( level, 0, 0 );
777
778             if ( !src || !dest )
779                 return E_POINTER;
780
781             const uint8_t* pSrc = src->pixels;
782             uint8_t* pDest = dest->pixels;
783
784             size_t rowPitch = src->rowPitch;
785
786             size_t nheight = height >> 1;
787
788             for( size_t y = 0; y < nheight; ++y )
789             {
790                 if ( !_LoadScanline( urow0, width, pSrc, rowPitch, src->format ) )
791                     return E_FAIL;
792                 pSrc += rowPitch;
793
794                 if ( urow0 != urow1 )
795                 {
796                     if ( !_LoadScanline( urow1, width, pSrc, rowPitch, src->format ) )
797                         return E_FAIL;
798                     pSrc += rowPitch;
799                 }
800
801                 size_t nwidth = width >> 1;
802
803                 for( size_t x = 0; x < nwidth; ++x )
804                 {
805                     size_t x2 = x*2;
806
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 ] );
811
812                     target[ x ] = XMVectorMultiply( v, s_boxScale );
813                 }
814
815                 if ( !_StoreScanline( pDest, dest->rowPitch, dest->format, target, nwidth ) )
816                     return E_FAIL;
817                 pDest += dest->rowPitch;
818             }
819         }
820
821         if ( height > 1 )
822             height >>= 1;
823
824         if ( width > 1 )
825             width >>= 1;
826
827         if ( depth > 1 )
828             depth >>= 1;
829     }
830
831     assert( height == 1 && width == 1 && depth == 1 );
832
833     return S_OK;
834 }
835
836
837 //=====================================================================================
838 // Entry-points
839 //=====================================================================================
840
841 //-------------------------------------------------------------------------------------
842 // Generate mipmap chain
843 //-------------------------------------------------------------------------------------
844 HRESULT GenerateMipMaps( const Image& baseImage, DWORD filter, size_t levels, ScratchImage& mipChain, bool allow1D )
845 {
846     if ( !IsValid( baseImage.format ) )
847         return E_INVALIDARG;
848
849     if ( !baseImage.pixels )
850         return E_POINTER;
851
852     if ( !_CalculateMipLevels(baseImage.width, baseImage.height, levels) )
853         return E_INVALIDARG;
854
855     if ( IsCompressed( baseImage.format ) || IsVideo( baseImage.format ) )
856     {
857         return HRESULT_FROM_WIN32( ERROR_NOT_SUPPORTED );
858     }
859
860     static_assert( TEX_FILTER_POINT == 0x100000, "TEX_FILTER_ flag values don't match TEX_FILTER_MASK" );
861     switch(filter & TEX_FILTER_MASK)
862     {
863     case 0:
864     case TEX_FILTER_POINT:
865     case TEX_FILTER_FANT: // Equivalent to Box filter
866     case TEX_FILTER_LINEAR:
867     case TEX_FILTER_CUBIC:
868         {
869             WICPixelFormatGUID pfGUID;
870             if ( _DXGIToWIC( baseImage.format, pfGUID ) )
871             {
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 ); 
876                 if ( FAILED(hr) )
877                     return hr;
878
879                 return _GenerateMipMapsUsingWIC( baseImage, filter, levels, pfGUID, mipChain, 0 );
880             }
881             else
882             {
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 );
885                 ScratchImage temp;
886                 HRESULT hr = _ConvertToR32G32B32A32( baseImage, temp );
887                 if ( FAILED(hr) )
888                     return hr;
889
890                 const Image *timg = temp.GetImage( 0, 0, 0 );
891                 if ( !timg )
892                     return E_POINTER;
893
894                 ScratchImage tMipChain;
895                 hr = _GenerateMipMapsUsingWIC( *timg, filter, levels, GUID_WICPixelFormat128bppRGBAFloat, tMipChain, 0 );
896                 if ( FAILED(hr) )
897                     return hr;
898
899                 temp.Release();
900
901                 return _ConvertFromR32G32B32A32( tMipChain.GetImages(), tMipChain.GetImageCount(), tMipChain.GetMetadata(), baseImage.format, mipChain );
902             }
903         }
904         break;
905
906     default:
907         return HRESULT_FROM_WIN32( ERROR_NOT_SUPPORTED );
908     }
909 }
910
911 HRESULT GenerateMipMaps( const Image* srcImages, size_t nimages, const TexMetadata& metadata,
912                          DWORD filter, size_t levels, ScratchImage& mipChain )
913 {
914     if ( !srcImages || !nimages || !IsValid(metadata.format) )
915         return E_INVALIDARG;
916
917     if ( metadata.dimension == TEX_DIMENSION_TEXTURE3D
918          || IsCompressed( metadata.format ) || IsVideo( metadata.format ) )
919         return HRESULT_FROM_WIN32( ERROR_NOT_SUPPORTED );
920
921     if ( !_CalculateMipLevels(metadata.width, metadata.height, levels) )
922         return E_INVALIDARG;
923
924     static_assert( TEX_FILTER_POINT == 0x100000, "TEX_FILTER_ flag values don't match TEX_FILTER_MASK" );
925     switch(filter & TEX_FILTER_MASK)
926     {
927     case 0:
928     case TEX_FILTER_POINT:
929     case TEX_FILTER_FANT: // Equivalent to Box filter
930     case TEX_FILTER_LINEAR:
931     case TEX_FILTER_CUBIC:
932         {
933             WICPixelFormatGUID pfGUID;
934             if ( _DXGIToWIC( metadata.format, pfGUID ) )
935             {
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 ); 
940                 if ( FAILED(hr) )
941                     return hr;
942
943                 for( size_t item = 0; item < metadata.arraySize; ++item )
944                 {
945                     size_t index = metadata.ComputeIndex( 0, item, 0 );
946                     if ( index >= nimages )
947                     {
948                         mipChain.Release();
949                         return E_FAIL;
950                     }
951
952                     const Image& baseImage = srcImages[ index ];
953
954                     hr = _GenerateMipMapsUsingWIC( baseImage, filter, levels, pfGUID, mipChain, item );
955                     if ( FAILED(hr) )
956                     {
957                         mipChain.Release();
958                         return hr;
959                     }
960                 }
961
962                 return S_OK;
963             }
964             else
965             {
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 );
968
969                 TexMetadata mdata2 = metadata;
970                 mdata2.mipLevels = levels;
971                 mdata2.format = DXGI_FORMAT_R32G32B32A32_FLOAT;
972                 ScratchImage tMipChain;
973                 HRESULT hr = tMipChain.Initialize( mdata2 ); 
974                 if ( FAILED(hr) )
975                     return hr;
976
977                 for( size_t item = 0; item < metadata.arraySize; ++item )
978                 {
979                     size_t index = metadata.ComputeIndex( 0, item, 0 );
980                     if ( index >= nimages )
981                         return E_FAIL;
982
983                     const Image& baseImage = srcImages[ index ];
984
985                     ScratchImage temp;
986                     hr = _ConvertToR32G32B32A32( baseImage, temp );
987                     if ( FAILED(hr) )
988                         return hr;
989
990                     const Image *timg = temp.GetImage( 0, 0, 0 );
991                     if ( !timg )
992                         return E_POINTER;
993
994                     hr = _GenerateMipMapsUsingWIC( *timg, filter, levels, GUID_WICPixelFormat128bppRGBAFloat, tMipChain, item );
995                     if ( FAILED(hr) )
996                         return hr;
997                 }
998
999                 return _ConvertFromR32G32B32A32( tMipChain.GetImages(), tMipChain.GetImageCount(), tMipChain.GetMetadata(), metadata.format, mipChain );
1000             }
1001         }
1002         break;
1003
1004     default:
1005         return HRESULT_FROM_WIN32( ERROR_NOT_SUPPORTED );;
1006     }
1007 }
1008
1009
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 )
1014 {
1015     if ( !baseImages || !depth )
1016         return E_INVALIDARG;
1017
1018     DXGI_FORMAT format = baseImages[0].format;
1019     size_t width = baseImages[0].width;
1020     size_t height = baseImages[0].height;
1021
1022     if ( !ispow2(width) || !ispow2(height) || !ispow2(depth) )
1023         return E_INVALIDARG;
1024
1025     if ( !_CalculateMipLevels3D(width, height, depth, levels) )
1026         return E_INVALIDARG;
1027
1028     for( size_t slice=0; slice < depth; ++slice )
1029     {
1030         if ( !baseImages[slice].pixels )
1031             return E_POINTER;
1032
1033         if ( baseImages[slice].format != format || baseImages[slice].width != width || baseImages[slice].height != height )
1034         {
1035             // All base images must be the same format, width, and height
1036             return E_FAIL;
1037         }
1038     }
1039
1040     if ( IsCompressed( format ) )
1041     {
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 );
1044     }
1045
1046     HRESULT hr;
1047
1048     static_assert( TEX_FILTER_POINT == 0x100000, "TEX_FILTER_ flag values don't match TEX_FILTER_MASK" );
1049     switch( filter & TEX_FILTER_MASK )
1050     {
1051     case 0:
1052     case TEX_FILTER_FANT:
1053         hr = _Setup3DMips( baseImages, depth, levels, mipChain );
1054         if ( FAILED(hr) )
1055             return hr;
1056
1057         // For decimation, Fant is equivalent to a Box filter
1058         hr = _Generate3DMipsBoxFilter( depth, levels, mipChain );
1059         if ( FAILED(hr) )
1060             mipChain.Release();
1061         return hr;
1062
1063     case WIC_FLAGS_FILTER_POINT:
1064         hr = _Setup3DMips( baseImages, depth, levels, mipChain );
1065         if ( FAILED(hr) )
1066             return hr;
1067
1068         hr = _Generate3DMipsPointFilter( depth, levels, mipChain );
1069         if ( FAILED(hr) )
1070             mipChain.Release();
1071         return hr;
1072
1073     case WIC_FLAGS_FILTER_LINEAR:
1074         // Need to implement a 3D bi-linear filter (2x2x2)
1075         return E_NOTIMPL;
1076
1077     case WIC_FLAGS_FILTER_CUBIC:
1078         // Need to implement a 3D bi-cubic filter (3x3x3)
1079         return E_NOTIMPL;
1080
1081     default:
1082         return HRESULT_FROM_WIN32( ERROR_NOT_SUPPORTED );;
1083     }
1084 }
1085
1086 HRESULT GenerateMipMaps3D( const Image* srcImages, size_t nimages, const TexMetadata& metadata,
1087                            DWORD filter, size_t levels, ScratchImage& mipChain )
1088 {
1089     if ( !srcImages || !nimages || !IsValid(metadata.format)
1090          || !ispow2(metadata.width) || !ispow2(metadata.height) || !ispow2(metadata.depth) )
1091         return E_INVALIDARG;
1092
1093     if ( metadata.dimension != TEX_DIMENSION_TEXTURE3D
1094          || IsCompressed( metadata.format ) || IsVideo( metadata.format ) )
1095         return HRESULT_FROM_WIN32( ERROR_NOT_SUPPORTED );
1096
1097     if ( !_CalculateMipLevels3D(metadata.width, metadata.height, metadata.depth, levels) )
1098         return E_INVALIDARG;
1099     
1100     std::vector<const Image> baseImages;
1101     baseImages.reserve( metadata.depth );
1102     for( size_t slice=0; slice < metadata.depth; ++slice )
1103     {
1104         size_t index = metadata.ComputeIndex( 0, 0, slice );
1105         if ( index >= nimages )
1106             return E_FAIL;
1107
1108         const Image& src = srcImages[ index ];
1109         if ( !src.pixels )
1110             return E_POINTER;
1111
1112         if ( src.format != metadata.format || src.width != metadata.width || src.height != metadata.height )
1113         {
1114             // All base images must be the same format, width, and height
1115             return E_FAIL;
1116         }
1117
1118         baseImages.push_back( src );
1119     }
1120
1121     assert( baseImages.size() == metadata.depth );
1122
1123     HRESULT hr;
1124
1125     static_assert( TEX_FILTER_POINT == 0x100000, "TEX_FILTER_ flag values don't match TEX_FILTER_MASK" );
1126     switch( filter & TEX_FILTER_MASK )
1127     {
1128     case 0:
1129     case TEX_FILTER_FANT:
1130         hr = _Setup3DMips( &baseImages[0], metadata.depth, levels, mipChain );
1131         if ( FAILED(hr) )
1132             return hr;
1133
1134         // For decimation, Fant is equivalent to a Box filter
1135         hr = _Generate3DMipsBoxFilter( metadata.depth, levels, mipChain );
1136         if ( FAILED(hr) )
1137             mipChain.Release();
1138         return hr;
1139
1140     case WIC_FLAGS_FILTER_POINT:
1141         hr = _Setup3DMips( &baseImages[0], metadata.depth, levels, mipChain );
1142         if ( FAILED(hr) )
1143             return hr;
1144
1145         hr = _Generate3DMipsPointFilter( metadata.depth, levels, mipChain );
1146         if ( FAILED(hr) )
1147             mipChain.Release();
1148         return hr;
1149
1150     case WIC_FLAGS_FILTER_LINEAR:
1151         // Need to implement a 3D bi-linear filter (2x2x2)
1152         return E_NOTIMPL;
1153
1154     case WIC_FLAGS_FILTER_CUBIC:
1155         // Need to implement a 3D bi-cubic filter (3x3x3)
1156         return E_NOTIMPL;
1157
1158     default:
1159         return HRESULT_FROM_WIN32( ERROR_NOT_SUPPORTED );;
1160     }
1161 }
1162
1163 }; // namespace