Paradox Game Engine  v1.0.0 beta06
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Events Macros Pages
DirectXTexResize.cpp
Go to the documentation of this file.
1 //-------------------------------------------------------------------------------------
2 // DirectXTexResize.cpp
3 //
4 // DirectX Texture Library - Image resizing operations
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 // WIC related helper functions
27 //-------------------------------------------------------------------------------------
28 
29 extern HRESULT _ResizeSeparateColorAndAlpha( _In_ IWICImagingFactory* pWIC, _In_ IWICBitmap* original,
30  _In_ size_t newWidth, _In_ size_t newHeight, _In_ DWORD filter, _Inout_ const Image* img );
31 
32 //--- Do image resize using WIC ---
33 static HRESULT _PerformResizeUsingWIC( _In_ const Image& srcImage, _In_ DWORD filter,
34  _In_ const WICPixelFormatGUID& pfGUID, _In_ const Image& destImage )
35 {
36  if ( !srcImage.pixels || !destImage.pixels )
37  return E_POINTER;
38 
39  assert( srcImage.format == destImage.format );
40 
41  IWICImagingFactory* pWIC = _GetWIC();
42  if ( !pWIC )
43  return E_NOINTERFACE;
44 
45  ComPtr<IWICComponentInfo> componentInfo;
46  HRESULT hr = pWIC->CreateComponentInfo( pfGUID, componentInfo.GetAddressOf() );
47  if ( FAILED(hr) )
48  return hr;
49 
50  ComPtr<IWICPixelFormatInfo2> pixelFormatInfo;
51  hr = componentInfo.As( &pixelFormatInfo );
52  if ( FAILED(hr) )
53  return hr;
54 
55  BOOL supportsTransparency = FALSE;
56  hr = pixelFormatInfo->SupportsTransparency( &supportsTransparency );
57  if ( FAILED(hr) )
58  return hr;
59 
60  ComPtr<IWICBitmap> source;
61  hr = pWIC->CreateBitmapFromMemory( static_cast<UINT>( srcImage.width ), static_cast<UINT>( srcImage.height ), pfGUID,
62  static_cast<UINT>( srcImage.rowPitch ), static_cast<UINT>( srcImage.slicePitch ),
63  srcImage.pixels, source.GetAddressOf() );
64  if ( FAILED(hr) )
65  return hr;
66 
67  if ( (filter & TEX_FILTER_SEPARATE_ALPHA) && supportsTransparency )
68  {
69  hr = _ResizeSeparateColorAndAlpha( pWIC, source.Get(), destImage.width, destImage.height, filter, &destImage );
70  if ( FAILED(hr) )
71  return hr;
72  }
73  else
74  {
75  ComPtr<IWICBitmapScaler> scaler;
76  hr = pWIC->CreateBitmapScaler( scaler.GetAddressOf() );
77  if ( FAILED(hr) )
78  return hr;
79 
80  hr = scaler->Initialize( source.Get(), static_cast<UINT>( destImage.width ), static_cast<UINT>( destImage.height ), _GetWICInterp( filter ) );
81  if ( FAILED(hr) )
82  return hr;
83 
84  WICPixelFormatGUID pfScaler;
85  hr = scaler->GetPixelFormat( &pfScaler );
86  if ( FAILED(hr) )
87  return hr;
88 
89  if ( memcmp( &pfScaler, &pfGUID, sizeof(WICPixelFormatGUID) ) == 0 )
90  {
91  hr = scaler->CopyPixels( 0, static_cast<UINT>( destImage.rowPitch ), static_cast<UINT>( destImage.slicePitch ), destImage.pixels );
92  if ( FAILED(hr) )
93  return hr;
94  }
95  else
96  {
97  // The WIC bitmap scaler is free to return a different pixel format than the source image, so here we
98  // convert it back
99  ComPtr<IWICFormatConverter> FC;
100  hr = pWIC->CreateFormatConverter( FC.GetAddressOf() );
101  if ( FAILED(hr) )
102  return hr;
103 
104  hr = FC->Initialize( scaler.Get(), pfGUID, _GetWICDither( filter ), 0, 0, WICBitmapPaletteTypeCustom );
105  if ( FAILED(hr) )
106  return hr;
107 
108  hr = FC->CopyPixels( 0, static_cast<UINT>( destImage.rowPitch ), static_cast<UINT>( destImage.slicePitch ), destImage.pixels );
109  if ( FAILED(hr) )
110  return hr;
111  }
112  }
113 
114  return S_OK;
115 }
116 
117 
118 //--- Do conversion, resize using WIC, conversion cycle ---
119 static HRESULT _PerformResizeViaF32( _In_ const Image& srcImage, _In_ DWORD filter, _In_ const Image& destImage )
120 {
121  if ( !srcImage.pixels || !destImage.pixels )
122  return E_POINTER;
123 
124  assert( srcImage.format != DXGI_FORMAT_R32G32B32A32_FLOAT );
125  assert( srcImage.format == destImage.format );
126 
127  ScratchImage temp;
128  HRESULT hr = _ConvertToR32G32B32A32( srcImage, temp );
129  if ( FAILED(hr) )
130  return hr;
131 
132  const Image *tsrc = temp.GetImage( 0, 0, 0 );
133  if ( !tsrc )
134  return E_POINTER;
135 
136  ScratchImage rtemp;
137  hr = rtemp.Initialize2D( DXGI_FORMAT_R32G32B32A32_FLOAT, destImage.width, destImage.height, 1, 1 );
138  if ( FAILED(hr) )
139  return hr;
140 
141  const Image *tdest = rtemp.GetImage( 0, 0, 0 );
142  if ( !tdest )
143  return E_POINTER;
144 
145  hr = _PerformResizeUsingWIC( *tsrc, filter, GUID_WICPixelFormat128bppRGBAFloat, *tdest );
146  if ( FAILED(hr) )
147  return hr;
148 
149  temp.Release();
150 
151  hr = _ConvertFromR32G32B32A32( *tdest, destImage );
152  if ( FAILED(hr) )
153  return hr;
154 
155  return S_OK;
156 }
157 
158 
159 //--- determine when to use WIC vs. non-WIC paths ---
160 static bool _UseWICFiltering( _In_ DXGI_FORMAT format, _In_ DWORD filter )
161 {
162  if ( filter & TEX_FILTER_FORCE_NON_WIC )
163  {
164  // Explicit flag indicates use of non-WIC code paths
165  return false;
166  }
167 
168  if ( filter & TEX_FILTER_FORCE_WIC )
169  {
170  // Explicit flag to use WIC code paths, skips all the case checks below
171  return true;
172  }
173 
174  if ( IsSRGB(format) || (filter & TEX_FILTER_SRGB) )
175  {
176  // Use non-WIC code paths for sRGB correct filtering
177  return false;
178  }
179 
180  static_assert( TEX_FILTER_POINT == 0x100000, "TEX_FILTER_ flag values don't match TEX_FILTER_MASK" );
181 
182  switch ( filter & TEX_FILTER_MASK )
183  {
184  case TEX_FILTER_LINEAR:
185  if ( filter & TEX_FILTER_WRAP )
186  {
187  // WIC only supports 'clamp' semantics (MIRROR is equivalent to clamp for linear)
188  return false;
189  }
190 
191  if ( BitsPerColor(format) > 8 )
192  {
193  // Avoid the WIC bitmap scaler when doing Linear filtering of XR/HDR formats
194  return false;
195  }
196  break;
197 
198  case TEX_FILTER_CUBIC:
199  if ( filter & ( TEX_FILTER_WRAP | TEX_FILTER_MIRROR ) )
200  {
201  // WIC only supports 'clamp' semantics
202  return false;
203  }
204 
205  if ( BitsPerColor(format) > 8 )
206  {
207  // Avoid the WIC bitmap scaler when doing Cubic filtering of XR/HDR formats
208  return false;
209  }
210  break;
211 
212  case TEX_FILTER_TRIANGLE:
213  // WIC does not implement this filter
214  return false;
215  }
216 
217  return true;
218 }
219 
220 
221 //-------------------------------------------------------------------------------------
222 // Resize custom filters
223 //-------------------------------------------------------------------------------------
224 
225 //--- Point Filter ---
226 static HRESULT _ResizePointFilter( _In_ const Image& srcImage, _In_ const Image& destImage )
227 {
228  assert( srcImage.pixels && destImage.pixels );
229  assert( srcImage.format == destImage.format );
230 
231  // Allocate temporary space (2 scanlines)
232  ScopedAlignedArrayXMVECTOR scanline( reinterpret_cast<XMVECTOR*>( _aligned_malloc(
233  ( sizeof(XMVECTOR) * (srcImage.width + destImage.width ) ), 16 ) ) );
234  if ( !scanline )
235  return E_OUTOFMEMORY;
236 
237  XMVECTOR* target = scanline.get();
238 
239  XMVECTOR* row = target + destImage.width;
240 
241 #ifdef _DEBUG
242  memset( row, 0xCD, sizeof(XMVECTOR)*srcImage.width );
243 #endif
244 
245  const uint8_t* pSrc = srcImage.pixels;
246  uint8_t* pDest = destImage.pixels;
247 
248  size_t rowPitch = srcImage.rowPitch;
249 
250  size_t xinc = ( srcImage.width << 16 ) / destImage.width;
251  size_t yinc = ( srcImage.height << 16 ) / destImage.height;
252 
253  size_t lasty = size_t(-1);
254 
255  size_t sy = 0;
256  for( size_t y = 0; y < destImage.height; ++y )
257  {
258  if ( (lasty ^ sy) >> 16 )
259  {
260  if ( !_LoadScanline( row, srcImage.width, pSrc + ( rowPitch * (sy >> 16) ), rowPitch, srcImage.format ) )
261  return E_FAIL;
262  lasty = sy;
263  }
264 
265  size_t sx = 0;
266  for( size_t x = 0; x < destImage.width; ++x )
267  {
268  target[ x ] = row[ sx >> 16 ];
269  sx += xinc;
270  }
271 
272  if ( !_StoreScanline( pDest, destImage.rowPitch, destImage.format, target, destImage.width ) )
273  return E_FAIL;
274  pDest += destImage.rowPitch;
275 
276  sy += yinc;
277  }
278 
279  return S_OK;
280 }
281 
282 
283 //--- Box Filter ---
284 static HRESULT _ResizeBoxFilter( _In_ const Image& srcImage, _In_ DWORD filter, _In_ const Image& destImage )
285 {
286  assert( srcImage.pixels && destImage.pixels );
287  assert( srcImage.format == destImage.format );
288 
289  if ( ( (destImage.width << 1) != srcImage.width ) || ( (destImage.height << 1) != srcImage.height ) )
290  return E_FAIL;
291 
292  // Allocate temporary space (3 scanlines)
293  ScopedAlignedArrayXMVECTOR scanline( reinterpret_cast<XMVECTOR*>( _aligned_malloc(
294  ( sizeof(XMVECTOR) * ( srcImage.width*2 + destImage.width ) ), 16 ) ) );
295  if ( !scanline )
296  return E_OUTOFMEMORY;
297 
298  XMVECTOR* target = scanline.get();
299 
300  XMVECTOR* urow0 = target + destImage.width;
301  XMVECTOR* urow1 = urow0 + srcImage.width;
302 
303 #ifdef _DEBUG
304  memset( urow0, 0xCD, sizeof(XMVECTOR)*srcImage.width );
305  memset( urow1, 0xDD, sizeof(XMVECTOR)*srcImage.width );
306 #endif
307 
308  const XMVECTOR* urow2 = urow0 + 1;
309  const XMVECTOR* urow3 = urow1 + 1;
310 
311  const uint8_t* pSrc = srcImage.pixels;
312  uint8_t* pDest = destImage.pixels;
313 
314  size_t rowPitch = srcImage.rowPitch;
315 
316  for( size_t y = 0; y < destImage.height; ++y )
317  {
318  if ( !_LoadScanlineLinear( urow0, srcImage.width, pSrc, rowPitch, srcImage.format, filter ) )
319  return E_FAIL;
320  pSrc += rowPitch;
321 
322  if ( urow0 != urow1 )
323  {
324  if ( !_LoadScanlineLinear( urow1, srcImage.width, pSrc, rowPitch, srcImage.format, filter ) )
325  return E_FAIL;
326  pSrc += rowPitch;
327  }
328 
329  for( size_t x = 0; x < destImage.width; ++x )
330  {
331  size_t x2 = x << 1;
332 
333  AVERAGE4( target[ x ], urow0[ x2 ], urow1[ x2 ], urow2[ x2 ], urow3[ x2 ] );
334  }
335 
336  if ( !_StoreScanlineLinear( pDest, destImage.rowPitch, destImage.format, target, destImage.width, filter ) )
337  return E_FAIL;
338  pDest += destImage.rowPitch;
339  }
340 
341  return S_OK;
342 }
343 
344 
345 //--- Linear Filter ---
346 static HRESULT _ResizeLinearFilter( _In_ const Image& srcImage, _In_ DWORD filter, _In_ const Image& destImage )
347 {
348  assert( srcImage.pixels && destImage.pixels );
349  assert( srcImage.format == destImage.format );
350 
351  // Allocate temporary space (3 scanlines, plus X and Y filters)
352  ScopedAlignedArrayXMVECTOR scanline( reinterpret_cast<XMVECTOR*>( _aligned_malloc(
353  ( sizeof(XMVECTOR) * ( srcImage.width*2 + destImage.width ) ), 16 ) ) );
354  if ( !scanline )
355  return E_OUTOFMEMORY;
356 
357  std::unique_ptr<LinearFilter[]> lf( new (std::nothrow) LinearFilter[ destImage.width + destImage.height ] );
358  if ( !lf )
359  return E_OUTOFMEMORY;
360 
361  LinearFilter* lfX = lf.get();
362  LinearFilter* lfY = lf.get() + destImage.width;
363 
364  _CreateLinearFilter( srcImage.width, destImage.width, (filter & TEX_FILTER_WRAP_U) != 0, lfX );
365  _CreateLinearFilter( srcImage.height, destImage.height, (filter & TEX_FILTER_WRAP_V) != 0, lfY );
366 
367  XMVECTOR* target = scanline.get();
368 
369  XMVECTOR* row0 = target + destImage.width;
370  XMVECTOR* row1 = row0 + srcImage.width;
371 
372 #ifdef _DEBUG
373  memset( row0, 0xCD, sizeof(XMVECTOR)*srcImage.width );
374  memset( row1, 0xDD, sizeof(XMVECTOR)*srcImage.width );
375 #endif
376 
377  const uint8_t* pSrc = srcImage.pixels;
378  uint8_t* pDest = destImage.pixels;
379 
380  size_t rowPitch = srcImage.rowPitch;
381 
382  size_t u0 = size_t(-1);
383  size_t u1 = size_t(-1);
384 
385  for( size_t y = 0; y < destImage.height; ++y )
386  {
387  auto& toY = lfY[ y ];
388 
389  if ( toY.u0 != u0 )
390  {
391  if ( toY.u0 != u1 )
392  {
393  u0 = toY.u0;
394 
395  if ( !_LoadScanlineLinear( row0, srcImage.width, pSrc + (rowPitch * u0), rowPitch, srcImage.format, filter ) )
396  return E_FAIL;
397  }
398  else
399  {
400  u0 = u1;
401  u1 = size_t(-1);
402 
403  std::swap( row0, row1 );
404  }
405  }
406 
407  if ( toY.u1 != u1 )
408  {
409  u1 = toY.u1;
410 
411  if ( !_LoadScanlineLinear( row1, srcImage.width, pSrc + (rowPitch * u1), rowPitch, srcImage.format, filter ) )
412  return E_FAIL;
413  }
414 
415  for( size_t x = 0; x < destImage.width; ++x )
416  {
417  auto& toX = lfX[ x ];
418 
419  BILINEAR_INTERPOLATE( target[x], toX, toY, row0, row1 );
420  }
421 
422  if ( !_StoreScanlineLinear( pDest, destImage.rowPitch, destImage.format, target, destImage.width, filter ) )
423  return E_FAIL;
424  pDest += destImage.rowPitch;
425  }
426 
427  return S_OK;
428 }
429 
430 
431 //--- Cubic Filter ---
432 static HRESULT _ResizeCubicFilter( _In_ const Image& srcImage, _In_ DWORD filter, _In_ const Image& destImage )
433 {
434  assert( srcImage.pixels && destImage.pixels );
435  assert( srcImage.format == destImage.format );
436 
437  // Allocate temporary space (5 scanlines, plus X and Y filters)
438  ScopedAlignedArrayXMVECTOR scanline( reinterpret_cast<XMVECTOR*>( _aligned_malloc(
439  ( sizeof(XMVECTOR) * ( srcImage.width*4 + destImage.width ) ), 16 ) ) );
440  if ( !scanline )
441  return E_OUTOFMEMORY;
442 
443  std::unique_ptr<CubicFilter[]> cf( new (std::nothrow) CubicFilter[ destImage.width + destImage.height ] );
444  if ( !cf )
445  return E_OUTOFMEMORY;
446 
447  CubicFilter* cfX = cf.get();
448  CubicFilter* cfY = cf.get() + destImage.width;
449 
450  _CreateCubicFilter( srcImage.width, destImage.width, (filter & TEX_FILTER_WRAP_U) != 0, (filter & TEX_FILTER_MIRROR_U) != 0, cfX );
451  _CreateCubicFilter( srcImage.height, destImage.height, (filter & TEX_FILTER_WRAP_V) != 0, (filter & TEX_FILTER_MIRROR_V) != 0, cfY );
452 
453  XMVECTOR* target = scanline.get();
454 
455  XMVECTOR* row0 = target + destImage.width;
456  XMVECTOR* row1 = row0 + srcImage.width;
457  XMVECTOR* row2 = row0 + srcImage.width*2;
458  XMVECTOR* row3 = row0 + srcImage.width*3;
459 
460 #ifdef _DEBUG
461  memset( row0, 0xCD, sizeof(XMVECTOR)*srcImage.width );
462  memset( row1, 0xDD, sizeof(XMVECTOR)*srcImage.width );
463  memset( row2, 0xED, sizeof(XMVECTOR)*srcImage.width );
464  memset( row3, 0xFD, sizeof(XMVECTOR)*srcImage.width );
465 #endif
466 
467  const uint8_t* pSrc = srcImage.pixels;
468  uint8_t* pDest = destImage.pixels;
469 
470  size_t rowPitch = srcImage.rowPitch;
471 
472  size_t u0 = size_t(-1);
473  size_t u1 = size_t(-1);
474  size_t u2 = size_t(-1);
475  size_t u3 = size_t(-1);
476 
477  for( size_t y = 0; y < destImage.height; ++y )
478  {
479  auto& toY = cfY[ y ];
480 
481  // Scanline 1
482  if ( toY.u0 != u0 )
483  {
484  if ( toY.u0 != u1 && toY.u0 != u2 && toY.u0 != u3 )
485  {
486  u0 = toY.u0;
487 
488  if ( !_LoadScanlineLinear( row0, srcImage.width, pSrc + (rowPitch * u0), rowPitch, srcImage.format, filter ) )
489  return E_FAIL;
490  }
491  else if ( toY.u0 == u1 )
492  {
493  u0 = u1;
494  u1 = size_t(-1);
495 
496  std::swap( row0, row1 );
497  }
498  else if ( toY.u0 == u2 )
499  {
500  u0 = u2;
501  u2 = size_t(-1);
502 
503  std::swap( row0, row2 );
504  }
505  else if ( toY.u0 == u3 )
506  {
507  u0 = u3;
508  u3 = size_t(-1);
509 
510  std::swap( row0, row3 );
511  }
512  }
513 
514  // Scanline 2
515  if ( toY.u1 != u1 )
516  {
517  if ( toY.u1 != u2 && toY.u1 != u3 )
518  {
519  u1 = toY.u1;
520 
521  if ( !_LoadScanlineLinear( row1, srcImage.width, pSrc + (rowPitch * u1), rowPitch, srcImage.format, filter ) )
522  return E_FAIL;
523  }
524  else if ( toY.u1 == u2 )
525  {
526  u1 = u2;
527  u2 = size_t(-1);
528 
529  std::swap( row1, row2 );
530  }
531  else if ( toY.u1 == u3 )
532  {
533  u1 = u3;
534  u3 = size_t(-1);
535 
536  std::swap( row1, row3 );
537  }
538  }
539 
540  // Scanline 3
541  if ( toY.u2 != u2 )
542  {
543  if ( toY.u2 != u3 )
544  {
545  u2 = toY.u2;
546 
547  if ( !_LoadScanlineLinear( row2, srcImage.width, pSrc + (rowPitch * u2), rowPitch, srcImage.format, filter ) )
548  return E_FAIL;
549  }
550  else
551  {
552  u2 = u3;
553  u3 = size_t(-1);
554 
555  std::swap( row2, row3 );
556  }
557  }
558 
559  // Scanline 4
560  if ( toY.u3 != u3 )
561  {
562  u3 = toY.u3;
563 
564  if ( !_LoadScanlineLinear( row3, srcImage.width, pSrc + (rowPitch * u3), rowPitch, srcImage.format, filter ) )
565  return E_FAIL;
566  }
567 
568  for( size_t x = 0; x < destImage.width; ++x )
569  {
570  auto& toX = cfX[ x ];
571 
572  XMVECTOR C0, C1, C2, C3;
573 
574  CUBIC_INTERPOLATE( C0, toX.x, row0[ toX.u0 ], row0[ toX.u1 ], row0[ toX.u2 ], row0[ toX.u3 ] );
575  CUBIC_INTERPOLATE( C1, toX.x, row1[ toX.u0 ], row1[ toX.u1 ], row1[ toX.u2 ], row1[ toX.u3 ] );
576  CUBIC_INTERPOLATE( C2, toX.x, row2[ toX.u0 ], row2[ toX.u1 ], row2[ toX.u2 ], row2[ toX.u3 ] );
577  CUBIC_INTERPOLATE( C3, toX.x, row3[ toX.u0 ], row3[ toX.u1 ], row3[ toX.u2 ], row3[ toX.u3 ] );
578 
579  CUBIC_INTERPOLATE( target[x], toY.x, C0, C1, C2, C3 );
580  }
581 
582  if ( !_StoreScanlineLinear( pDest, destImage.rowPitch, destImage.format, target, destImage.width, filter ) )
583  return E_FAIL;
584  pDest += destImage.rowPitch;
585  }
586 
587  return S_OK;
588 }
589 
590 
591 //--- Triangle Filter ---
592 static HRESULT _ResizeTriangleFilter( _In_ const Image& srcImage, _In_ DWORD filter, _In_ const Image& destImage )
593 {
594  assert( srcImage.pixels && destImage.pixels );
595  assert( srcImage.format == destImage.format );
596 
597  using namespace TriangleFilter;
598 
599  // Allocate initial temporary space (1 scanline, accumulation rows, plus X and Y filters)
600  ScopedAlignedArrayXMVECTOR scanline( reinterpret_cast<XMVECTOR*>( _aligned_malloc( sizeof(XMVECTOR) * srcImage.width, 16 ) ) );
601  if ( !scanline )
602  return E_OUTOFMEMORY;
603 
604  std::unique_ptr<TriangleRow[]> rowActive( new (std::nothrow) TriangleRow[ destImage.height ] );
605  if ( !rowActive )
606  return E_OUTOFMEMORY;
607 
608  TriangleRow * rowFree = nullptr;
609 
610  std::unique_ptr<Filter> tfX;
611  HRESULT hr = _Create( srcImage.width, destImage.width, (filter & TEX_FILTER_WRAP_U) != 0, tfX );
612  if ( FAILED(hr) )
613  return hr;
614 
615  std::unique_ptr<Filter> tfY;
616  hr = _Create( srcImage.height, destImage.height, (filter & TEX_FILTER_WRAP_V) != 0, tfY );
617  if ( FAILED(hr) )
618  return hr;
619 
620  XMVECTOR* row = scanline.get();
621 
622 #ifdef _DEBUG
623  memset( row, 0xCD, sizeof(XMVECTOR)*srcImage.width );
624 #endif
625 
626  auto xFromEnd = reinterpret_cast<const FilterFrom*>( reinterpret_cast<const uint8_t*>( tfX.get() ) + tfX->sizeInBytes );
627  auto yFromEnd = reinterpret_cast<const FilterFrom*>( reinterpret_cast<const uint8_t*>( tfY.get() ) + tfY->sizeInBytes );
628 
629  // Count times rows get written
630  for( FilterFrom* yFrom = tfY->from; yFrom < yFromEnd; )
631  {
632  for ( size_t j = 0; j < yFrom->count; ++j )
633  {
634  size_t v = yFrom->to[ j ].u;
635  assert( v < destImage.height );
636  ++rowActive.get()[ v ].remaining;
637  }
638 
639  yFrom = reinterpret_cast<FilterFrom*>( reinterpret_cast<uint8_t*>( yFrom ) + yFrom->sizeInBytes );
640  }
641 
642  // Filter image
643  const uint8_t* pSrc = srcImage.pixels;
644  size_t rowPitch = srcImage.rowPitch;
645  const uint8_t* pEndSrc = pSrc + rowPitch * srcImage.height;
646 
647  uint8_t* pDest = destImage.pixels;
648 
649  for( FilterFrom* yFrom = tfY->from; yFrom < yFromEnd; )
650  {
651  // Create accumulation rows as needed
652  for ( size_t j = 0; j < yFrom->count; ++j )
653  {
654  size_t v = yFrom->to[ j ].u;
655  assert( v < destImage.height );
656  TriangleRow* rowAcc = &rowActive.get()[ v ];
657 
658  if ( !rowAcc->scanline )
659  {
660  if ( rowFree )
661  {
662  // Steal and reuse scanline from 'free row' list
663  assert( rowFree->scanline != 0 );
664  rowAcc->scanline.reset( rowFree->scanline.release() );
665  rowFree = rowFree->next;
666  }
667  else
668  {
669  rowAcc->scanline.reset( reinterpret_cast<XMVECTOR*>( _aligned_malloc( sizeof(XMVECTOR) * destImage.width, 16 ) ) );
670  if ( !rowAcc->scanline )
671  return E_OUTOFMEMORY;
672  }
673 
674  memset( rowAcc->scanline.get(), 0, sizeof(XMVECTOR) * destImage.width );
675  }
676  }
677 
678  // Load source scanline
679  if ( (pSrc + rowPitch) > pEndSrc )
680  return E_FAIL;
681 
682  if ( !_LoadScanlineLinear( row, srcImage.width, pSrc, rowPitch, srcImage.format, filter ) )
683  return E_FAIL;
684 
685  pSrc += rowPitch;
686 
687  // Process row
688  size_t x = 0;
689  for( FilterFrom* xFrom = tfX->from; xFrom < xFromEnd; ++x )
690  {
691  for ( size_t j = 0; j < yFrom->count; ++j )
692  {
693  size_t v = yFrom->to[ j ].u;
694  assert( v < destImage.height );
695  float yweight = yFrom->to[ j ].weight;
696 
697  XMVECTOR* accPtr = rowActive[ v ].scanline.get();
698  if ( !accPtr )
699  return E_POINTER;
700 
701  for ( size_t k = 0; k < xFrom->count; ++k )
702  {
703  size_t u = xFrom->to[ k ].u;
704  assert( u < destImage.width );
705 
706  XMVECTOR weight = XMVectorReplicate( yweight * xFrom->to[ k ].weight );
707 
708  assert( x < srcImage.width );
709  accPtr[ u ] = XMVectorMultiplyAdd( row[ x ], weight, accPtr[ u ] );
710  }
711  }
712 
713  xFrom = reinterpret_cast<FilterFrom*>( reinterpret_cast<uint8_t*>( xFrom ) + xFrom->sizeInBytes );
714  }
715 
716  // Write completed accumulation rows
717  for ( size_t j = 0; j < yFrom->count; ++j )
718  {
719  size_t v = yFrom->to[ j ].u;
720  assert( v < destImage.height );
721  TriangleRow* rowAcc = &rowActive.get()[ v ];
722 
723  assert( rowAcc->remaining > 0 );
724  --rowAcc->remaining;
725 
726  if ( !rowAcc->remaining )
727  {
728  XMVECTOR* pAccSrc = rowAcc->scanline.get();
729  if ( !pAccSrc )
730  return E_POINTER;
731 
732  switch( destImage.format )
733  {
734  case DXGI_FORMAT_R10G10B10A2_UNORM:
735  case DXGI_FORMAT_R10G10B10A2_UINT:
736  {
737  // Need to slightly bias results for floating-point error accumulation which can
738  // be visible with harshly quantized values
739  static const XMVECTORF32 Bias = { 0.f, 0.f, 0.f, 0.1f };
740 
741  XMVECTOR* ptr = pAccSrc;
742  for( size_t i=0; i < destImage.width; ++i, ++ptr )
743  {
744  *ptr = XMVectorAdd( *ptr, Bias );
745  }
746  }
747  break;
748  }
749 
750  // This performs any required clamping
751  if ( !_StoreScanlineLinear( pDest + (destImage.rowPitch * v), destImage.rowPitch, destImage.format, pAccSrc, destImage.width, filter ) )
752  return E_FAIL;
753 
754  // Put row on freelist to reuse it's allocated scanline
755  rowAcc->next = rowFree;
756  rowFree = rowAcc;
757  }
758  }
759 
760  yFrom = reinterpret_cast<FilterFrom*>( reinterpret_cast<uint8_t*>( yFrom ) + yFrom->sizeInBytes );
761  }
762 
763  return S_OK;
764 }
765 
766 
767 //--- Custom filter resize ---
768 static HRESULT _PerformResizeUsingCustomFilters( _In_ const Image& srcImage, _In_ DWORD filter, _In_ const Image& destImage )
769 {
770  if ( !srcImage.pixels || !destImage.pixels )
771  return E_POINTER;
772 
773  static_assert( TEX_FILTER_POINT == 0x100000, "TEX_FILTER_ flag values don't match TEX_FILTER_MASK" );
774 
775  DWORD filter_select = ( filter & TEX_FILTER_MASK );
776  if ( !filter_select )
777  {
778  // Default filter choice
779  filter_select = ( ( (destImage.width << 1) == srcImage.width ) && ( (destImage.height << 1) == srcImage.height ) )
781  }
782 
783  switch( filter_select )
784  {
785  case TEX_FILTER_POINT:
786  return _ResizePointFilter( srcImage, destImage );
787 
788  case TEX_FILTER_BOX:
789  return _ResizeBoxFilter( srcImage, filter, destImage );
790 
791  case TEX_FILTER_LINEAR:
792  return _ResizeLinearFilter( srcImage, filter, destImage );
793 
794  case TEX_FILTER_CUBIC:
795  return _ResizeCubicFilter( srcImage, filter, destImage );
796 
797  case TEX_FILTER_TRIANGLE:
798  return _ResizeTriangleFilter( srcImage, filter, destImage );
799 
800  default:
801  return HRESULT_FROM_WIN32( ERROR_NOT_SUPPORTED );
802  }
803 }
804 
805 
806 //=====================================================================================
807 // Entry-points
808 //=====================================================================================
809 
810 //-------------------------------------------------------------------------------------
811 // Resize image
812 //-------------------------------------------------------------------------------------
813 _Use_decl_annotations_
814 HRESULT Resize( const Image& srcImage, size_t width, size_t height, DWORD filter, ScratchImage& image )
815 {
816  if ( width == 0 || height == 0 )
817  return E_INVALIDARG;
818 
819 #ifdef _M_X64
820  if ( (srcImage.width > 0xFFFFFFFF) || (srcImage.height > 0xFFFFFFFF) )
821  return E_INVALIDARG;
822 
823  if ( (width > 0xFFFFFFFF) || (height > 0xFFFFFFFF) )
824  return E_INVALIDARG;
825 #endif
826 
827  if ( !srcImage.pixels )
828  return E_POINTER;
829 
830  if ( IsCompressed( srcImage.format ) )
831  {
832  // We don't support resizing compressed images
833  return HRESULT_FROM_WIN32( ERROR_NOT_SUPPORTED );
834  }
835 
836  HRESULT hr = image.Initialize2D( srcImage.format, width, height, 1, 1 );
837  if ( FAILED(hr) )
838  return hr;
839 
840  const Image *rimage = image.GetImage( 0, 0, 0 );
841  if ( !rimage )
842  return E_POINTER;
843 
844  if ( _UseWICFiltering( srcImage.format, filter ) )
845  {
846  WICPixelFormatGUID pfGUID;
847  if ( _DXGIToWIC( srcImage.format, pfGUID, true ) )
848  {
849  // Case 1: Source format is supported by Windows Imaging Component
850  hr = _PerformResizeUsingWIC( srcImage, filter, pfGUID, *rimage );
851  }
852  else
853  {
854  // Case 2: Source format is not supported by WIC, so we have to convert, resize, and convert back
855  hr = _PerformResizeViaF32( srcImage, filter, *rimage );
856  }
857  }
858  else
859  {
860  hr = _PerformResizeUsingCustomFilters( srcImage, filter, *rimage );
861  }
862 
863  if ( FAILED(hr) )
864  {
865  image.Release();
866  return hr;
867  }
868 
869  return S_OK;
870 }
871 
872 
873 //-------------------------------------------------------------------------------------
874 // Resize image (complex)
875 //-------------------------------------------------------------------------------------
876 _Use_decl_annotations_
877 HRESULT Resize( const Image* srcImages, size_t nimages, const TexMetadata& metadata,
878  size_t width, size_t height, DWORD filter, ScratchImage& result )
879 {
880  if ( !srcImages || !nimages || width == 0 || height == 0 )
881  return E_INVALIDARG;
882 
883 #ifdef _M_X64
884  if ( (width > 0xFFFFFFFF) || (height > 0xFFFFFFFF) )
885  return E_INVALIDARG;
886 #endif
887 
888  TexMetadata mdata2 = metadata;
889  mdata2.width = width;
890  mdata2.height = height;
891  mdata2.mipLevels = 1;
892  HRESULT hr = result.Initialize( mdata2 );
893  if ( FAILED(hr) )
894  return hr;
895 
896  bool usewic = _UseWICFiltering( metadata.format, filter );
897 
898  WICPixelFormatGUID pfGUID = {0};
899  bool wicpf = ( usewic ) ? _DXGIToWIC( metadata.format, pfGUID, true ) : false;
900 
901  switch ( metadata.dimension )
902  {
905  assert( metadata.depth == 1 );
906 
907  for( size_t item = 0; item < metadata.arraySize; ++item )
908  {
909  size_t srcIndex = metadata.ComputeIndex( 0, item, 0 );
910  if ( srcIndex >= nimages )
911  {
912  result.Release();
913  return E_FAIL;
914  }
915 
916  const Image* srcimg = &srcImages[ srcIndex ];
917  const Image* destimg = result.GetImage( 0, item, 0 );
918  if ( !srcimg || !destimg )
919  {
920  result.Release();
921  return E_POINTER;
922  }
923 
924  if ( srcimg->format != metadata.format )
925  {
926  result.Release();
927  return E_FAIL;
928  }
929 
930 #ifdef _M_X64
931  if ( (srcimg->width > 0xFFFFFFFF) || (srcimg->height > 0xFFFFFFFF) )
932  {
933  result.Release();
934  return E_FAIL;
935  }
936 #endif
937 
938  if ( usewic )
939  {
940  if ( wicpf )
941  {
942  // Case 1: Source format is supported by Windows Imaging Component
943  hr = _PerformResizeUsingWIC( *srcimg, filter, pfGUID, *destimg );
944  }
945  else
946  {
947  // Case 2: Source format is not supported by WIC, so we have to convert, resize, and convert back
948  hr = _PerformResizeViaF32( *srcimg, filter, *destimg );
949  }
950  }
951  else
952  {
953  // Case 3: not using WIC resizing
954  hr = _PerformResizeUsingCustomFilters( *srcimg, filter, *destimg );
955  }
956 
957  if ( FAILED(hr) )
958  {
959  result.Release();
960  return hr;
961  }
962  }
963  break;
964 
966  assert( metadata.arraySize == 1 );
967 
968  for( size_t slice = 0; slice < metadata.depth; ++slice )
969  {
970  size_t srcIndex = metadata.ComputeIndex( 0, 0, slice );
971  if ( srcIndex >= nimages )
972  {
973  result.Release();
974  return E_FAIL;
975  }
976 
977  const Image* srcimg = &srcImages[ srcIndex ];
978  const Image* destimg = result.GetImage( 0, 0, slice );
979  if ( !srcimg || !destimg )
980  {
981  result.Release();
982  return E_POINTER;
983  }
984 
985  if ( srcimg->format != metadata.format )
986  {
987  result.Release();
988  return E_FAIL;
989  }
990 
991 #ifdef _M_X64
992  if ( (srcimg->width > 0xFFFFFFFF) || (srcimg->height > 0xFFFFFFFF) )
993  {
994  result.Release();
995  return E_FAIL;
996  }
997 #endif
998 
999  if ( usewic )
1000  {
1001  if ( wicpf )
1002  {
1003  // Case 1: Source format is supported by Windows Imaging Component
1004  hr = _PerformResizeUsingWIC( *srcimg, filter, pfGUID, *destimg );
1005  }
1006  else
1007  {
1008  // Case 2: Source format is not supported by WIC, so we have to convert, resize, and convert back
1009  hr = _PerformResizeViaF32( *srcimg, filter, *destimg );
1010  }
1011  }
1012  else
1013  {
1014  // Case 3: not using WIC resizing
1015  hr = _PerformResizeUsingCustomFilters( *srcimg, filter, *destimg );
1016  }
1017 
1018  if ( FAILED(hr) )
1019  {
1020  result.Release();
1021  return hr;
1022  }
1023  }
1024  break;
1025 
1026  default:
1027  result.Release();
1028  return E_FAIL;
1029  }
1030 
1031  return S_OK;
1032 }
1033 
1034 }; // namespace
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 _PerformResizeViaF32(_In_ const Image &srcImage, _In_ DWORD filter, _In_ const Image &destImage)
uint8_t * pixels
Definition: DirectXTex.h:230
size_t BitsPerColor(_In_ DXGI_FORMAT fmt)
HRESULT Resize(_In_ const Image &srcImage, _In_ size_t width, _In_ size_t height, _In_ DWORD filter, _Out_ ScratchImage &image)
DXGI_FORMAT format
Definition: DirectXTex.h:125
static HRESULT _ResizePointFilter(_In_ const Image &srcImage, _In_ const Image &destImage)
_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)
static HRESULT _PerformResizeUsingCustomFilters(_In_ const Image &srcImage, _In_ DWORD filter, _In_ const Image &destImage)
_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)
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()
static HRESULT _ResizeCubicFilter(_In_ const Image &srcImage, _In_ DWORD filter, _In_ const Image &destImage)
size_t ComputeIndex(_In_ size_t mip, _In_ size_t item, _In_ size_t slice) const
bool IsCompressed(_In_ DXGI_FORMAT fmt)
_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
static HRESULT _PerformResizeUsingWIC(_In_ const Image &srcImage, _In_ DWORD filter, _In_ const WICPixelFormatGUID &pfGUID, _In_ const Image &destImage)
static HRESULT _ResizeTriangleFilter(_In_ const Image &srcImage, _In_ DWORD filter, _In_ const Image &destImage)
WICBitmapDitherType _GetWICDither(_In_ DWORD flags)
Definition: DirectXTexP.h:64
_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)
TEX_DIMENSION dimension
Definition: DirectXTex.h:126
static HRESULT _ResizeBoxFilter(_In_ const Image &srcImage, _In_ DWORD filter, _In_ const Image &destImage)
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 bool _UseWICFiltering(_In_ DXGI_FORMAT format, _In_ DWORD filter)
static HRESULT _ResizeLinearFilter(_In_ const Image &srcImage, _In_ DWORD filter, _In_ const Image &destImage)
#define TEX_FILTER_MASK
Definition: DirectXTexP.h:49
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)
#define AVERAGE4(res, p0, p1, p2, p3)
Definition: Filters.h:35
_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
WICBitmapInterpolationMode _GetWICInterp(_In_ DWORD flags)
Definition: DirectXTexP.h:84