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