Paradox Game Engine  v1.0.0 beta06
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Events Macros Pages
DirectXTexMipmaps.cpp
Go to the documentation of this file.
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 #include "filters.h"
19 
20 using Microsoft::WRL::ComPtr;
21 
22 namespace DirectX
23 {
24 
25 //-------------------------------------------------------------------------------------
26 // Mipmap helper functions
27 //-------------------------------------------------------------------------------------
28 inline static bool ispow2( _In_ size_t x )
29 {
30  return ((x != 0) && !(x & (x - 1)));
31 }
32 
33 
34 //--- mipmap (1D/2D) levels computation ---
35 static size_t _CountMips( _In_ size_t width, _In_ size_t height )
36 {
37  size_t mipLevels = 1;
38 
39  while ( height > 1 || width > 1 )
40  {
41  if ( height > 1 )
42  height >>= 1;
43 
44  if ( width > 1 )
45  width >>= 1;
46 
47  ++mipLevels;
48  }
49 
50  return mipLevels;
51 }
52 
53 bool _CalculateMipLevels( _In_ size_t width, _In_ size_t height, _Inout_ size_t& mipLevels )
54 {
55  if ( mipLevels > 1 )
56  {
57  size_t maxMips = _CountMips(width,height);
58  if ( mipLevels > maxMips )
59  return false;
60  }
61  else if ( mipLevels == 0 )
62  {
63  mipLevels = _CountMips(width,height);
64  }
65  else
66  {
67  mipLevels = 1;
68  }
69  return true;
70 }
71 
72 
73 //--- volume mipmap (3D) levels computation ---
74 static size_t _CountMips3D( _In_ size_t width, _In_ size_t height, _In_ size_t depth )
75 {
76  size_t mipLevels = 1;
77 
78  while ( height > 1 || width > 1 || depth > 1 )
79  {
80  if ( height > 1 )
81  height >>= 1;
82 
83  if ( width > 1 )
84  width >>= 1;
85 
86  if ( depth > 1 )
87  depth >>= 1;
88 
89  ++mipLevels;
90  }
91 
92  return mipLevels;
93 }
94 
95 bool _CalculateMipLevels3D( _In_ size_t width, _In_ size_t height, _In_ size_t depth, _Inout_ size_t& mipLevels )
96 {
97  if ( mipLevels > 1 )
98  {
99  size_t maxMips = _CountMips3D(width,height,depth);
100  if ( mipLevels > maxMips )
101  return false;
102  }
103  else if ( mipLevels == 0 )
104  {
105  mipLevels = _CountMips3D(width,height,depth);
106  }
107  else
108  {
109  mipLevels = 1;
110  }
111  return true;
112 }
113 
114 
115 //-------------------------------------------------------------------------------------
116 // WIC related helper functions
117 //-------------------------------------------------------------------------------------
118 static HRESULT _EnsureWicBitmapPixelFormat( _In_ IWICImagingFactory* pWIC, _In_ IWICBitmap* src, _In_ DWORD filter,
119  _In_ const WICPixelFormatGUID& desiredPixelFormat,
120  _Deref_out_ IWICBitmap** dest )
121 {
122  if ( !pWIC || !src || !dest )
123  return E_POINTER;
124 
125  *dest = nullptr;
126 
127  WICPixelFormatGUID actualPixelFormat;
128  HRESULT hr = src->GetPixelFormat( &actualPixelFormat );
129 
130  if ( SUCCEEDED(hr) )
131  {
132  if ( memcmp( &actualPixelFormat, &desiredPixelFormat, sizeof(WICPixelFormatGUID) ) == 0 )
133  {
134  src->AddRef();
135  *dest = src;
136  }
137  else
138  {
139  ComPtr<IWICFormatConverter> converter;
140  hr = pWIC->CreateFormatConverter( converter.GetAddressOf() );
141  if ( SUCCEEDED(hr) )
142  {
143  hr = converter->Initialize( src, desiredPixelFormat, _GetWICDither(filter), 0, 0, WICBitmapPaletteTypeCustom );
144  }
145 
146  if ( SUCCEEDED(hr) )
147  {
148  hr = pWIC->CreateBitmapFromSource( converter.Get(), WICBitmapCacheOnDemand, dest );
149  }
150  }
151  }
152 
153  return hr;
154 }
155 
156 
157 //--- Resizing color and alpha channels separately using WIC ---
158 HRESULT _ResizeSeparateColorAndAlpha( _In_ IWICImagingFactory* pWIC, _In_ IWICBitmap* original,
159  _In_ size_t newWidth, _In_ size_t newHeight, _In_ DWORD filter, _Inout_ const Image* img )
160 {
161  if ( !pWIC || !original || !img )
162  return E_POINTER;
163 
164  const WICBitmapInterpolationMode interpolationMode = _GetWICInterp(filter);
165 
166  WICPixelFormatGUID desiredPixelFormat = GUID_WICPixelFormatUndefined;
167  HRESULT hr = original->GetPixelFormat( &desiredPixelFormat );
168 
169  size_t colorBytesInPixel = 0;
170  size_t colorBytesPerPixel = 0;
171  size_t colorWithAlphaBytesPerPixel = 0;
172  WICPixelFormatGUID colorPixelFormat = GUID_WICPixelFormatUndefined;
173  WICPixelFormatGUID colorWithAlphaPixelFormat = GUID_WICPixelFormatUndefined;
174 
175  if ( SUCCEEDED(hr) )
176  {
177  ComPtr<IWICComponentInfo> componentInfo;
178  hr = pWIC->CreateComponentInfo( desiredPixelFormat, componentInfo.GetAddressOf() );
179 
180  ComPtr<IWICPixelFormatInfo> pixelFormatInfo;
181  if ( SUCCEEDED(hr) )
182  {
183  hr = componentInfo.As( &pixelFormatInfo );
184  }
185 
186  UINT bitsPerPixel = 0;
187  if ( SUCCEEDED(hr) )
188  {
189  hr = pixelFormatInfo->GetBitsPerPixel( &bitsPerPixel );
190  }
191 
192  if ( SUCCEEDED(hr) )
193  {
194  if ( bitsPerPixel <= 32 )
195  {
196  colorBytesInPixel = colorBytesPerPixel = 3;
197  colorPixelFormat = GUID_WICPixelFormat24bppBGR;
198 
199  colorWithAlphaBytesPerPixel = 4;
200  colorWithAlphaPixelFormat = GUID_WICPixelFormat32bppBGRA;
201  }
202  else
203  {
204 #if(_WIN32_WINNT >= _WIN32_WINNT_WIN8) || defined(_WIN7_PLATFORM_UPDATE)
205  if ( _IsWIC2() )
206  {
207  colorBytesInPixel = colorBytesPerPixel = 12;
208  colorPixelFormat = GUID_WICPixelFormat96bppRGBFloat;
209  }
210  else
211 #endif
212  {
213  colorBytesInPixel = 12;
214  colorBytesPerPixel = 16;
215  colorPixelFormat = GUID_WICPixelFormat128bppRGBFloat;
216  }
217 
218  colorWithAlphaBytesPerPixel = 16;
219  colorWithAlphaPixelFormat = GUID_WICPixelFormat128bppRGBAFloat;
220  }
221  }
222  }
223 
224  // Resize color only image (no alpha channel)
225  ComPtr<IWICBitmap> resizedColor;
226  if ( SUCCEEDED(hr) )
227  {
228  ComPtr<IWICBitmapScaler> colorScaler;
229  hr = pWIC->CreateBitmapScaler( colorScaler.GetAddressOf() );
230  if ( SUCCEEDED(hr) )
231  {
232  ComPtr<IWICBitmap> converted;
233  hr = _EnsureWicBitmapPixelFormat( pWIC, original, filter, colorPixelFormat, converted.GetAddressOf() );
234  if ( SUCCEEDED(hr) )
235  {
236  hr = colorScaler->Initialize( converted.Get(), static_cast<UINT>(newWidth), static_cast<UINT>(newHeight), interpolationMode );
237  }
238  }
239 
240  if ( SUCCEEDED(hr) )
241  {
242  ComPtr<IWICBitmap> resized;
243  hr = pWIC->CreateBitmapFromSource( colorScaler.Get(), WICBitmapCacheOnDemand, resized.GetAddressOf() );
244  if ( SUCCEEDED(hr) )
245  {
246  hr = _EnsureWicBitmapPixelFormat( pWIC, resized.Get(), filter, colorPixelFormat, resizedColor.GetAddressOf() );
247  }
248  }
249  }
250 
251  // Resize color+alpha image
252  ComPtr<IWICBitmap> resizedColorWithAlpha;
253  if ( SUCCEEDED(hr) )
254  {
255  ComPtr<IWICBitmapScaler> colorWithAlphaScaler;
256  hr = pWIC->CreateBitmapScaler( colorWithAlphaScaler.GetAddressOf() );
257  if ( SUCCEEDED(hr) )
258  {
259  ComPtr<IWICBitmap> converted;
260  hr = _EnsureWicBitmapPixelFormat( pWIC, original, filter, colorWithAlphaPixelFormat, converted.GetAddressOf() );
261  if ( SUCCEEDED(hr) )
262  {
263  hr = colorWithAlphaScaler->Initialize( converted.Get(), static_cast<UINT>(newWidth), static_cast<UINT>(newHeight), interpolationMode );
264  }
265  }
266 
267  if ( SUCCEEDED(hr) )
268  {
269  ComPtr<IWICBitmap> resized;
270  hr = pWIC->CreateBitmapFromSource( colorWithAlphaScaler.Get(), WICBitmapCacheOnDemand, resized.GetAddressOf() );
271  if ( SUCCEEDED(hr) )
272  {
273  hr = _EnsureWicBitmapPixelFormat( pWIC, resized.Get(), filter, colorWithAlphaPixelFormat, resizedColorWithAlpha.GetAddressOf() );
274  }
275  }
276  }
277 
278  // Merge pixels (copying color channels from color only image to color+alpha image)
279  if ( SUCCEEDED(hr) )
280  {
281  ComPtr<IWICBitmapLock> colorLock;
282  ComPtr<IWICBitmapLock> colorWithAlphaLock;
283  hr = resizedColor->Lock( nullptr, WICBitmapLockRead, colorLock.GetAddressOf() );
284  if ( SUCCEEDED(hr) )
285  {
286  hr = resizedColorWithAlpha->Lock( nullptr, WICBitmapLockWrite, colorWithAlphaLock.GetAddressOf() );
287  }
288 
289  if ( SUCCEEDED(hr) )
290  {
291  WICInProcPointer colorWithAlphaData = nullptr;
292  UINT colorWithAlphaSizeInBytes = 0;
293  UINT colorWithAlphaStride = 0;
294 
295  hr = colorWithAlphaLock->GetDataPointer( &colorWithAlphaSizeInBytes, &colorWithAlphaData );
296  if ( SUCCEEDED(hr) )
297  {
298  if ( !colorWithAlphaData )
299  {
300  hr = E_POINTER;
301  }
302  else
303  {
304  hr = colorWithAlphaLock->GetStride( &colorWithAlphaStride );
305  }
306  }
307 
308  WICInProcPointer colorData = nullptr;
309  UINT colorSizeInBytes = 0;
310  UINT colorStride = 0;
311  if ( SUCCEEDED(hr) )
312  {
313  hr = colorLock->GetDataPointer( &colorSizeInBytes, &colorData );
314  if ( SUCCEEDED(hr) )
315  {
316  if ( !colorData )
317  {
318  hr = E_POINTER;
319  }
320  else
321  {
322  hr = colorLock->GetStride( &colorStride );
323  }
324  }
325  }
326 
327  for ( size_t j = 0; SUCCEEDED(hr) && j < newHeight; j++ )
328  {
329  for ( size_t i = 0; SUCCEEDED(hr) && i < newWidth; i++ )
330  {
331  size_t colorWithAlphaIndex = (j * colorWithAlphaStride) + (i * colorWithAlphaBytesPerPixel);
332  size_t colorIndex = (j * colorStride) + (i * colorBytesPerPixel);
333 
334  if ( ((colorWithAlphaIndex + colorBytesInPixel) > colorWithAlphaSizeInBytes)
335  || ( (colorIndex + colorBytesPerPixel) > colorSizeInBytes) )
336  {
337  hr = E_INVALIDARG;
338  }
339  else
340  {
341 #pragma warning( suppress : 26014 6386 ) // No overflow possible here
342  memcpy_s( colorWithAlphaData + colorWithAlphaIndex, colorWithAlphaBytesPerPixel, colorData + colorIndex, colorBytesInPixel );
343  }
344  }
345  }
346  }
347  }
348 
349  if ( SUCCEEDED(hr) )
350  {
351  ComPtr<IWICBitmap> wicBitmap;
352  hr = _EnsureWicBitmapPixelFormat( pWIC, resizedColorWithAlpha.Get(), filter, desiredPixelFormat, wicBitmap.GetAddressOf() );
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 //--- determine when to use WIC vs. non-WIC paths ---
364 static bool _UseWICFiltering( _In_ DXGI_FORMAT format, _In_ DWORD filter )
365 {
366  if ( filter & TEX_FILTER_FORCE_NON_WIC )
367  {
368  // Explicit flag indicates use of non-WIC code paths
369  return false;
370  }
371 
372  if ( filter & TEX_FILTER_FORCE_WIC )
373  {
374  // Explicit flag to use WIC code paths, skips all the case checks below
375  return true;
376  }
377 
378  if ( IsSRGB(format) || (filter & TEX_FILTER_SRGB) )
379  {
380  // Use non-WIC code paths for sRGB correct filtering
381  return false;
382  }
383 
384  static_assert( TEX_FILTER_POINT == 0x100000, "TEX_FILTER_ flag values don't match TEX_FILTER_MASK" );
385 
386  switch ( filter & TEX_FILTER_MASK )
387  {
388  case TEX_FILTER_LINEAR:
389  if ( filter & TEX_FILTER_WRAP )
390  {
391  // WIC only supports 'clamp' semantics (MIRROR is equivalent to clamp for linear)
392  return false;
393  }
394 
395  if ( BitsPerColor(format) > 8 )
396  {
397  // Avoid the WIC bitmap scaler when doing Linear filtering of XR/HDR formats
398  return false;
399  }
400  break;
401 
402  case TEX_FILTER_CUBIC:
403  if ( filter & ( TEX_FILTER_WRAP | TEX_FILTER_MIRROR ) )
404  {
405  // WIC only supports 'clamp' semantics
406  return false;
407  }
408 
409  if ( BitsPerColor(format) > 8 )
410  {
411  // Avoid the WIC bitmap scaler when doing Cubic filtering of XR/HDR formats
412  return false;
413  }
414  break;
415 
416  case TEX_FILTER_TRIANGLE:
417  // WIC does not implement this filter
418  return false;
419  }
420 
421  return true;
422 }
423 
424 
425 //--- mipmap (1D/2D) generation using WIC image scalar ---
426 static HRESULT _GenerateMipMapsUsingWIC( _In_ const Image& baseImage, _In_ DWORD filter, _In_ size_t levels,
427  _In_ const WICPixelFormatGUID& pfGUID, _In_ const ScratchImage& mipChain, _In_ size_t item )
428 {
429  assert( levels > 1 );
430 
431  if ( !baseImage.pixels || !mipChain.GetPixels() )
432  return E_POINTER;
433 
434  IWICImagingFactory* pWIC = _GetWIC();
435  if ( !pWIC )
436  return E_NOINTERFACE;
437 
438  size_t width = baseImage.width;
439  size_t height = baseImage.height;
440 
441  ComPtr<IWICBitmap> source;
442  HRESULT hr = pWIC->CreateBitmapFromMemory( static_cast<UINT>( width ), static_cast<UINT>( height ), pfGUID,
443  static_cast<UINT>( baseImage.rowPitch ), static_cast<UINT>( baseImage.slicePitch ),
444  baseImage.pixels, source.GetAddressOf() );
445  if ( FAILED(hr) )
446  return hr;
447 
448  // Copy base image to top miplevel
449  const Image *img0 = mipChain.GetImage( 0, item, 0 );
450  if ( !img0 )
451  return E_POINTER;
452 
453  uint8_t* pDest = img0->pixels;
454  if ( !pDest )
455  return E_POINTER;
456 
457  const uint8_t *pSrc = baseImage.pixels;
458  for( size_t h=0; h < height; ++h )
459  {
460  size_t msize = std::min<size_t>( img0->rowPitch, baseImage.rowPitch );
461  memcpy_s( pDest, img0->rowPitch, pSrc, msize );
462  pSrc += baseImage.rowPitch;
463  pDest += img0->rowPitch;
464  }
465 
466  ComPtr<IWICComponentInfo> componentInfo;
467  hr = pWIC->CreateComponentInfo( pfGUID, componentInfo.GetAddressOf() );
468  if ( FAILED(hr) )
469  return hr;
470 
471  ComPtr<IWICPixelFormatInfo2> pixelFormatInfo;
472  hr = componentInfo.As( &pixelFormatInfo );
473  if ( FAILED(hr) )
474  return hr;
475 
476  BOOL supportsTransparency = FALSE;
477  hr = pixelFormatInfo->SupportsTransparency( &supportsTransparency );
478  if ( FAILED(hr) )
479  return hr;
480 
481  // Resize base image to each target mip level
482  for( size_t level = 1; level < levels; ++level )
483  {
484  const Image *img = mipChain.GetImage( level, item, 0 );
485  if ( !img )
486  return E_POINTER;
487 
488  if ( height > 1 )
489  height >>= 1;
490 
491  if ( width > 1 )
492  width >>= 1;
493 
494  assert( img->width == width && img->height == height && img->format == baseImage.format );
495 
496  if ( (filter & TEX_FILTER_SEPARATE_ALPHA) && supportsTransparency )
497  {
498  hr = _ResizeSeparateColorAndAlpha( pWIC, source.Get(), width, height, filter, img );
499  if ( FAILED(hr) )
500  return hr;
501  }
502  else
503  {
504  ComPtr<IWICBitmapScaler> scaler;
505  hr = pWIC->CreateBitmapScaler( scaler.GetAddressOf() );
506  if ( FAILED(hr) )
507  return hr;
508 
509  hr = scaler->Initialize( source.Get(), static_cast<UINT>( width ), static_cast<UINT>( height ), _GetWICInterp( filter ) );
510  if ( FAILED(hr) )
511  return hr;
512 
513  WICPixelFormatGUID pfScaler;
514  hr = scaler->GetPixelFormat( &pfScaler );
515  if ( FAILED(hr) )
516  return hr;
517 
518  if ( memcmp( &pfScaler, &pfGUID, sizeof(WICPixelFormatGUID) ) == 0 )
519  {
520  hr = scaler->CopyPixels( 0, static_cast<UINT>( img->rowPitch ), static_cast<UINT>( img->slicePitch ), img->pixels );
521  if ( FAILED(hr) )
522  return hr;
523  }
524  else
525  {
526  // The WIC bitmap scaler is free to return a different pixel format than the source image, so here we
527  // convert it back
528  ComPtr<IWICFormatConverter> FC;
529  hr = pWIC->CreateFormatConverter( FC.GetAddressOf() );
530  if ( FAILED(hr) )
531  return hr;
532 
533  hr = FC->Initialize( scaler.Get(), pfGUID, _GetWICDither( filter ), 0, 0, WICBitmapPaletteTypeCustom );
534  if ( FAILED(hr) )
535  return hr;
536 
537  hr = FC->CopyPixels( 0, static_cast<UINT>( img->rowPitch ), static_cast<UINT>( img->slicePitch ), img->pixels );
538  if ( FAILED(hr) )
539  return hr;
540  }
541  }
542  }
543 
544  return S_OK;
545 }
546 
547 
548 //-------------------------------------------------------------------------------------
549 // Generate (1D/2D) mip-map helpers (custom filtering)
550 //-------------------------------------------------------------------------------------
551 static HRESULT _Setup2DMips( _In_reads_(nimages) const Image* baseImages, _In_ size_t nimages, _In_ const TexMetadata& mdata,
552  _Out_ ScratchImage& mipChain )
553 {
554  if ( !baseImages || !nimages )
555  return E_INVALIDARG;
556 
557  assert( mdata.mipLevels > 1 );
558  assert( mdata.arraySize == nimages );
559  assert( mdata.depth == 1 && mdata.dimension != TEX_DIMENSION_TEXTURE3D );
560  assert( mdata.width == baseImages[0].width );
561  assert( mdata.height == baseImages[0].height );
562  assert( mdata.format == baseImages[0].format );
563 
564  HRESULT hr = mipChain.Initialize( mdata );
565  if ( FAILED(hr) )
566  return hr;
567 
568  // Copy base image(s) to top of mip chain
569  for( size_t item=0; item < nimages; ++item )
570  {
571  const Image& src = baseImages[item];
572 
573  const Image *dest = mipChain.GetImage( 0, item, 0 );
574  if ( !dest )
575  {
576  mipChain.Release();
577  return E_POINTER;
578  }
579 
580  assert( src.format == dest->format );
581 
582  uint8_t* pDest = dest->pixels;
583  if ( !pDest )
584  {
585  mipChain.Release();
586  return E_POINTER;
587  }
588 
589  const uint8_t *pSrc = src.pixels;
590  size_t rowPitch = src.rowPitch;
591  for( size_t h=0; h < mdata.height; ++h )
592  {
593  size_t msize = std::min<size_t>( dest->rowPitch, rowPitch );
594  memcpy_s( pDest, dest->rowPitch, pSrc, msize );
595  pSrc += rowPitch;
596  pDest += dest->rowPitch;
597  }
598  }
599 
600  return S_OK;
601 }
602 
603 //--- 2D Point Filter ---
604 static HRESULT _Generate2DMipsPointFilter( _In_ size_t levels, _In_ const ScratchImage& mipChain, _In_ size_t item )
605 {
606  if ( !mipChain.GetImages() )
607  return E_INVALIDARG;
608 
609  // This assumes that the base image is already placed into the mipChain at the top level... (see _Setup2DMips)
610 
611  assert( levels > 1 );
612 
613  size_t width = mipChain.GetMetadata().width;
614  size_t height = mipChain.GetMetadata().height;
615 
616  // Allocate temporary space (2 scanlines)
617  ScopedAlignedArrayXMVECTOR scanline( reinterpret_cast<XMVECTOR*>( _aligned_malloc( (sizeof(XMVECTOR)*width*2), 16 ) ) );
618  if ( !scanline )
619  return E_OUTOFMEMORY;
620 
621  XMVECTOR* target = scanline.get();
622 
623  XMVECTOR* row = target + width;
624 
625  // Resize base image to each target mip level
626  for( size_t level=1; level < levels; ++level )
627  {
628 #ifdef _DEBUG
629  memset( row, 0xCD, sizeof(XMVECTOR)*width );
630 #endif
631 
632  // 2D point filter
633  const Image* src = mipChain.GetImage( level-1, item, 0 );
634  const Image* dest = mipChain.GetImage( level, item, 0 );
635 
636  if ( !src || !dest )
637  return E_POINTER;
638 
639  const uint8_t* pSrc = src->pixels;
640  uint8_t* pDest = dest->pixels;
641 
642  size_t rowPitch = src->rowPitch;
643 
644  size_t nwidth = (width > 1) ? (width >> 1) : 1;
645  size_t nheight = (height > 1) ? (height >> 1) : 1;
646 
647  size_t xinc = ( width << 16 ) / nwidth;
648  size_t yinc = ( height << 16 ) / nheight;
649 
650  size_t lasty = size_t(-1);
651 
652  size_t sy = 0;
653  for( size_t y = 0; y < nheight; ++y )
654  {
655  if ( (lasty ^ sy) >> 16 )
656  {
657  if ( !_LoadScanline( row, width, pSrc + ( rowPitch * (sy >> 16) ), rowPitch, src->format ) )
658  return E_FAIL;
659  lasty = sy;
660  }
661 
662  size_t sx = 0;
663  for( size_t x = 0; x < nwidth; ++x )
664  {
665  target[ x ] = row[ sx >> 16 ];
666  sx += xinc;
667  }
668 
669  if ( !_StoreScanline( pDest, dest->rowPitch, dest->format, target, nwidth ) )
670  return E_FAIL;
671  pDest += dest->rowPitch;
672 
673  sy += yinc;
674  }
675 
676  if ( height > 1 )
677  height >>= 1;
678 
679  if ( width > 1 )
680  width >>= 1;
681  }
682 
683  return S_OK;
684 }
685 
686 
687 //--- 2D Box Filter ---
688 static HRESULT _Generate2DMipsBoxFilter( _In_ size_t levels, _In_ DWORD filter, _In_ const ScratchImage& mipChain, _In_ size_t item )
689 {
690  if ( !mipChain.GetImages() )
691  return E_INVALIDARG;
692 
693  // This assumes that the base image is already placed into the mipChain at the top level... (see _Setup2DMips)
694 
695  assert( levels > 1 );
696 
697  size_t width = mipChain.GetMetadata().width;
698  size_t height = mipChain.GetMetadata().height;
699 
700  if ( !ispow2(width) || !ispow2(height) )
701  return E_FAIL;
702 
703  // Allocate temporary space (3 scanlines)
704  ScopedAlignedArrayXMVECTOR scanline( reinterpret_cast<XMVECTOR*>( _aligned_malloc( (sizeof(XMVECTOR)*width*3), 16 ) ) );
705  if ( !scanline )
706  return E_OUTOFMEMORY;
707 
708  XMVECTOR* target = scanline.get();
709 
710  XMVECTOR* urow0 = target + width;
711  XMVECTOR* urow1 = target + width*2;
712 
713  const XMVECTOR* urow2 = urow0 + 1;
714  const XMVECTOR* urow3 = urow1 + 1;
715 
716  // Resize base image to each target mip level
717  for( size_t level=1; level < levels; ++level )
718  {
719  if ( height <= 1 )
720  {
721  urow1 = urow0;
722  }
723 
724  if ( width <= 1 )
725  {
726  urow2 = urow0;
727  urow3 = urow1;
728  }
729 
730  // 2D box filter
731  const Image* src = mipChain.GetImage( level-1, item, 0 );
732  const Image* dest = mipChain.GetImage( level, item, 0 );
733 
734  if ( !src || !dest )
735  return E_POINTER;
736 
737  const uint8_t* pSrc = src->pixels;
738  uint8_t* pDest = dest->pixels;
739 
740  size_t rowPitch = src->rowPitch;
741 
742  size_t nwidth = (width > 1) ? (width >> 1) : 1;
743  size_t nheight = (height > 1) ? (height >> 1) : 1;
744 
745  for( size_t y = 0; y < nheight; ++y )
746  {
747  if ( !_LoadScanlineLinear( urow0, width, pSrc, rowPitch, src->format, filter ) )
748  return E_FAIL;
749  pSrc += rowPitch;
750 
751  if ( urow0 != urow1 )
752  {
753  if ( !_LoadScanlineLinear( urow1, width, pSrc, rowPitch, src->format, filter ) )
754  return E_FAIL;
755  pSrc += rowPitch;
756  }
757 
758  for( size_t x = 0; x < nwidth; ++x )
759  {
760  size_t x2 = x << 1;
761 
762  AVERAGE4( target[ x ], urow0[ x2 ], urow1[ x2 ], urow2[ x2 ], urow3[ x2 ] );
763  }
764 
765  if ( !_StoreScanlineLinear( pDest, dest->rowPitch, dest->format, target, nwidth, filter ) )
766  return E_FAIL;
767  pDest += dest->rowPitch;
768  }
769 
770  if ( height > 1 )
771  height >>= 1;
772 
773  if ( width > 1 )
774  width >>= 1;
775  }
776 
777  return S_OK;
778 }
779 
780 
781 //--- 2D Linear Filter ---
782 static HRESULT _Generate2DMipsLinearFilter( _In_ size_t levels, _In_ DWORD filter, _In_ const ScratchImage& mipChain, _In_ size_t item )
783 {
784  if ( !mipChain.GetImages() )
785  return E_INVALIDARG;
786 
787  // This assumes that the base image is already placed into the mipChain at the top level... (see _Setup2DMips)
788 
789  assert( levels > 1 );
790 
791  size_t width = mipChain.GetMetadata().width;
792  size_t height = mipChain.GetMetadata().height;
793 
794  // Allocate temporary space (3 scanlines, plus X and Y filters)
795  ScopedAlignedArrayXMVECTOR scanline( reinterpret_cast<XMVECTOR*>( _aligned_malloc( (sizeof(XMVECTOR)*width*3), 16 ) ) );
796  if ( !scanline )
797  return E_OUTOFMEMORY;
798 
799  std::unique_ptr<LinearFilter[]> lf( new (std::nothrow) LinearFilter[ width+height ] );
800  if ( !lf )
801  return E_OUTOFMEMORY;
802 
803  LinearFilter* lfX = lf.get();
804  LinearFilter* lfY = lf.get() + width;
805 
806  XMVECTOR* target = scanline.get();
807 
808  XMVECTOR* row0 = target + width;
809  XMVECTOR* row1 = target + width*2;
810 
811  // Resize base image to each target mip level
812  for( size_t level=1; level < levels; ++level )
813  {
814  // 2D linear filter
815  const Image* src = mipChain.GetImage( level-1, item, 0 );
816  const Image* dest = mipChain.GetImage( level, item, 0 );
817 
818  if ( !src || !dest )
819  return E_POINTER;
820 
821  const uint8_t* pSrc = src->pixels;
822  uint8_t* pDest = dest->pixels;
823 
824  size_t rowPitch = src->rowPitch;
825 
826  size_t nwidth = (width > 1) ? (width >> 1) : 1;
827  _CreateLinearFilter( width, nwidth, (filter & TEX_FILTER_WRAP_U) != 0, lfX );
828 
829  size_t nheight = (height > 1) ? (height >> 1) : 1;
830  _CreateLinearFilter( height, nheight, (filter & TEX_FILTER_WRAP_V) != 0, lfY );
831 
832 #ifdef _DEBUG
833  memset( row0, 0xCD, sizeof(XMVECTOR)*width );
834  memset( row1, 0xDD, sizeof(XMVECTOR)*width );
835 #endif
836 
837  size_t u0 = size_t(-1);
838  size_t u1 = size_t(-1);
839 
840  for( size_t y = 0; y < nheight; ++y )
841  {
842  auto& toY = lfY[ y ];
843 
844  if ( toY.u0 != u0 )
845  {
846  if ( toY.u0 != u1 )
847  {
848  u0 = toY.u0;
849 
850  if ( !_LoadScanlineLinear( row0, width, pSrc + (rowPitch * u0), rowPitch, src->format, filter ) )
851  return E_FAIL;
852  }
853  else
854  {
855  u0 = u1;
856  u1 = size_t(-1);
857 
858  std::swap( row0, row1 );
859  }
860  }
861 
862  if ( toY.u1 != u1 )
863  {
864  u1 = toY.u1;
865 
866  if ( !_LoadScanlineLinear( row1, width, pSrc + (rowPitch * u1), rowPitch, src->format, filter ) )
867  return E_FAIL;
868  }
869 
870  for( size_t x = 0; x < nwidth; ++x )
871  {
872  auto& toX = lfX[ x ];
873 
874  BILINEAR_INTERPOLATE( target[x], toX, toY, row0, row1 );
875  }
876 
877  if ( !_StoreScanlineLinear( pDest, dest->rowPitch, dest->format, target, nwidth, filter ) )
878  return E_FAIL;
879  pDest += dest->rowPitch;
880  }
881 
882  if ( height > 1 )
883  height >>= 1;
884 
885  if ( width > 1 )
886  width >>= 1;
887  }
888 
889  return S_OK;
890 }
891 
892 
893 //--- 2D Cubic Filter ---
894 static HRESULT _Generate2DMipsCubicFilter( _In_ size_t levels, _In_ DWORD filter, _In_ const ScratchImage& mipChain, _In_ size_t item )
895 {
896  if ( !mipChain.GetImages() )
897  return E_INVALIDARG;
898 
899  // This assumes that the base image is already placed into the mipChain at the top level... (see _Setup2DMips)
900 
901  assert( levels > 1 );
902 
903  size_t width = mipChain.GetMetadata().width;
904  size_t height = mipChain.GetMetadata().height;
905 
906  // Allocate temporary space (5 scanlines, plus X and Y filters)
907  ScopedAlignedArrayXMVECTOR scanline( reinterpret_cast<XMVECTOR*>( _aligned_malloc( (sizeof(XMVECTOR)*width*5), 16 ) ) );
908  if ( !scanline )
909  return E_OUTOFMEMORY;
910 
911  std::unique_ptr<CubicFilter[]> cf( new (std::nothrow) CubicFilter[ width+height ] );
912  if ( !cf )
913  return E_OUTOFMEMORY;
914 
915  CubicFilter* cfX = cf.get();
916  CubicFilter* cfY = cf.get() + width;
917 
918  XMVECTOR* target = scanline.get();
919 
920  XMVECTOR* row0 = target + width;
921  XMVECTOR* row1 = target + width*2;
922  XMVECTOR* row2 = target + width*3;
923  XMVECTOR* row3 = target + width*4;
924 
925  // Resize base image to each target mip level
926  for( size_t level=1; level < levels; ++level )
927  {
928  // 2D cubic filter
929  const Image* src = mipChain.GetImage( level-1, item, 0 );
930  const Image* dest = mipChain.GetImage( level, item, 0 );
931 
932  if ( !src || !dest )
933  return E_POINTER;
934 
935  const uint8_t* pSrc = src->pixels;
936  uint8_t* pDest = dest->pixels;
937 
938  size_t rowPitch = src->rowPitch;
939 
940  size_t nwidth = (width > 1) ? (width >> 1) : 1;
941  _CreateCubicFilter( width, nwidth, (filter & TEX_FILTER_WRAP_U) != 0, (filter & TEX_FILTER_MIRROR_U) != 0, cfX );
942 
943  size_t nheight = (height > 1) ? (height >> 1) : 1;
944  _CreateCubicFilter( height, nheight, (filter & TEX_FILTER_WRAP_V) != 0, (filter & TEX_FILTER_MIRROR_V) != 0, cfY );
945 
946 #ifdef _DEBUG
947  memset( row0, 0xCD, sizeof(XMVECTOR)*width );
948  memset( row1, 0xDD, sizeof(XMVECTOR)*width );
949  memset( row2, 0xED, sizeof(XMVECTOR)*width );
950  memset( row3, 0xFD, sizeof(XMVECTOR)*width );
951 #endif
952 
953  size_t u0 = size_t(-1);
954  size_t u1 = size_t(-1);
955  size_t u2 = size_t(-1);
956  size_t u3 = size_t(-1);
957 
958  for( size_t y = 0; y < nheight; ++y )
959  {
960  auto& toY = cfY[ y ];
961 
962  // Scanline 1
963  if ( toY.u0 != u0 )
964  {
965  if ( toY.u0 != u1 && toY.u0 != u2 && toY.u0 != u3 )
966  {
967  u0 = toY.u0;
968 
969  if ( !_LoadScanlineLinear( row0, width, pSrc + (rowPitch * u0), rowPitch, src->format, filter ) )
970  return E_FAIL;
971  }
972  else if ( toY.u0 == u1 )
973  {
974  u0 = u1;
975  u1 = size_t(-1);
976 
977  std::swap( row0, row1 );
978  }
979  else if ( toY.u0 == u2 )
980  {
981  u0 = u2;
982  u2 = size_t(-1);
983 
984  std::swap( row0, row2 );
985  }
986  else if ( toY.u0 == u3 )
987  {
988  u0 = u3;
989  u3 = size_t(-1);
990 
991  std::swap( row0, row3 );
992  }
993  }
994 
995  // Scanline 2
996  if ( toY.u1 != u1 )
997  {
998  if ( toY.u1 != u2 && toY.u1 != u3 )
999  {
1000  u1 = toY.u1;
1001 
1002  if ( !_LoadScanlineLinear( row1, width, pSrc + (rowPitch * u1), rowPitch, src->format, filter ) )
1003  return E_FAIL;
1004  }
1005  else if ( toY.u1 == u2 )
1006  {
1007  u1 = u2;
1008  u2 = size_t(-1);
1009 
1010  std::swap( row1, row2 );
1011  }
1012  else if ( toY.u1 == u3 )
1013  {
1014  u1 = u3;
1015  u3 = size_t(-1);
1016 
1017  std::swap( row1, row3 );
1018  }
1019  }
1020 
1021  // Scanline 3
1022  if ( toY.u2 != u2 )
1023  {
1024  if ( toY.u2 != u3 )
1025  {
1026  u2 = toY.u2;
1027 
1028  if ( !_LoadScanlineLinear( row2, width, pSrc + (rowPitch * u2), rowPitch, src->format, filter ) )
1029  return E_FAIL;
1030  }
1031  else
1032  {
1033  u2 = u3;
1034  u3 = size_t(-1);
1035 
1036  std::swap( row2, row3 );
1037  }
1038  }
1039 
1040  // Scanline 4
1041  if ( toY.u3 != u3 )
1042  {
1043  u3 = toY.u3;
1044 
1045  if ( !_LoadScanlineLinear( row3, width, pSrc + (rowPitch * u3), rowPitch, src->format, filter ) )
1046  return E_FAIL;
1047  }
1048 
1049  for( size_t x = 0; x < nwidth; ++x )
1050  {
1051  auto& toX = cfX[ x ];
1052 
1053  XMVECTOR C0, C1, C2, C3;
1054 
1055  CUBIC_INTERPOLATE( C0, toX.x, row0[ toX.u0 ], row0[ toX.u1 ], row0[ toX.u2 ], row0[ toX.u3 ] );
1056  CUBIC_INTERPOLATE( C1, toX.x, row1[ toX.u0 ], row1[ toX.u1 ], row1[ toX.u2 ], row1[ toX.u3 ] );
1057  CUBIC_INTERPOLATE( C2, toX.x, row2[ toX.u0 ], row2[ toX.u1 ], row2[ toX.u2 ], row2[ toX.u3 ] );
1058  CUBIC_INTERPOLATE( C3, toX.x, row3[ toX.u0 ], row3[ toX.u1 ], row3[ toX.u2 ], row3[ toX.u3 ] );
1059 
1060  CUBIC_INTERPOLATE( target[x], toY.x, C0, C1, C2, C3 );
1061  }
1062 
1063  if ( !_StoreScanlineLinear( pDest, dest->rowPitch, dest->format, target, nwidth, filter ) )
1064  return E_FAIL;
1065  pDest += dest->rowPitch;
1066  }
1067 
1068  if ( height > 1 )
1069  height >>= 1;
1070 
1071  if ( width > 1 )
1072  width >>= 1;
1073  }
1074 
1075  return S_OK;
1076 }
1077 
1078 
1079 //--- 2D Triangle Filter ---
1080 static HRESULT _Generate2DMipsTriangleFilter( _In_ size_t levels, _In_ DWORD filter, _In_ const ScratchImage& mipChain, _In_ size_t item )
1081 {
1082  if ( !mipChain.GetImages() )
1083  return E_INVALIDARG;
1084 
1085  using namespace TriangleFilter;
1086 
1087  // This assumes that the base image is already placed into the mipChain at the top level... (see _Setup2DMips)
1088 
1089  assert( levels > 1 );
1090 
1091  size_t width = mipChain.GetMetadata().width;
1092  size_t height = mipChain.GetMetadata().height;
1093 
1094  // Allocate initial temporary space (1 scanline, accumulation rows, plus X and Y filters)
1095  ScopedAlignedArrayXMVECTOR scanline( reinterpret_cast<XMVECTOR*>( _aligned_malloc( sizeof(XMVECTOR) * width, 16 ) ) );
1096  if ( !scanline )
1097  return E_OUTOFMEMORY;
1098 
1099  std::unique_ptr<TriangleRow[]> rowActive( new (std::nothrow) TriangleRow[ height ] );
1100  if ( !rowActive )
1101  return E_OUTOFMEMORY;
1102 
1103  TriangleRow * rowFree = nullptr;
1104 
1105  std::unique_ptr<Filter> tfX, tfY;
1106 
1107  XMVECTOR* row = scanline.get();
1108 
1109  // Resize base image to each target mip level
1110  for( size_t level=1; level < levels; ++level )
1111  {
1112  // 2D triangle filter
1113  const Image* src = mipChain.GetImage( level-1, item, 0 );
1114  const Image* dest = mipChain.GetImage( level, item, 0 );
1115 
1116  if ( !src || !dest )
1117  return E_POINTER;
1118 
1119  const uint8_t* pSrc = src->pixels;
1120  size_t rowPitch = src->rowPitch;
1121  const uint8_t* pEndSrc = pSrc + rowPitch * height;
1122 
1123  uint8_t* pDest = dest->pixels;
1124 
1125  size_t nwidth = (width > 1) ? (width >> 1) : 1;
1126  HRESULT hr = _Create( width, nwidth, (filter & TEX_FILTER_WRAP_U) != 0, tfX );
1127  if ( FAILED(hr) )
1128  return hr;
1129 
1130  size_t nheight = (height > 1) ? (height >> 1) : 1;
1131  hr = _Create( height, nheight, (filter & TEX_FILTER_WRAP_V) != 0, tfY );
1132  if ( FAILED(hr) )
1133  return hr;
1134 
1135 #ifdef _DEBUG
1136  memset( row, 0xCD, sizeof(XMVECTOR)*width );
1137 #endif
1138 
1139  auto xFromEnd = reinterpret_cast<const FilterFrom*>( reinterpret_cast<const uint8_t*>( tfX.get() ) + tfX->sizeInBytes );
1140  auto yFromEnd = reinterpret_cast<const FilterFrom*>( reinterpret_cast<const uint8_t*>( tfY.get() ) + tfY->sizeInBytes );
1141 
1142  // Count times rows get written (and clear out any leftover accumulation rows from last miplevel)
1143  for( FilterFrom* yFrom = tfY->from; yFrom < yFromEnd; )
1144  {
1145  for ( size_t j = 0; j < yFrom->count; ++j )
1146  {
1147  size_t v = yFrom->to[ j ].u;
1148  assert( v < nheight );
1149  TriangleRow* rowAcc = &rowActive.get()[ v ];
1150 
1151  ++rowAcc->remaining;
1152 
1153  if ( rowAcc->scanline )
1154  {
1155  memset( rowAcc->scanline.get(), 0, sizeof(XMVECTOR) * nwidth );
1156  }
1157  }
1158 
1159  yFrom = reinterpret_cast<FilterFrom*>( reinterpret_cast<uint8_t*>( yFrom ) + yFrom->sizeInBytes );
1160  }
1161 
1162  // Filter image
1163  for( FilterFrom* yFrom = tfY->from; yFrom < yFromEnd; )
1164  {
1165  // Create accumulation rows as needed
1166  for ( size_t j = 0; j < yFrom->count; ++j )
1167  {
1168  size_t v = yFrom->to[ j ].u;
1169  assert( v < nheight );
1170  TriangleRow* rowAcc = &rowActive.get()[ v ];
1171 
1172  if ( !rowAcc->scanline )
1173  {
1174  if ( rowFree )
1175  {
1176  // Steal and reuse scanline from 'free row' list
1177  // (it will always be at least as wide as nwidth due to loop decending order)
1178  assert( rowFree->scanline != 0 );
1179  rowAcc->scanline.reset( rowFree->scanline.release() );
1180  rowFree = rowFree->next;
1181  }
1182  else
1183  {
1184  rowAcc->scanline.reset( reinterpret_cast<XMVECTOR*>( _aligned_malloc( sizeof(XMVECTOR) * nwidth, 16 ) ) );
1185  if ( !rowAcc->scanline )
1186  return E_OUTOFMEMORY;
1187  }
1188 
1189  memset( rowAcc->scanline.get(), 0, sizeof(XMVECTOR) * nwidth );
1190  }
1191  }
1192 
1193  // Load source scanline
1194  if ( (pSrc + rowPitch) > pEndSrc )
1195  return E_FAIL;
1196 
1197  if ( !_LoadScanlineLinear( row, width, pSrc, rowPitch, src->format, filter ) )
1198  return E_FAIL;
1199 
1200  pSrc += rowPitch;
1201 
1202  // Process row
1203  size_t x = 0;
1204  for( FilterFrom* xFrom = tfX->from; xFrom < xFromEnd; ++x )
1205  {
1206  for ( size_t j = 0; j < yFrom->count; ++j )
1207  {
1208  size_t v = yFrom->to[ j ].u;
1209  assert( v < nheight );
1210  float yweight = yFrom->to[ j ].weight;
1211 
1212  XMVECTOR* accPtr = rowActive[ v ].scanline.get();
1213  if ( !accPtr )
1214  return E_POINTER;
1215 
1216  for ( size_t k = 0; k < xFrom->count; ++k )
1217  {
1218  size_t u = xFrom->to[ k ].u;
1219  assert( u < nwidth );
1220 
1221  XMVECTOR weight = XMVectorReplicate( yweight * xFrom->to[ k ].weight );
1222 
1223  assert( x < width );
1224  accPtr[ u ] = XMVectorMultiplyAdd( row[ x ], weight, accPtr[ u ] );
1225  }
1226  }
1227 
1228  xFrom = reinterpret_cast<FilterFrom*>( reinterpret_cast<uint8_t*>( xFrom ) + xFrom->sizeInBytes );
1229  }
1230 
1231  // Write completed accumulation rows
1232  for ( size_t j = 0; j < yFrom->count; ++j )
1233  {
1234  size_t v = yFrom->to[ j ].u;
1235  assert( v < nheight );
1236  TriangleRow* rowAcc = &rowActive.get()[ v ];
1237 
1238  assert( rowAcc->remaining > 0 );
1239  --rowAcc->remaining;
1240 
1241  if ( !rowAcc->remaining )
1242  {
1243  XMVECTOR* pAccSrc = rowAcc->scanline.get();
1244  if ( !pAccSrc )
1245  return E_POINTER;
1246 
1247  switch( dest->format )
1248  {
1249  case DXGI_FORMAT_R10G10B10A2_UNORM:
1250  case DXGI_FORMAT_R10G10B10A2_UINT:
1251  {
1252  // Need to slightly bias results for floating-point error accumulation which can
1253  // be visible with harshly quantized values
1254  static const XMVECTORF32 Bias = { 0.f, 0.f, 0.f, 0.1f };
1255 
1256  XMVECTOR* ptr = pAccSrc;
1257  for( size_t i=0; i < dest->width; ++i, ++ptr )
1258  {
1259  *ptr = XMVectorAdd( *ptr, Bias );
1260  }
1261  }
1262  break;
1263  }
1264 
1265  // This performs any required clamping
1266  if ( !_StoreScanlineLinear( pDest + (dest->rowPitch * v), dest->rowPitch, dest->format, pAccSrc, dest->width, filter ) )
1267  return E_FAIL;
1268 
1269  // Put row on freelist to reuse it's allocated scanline
1270  rowAcc->next = rowFree;
1271  rowFree = rowAcc;
1272  }
1273  }
1274 
1275  yFrom = reinterpret_cast<FilterFrom*>( reinterpret_cast<uint8_t*>( yFrom ) + yFrom->sizeInBytes );
1276  }
1277 
1278  if ( height > 1 )
1279  height >>= 1;
1280 
1281  if ( width > 1 )
1282  width >>= 1;
1283  }
1284 
1285  return S_OK;
1286 }
1287 
1288 
1289 //-------------------------------------------------------------------------------------
1290 // Generate volume mip-map helpers
1291 //-------------------------------------------------------------------------------------
1292 static HRESULT _Setup3DMips( _In_reads_(depth) const Image* baseImages, _In_ size_t depth, size_t levels,
1293  _Out_ ScratchImage& mipChain )
1294 {
1295  if ( !baseImages || !depth )
1296  return E_INVALIDARG;
1297 
1298  assert( levels > 1 );
1299 
1300  size_t width = baseImages[0].width;
1301  size_t height = baseImages[0].height;
1302 
1303  HRESULT hr = mipChain.Initialize3D( baseImages[0].format, width, height, depth, levels );
1304  if ( FAILED(hr) )
1305  return hr;
1306 
1307  // Copy base images to top slice
1308  for( size_t slice=0; slice < depth; ++slice )
1309  {
1310  const Image& src = baseImages[slice];
1311 
1312  const Image *dest = mipChain.GetImage( 0, 0, slice );
1313  if ( !dest )
1314  {
1315  mipChain.Release();
1316  return E_POINTER;
1317  }
1318 
1319  assert( src.format == dest->format );
1320 
1321  uint8_t* pDest = dest->pixels;
1322  if ( !pDest )
1323  {
1324  mipChain.Release();
1325  return E_POINTER;
1326  }
1327 
1328  const uint8_t *pSrc = src.pixels;
1329  size_t rowPitch = src.rowPitch;
1330  for( size_t h=0; h < height; ++h )
1331  {
1332  size_t msize = std::min<size_t>( dest->rowPitch, rowPitch );
1333  memcpy_s( pDest, dest->rowPitch, pSrc, msize );
1334  pSrc += rowPitch;
1335  pDest += dest->rowPitch;
1336  }
1337  }
1338 
1339  return S_OK;
1340 }
1341 
1342 
1343 //--- 3D Point Filter ---
1344 static HRESULT _Generate3DMipsPointFilter( _In_ size_t depth, _In_ size_t levels, _In_ const ScratchImage& mipChain )
1345 {
1346  if ( !depth || !mipChain.GetImages() )
1347  return E_INVALIDARG;
1348 
1349  // This assumes that the base images are already placed into the mipChain at the top level... (see _Setup3DMips)
1350 
1351  assert( levels > 1 );
1352 
1353  size_t width = mipChain.GetMetadata().width;
1354  size_t height = mipChain.GetMetadata().height;
1355 
1356  // Allocate temporary space (2 scanlines)
1357  ScopedAlignedArrayXMVECTOR scanline( reinterpret_cast<XMVECTOR*>( _aligned_malloc( (sizeof(XMVECTOR)*width*2), 16 ) ) );
1358  if ( !scanline )
1359  return E_OUTOFMEMORY;
1360 
1361  XMVECTOR* target = scanline.get();
1362 
1363  XMVECTOR* row = target + width;
1364 
1365  // Resize base image to each target mip level
1366  for( size_t level=1; level < levels; ++level )
1367  {
1368 #ifdef _DEBUG
1369  memset( row, 0xCD, sizeof(XMVECTOR)*width );
1370 #endif
1371 
1372  if ( depth > 1 )
1373  {
1374  // 3D point filter
1375  size_t ndepth = depth >> 1;
1376 
1377  size_t zinc = ( depth << 16 ) / ndepth;
1378 
1379  size_t sz = 0;
1380  for( size_t slice=0; slice < ndepth; ++slice )
1381  {
1382  const Image* src = mipChain.GetImage( level-1, 0, (sz >> 16) );
1383  const Image* dest = mipChain.GetImage( level, 0, slice );
1384 
1385  if ( !src || !dest )
1386  return E_POINTER;
1387 
1388  const uint8_t* pSrc = src->pixels;
1389  uint8_t* pDest = dest->pixels;
1390 
1391  size_t rowPitch = src->rowPitch;
1392 
1393  size_t nwidth = (width > 1) ? (width >> 1) : 1;
1394  size_t nheight = (height > 1) ? (height >> 1) : 1;
1395 
1396  size_t xinc = ( width << 16 ) / nwidth;
1397  size_t yinc = ( height << 16 ) / nheight;
1398 
1399  size_t lasty = size_t(-1);
1400 
1401  size_t sy = 0;
1402  for( size_t y = 0; y < nheight; ++y )
1403  {
1404  if ( (lasty ^ sy) >> 16 )
1405  {
1406  if ( !_LoadScanline( row, width, pSrc + ( rowPitch * (sy >> 16) ), rowPitch, src->format ) )
1407  return E_FAIL;
1408  lasty = sy;
1409  }
1410 
1411  size_t sx = 0;
1412  for( size_t x = 0; x < nwidth; ++x )
1413  {
1414  target[ x ] = row[ sx >> 16 ];
1415  sx += xinc;
1416  }
1417 
1418  if ( !_StoreScanline( pDest, dest->rowPitch, dest->format, target, nwidth ) )
1419  return E_FAIL;
1420  pDest += dest->rowPitch;
1421 
1422  sy += yinc;
1423  }
1424 
1425  sz += zinc;
1426  }
1427  }
1428  else
1429  {
1430  // 2D point filter
1431  const Image* src = mipChain.GetImage( level-1, 0, 0 );
1432  const Image* dest = mipChain.GetImage( level, 0, 0 );
1433 
1434  if ( !src || !dest )
1435  return E_POINTER;
1436 
1437  const uint8_t* pSrc = src->pixels;
1438  uint8_t* pDest = dest->pixels;
1439 
1440  size_t rowPitch = src->rowPitch;
1441 
1442  size_t nwidth = (width > 1) ? (width >> 1) : 1;
1443  size_t nheight = (height > 1) ? (height >> 1) : 1;
1444 
1445  size_t xinc = ( width << 16 ) / nwidth;
1446  size_t yinc = ( height << 16 ) / nheight;
1447 
1448  size_t lasty = size_t(-1);
1449 
1450  size_t sy = 0;
1451  for( size_t y = 0; y < nheight; ++y )
1452  {
1453  if ( (lasty ^ sy) >> 16 )
1454  {
1455  if ( !_LoadScanline( row, width, pSrc + ( rowPitch * (sy >> 16) ), rowPitch, src->format ) )
1456  return E_FAIL;
1457  lasty = sy;
1458  }
1459 
1460  size_t sx = 0;
1461  for( size_t x = 0; x < nwidth; ++x )
1462  {
1463  target[ x ] = row[ sx >> 16 ];
1464  sx += xinc;
1465  }
1466 
1467  if ( !_StoreScanline( pDest, dest->rowPitch, dest->format, target, nwidth ) )
1468  return E_FAIL;
1469  pDest += dest->rowPitch;
1470 
1471  sy += yinc;
1472  }
1473  }
1474 
1475  if ( height > 1 )
1476  height >>= 1;
1477 
1478  if ( width > 1 )
1479  width >>= 1;
1480 
1481  if ( depth > 1 )
1482  depth >>= 1;
1483  }
1484 
1485  return S_OK;
1486 }
1487 
1488 
1489 //--- 3D Box Filter ---
1490 static HRESULT _Generate3DMipsBoxFilter( _In_ size_t depth, _In_ size_t levels, _In_ DWORD filter, _In_ const ScratchImage& mipChain )
1491 {
1492  if ( !depth || !mipChain.GetImages() )
1493  return E_INVALIDARG;
1494 
1495  // This assumes that the base images are already placed into the mipChain at the top level... (see _Setup3DMips)
1496 
1497  assert( levels > 1 );
1498 
1499  size_t width = mipChain.GetMetadata().width;
1500  size_t height = mipChain.GetMetadata().height;
1501 
1502  if ( !ispow2(width) || !ispow2(height) || !ispow2(depth) )
1503  return E_FAIL;
1504 
1505  // Allocate temporary space (5 scanlines)
1506  ScopedAlignedArrayXMVECTOR scanline( reinterpret_cast<XMVECTOR*>( _aligned_malloc( (sizeof(XMVECTOR)*width*5), 16 ) ) );
1507  if ( !scanline )
1508  return E_OUTOFMEMORY;
1509 
1510  XMVECTOR* target = scanline.get();
1511 
1512  XMVECTOR* urow0 = target + width;
1513  XMVECTOR* urow1 = target + width*2;
1514  XMVECTOR* vrow0 = target + width*3;
1515  XMVECTOR* vrow1 = target + width*4;
1516 
1517  const XMVECTOR* urow2 = urow0 + 1;
1518  const XMVECTOR* urow3 = urow1 + 1;
1519  const XMVECTOR* vrow2 = vrow0 + 1;
1520  const XMVECTOR* vrow3 = vrow1 + 1;
1521 
1522  // Resize base image to each target mip level
1523  for( size_t level=1; level < levels; ++level )
1524  {
1525  if ( height <= 1 )
1526  {
1527  urow1 = urow0;
1528  vrow1 = vrow0;
1529  }
1530 
1531  if ( width <= 1 )
1532  {
1533  urow2 = urow0;
1534  urow3 = urow1;
1535  vrow2 = vrow0;
1536  vrow3 = vrow1;
1537  }
1538 
1539  if ( depth > 1 )
1540  {
1541  // 3D box filter
1542  size_t ndepth = depth >> 1;
1543 
1544  for( size_t slice=0; slice < ndepth; ++slice )
1545  {
1546  size_t slicea = std::min<size_t>( slice * 2, depth-1 );
1547  size_t sliceb = std::min<size_t>( slicea + 1, depth-1 );
1548 
1549  const Image* srca = mipChain.GetImage( level-1, 0, slicea );
1550  const Image* srcb = mipChain.GetImage( level-1, 0, sliceb );
1551  const Image* dest = mipChain.GetImage( level, 0, slice );
1552 
1553  if ( !srca || !srcb || !dest )
1554  return E_POINTER;
1555 
1556  const uint8_t* pSrc1 = srca->pixels;
1557  const uint8_t* pSrc2 = srcb->pixels;
1558  uint8_t* pDest = dest->pixels;
1559 
1560  size_t aRowPitch = srca->rowPitch;
1561  size_t bRowPitch = srcb->rowPitch;
1562 
1563  size_t nwidth = (width > 1) ? (width >> 1) : 1;
1564  size_t nheight = (height > 1) ? (height >> 1) : 1;
1565 
1566  for( size_t y = 0; y < nheight; ++y )
1567  {
1568  if ( !_LoadScanlineLinear( urow0, width, pSrc1, aRowPitch, srca->format, filter ) )
1569  return E_FAIL;
1570  pSrc1 += aRowPitch;
1571 
1572  if ( urow0 != urow1 )
1573  {
1574  if ( !_LoadScanlineLinear( urow1, width, pSrc1, aRowPitch, srca->format, filter ) )
1575  return E_FAIL;
1576  pSrc1 += aRowPitch;
1577  }
1578 
1579  if ( !_LoadScanlineLinear( vrow0, width, pSrc2, bRowPitch, srcb->format, filter ) )
1580  return E_FAIL;
1581  pSrc2 += bRowPitch;
1582 
1583  if ( vrow0 != vrow1 )
1584  {
1585  if ( !_LoadScanlineLinear( vrow1, width, pSrc2, bRowPitch, srcb->format, filter ) )
1586  return E_FAIL;
1587  pSrc2 += bRowPitch;
1588  }
1589 
1590  for( size_t x = 0; x < nwidth; ++x )
1591  {
1592  size_t x2 = x << 1;
1593 
1594  AVERAGE8( target[x], urow0[ x2 ], urow1[ x2 ], urow2[ x2 ], urow3[ x2 ],
1595  vrow0[ x2 ], vrow1[ x2 ], vrow2[ x2 ], vrow3[ x2 ] );
1596  }
1597 
1598  if ( !_StoreScanlineLinear( pDest, dest->rowPitch, dest->format, target, nwidth, filter ) )
1599  return E_FAIL;
1600  pDest += dest->rowPitch;
1601  }
1602  }
1603  }
1604  else
1605  {
1606  // 2D box filter
1607  const Image* src = mipChain.GetImage( level-1, 0, 0 );
1608  const Image* dest = mipChain.GetImage( level, 0, 0 );
1609 
1610  if ( !src || !dest )
1611  return E_POINTER;
1612 
1613  const uint8_t* pSrc = src->pixels;
1614  uint8_t* pDest = dest->pixels;
1615 
1616  size_t rowPitch = src->rowPitch;
1617 
1618  size_t nwidth = (width > 1) ? (width >> 1) : 1;
1619  size_t nheight = (height > 1) ? (height >> 1) : 1;
1620 
1621  for( size_t y = 0; y < nheight; ++y )
1622  {
1623  if ( !_LoadScanlineLinear( urow0, width, pSrc, rowPitch, src->format, filter ) )
1624  return E_FAIL;
1625  pSrc += rowPitch;
1626 
1627  if ( urow0 != urow1 )
1628  {
1629  if ( !_LoadScanlineLinear( urow1, width, pSrc, rowPitch, src->format, filter ) )
1630  return E_FAIL;
1631  pSrc += rowPitch;
1632  }
1633 
1634  for( size_t x = 0; x < nwidth; ++x )
1635  {
1636  size_t x2 = x << 1;
1637 
1638  AVERAGE4( target[ x ], urow0[ x2 ], urow1[ x2 ], urow2[ x2 ], urow3[ x2 ] );
1639  }
1640 
1641  if ( !_StoreScanlineLinear( pDest, dest->rowPitch, dest->format, target, nwidth, filter ) )
1642  return E_FAIL;
1643  pDest += dest->rowPitch;
1644  }
1645  }
1646 
1647  if ( height > 1 )
1648  height >>= 1;
1649 
1650  if ( width > 1 )
1651  width >>= 1;
1652 
1653  if ( depth > 1 )
1654  depth >>= 1;
1655  }
1656 
1657  return S_OK;
1658 }
1659 
1660 
1661 //--- 3D Linear Filter ---
1662 static HRESULT _Generate3DMipsLinearFilter( _In_ size_t depth, _In_ size_t levels, _In_ DWORD filter, _In_ const ScratchImage& mipChain )
1663 {
1664  if ( !depth || !mipChain.GetImages() )
1665  return E_INVALIDARG;
1666 
1667  // This assumes that the base images are already placed into the mipChain at the top level... (see _Setup3DMips)
1668 
1669  assert( levels > 1 );
1670 
1671  size_t width = mipChain.GetMetadata().width;
1672  size_t height = mipChain.GetMetadata().height;
1673 
1674  // Allocate temporary space (5 scanlines, plus X/Y/Z filters)
1675  ScopedAlignedArrayXMVECTOR scanline( reinterpret_cast<XMVECTOR*>( _aligned_malloc( (sizeof(XMVECTOR)*width*5), 16 ) ) );
1676  if ( !scanline )
1677  return E_OUTOFMEMORY;
1678 
1679  std::unique_ptr<LinearFilter[]> lf( new (std::nothrow) LinearFilter[ width+height+depth ] );
1680  if ( !lf )
1681  return E_OUTOFMEMORY;
1682 
1683  LinearFilter* lfX = lf.get();
1684  LinearFilter* lfY = lf.get() + width;
1685  LinearFilter* lfZ = lf.get() + width + height;
1686 
1687  XMVECTOR* target = scanline.get();
1688 
1689  XMVECTOR* urow0 = target + width;
1690  XMVECTOR* urow1 = target + width*2;
1691  XMVECTOR* vrow0 = target + width*3;
1692  XMVECTOR* vrow1 = target + width*4;
1693 
1694  // Resize base image to each target mip level
1695  for( size_t level=1; level < levels; ++level )
1696  {
1697  size_t nwidth = (width > 1) ? (width >> 1) : 1;
1698  _CreateLinearFilter( width, nwidth, (filter & TEX_FILTER_WRAP_U) != 0, lfX );
1699 
1700  size_t nheight = (height > 1) ? (height >> 1) : 1;
1701  _CreateLinearFilter( height, nheight, (filter & TEX_FILTER_WRAP_V) != 0, lfY );
1702 
1703 #ifdef _DEBUG
1704  memset( urow0, 0xCD, sizeof(XMVECTOR)*width );
1705  memset( urow1, 0xDD, sizeof(XMVECTOR)*width );
1706  memset( vrow0, 0xED, sizeof(XMVECTOR)*width );
1707  memset( vrow1, 0xFD, sizeof(XMVECTOR)*width );
1708 #endif
1709 
1710  if ( depth > 1 )
1711  {
1712  // 3D linear filter
1713  size_t ndepth = depth >> 1;
1714  _CreateLinearFilter( depth, ndepth, (filter & TEX_FILTER_WRAP_W) != 0, lfZ );
1715 
1716  for( size_t slice=0; slice < ndepth; ++slice )
1717  {
1718  auto& toZ = lfZ[ slice ];
1719 
1720  const Image* srca = mipChain.GetImage( level-1, 0, toZ.u0 );
1721  const Image* srcb = mipChain.GetImage( level-1, 0, toZ.u1 );
1722  if ( !srca || !srcb )
1723  return E_POINTER;
1724 
1725  size_t u0 = size_t(-1);
1726  size_t u1 = size_t(-1);
1727 
1728  const Image* dest = mipChain.GetImage( level, 0, slice );
1729  if ( !dest )
1730  return E_POINTER;
1731 
1732  uint8_t* pDest = dest->pixels;
1733 
1734  for( size_t y = 0; y < nheight; ++y )
1735  {
1736  auto& toY = lfY[ y ];
1737 
1738  if ( toY.u0 != u0 )
1739  {
1740  if ( toY.u0 != u1 )
1741  {
1742  u0 = toY.u0;
1743 
1744  if ( !_LoadScanlineLinear( urow0, width, srca->pixels + (srca->rowPitch * u0), srca->rowPitch, srca->format, filter )
1745  || !_LoadScanlineLinear( vrow0, width, srcb->pixels + (srcb->rowPitch * u0), srcb->rowPitch, srcb->format, filter ) )
1746  return E_FAIL;
1747  }
1748  else
1749  {
1750  u0 = u1;
1751  u1 = size_t(-1);
1752 
1753  std::swap( urow0, urow1 );
1754  std::swap( vrow0, vrow1 );
1755  }
1756  }
1757 
1758  if ( toY.u1 != u1 )
1759  {
1760  u1 = toY.u1;
1761 
1762  if ( !_LoadScanlineLinear( urow1, width, srca->pixels + (srca->rowPitch * u1), srca->rowPitch, srca->format, filter )
1763  || !_LoadScanlineLinear( vrow1, width, srcb->pixels + (srcb->rowPitch * u1), srcb->rowPitch, srcb->format, filter ) )
1764  return E_FAIL;
1765  }
1766 
1767  for( size_t x = 0; x < nwidth; ++x )
1768  {
1769  auto& toX = lfX[ x ];
1770 
1771  TRILINEAR_INTERPOLATE( target[x], toX, toY, toZ, urow0, urow1, vrow0, vrow1 );
1772  }
1773 
1774  if ( !_StoreScanlineLinear( pDest, dest->rowPitch, dest->format, target, nwidth, filter ) )
1775  return E_FAIL;
1776  pDest += dest->rowPitch;
1777  }
1778  }
1779  }
1780  else
1781  {
1782  // 2D linear filter
1783  const Image* src = mipChain.GetImage( level-1, 0, 0 );
1784  const Image* dest = mipChain.GetImage( level, 0, 0 );
1785 
1786  if ( !src || !dest )
1787  return E_POINTER;
1788 
1789  const uint8_t* pSrc = src->pixels;
1790  uint8_t* pDest = dest->pixels;
1791 
1792  size_t rowPitch = src->rowPitch;
1793 
1794  size_t u0 = size_t(-1);
1795  size_t u1 = size_t(-1);
1796 
1797  for( size_t y = 0; y < nheight; ++y )
1798  {
1799  auto& toY = lfY[ y ];
1800 
1801  if ( toY.u0 != u0 )
1802  {
1803  if ( toY.u0 != u1 )
1804  {
1805  u0 = toY.u0;
1806 
1807  if ( !_LoadScanlineLinear( urow0, width, pSrc + (rowPitch * u0), rowPitch, src->format, filter ) )
1808  return E_FAIL;
1809  }
1810  else
1811  {
1812  u0 = u1;
1813  u1 = size_t(-1);
1814 
1815  std::swap( urow0, urow1 );
1816  }
1817  }
1818 
1819  if ( toY.u1 != u1 )
1820  {
1821  u1 = toY.u1;
1822 
1823  if ( !_LoadScanlineLinear( urow1, width, pSrc + (rowPitch * u1), rowPitch, src->format, filter ) )
1824  return E_FAIL;
1825  }
1826 
1827  for( size_t x = 0; x < nwidth; ++x )
1828  {
1829  auto& toX = lfX[ x ];
1830 
1831  BILINEAR_INTERPOLATE( target[x], toX, toY, urow0, urow1 );
1832  }
1833 
1834  if ( !_StoreScanlineLinear( pDest, dest->rowPitch, dest->format, target, nwidth, filter ) )
1835  return E_FAIL;
1836  pDest += dest->rowPitch;
1837  }
1838  }
1839 
1840  if ( height > 1 )
1841  height >>= 1;
1842 
1843  if ( width > 1 )
1844  width >>= 1;
1845 
1846  if ( depth > 1 )
1847  depth >>= 1;
1848  }
1849 
1850  return S_OK;
1851 }
1852 
1853 
1854 //--- 3D Cubic Filter ---
1855 static HRESULT _Generate3DMipsCubicFilter( _In_ size_t depth, _In_ size_t levels, _In_ DWORD filter, _In_ const ScratchImage& mipChain )
1856 {
1857  if ( !depth || !mipChain.GetImages() )
1858  return E_INVALIDARG;
1859 
1860  // This assumes that the base images are already placed into the mipChain at the top level... (see _Setup3DMips)
1861 
1862  assert( levels > 1 );
1863 
1864  size_t width = mipChain.GetMetadata().width;
1865  size_t height = mipChain.GetMetadata().height;
1866 
1867  // Allocate temporary space (17 scanlines, plus X/Y/Z filters)
1868  ScopedAlignedArrayXMVECTOR scanline( reinterpret_cast<XMVECTOR*>( _aligned_malloc( (sizeof(XMVECTOR)*width*17), 16 ) ) );
1869  if ( !scanline )
1870  return E_OUTOFMEMORY;
1871 
1872  std::unique_ptr<CubicFilter[]> cf( new (std::nothrow) CubicFilter[ width+height+depth ] );
1873  if ( !cf )
1874  return E_OUTOFMEMORY;
1875 
1876  CubicFilter* cfX = cf.get();
1877  CubicFilter* cfY = cf.get() + width;
1878  CubicFilter* cfZ = cf.get() + width + height;
1879 
1880  XMVECTOR* target = scanline.get();
1881 
1882  XMVECTOR* urow[4];
1883  XMVECTOR* vrow[4];
1884  XMVECTOR* srow[4];
1885  XMVECTOR* trow[4];
1886 
1887  XMVECTOR *ptr = scanline.get() + width;
1888  for( size_t j = 0; j < 4; ++j )
1889  {
1890  urow[j] = ptr; ptr += width;
1891  vrow[j] = ptr; ptr += width;
1892  srow[j] = ptr; ptr += width;
1893  trow[j] = ptr; ptr += width;
1894  }
1895 
1896  // Resize base image to each target mip level
1897  for( size_t level=1; level < levels; ++level )
1898  {
1899  size_t nwidth = (width > 1) ? (width >> 1) : 1;
1900  _CreateCubicFilter( width, nwidth, (filter & TEX_FILTER_WRAP_U) != 0, (filter & TEX_FILTER_MIRROR_U) != 0, cfX );
1901 
1902  size_t nheight = (height > 1) ? (height >> 1) : 1;
1903  _CreateCubicFilter( height, nheight, (filter & TEX_FILTER_WRAP_V) != 0, (filter & TEX_FILTER_MIRROR_V) != 0, cfY );
1904 
1905 #ifdef _DEBUG
1906  for( size_t j = 0; j < 4; ++j )
1907  {
1908  memset( urow[j], 0xCD, sizeof(XMVECTOR)*width );
1909  memset( vrow[j], 0xDD, sizeof(XMVECTOR)*width );
1910  memset( srow[j], 0xED, sizeof(XMVECTOR)*width );
1911  memset( trow[j], 0xFD, sizeof(XMVECTOR)*width );
1912  }
1913 #endif
1914 
1915  if ( depth > 1 )
1916  {
1917  // 3D cubic filter
1918  size_t ndepth = depth >> 1;
1919  _CreateCubicFilter( depth, ndepth, (filter & TEX_FILTER_WRAP_W) != 0, (filter & TEX_FILTER_MIRROR_W) != 0, cfZ );
1920 
1921  for( size_t slice=0; slice < ndepth; ++slice )
1922  {
1923  auto& toZ = cfZ[ slice ];
1924 
1925  const Image* srca = mipChain.GetImage( level-1, 0, toZ.u0 );
1926  const Image* srcb = mipChain.GetImage( level-1, 0, toZ.u1 );
1927  const Image* srcc = mipChain.GetImage( level-1, 0, toZ.u2 );
1928  const Image* srcd = mipChain.GetImage( level-1, 0, toZ.u3 );
1929  if ( !srca || !srcb || !srcc || !srcd )
1930  return E_POINTER;
1931 
1932  size_t u0 = size_t(-1);
1933  size_t u1 = size_t(-1);
1934  size_t u2 = size_t(-1);
1935  size_t u3 = size_t(-1);
1936 
1937  const Image* dest = mipChain.GetImage( level, 0, slice );
1938  if ( !dest )
1939  return E_POINTER;
1940 
1941  uint8_t* pDest = dest->pixels;
1942 
1943  for( size_t y = 0; y < nheight; ++y )
1944  {
1945  auto& toY = cfY[ y ];
1946 
1947  // Scanline 1
1948  if ( toY.u0 != u0 )
1949  {
1950  if ( toY.u0 != u1 && toY.u0 != u2 && toY.u0 != u3 )
1951  {
1952  u0 = toY.u0;
1953 
1954  if ( !_LoadScanlineLinear( urow[0], width, srca->pixels + (srca->rowPitch * u0), srca->rowPitch, srca->format, filter )
1955  || !_LoadScanlineLinear( urow[1], width, srcb->pixels + (srcb->rowPitch * u0), srcb->rowPitch, srcb->format, filter )
1956  || !_LoadScanlineLinear( urow[2], width, srcc->pixels + (srcc->rowPitch * u0), srcc->rowPitch, srcc->format, filter )
1957  || !_LoadScanlineLinear( urow[3], width, srcd->pixels + (srcd->rowPitch * u0), srcd->rowPitch, srcd->format, filter ) )
1958  return E_FAIL;
1959  }
1960  else if ( toY.u0 == u1 )
1961  {
1962  u0 = u1;
1963  u1 = size_t(-1);
1964 
1965  std::swap( urow[0], vrow[0] );
1966  std::swap( urow[1], vrow[1] );
1967  std::swap( urow[2], vrow[2] );
1968  std::swap( urow[3], vrow[3] );
1969  }
1970  else if ( toY.u0 == u2 )
1971  {
1972  u0 = u2;
1973  u2 = size_t(-1);
1974 
1975  std::swap( urow[0], srow[0] );
1976  std::swap( urow[1], srow[1] );
1977  std::swap( urow[2], srow[2] );
1978  std::swap( urow[3], srow[3] );
1979  }
1980  else if ( toY.u0 == u3 )
1981  {
1982  u0 = u3;
1983  u3 = size_t(-1);
1984 
1985  std::swap( urow[0], trow[0] );
1986  std::swap( urow[1], trow[1] );
1987  std::swap( urow[2], trow[2] );
1988  std::swap( urow[3], trow[3] );
1989  }
1990  }
1991 
1992  // Scanline 2
1993  if ( toY.u1 != u1 )
1994  {
1995  if ( toY.u1 != u2 && toY.u1 != u3 )
1996  {
1997  u1 = toY.u1;
1998 
1999  if ( !_LoadScanlineLinear( vrow[0], width, srca->pixels + (srca->rowPitch * u1), srca->rowPitch, srca->format, filter )
2000  || !_LoadScanlineLinear( vrow[1], width, srcb->pixels + (srcb->rowPitch * u1), srcb->rowPitch, srcb->format, filter )
2001  || !_LoadScanlineLinear( vrow[2], width, srcc->pixels + (srcc->rowPitch * u1), srcc->rowPitch, srcc->format, filter )
2002  || !_LoadScanlineLinear( vrow[3], width, srcd->pixels + (srcd->rowPitch * u1), srcd->rowPitch, srcd->format, filter ) )
2003  return E_FAIL;
2004  }
2005  else if ( toY.u1 == u2 )
2006  {
2007  u1 = u2;
2008  u2 = size_t(-1);
2009 
2010  std::swap( vrow[0], srow[0] );
2011  std::swap( vrow[1], srow[1] );
2012  std::swap( vrow[2], srow[2] );
2013  std::swap( vrow[3], srow[3] );
2014  }
2015  else if ( toY.u1 == u3 )
2016  {
2017  u1 = u3;
2018  u3 = size_t(-1);
2019 
2020  std::swap( vrow[0], trow[0] );
2021  std::swap( vrow[1], trow[1] );
2022  std::swap( vrow[2], trow[2] );
2023  std::swap( vrow[3], trow[3] );
2024  }
2025  }
2026 
2027  // Scanline 3
2028  if ( toY.u2 != u2 )
2029  {
2030  if ( toY.u2 != u3 )
2031  {
2032  u2 = toY.u2;
2033 
2034  if ( !_LoadScanlineLinear( srow[0], width, srca->pixels + (srca->rowPitch * u2), srca->rowPitch, srca->format, filter )
2035  || !_LoadScanlineLinear( srow[1], width, srcb->pixels + (srcb->rowPitch * u2), srcb->rowPitch, srcb->format, filter )
2036  || !_LoadScanlineLinear( srow[2], width, srcc->pixels + (srcc->rowPitch * u2), srcc->rowPitch, srcc->format, filter )
2037  || !_LoadScanlineLinear( srow[3], width, srcd->pixels + (srcd->rowPitch * u2), srcd->rowPitch, srcd->format, filter ) )
2038  return E_FAIL;
2039  }
2040  else
2041  {
2042  u2 = u3;
2043  u3 = size_t(-1);
2044 
2045  std::swap( srow[0], trow[0] );
2046  std::swap( srow[1], trow[1] );
2047  std::swap( srow[2], trow[2] );
2048  std::swap( srow[3], trow[3] );
2049  }
2050  }
2051 
2052  // Scanline 4
2053  if ( toY.u3 != u3 )
2054  {
2055  u3 = toY.u3;
2056 
2057  if ( !_LoadScanlineLinear( trow[0], width, srca->pixels + (srca->rowPitch * u3), srca->rowPitch, srca->format, filter )
2058  || !_LoadScanlineLinear( trow[1], width, srcb->pixels + (srcb->rowPitch * u3), srcb->rowPitch, srcb->format, filter )
2059  || !_LoadScanlineLinear( trow[2], width, srcc->pixels + (srcc->rowPitch * u3), srcc->rowPitch, srcc->format, filter )
2060  || !_LoadScanlineLinear( trow[3], width, srcd->pixels + (srcd->rowPitch * u3), srcd->rowPitch, srcd->format, filter ) )
2061  return E_FAIL;
2062  }
2063 
2064  for( size_t x = 0; x < nwidth; ++x )
2065  {
2066  auto& toX = cfX[ x ];
2067 
2068  XMVECTOR D[4];
2069 
2070  for( size_t j=0; j < 4; ++j )
2071  {
2072  XMVECTOR C0, C1, C2, C3;
2073  CUBIC_INTERPOLATE( C0, toX.x, urow[j][ toX.u0 ], urow[j][ toX.u1 ], urow[j][ toX.u2 ], urow[j][ toX.u3 ] );
2074  CUBIC_INTERPOLATE( C1, toX.x, vrow[j][ toX.u0 ], vrow[j][ toX.u1 ], vrow[j][ toX.u2 ], vrow[j][ toX.u3 ] );
2075  CUBIC_INTERPOLATE( C2, toX.x, srow[j][ toX.u0 ], srow[j][ toX.u1 ], srow[j][ toX.u2 ], srow[j][ toX.u3 ] );
2076  CUBIC_INTERPOLATE( C3, toX.x, trow[j][ toX.u0 ], trow[j][ toX.u1 ], trow[j][ toX.u2 ], trow[j][ toX.u3 ] );
2077 
2078  CUBIC_INTERPOLATE( D[j], toY.x, C0, C1, C2, C3 );
2079  }
2080 
2081  CUBIC_INTERPOLATE( target[x], toZ.x, D[0], D[1], D[2], D[3] );
2082  }
2083 
2084  if ( !_StoreScanlineLinear( pDest, dest->rowPitch, dest->format, target, nwidth, filter ) )
2085  return E_FAIL;
2086  pDest += dest->rowPitch;
2087  }
2088  }
2089  }
2090  else
2091  {
2092  // 2D cubic filter
2093  const Image* src = mipChain.GetImage( level-1, 0, 0 );
2094  const Image* dest = mipChain.GetImage( level, 0, 0 );
2095 
2096  if ( !src || !dest )
2097  return E_POINTER;
2098 
2099  const uint8_t* pSrc = src->pixels;
2100  uint8_t* pDest = dest->pixels;
2101 
2102  size_t rowPitch = src->rowPitch;
2103 
2104  size_t u0 = size_t(-1);
2105  size_t u1 = size_t(-1);
2106  size_t u2 = size_t(-1);
2107  size_t u3 = size_t(-1);
2108 
2109  for( size_t y = 0; y < nheight; ++y )
2110  {
2111  auto& toY = cfY[ y ];
2112 
2113  // Scanline 1
2114  if ( toY.u0 != u0 )
2115  {
2116  if ( toY.u0 != u1 && toY.u0 != u2 && toY.u0 != u3 )
2117  {
2118  u0 = toY.u0;
2119 
2120  if ( !_LoadScanlineLinear( urow[0], width, pSrc + (rowPitch * u0), rowPitch, src->format, filter ) )
2121  return E_FAIL;
2122  }
2123  else if ( toY.u0 == u1 )
2124  {
2125  u0 = u1;
2126  u1 = size_t(-1);
2127 
2128  std::swap( urow[0], vrow[0] );
2129  }
2130  else if ( toY.u0 == u2 )
2131  {
2132  u0 = u2;
2133  u2 = size_t(-1);
2134 
2135  std::swap( urow[0], srow[0] );
2136  }
2137  else if ( toY.u0 == u3 )
2138  {
2139  u0 = u3;
2140  u3 = size_t(-1);
2141 
2142  std::swap( urow[0], trow[0] );
2143  }
2144  }
2145 
2146  // Scanline 2
2147  if ( toY.u1 != u1 )
2148  {
2149  if ( toY.u1 != u2 && toY.u1 != u3 )
2150  {
2151  u1 = toY.u1;
2152 
2153  if ( !_LoadScanlineLinear( vrow[0], width, pSrc + (rowPitch * u1), rowPitch, src->format, filter ) )
2154  return E_FAIL;
2155  }
2156  else if ( toY.u1 == u2 )
2157  {
2158  u1 = u2;
2159  u2 = size_t(-1);
2160 
2161  std::swap( vrow[0], srow[0] );
2162  }
2163  else if ( toY.u1 == u3 )
2164  {
2165  u1 = u3;
2166  u3 = size_t(-1);
2167 
2168  std::swap( vrow[0], trow[0] );
2169  }
2170  }
2171 
2172  // Scanline 3
2173  if ( toY.u2 != u2 )
2174  {
2175  if ( toY.u2 != u3 )
2176  {
2177  u2 = toY.u2;
2178 
2179  if ( !_LoadScanlineLinear( srow[0], width, pSrc + (rowPitch * u2), rowPitch, src->format, filter ) )
2180  return E_FAIL;
2181  }
2182  else
2183  {
2184  u2 = u3;
2185  u3 = size_t(-1);
2186 
2187  std::swap( srow[0], trow[0] );
2188  }
2189  }
2190 
2191  // Scanline 4
2192  if ( toY.u3 != u3 )
2193  {
2194  u3 = toY.u3;
2195 
2196  if ( !_LoadScanlineLinear( trow[0], width, pSrc + (rowPitch * u3), rowPitch, src->format, filter ) )
2197  return E_FAIL;
2198  }
2199 
2200  for( size_t x = 0; x < nwidth; ++x )
2201  {
2202  auto& toX = cfX[ x ];
2203 
2204  XMVECTOR C0, C1, C2, C3;
2205  CUBIC_INTERPOLATE( C0, toX.x, urow[0][ toX.u0 ], urow[0][ toX.u1 ], urow[0][ toX.u2 ], urow[0][ toX.u3 ] );
2206  CUBIC_INTERPOLATE( C1, toX.x, vrow[0][ toX.u0 ], vrow[0][ toX.u1 ], vrow[0][ toX.u2 ], vrow[0][ toX.u3 ] );
2207  CUBIC_INTERPOLATE( C2, toX.x, srow[0][ toX.u0 ], srow[0][ toX.u1 ], srow[0][ toX.u2 ], srow[0][ toX.u3 ] );
2208  CUBIC_INTERPOLATE( C3, toX.x, trow[0][ toX.u0 ], trow[0][ toX.u1 ], trow[0][ toX.u2 ], trow[0][ toX.u3 ] );
2209 
2210  CUBIC_INTERPOLATE( target[x], toY.x, C0, C1, C2, C3 );
2211  }
2212 
2213  if ( !_StoreScanlineLinear( pDest, dest->rowPitch, dest->format, target, nwidth, filter ) )
2214  return E_FAIL;
2215  pDest += dest->rowPitch;
2216  }
2217  }
2218 
2219  if ( height > 1 )
2220  height >>= 1;
2221 
2222  if ( width > 1 )
2223  width >>= 1;
2224 
2225  if ( depth > 1 )
2226  depth >>= 1;
2227  }
2228 
2229  return S_OK;
2230 }
2231 
2232 
2233 //--- 3D Triangle Filter ---
2234 static HRESULT _Generate3DMipsTriangleFilter( _In_ size_t depth, _In_ size_t levels, _In_ DWORD filter, _In_ const ScratchImage& mipChain )
2235 {
2236  if ( !depth || !mipChain.GetImages() )
2237  return E_INVALIDARG;
2238 
2239  using namespace TriangleFilter;
2240 
2241  // This assumes that the base images are already placed into the mipChain at the top level... (see _Setup3DMips)
2242 
2243  assert( levels > 1 );
2244 
2245  size_t width = mipChain.GetMetadata().width;
2246  size_t height = mipChain.GetMetadata().height;
2247 
2248  // Allocate initial temporary space (1 scanline, accumulation rows, plus X/Y/Z filters)
2249  ScopedAlignedArrayXMVECTOR scanline( reinterpret_cast<XMVECTOR*>( _aligned_malloc( sizeof(XMVECTOR) * width, 16 ) ) );
2250  if ( !scanline )
2251  return E_OUTOFMEMORY;
2252 
2253  std::unique_ptr<TriangleRow[]> sliceActive( new (std::nothrow) TriangleRow[ depth ] );
2254  if ( !sliceActive )
2255  return E_OUTOFMEMORY;
2256 
2257  TriangleRow * sliceFree = nullptr;
2258 
2259  std::unique_ptr<Filter> tfX, tfY, tfZ;
2260 
2261  XMVECTOR* row = scanline.get();
2262 
2263  // Resize base image to each target mip level
2264  for( size_t level=1; level < levels; ++level )
2265  {
2266  size_t nwidth = (width > 1) ? (width >> 1) : 1;
2267  HRESULT hr = _Create( width, nwidth, (filter & TEX_FILTER_WRAP_U) != 0, tfX );
2268  if ( FAILED(hr) )
2269  return hr;
2270 
2271  size_t nheight = (height > 1) ? (height >> 1) : 1;
2272  hr = _Create( height, nheight, (filter & TEX_FILTER_WRAP_V) != 0, tfY );
2273  if ( FAILED(hr) )
2274  return hr;
2275 
2276  size_t ndepth = (depth > 1 ) ? (depth >> 1) : 1;
2277  hr = _Create( depth, ndepth, (filter & TEX_FILTER_WRAP_W) != 0, tfZ );
2278  if ( FAILED(hr) )
2279  return hr;
2280 
2281 #ifdef _DEBUG
2282  memset( row, 0xCD, sizeof(XMVECTOR)*width );
2283 #endif
2284 
2285  auto xFromEnd = reinterpret_cast<const FilterFrom*>( reinterpret_cast<const uint8_t*>( tfX.get() ) + tfX->sizeInBytes );
2286  auto yFromEnd = reinterpret_cast<const FilterFrom*>( reinterpret_cast<const uint8_t*>( tfY.get() ) + tfY->sizeInBytes );
2287  auto zFromEnd = reinterpret_cast<const FilterFrom*>( reinterpret_cast<const uint8_t*>( tfZ.get() ) + tfZ->sizeInBytes );
2288 
2289  // Count times slices get written (and clear out any leftover accumulation slices from last miplevel)
2290  for( FilterFrom* zFrom = tfZ->from; zFrom < zFromEnd; )
2291  {
2292  for ( size_t j = 0; j < zFrom->count; ++j )
2293  {
2294  size_t w = zFrom->to[ j ].u;
2295  assert( w < ndepth );
2296  TriangleRow* sliceAcc = &sliceActive.get()[ w ];
2297 
2298  ++sliceAcc->remaining;
2299 
2300  if ( sliceAcc->scanline )
2301  {
2302  memset( sliceAcc->scanline.get(), 0, sizeof(XMVECTOR) * nwidth * nheight );
2303  }
2304  }
2305 
2306  zFrom = reinterpret_cast<FilterFrom*>( reinterpret_cast<uint8_t*>( zFrom ) + zFrom->sizeInBytes );
2307  }
2308 
2309  // Filter image
2310  size_t z = 0;
2311  for( FilterFrom* zFrom = tfZ->from; zFrom < zFromEnd; ++z )
2312  {
2313  // Create accumulation slices as needed
2314  for ( size_t j = 0; j < zFrom->count; ++j )
2315  {
2316  size_t w = zFrom->to[ j ].u;
2317  assert( w < ndepth );
2318  TriangleRow* sliceAcc = &sliceActive.get()[ w ];
2319 
2320  if ( !sliceAcc->scanline )
2321  {
2322  if ( sliceFree )
2323  {
2324  // Steal and reuse scanline from 'free slice' list
2325  // (it will always be at least as large as nwidth*nheight due to loop decending order)
2326  assert( sliceFree->scanline != 0 );
2327  sliceAcc->scanline.reset( sliceFree->scanline.release() );
2328  sliceFree = sliceFree->next;
2329  }
2330  else
2331  {
2332  size_t bytes = sizeof(XMVECTOR) * nwidth * nheight;
2333  sliceAcc->scanline.reset( reinterpret_cast<XMVECTOR*>( _aligned_malloc( bytes, 16 ) ) );
2334  if ( !sliceAcc->scanline )
2335  return E_OUTOFMEMORY;
2336  }
2337 
2338  memset( sliceAcc->scanline.get(), 0, sizeof(XMVECTOR) * nwidth * nheight );
2339  }
2340  }
2341 
2342  assert( z < depth );
2343  const Image* src = mipChain.GetImage( level-1, 0, z );
2344  if ( !src )
2345  return E_POINTER;
2346 
2347  const uint8_t* pSrc = src->pixels;
2348  size_t rowPitch = src->rowPitch;
2349  const uint8_t* pEndSrc = pSrc + rowPitch * height;
2350 
2351  for( FilterFrom* yFrom = tfY->from; yFrom < yFromEnd; )
2352  {
2353  // Load source scanline
2354  if ( (pSrc + rowPitch) > pEndSrc )
2355  return E_FAIL;
2356 
2357  if ( !_LoadScanlineLinear( row, width, pSrc, rowPitch, src->format, filter ) )
2358  return E_FAIL;
2359 
2360  pSrc += rowPitch;
2361 
2362  // Process row
2363  size_t x = 0;
2364  for( FilterFrom* xFrom = tfX->from; xFrom < xFromEnd; ++x )
2365  {
2366  for ( size_t j = 0; j < zFrom->count; ++j )
2367  {
2368  size_t w = zFrom->to[ j ].u;
2369  assert( w < ndepth );
2370  float zweight = zFrom->to[ j ].weight;
2371 
2372  XMVECTOR* accSlice = sliceActive[ w ].scanline.get();
2373  if ( !accSlice )
2374  return E_POINTER;
2375 
2376  for ( size_t k = 0; k < yFrom->count; ++k )
2377  {
2378  size_t v = yFrom->to[ k ].u;
2379  assert( v < nheight );
2380  float yweight = yFrom->to[ k ].weight;
2381 
2382  XMVECTOR * accPtr = accSlice + v * nwidth;
2383 
2384  for ( size_t l = 0; l < xFrom->count; ++l )
2385  {
2386  size_t u = xFrom->to[ l ].u;
2387  assert( u < nwidth );
2388 
2389  XMVECTOR weight = XMVectorReplicate( zweight * yweight * xFrom->to[ l ].weight );
2390 
2391  assert( x < width );
2392  accPtr[ u ] = XMVectorMultiplyAdd( row[ x ], weight, accPtr[ u ] );
2393  }
2394  }
2395  }
2396 
2397  xFrom = reinterpret_cast<FilterFrom*>( reinterpret_cast<uint8_t*>( xFrom ) + xFrom->sizeInBytes );
2398  }
2399 
2400  yFrom = reinterpret_cast<FilterFrom*>( reinterpret_cast<uint8_t*>( yFrom ) + yFrom->sizeInBytes );
2401  }
2402 
2403  // Write completed accumulation slices
2404  for ( size_t j = 0; j < zFrom->count; ++j )
2405  {
2406  size_t w = zFrom->to[ j ].u;
2407  assert( w < ndepth );
2408  TriangleRow* sliceAcc = &sliceActive.get()[ w ];
2409 
2410  assert( sliceAcc->remaining > 0 );
2411  --sliceAcc->remaining;
2412 
2413  if ( !sliceAcc->remaining )
2414  {
2415  const Image* dest = mipChain.GetImage( level, 0, w );
2416  XMVECTOR* pAccSrc = sliceAcc->scanline.get();
2417  if ( !dest || !pAccSrc )
2418  return E_POINTER;
2419 
2420  uint8_t* pDest = dest->pixels;
2421 
2422  for( size_t h = 0; h < nheight; ++h )
2423  {
2424  switch( dest->format )
2425  {
2426  case DXGI_FORMAT_R10G10B10A2_UNORM:
2427  case DXGI_FORMAT_R10G10B10A2_UINT:
2428  {
2429  // Need to slightly bias results for floating-point error accumulation which can
2430  // be visible with harshly quantized values
2431  static const XMVECTORF32 Bias = { 0.f, 0.f, 0.f, 0.1f };
2432 
2433  XMVECTOR* ptr = pAccSrc;
2434  for( size_t i=0; i < dest->width; ++i, ++ptr )
2435  {
2436  *ptr = XMVectorAdd( *ptr, Bias );
2437  }
2438  }
2439  break;
2440  }
2441 
2442  // This performs any required clamping
2443  if ( !_StoreScanlineLinear( pDest, dest->rowPitch, dest->format, pAccSrc, dest->width, filter ) )
2444  return E_FAIL;
2445 
2446  pDest += dest->rowPitch;
2447  pAccSrc += nwidth;
2448  }
2449 
2450  // Put slice on freelist to reuse it's allocated scanline
2451  sliceAcc->next = sliceFree;
2452  sliceFree = sliceAcc;
2453  }
2454  }
2455 
2456  zFrom = reinterpret_cast<FilterFrom*>( reinterpret_cast<uint8_t*>( zFrom ) + zFrom->sizeInBytes );
2457  }
2458 
2459  if ( height > 1 )
2460  height >>= 1;
2461 
2462  if ( width > 1 )
2463  width >>= 1;
2464 
2465  if ( depth > 1 )
2466  depth >>= 1;
2467  }
2468 
2469  return S_OK;
2470 }
2471 
2472 
2473 //=====================================================================================
2474 // Entry-points
2475 //=====================================================================================
2476 
2477 //-------------------------------------------------------------------------------------
2478 // Generate mipmap chain
2479 //-------------------------------------------------------------------------------------
2480 _Use_decl_annotations_
2481 HRESULT GenerateMipMaps( const Image& baseImage, DWORD filter, size_t levels, ScratchImage& mipChain, bool allow1D )
2482 {
2483  if ( !IsValid( baseImage.format ) )
2484  return E_INVALIDARG;
2485 
2486  if ( !baseImage.pixels )
2487  return E_POINTER;
2488 
2489  if ( !_CalculateMipLevels(baseImage.width, baseImage.height, levels) )
2490  return E_INVALIDARG;
2491 
2492  if ( IsCompressed(baseImage.format) || IsTypeless(baseImage.format) || IsPlanar(baseImage.format) || IsPalettized(baseImage.format) )
2493  {
2494  return HRESULT_FROM_WIN32( ERROR_NOT_SUPPORTED );
2495  }
2496 
2497  HRESULT hr;
2498 
2499  static_assert( TEX_FILTER_POINT == 0x100000, "TEX_FILTER_ flag values don't match TEX_FILTER_MASK" );
2500 
2501  if ( _UseWICFiltering( baseImage.format, filter ) )
2502  {
2503  //--- Use WIC filtering to generate mipmaps -----------------------------------
2504  switch(filter & TEX_FILTER_MASK)
2505  {
2506  case 0:
2507  case TEX_FILTER_POINT:
2508  case TEX_FILTER_FANT: // Equivalent to Box filter
2509  case TEX_FILTER_LINEAR:
2510  case TEX_FILTER_CUBIC:
2511  {
2512  static_assert( TEX_FILTER_FANT == TEX_FILTER_BOX, "TEX_FILTER_ flag alias mismatch" );
2513 
2514  WICPixelFormatGUID pfGUID;
2515  if ( _DXGIToWIC( baseImage.format, pfGUID, true ) )
2516  {
2517  // Case 1: Base image format is supported by Windows Imaging Component
2518  hr = (baseImage.height > 1 || !allow1D)
2519  ? mipChain.Initialize2D( baseImage.format, baseImage.width, baseImage.height, 1, levels )
2520  : mipChain.Initialize1D( baseImage.format, baseImage.width, 1, levels );
2521  if ( FAILED(hr) )
2522  return hr;
2523 
2524  return _GenerateMipMapsUsingWIC( baseImage, filter, levels, pfGUID, mipChain, 0 );
2525  }
2526  else
2527  {
2528  // Case 2: Base image format is not supported by WIC, so we have to convert, generate, and convert back
2529  assert( baseImage.format != DXGI_FORMAT_R32G32B32A32_FLOAT );
2530  ScratchImage temp;
2531  hr = _ConvertToR32G32B32A32( baseImage, temp );
2532  if ( FAILED(hr) )
2533  return hr;
2534 
2535  const Image *timg = temp.GetImage( 0, 0, 0 );
2536  if ( !timg )
2537  return E_POINTER;
2538 
2539  ScratchImage tMipChain;
2540  hr = _GenerateMipMapsUsingWIC( *timg, filter, levels, GUID_WICPixelFormat128bppRGBAFloat, tMipChain, 0 );
2541  if ( FAILED(hr) )
2542  return hr;
2543 
2544  temp.Release();
2545 
2546  return _ConvertFromR32G32B32A32( tMipChain.GetImages(), tMipChain.GetImageCount(), tMipChain.GetMetadata(), baseImage.format, mipChain );
2547  }
2548  }
2549  break;
2550 
2551  default:
2552  return HRESULT_FROM_WIN32( ERROR_NOT_SUPPORTED );
2553  }
2554  }
2555  else
2556  {
2557  //--- Use custom filters to generate mipmaps ----------------------------------
2558  TexMetadata mdata;
2559  memset( &mdata, 0, sizeof(mdata) );
2560  mdata.width = baseImage.width;
2561  if ( baseImage.height > 1 || !allow1D )
2562  {
2563  mdata.height = baseImage.height;
2565  }
2566  else
2567  {
2568  mdata.height = 1;
2570  }
2571  mdata.depth = mdata.arraySize = 1;
2572  mdata.mipLevels = levels;
2573  mdata.format = baseImage.format;
2574 
2575  DWORD filter_select = ( filter & TEX_FILTER_MASK );
2576  if ( !filter_select )
2577  {
2578  // Default filter choice
2579  filter_select = ( ispow2(baseImage.width) && ispow2(baseImage.height) ) ? TEX_FILTER_BOX : TEX_FILTER_LINEAR;
2580  }
2581 
2582  switch( filter_select )
2583  {
2584  case TEX_FILTER_BOX:
2585  hr = _Setup2DMips( &baseImage, 1, mdata, mipChain );
2586  if ( FAILED(hr) )
2587  return hr;
2588 
2589  hr = _Generate2DMipsBoxFilter( levels, filter, mipChain, 0 );
2590  if ( FAILED(hr) )
2591  mipChain.Release();
2592  return hr;
2593 
2594  case TEX_FILTER_POINT:
2595  hr = _Setup2DMips( &baseImage, 1, mdata, mipChain );
2596  if ( FAILED(hr) )
2597  return hr;
2598 
2599  hr = _Generate2DMipsPointFilter( levels, mipChain, 0 );
2600  if ( FAILED(hr) )
2601  mipChain.Release();
2602  return hr;
2603 
2604  case TEX_FILTER_LINEAR:
2605  hr = _Setup2DMips( &baseImage, 1, mdata, mipChain );
2606  if ( FAILED(hr) )
2607  return hr;
2608 
2609  hr = _Generate2DMipsLinearFilter( levels, filter, mipChain, 0 );
2610  if ( FAILED(hr) )
2611  mipChain.Release();
2612  return hr;
2613 
2614  case TEX_FILTER_CUBIC:
2615  hr = _Setup2DMips( &baseImage, 1, mdata, mipChain );
2616  if ( FAILED(hr) )
2617  return hr;
2618 
2619  hr = _Generate2DMipsCubicFilter( levels, filter, mipChain, 0 );
2620  if ( FAILED(hr) )
2621  mipChain.Release();
2622  return hr;
2623 
2624  case TEX_FILTER_TRIANGLE:
2625  hr = _Setup2DMips( &baseImage, 1, mdata, mipChain );
2626  if ( FAILED(hr) )
2627  return hr;
2628 
2629  hr = _Generate2DMipsTriangleFilter( levels, filter, mipChain, 0 );
2630  if ( FAILED(hr) )
2631  mipChain.Release();
2632  return hr;
2633 
2634  default:
2635  return HRESULT_FROM_WIN32( ERROR_NOT_SUPPORTED );
2636  }
2637  }
2638 }
2639 
2640 _Use_decl_annotations_
2641 HRESULT GenerateMipMaps( const Image* srcImages, size_t nimages, const TexMetadata& metadata,
2642  DWORD filter, size_t levels, ScratchImage& mipChain )
2643 {
2644  if ( !srcImages || !nimages || !IsValid(metadata.format) )
2645  return E_INVALIDARG;
2646 
2647  if ( metadata.IsVolumemap()
2648  || IsCompressed(metadata.format) || IsTypeless(metadata.format) || IsPlanar(metadata.format) || IsPalettized(metadata.format) )
2649  return HRESULT_FROM_WIN32( ERROR_NOT_SUPPORTED );
2650 
2651  if ( !_CalculateMipLevels(metadata.width, metadata.height, levels) )
2652  return E_INVALIDARG;
2653 
2654  std::vector<const Image> baseImages;
2655  baseImages.reserve( metadata.arraySize );
2656  for( size_t item=0; item < metadata.arraySize; ++item )
2657  {
2658  size_t index = metadata.ComputeIndex( 0, item, 0);
2659  if ( index >= nimages )
2660  return E_FAIL;
2661 
2662  const Image& src = srcImages[ index ];
2663  if ( !src.pixels )
2664  return E_POINTER;
2665 
2666  if ( src.format != metadata.format || src.width != metadata.width || src.height != metadata.height )
2667  {
2668  // All base images must be the same format, width, and height
2669  return E_FAIL;
2670  }
2671 
2672  baseImages.push_back( src );
2673  }
2674 
2675  assert( baseImages.size() == metadata.arraySize );
2676 
2677  HRESULT hr;
2678 
2679  static_assert( TEX_FILTER_POINT == 0x100000, "TEX_FILTER_ flag values don't match TEX_FILTER_MASK" );
2680 
2681  if ( _UseWICFiltering( metadata.format, filter ) )
2682  {
2683  //--- Use WIC filtering to generate mipmaps -----------------------------------
2684  switch(filter & TEX_FILTER_MASK)
2685  {
2686  case 0:
2687  case TEX_FILTER_POINT:
2688  case TEX_FILTER_FANT: // Equivalent to Box filter
2689  case TEX_FILTER_LINEAR:
2690  case TEX_FILTER_CUBIC:
2691  {
2692  static_assert( TEX_FILTER_FANT == TEX_FILTER_BOX, "TEX_FILTER_ flag alias mismatch" );
2693 
2694  WICPixelFormatGUID pfGUID;
2695  if ( _DXGIToWIC( metadata.format, pfGUID, true ) )
2696  {
2697  // Case 1: Base image format is supported by Windows Imaging Component
2698  TexMetadata mdata2 = metadata;
2699  mdata2.mipLevels = levels;
2700  hr = mipChain.Initialize( mdata2 );
2701  if ( FAILED(hr) )
2702  return hr;
2703 
2704  for( size_t item = 0; item < metadata.arraySize; ++item )
2705  {
2706  hr = _GenerateMipMapsUsingWIC( baseImages[item], filter, levels, pfGUID, mipChain, item );
2707  if ( FAILED(hr) )
2708  {
2709  mipChain.Release();
2710  return hr;
2711  }
2712  }
2713 
2714  return S_OK;
2715  }
2716  else
2717  {
2718  // Case 2: Base image format is not supported by WIC, so we have to convert, generate, and convert back
2719  assert( metadata.format != DXGI_FORMAT_R32G32B32A32_FLOAT );
2720 
2721  TexMetadata mdata2 = metadata;
2722  mdata2.mipLevels = levels;
2723  mdata2.format = DXGI_FORMAT_R32G32B32A32_FLOAT;
2724  ScratchImage tMipChain;
2725  hr = tMipChain.Initialize( mdata2 );
2726  if ( FAILED(hr) )
2727  return hr;
2728 
2729  for( size_t item = 0; item < metadata.arraySize; ++item )
2730  {
2731  ScratchImage temp;
2732  hr = _ConvertToR32G32B32A32( baseImages[item], temp );
2733  if ( FAILED(hr) )
2734  return hr;
2735 
2736  const Image *timg = temp.GetImage( 0, 0, 0 );
2737  if ( !timg )
2738  return E_POINTER;
2739 
2740  hr = _GenerateMipMapsUsingWIC( *timg, filter, levels, GUID_WICPixelFormat128bppRGBAFloat, tMipChain, item );
2741  if ( FAILED(hr) )
2742  return hr;
2743  }
2744 
2745  return _ConvertFromR32G32B32A32( tMipChain.GetImages(), tMipChain.GetImageCount(), tMipChain.GetMetadata(), metadata.format, mipChain );
2746  }
2747  }
2748  break;
2749 
2750  default:
2751  return HRESULT_FROM_WIN32( ERROR_NOT_SUPPORTED );
2752  }
2753  }
2754  else
2755  {
2756  //--- Use custom filters to generate mipmaps ----------------------------------
2757  TexMetadata mdata2 = metadata;
2758  mdata2.mipLevels = levels;
2759 
2760  DWORD filter_select = ( filter & TEX_FILTER_MASK );
2761  if ( !filter_select )
2762  {
2763  // Default filter choice
2764  filter_select = ( ispow2(metadata.width) && ispow2(metadata.height) ) ? TEX_FILTER_BOX : TEX_FILTER_LINEAR;
2765  }
2766 
2767  switch( filter_select )
2768  {
2769  case TEX_FILTER_BOX:
2770  hr = _Setup2DMips( &baseImages[0], metadata.arraySize, mdata2, mipChain );
2771  if ( FAILED(hr) )
2772  return hr;
2773 
2774  for( size_t item = 0; item < metadata.arraySize; ++item )
2775  {
2776  hr = _Generate2DMipsBoxFilter( levels, filter, mipChain, item );
2777  if ( FAILED(hr) )
2778  mipChain.Release();
2779  }
2780  return hr;
2781 
2782  case TEX_FILTER_POINT:
2783  hr = _Setup2DMips( &baseImages[0], metadata.arraySize, mdata2, mipChain );
2784  if ( FAILED(hr) )
2785  return hr;
2786 
2787  for( size_t item = 0; item < metadata.arraySize; ++item )
2788  {
2789  hr = _Generate2DMipsPointFilter( levels, mipChain, item );
2790  if ( FAILED(hr) )
2791  mipChain.Release();
2792  }
2793  return hr;
2794 
2795  case TEX_FILTER_LINEAR:
2796  hr = _Setup2DMips( &baseImages[0], metadata.arraySize, mdata2, mipChain );
2797  if ( FAILED(hr) )
2798  return hr;
2799 
2800  for( size_t item = 0; item < metadata.arraySize; ++item )
2801  {
2802  hr = _Generate2DMipsLinearFilter( levels, filter, mipChain, item );
2803  if ( FAILED(hr) )
2804  mipChain.Release();
2805  }
2806  return hr;
2807 
2808  case TEX_FILTER_CUBIC:
2809  hr = _Setup2DMips( &baseImages[0], metadata.arraySize, mdata2, mipChain );
2810  if ( FAILED(hr) )
2811  return hr;
2812 
2813  for( size_t item = 0; item < metadata.arraySize; ++item )
2814  {
2815  hr = _Generate2DMipsCubicFilter( levels, filter, mipChain, item );
2816  if ( FAILED(hr) )
2817  mipChain.Release();
2818  }
2819  return hr;
2820 
2821  case TEX_FILTER_TRIANGLE:
2822  hr = _Setup2DMips( &baseImages[0], metadata.arraySize, mdata2, mipChain );
2823  if ( FAILED(hr) )
2824  return hr;
2825 
2826  for( size_t item = 0; item < metadata.arraySize; ++item )
2827  {
2828  hr = _Generate2DMipsTriangleFilter( levels, filter, mipChain, item );
2829  if ( FAILED(hr) )
2830  mipChain.Release();
2831  }
2832  return hr;
2833 
2834  default:
2835  return HRESULT_FROM_WIN32( ERROR_NOT_SUPPORTED );
2836  }
2837  }
2838 }
2839 
2840 
2841 //-------------------------------------------------------------------------------------
2842 // Generate mipmap chain for volume texture
2843 //-------------------------------------------------------------------------------------
2844 _Use_decl_annotations_
2845 HRESULT GenerateMipMaps3D( const Image* baseImages, size_t depth, DWORD filter, size_t levels, ScratchImage& mipChain )
2846 {
2847  if ( !baseImages || !depth )
2848  return E_INVALIDARG;
2849 
2850  if ( filter & TEX_FILTER_FORCE_WIC )
2851  return HRESULT_FROM_WIN32( ERROR_NOT_SUPPORTED );
2852 
2853  DXGI_FORMAT format = baseImages[0].format;
2854  size_t width = baseImages[0].width;
2855  size_t height = baseImages[0].height;
2856 
2857  if ( !_CalculateMipLevels3D(width, height, depth, levels) )
2858  return E_INVALIDARG;
2859 
2860  for( size_t slice=0; slice < depth; ++slice )
2861  {
2862  if ( !baseImages[slice].pixels )
2863  return E_POINTER;
2864 
2865  if ( baseImages[slice].format != format || baseImages[slice].width != width || baseImages[slice].height != height )
2866  {
2867  // All base images must be the same format, width, and height
2868  return E_FAIL;
2869  }
2870  }
2871 
2872  if ( IsCompressed(format) || IsTypeless(format) || IsPlanar(format) || IsPalettized(format) )
2873  return HRESULT_FROM_WIN32( ERROR_NOT_SUPPORTED );
2874 
2875  static_assert( TEX_FILTER_POINT == 0x100000, "TEX_FILTER_ flag values don't match TEX_FILTER_MASK" );
2876 
2877  HRESULT hr;
2878 
2879  DWORD filter_select = ( filter & TEX_FILTER_MASK );
2880  if ( !filter_select )
2881  {
2882  // Default filter choice
2883  filter_select = ( ispow2(width) && ispow2(height) && ispow2(depth) ) ? TEX_FILTER_BOX : TEX_FILTER_TRIANGLE;
2884  }
2885 
2886  switch( filter_select )
2887  {
2888  case TEX_FILTER_BOX:
2889  hr = _Setup3DMips( baseImages, depth, levels, mipChain );
2890  if ( FAILED(hr) )
2891  return hr;
2892 
2893  hr = _Generate3DMipsBoxFilter( depth, levels, filter, mipChain );
2894  if ( FAILED(hr) )
2895  mipChain.Release();
2896  return hr;
2897 
2898  case TEX_FILTER_POINT:
2899  hr = _Setup3DMips( baseImages, depth, levels, mipChain );
2900  if ( FAILED(hr) )
2901  return hr;
2902 
2903  hr = _Generate3DMipsPointFilter( depth, levels, mipChain );
2904  if ( FAILED(hr) )
2905  mipChain.Release();
2906  return hr;
2907 
2908  case TEX_FILTER_LINEAR:
2909  hr = _Setup3DMips( baseImages, depth, levels, mipChain );
2910  if ( FAILED(hr) )
2911  return hr;
2912 
2913  hr = _Generate3DMipsLinearFilter( depth, levels, filter, mipChain );
2914  if ( FAILED(hr) )
2915  mipChain.Release();
2916  return hr;
2917 
2918  case TEX_FILTER_CUBIC:
2919  hr = _Setup3DMips( baseImages, depth, levels, mipChain );
2920  if ( FAILED(hr) )
2921  return hr;
2922 
2923  hr = _Generate3DMipsCubicFilter( depth, levels, filter, mipChain );
2924  if ( FAILED(hr) )
2925  mipChain.Release();
2926  return hr;
2927 
2928  case TEX_FILTER_TRIANGLE:
2929  hr = _Setup3DMips( baseImages, depth, levels, mipChain );
2930  if ( FAILED(hr) )
2931  return hr;
2932 
2933  hr = _Generate3DMipsTriangleFilter( depth, levels, filter, mipChain );
2934  if ( FAILED(hr) )
2935  mipChain.Release();
2936  return hr;
2937 
2938  default:
2939  return HRESULT_FROM_WIN32( ERROR_NOT_SUPPORTED );
2940  }
2941 }
2942 
2943 _Use_decl_annotations_
2944 HRESULT GenerateMipMaps3D( const Image* srcImages, size_t nimages, const TexMetadata& metadata,
2945  DWORD filter, size_t levels, ScratchImage& mipChain )
2946 {
2947  if ( !srcImages || !nimages || !IsValid(metadata.format) )
2948  return E_INVALIDARG;
2949 
2950  if ( filter & TEX_FILTER_FORCE_WIC )
2951  return HRESULT_FROM_WIN32( ERROR_NOT_SUPPORTED );
2952 
2953  if ( !metadata.IsVolumemap()
2954  || IsCompressed(metadata.format) || IsTypeless(metadata.format) || IsPlanar(metadata.format) || IsPalettized(metadata.format) )
2955  return HRESULT_FROM_WIN32( ERROR_NOT_SUPPORTED );
2956 
2957  if ( !_CalculateMipLevels3D(metadata.width, metadata.height, metadata.depth, levels) )
2958  return E_INVALIDARG;
2959 
2960  std::vector<const Image> baseImages;
2961  baseImages.reserve( metadata.depth );
2962  for( size_t slice=0; slice < metadata.depth; ++slice )
2963  {
2964  size_t index = metadata.ComputeIndex( 0, 0, slice );
2965  if ( index >= nimages )
2966  return E_FAIL;
2967 
2968  const Image& src = srcImages[ index ];
2969  if ( !src.pixels )
2970  return E_POINTER;
2971 
2972  if ( src.format != metadata.format || src.width != metadata.width || src.height != metadata.height )
2973  {
2974  // All base images must be the same format, width, and height
2975  return E_FAIL;
2976  }
2977 
2978  baseImages.push_back( src );
2979  }
2980 
2981  assert( baseImages.size() == metadata.depth );
2982 
2983  HRESULT hr;
2984 
2985  static_assert( TEX_FILTER_POINT == 0x100000, "TEX_FILTER_ flag values don't match TEX_FILTER_MASK" );
2986 
2987  DWORD filter_select = ( filter & TEX_FILTER_MASK );
2988  if ( !filter_select )
2989  {
2990  // Default filter choice
2991  filter_select = ( ispow2(metadata.width) && ispow2(metadata.height) && ispow2(metadata.depth) ) ? TEX_FILTER_BOX : TEX_FILTER_TRIANGLE;
2992  }
2993 
2994  switch( filter_select )
2995  {
2996  case TEX_FILTER_BOX:
2997  hr = _Setup3DMips( &baseImages[0], metadata.depth, levels, mipChain );
2998  if ( FAILED(hr) )
2999  return hr;
3000 
3001  hr = _Generate3DMipsBoxFilter( metadata.depth, levels, filter, mipChain );
3002  if ( FAILED(hr) )
3003  mipChain.Release();
3004  return hr;
3005 
3006  case TEX_FILTER_POINT:
3007  hr = _Setup3DMips( &baseImages[0], metadata.depth, levels, mipChain );
3008  if ( FAILED(hr) )
3009  return hr;
3010 
3011  hr = _Generate3DMipsPointFilter( metadata.depth, levels, mipChain );
3012  if ( FAILED(hr) )
3013  mipChain.Release();
3014  return hr;
3015 
3016  case TEX_FILTER_LINEAR:
3017  hr = _Setup3DMips( &baseImages[0], metadata.depth, levels, mipChain );
3018  if ( FAILED(hr) )
3019  return hr;
3020 
3021  hr = _Generate3DMipsLinearFilter( metadata.depth, levels, filter, mipChain );
3022  if ( FAILED(hr) )
3023  mipChain.Release();
3024  return hr;
3025 
3026  case TEX_FILTER_CUBIC:
3027  hr = _Setup3DMips( &baseImages[0], metadata.depth, levels, mipChain );
3028  if ( FAILED(hr) )
3029  return hr;
3030 
3031  hr = _Generate3DMipsCubicFilter( metadata.depth, levels, filter, mipChain );
3032  if ( FAILED(hr) )
3033  mipChain.Release();
3034  return hr;
3035 
3036  case TEX_FILTER_TRIANGLE:
3037  hr = _Setup3DMips( &baseImages[0], metadata.depth, levels, mipChain );
3038  if ( FAILED(hr) )
3039  return hr;
3040 
3041  hr = _Generate3DMipsTriangleFilter( metadata.depth, levels, filter, mipChain );
3042  if ( FAILED(hr) )
3043  mipChain.Release();
3044  return hr;
3045 
3046  default:
3047  return HRESULT_FROM_WIN32( ERROR_NOT_SUPPORTED );
3048  }
3049 }
3050 
3051 }; // namespace
bool _CalculateMipLevels3D(_In_ size_t width, _In_ size_t height, _In_ size_t depth, _Inout_ size_t &mipLevels)
static HRESULT _Generate3DMipsCubicFilter(_In_ size_t depth, _In_ size_t levels, _In_ DWORD filter, _In_ const ScratchImage &mipChain)
std::unique_ptr< DirectX::XMVECTOR, aligned_deleter > ScopedAlignedArrayXMVECTOR
Definition: scoped.h:27
const Image * GetImage(_In_ size_t mip, _In_ size_t item, _In_ size_t slice) const
static HRESULT _EnsureWicBitmapPixelFormat(_In_ IWICImagingFactory *pWIC, _In_ IWICBitmap *src, _In_ DWORD filter, _In_ const WICPixelFormatGUID &desiredPixelFormat, _Deref_out_ IWICBitmap **dest)
uint8_t * pixels
Definition: DirectXTex.h:230
bool IsVolumemap() const
Definition: DirectXTex.h:138
size_t BitsPerColor(_In_ DXGI_FORMAT fmt)
bool IsPlanar(_In_ DXGI_FORMAT fmt)
DXGI_FORMAT format
Definition: DirectXTex.h:125
static HRESULT _Generate3DMipsBoxFilter(_In_ size_t depth, _In_ size_t levels, _In_ DWORD filter, _In_ const ScratchImage &mipChain)
_Use_decl_annotations_ HRESULT _ConvertFromR32G32B32A32(const Image &srcImage, const Image &destImage)
_Use_decl_annotations_ HRESULT _ConvertToR32G32B32A32(const Image &srcImage, ScratchImage &image)
size_t _In_ DXGI_FORMAT size_t _In_ TEXP_LEGACY_FORMAT _In_ DWORD flags assert(pDestination &&outSize > 0)
_In_ size_t _In_ DXGI_FORMAT _In_ size_t _In_ float size_t y
Definition: DirectXTexP.h:191
HRESULT Initialize(_In_ const TexMetadata &mdata, _In_ DWORD flags=CP_FLAGS_NONE)
static HRESULT _Generate3DMipsPointFilter(_In_ size_t depth, _In_ size_t levels, _In_ const ScratchImage &mipChain)
static HRESULT _Generate2DMipsTriangleFilter(_In_ size_t levels, _In_ DWORD filter, _In_ const ScratchImage &mipChain, _In_ size_t item)
HRESULT Initialize2D(_In_ DXGI_FORMAT fmt, _In_ size_t width, _In_ size_t height, _In_ size_t arraySize, _In_ size_t mipLevels, _In_ DWORD flags=CP_FLAGS_NONE)
_Use_decl_annotations_ bool _LoadScanline(XMVECTOR *pDestination, size_t count, LPCVOID pSource, size_t size, DXGI_FORMAT format)
#define CUBIC_INTERPOLATE(res, dx, p0, p1, p2, p3)
Definition: Filters.h:194
void _CreateCubicFilter(_In_ size_t source, _In_ size_t dest, _In_ bool wrap, _In_ bool mirror, _Out_writes_(dest) CubicFilter *cf)
Definition: Filters.h:166
IWICImagingFactory * _GetWIC()
size_t ComputeIndex(_In_ size_t mip, _In_ size_t item, _In_ size_t slice) const
bool IsCompressed(_In_ DXGI_FORMAT fmt)
size_t rowPitch
Definition: DirectXTex.h:228
size_t GetImageCount() const
Definition: DirectXTex.h:264
HRESULT GenerateMipMaps(_In_ const Image &baseImage, _In_ DWORD filter, _In_ size_t levels, _Inout_ ScratchImage &mipChain, _In_ bool allow1D=false)
static HRESULT _Setup2DMips(_In_reads_(nimages) const Image *baseImages, _In_ size_t nimages, _In_ const TexMetadata &mdata, _Out_ ScratchImage &mipChain)
_In_ size_t _In_ const TexMetadata & metadata
Definition: DirectXTexP.h:116
HRESULT _Create(_In_ size_t source, _In_ size_t dest, _In_ bool wrap, _Inout_ std::unique_ptr< Filter > &tf)
Definition: Filters.h:251
bool _IsWIC2()
bool IsValid(_In_ DXGI_FORMAT fmt)
static HRESULT _GenerateMipMapsUsingWIC(_In_ const Image &baseImage, _In_ DWORD filter, _In_ size_t levels, _In_ const WICPixelFormatGUID &pfGUID, _In_ const ScratchImage &mipChain, _In_ size_t item)
bool IsPalettized(_In_ DXGI_FORMAT fmt)
HRESULT GenerateMipMaps3D(_In_reads_(depth) const Image *baseImages, _In_ size_t depth, _In_ DWORD filter, _In_ size_t levels, _Out_ ScratchImage &mipChain)
static HRESULT _Generate2DMipsLinearFilter(_In_ size_t levels, _In_ DWORD filter, _In_ const ScratchImage &mipChain, _In_ size_t item)
static size_t _CountMips(_In_ size_t width, _In_ size_t height)
HRESULT Initialize1D(_In_ DXGI_FORMAT fmt, _In_ size_t length, _In_ size_t arraySize, _In_ size_t mipLevels, _In_ DWORD flags=CP_FLAGS_NONE)
static HRESULT _Generate2DMipsBoxFilter(_In_ size_t levels, _In_ DWORD filter, _In_ const ScratchImage &mipChain, _In_ size_t item)
WICBitmapDitherType _GetWICDither(_In_ DWORD flags)
Definition: DirectXTexP.h:64
_In_ size_t _In_ DXGI_FORMAT _In_reads_(count) const XMVECTOR *pSource
_Use_decl_annotations_ bool _LoadScanlineLinear(XMVECTOR *pDestination, size_t count, LPCVOID pSource, size_t size, DXGI_FORMAT format, DWORD flags)
_Use_decl_annotations_ bool _StoreScanlineLinear(LPVOID pDestination, size_t size, DXGI_FORMAT format, XMVECTOR *pSource, size_t count, DWORD flags)
bool IsTypeless(_In_ DXGI_FORMAT fmt, _In_ bool partialTypeless=true)
TEX_DIMENSION dimension
Definition: DirectXTex.h:126
#define AVERAGE8(res, p0, p1, p2, p3, p4, p5, p6, p7)
Definition: Filters.h:43
#define TRILINEAR_INTERPOLATE(res, x, y, z, r0, r1, r2, r3)
Definition: Filters.h:110
static size_t _CountMips3D(_In_ size_t width, _In_ size_t height, _In_ size_t depth)
char * dest
Definition: lz4.h:61
bool IsSRGB(_In_ DXGI_FORMAT fmt)
#define BILINEAR_INTERPOLATE(res, x, y, r0, r1)
Definition: Filters.h:106
DXGI_FORMAT format
Definition: DirectXTex.h:227
_In_ size_t _In_ size_t _In_ DXGI_FORMAT format
Definition: DirectXTexP.h:175
static HRESULT _Generate3DMipsTriangleFilter(_In_ size_t depth, _In_ size_t levels, _In_ DWORD filter, _In_ const ScratchImage &mipChain)
static HRESULT _Generate2DMipsCubicFilter(_In_ size_t levels, _In_ DWORD filter, _In_ const ScratchImage &mipChain, _In_ size_t item)
static bool _UseWICFiltering(_In_ DXGI_FORMAT format, _In_ DWORD filter)
#define TEX_FILTER_MASK
Definition: DirectXTexP.h:49
const Image * GetImages() const
Definition: DirectXTex.h:263
bool _DXGIToWIC(_In_ DXGI_FORMAT format, _Out_ GUID &guid, _In_ bool ignoreRGBvsBGR=false)
HRESULT _ResizeSeparateColorAndAlpha(_In_ IWICImagingFactory *pWIC, _In_ IWICBitmap *original, _In_ size_t newWidth, _In_ size_t newHeight, _In_ DWORD filter, _Inout_ const Image *img)
const TexMetadata & GetMetadata() const
Definition: DirectXTex.h:260
bool _CalculateMipLevels(_In_ size_t width, _In_ size_t height, _Inout_ size_t &mipLevels)
static bool ispow2(_In_ size_t x)
static HRESULT _Generate2DMipsPointFilter(_In_ size_t levels, _In_ const ScratchImage &mipChain, _In_ size_t item)
#define AVERAGE4(res, p0, p1, p2, p3)
Definition: Filters.h:35
size_t slicePitch
Definition: DirectXTex.h:229
_Use_decl_annotations_ bool _StoreScanline(LPVOID pDestination, size_t size, DXGI_FORMAT format, const XMVECTOR *pSource, size_t count, float threshold)
void _CreateLinearFilter(_In_ size_t source, _In_ size_t dest, _In_ bool wrap, _Out_writes_(dest) LinearFilter *lf)
Definition: Filters.h:68
static HRESULT _Generate3DMipsLinearFilter(_In_ size_t depth, _In_ size_t levels, _In_ DWORD filter, _In_ const ScratchImage &mipChain)
WICBitmapInterpolationMode _GetWICInterp(_In_ DWORD flags)
Definition: DirectXTexP.h:84
_In_ size_t _In_ DXGI_FORMAT _In_ size_t _In_ float size_t size_t z
Definition: DirectXTexP.h:191
static HRESULT _Setup3DMips(_In_reads_(depth) const Image *baseImages, _In_ size_t depth, size_t levels, _Out_ ScratchImage &mipChain)