Paradox Game Engine  v1.0.0 beta06
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Events Macros Pages
DirectXTexMisc.cpp
Go to the documentation of this file.
1 //-------------------------------------------------------------------------------------
2 // DirectXTexMisc.cpp
3 //
4 // DirectX Texture Library - Misc image 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 namespace DirectX
19 {
20 static const XMVECTORF32 g_Gamma22 = { 2.2f, 2.2f, 2.2f, 1.f };
21 
22 //-------------------------------------------------------------------------------------
23 static HRESULT _ComputeMSE( _In_ const Image& image1, _In_ const Image& image2,
24  _Out_ float& mse, _Out_writes_opt_(4) float* mseV,
25  _In_ DWORD flags )
26 {
27  if ( !image1.pixels || !image2.pixels )
28  return E_POINTER;
29 
30  assert( image1.width == image2.width && image1.height == image2.height );
31  assert( !IsCompressed( image1.format ) && !IsCompressed( image2.format ) );
32 
33  const size_t width = image1.width;
34 
35  ScopedAlignedArrayXMVECTOR scanline( reinterpret_cast<XMVECTOR*>( _aligned_malloc( (sizeof(XMVECTOR)*width)*2, 16 ) ) );
36  if ( !scanline )
37  return E_OUTOFMEMORY;
38 
39  // Flags implied from image formats
40  switch( image1.format )
41  {
42  case DXGI_FORMAT_B8G8R8X8_UNORM:
43  flags |= CMSE_IGNORE_ALPHA;
44  break;
45 
46  case DXGI_FORMAT_B8G8R8X8_UNORM_SRGB:
48  break;
49 
50  case DXGI_FORMAT_R8G8B8A8_UNORM_SRGB:
51  case DXGI_FORMAT_BC1_UNORM_SRGB:
52  case DXGI_FORMAT_BC2_UNORM_SRGB:
53  case DXGI_FORMAT_BC3_UNORM_SRGB:
54  case DXGI_FORMAT_B8G8R8A8_UNORM_SRGB:
55  case DXGI_FORMAT_BC7_UNORM_SRGB:
56  flags |= CMSE_IMAGE1_SRGB;
57  break;
58  }
59 
60  switch( image2.format )
61  {
62  case DXGI_FORMAT_B8G8R8X8_UNORM:
63  flags |= CMSE_IGNORE_ALPHA;
64  break;
65 
66  case DXGI_FORMAT_B8G8R8X8_UNORM_SRGB:
68  break;
69 
70  case DXGI_FORMAT_R8G8B8A8_UNORM_SRGB:
71  case DXGI_FORMAT_BC1_UNORM_SRGB:
72  case DXGI_FORMAT_BC2_UNORM_SRGB:
73  case DXGI_FORMAT_BC3_UNORM_SRGB:
74  case DXGI_FORMAT_B8G8R8A8_UNORM_SRGB:
75  case DXGI_FORMAT_BC7_UNORM_SRGB:
76  flags |= CMSE_IMAGE2_SRGB;
77  break;
78  }
79 
80  const uint8_t *pSrc1 = image1.pixels;
81  const size_t rowPitch1 = image1.rowPitch;
82 
83  const uint8_t *pSrc2 = image2.pixels;
84  const size_t rowPitch2 = image2.rowPitch;
85 
86  XMVECTOR acc = g_XMZero;
87  static XMVECTORF32 two = { 2.0f, 2.0f, 2.0f, 2.0f };
88 
89  for( size_t h = 0; h < image1.height; ++h )
90  {
91  XMVECTOR* ptr1 = scanline.get();
92  if ( !_LoadScanline( ptr1, width, pSrc1, rowPitch1, image1.format ) )
93  return E_FAIL;
94 
95  XMVECTOR* ptr2 = scanline.get() + width;
96  if ( !_LoadScanline( ptr2, width, pSrc2, rowPitch2, image2.format ) )
97  return E_FAIL;
98 
99  for( size_t i = 0; i < width; ++i )
100  {
101  XMVECTOR v1 = *(ptr1++);
102  if ( flags & CMSE_IMAGE1_SRGB )
103  {
104  v1 = XMVectorPow( v1, g_Gamma22 );
105  }
106  if ( flags & CMSE_IMAGE1_X2_BIAS )
107  {
108  v1 = XMVectorMultiplyAdd( v1, two, g_XMNegativeOne );
109  }
110 
111  XMVECTOR v2 = *(ptr2++);
112  if ( flags & CMSE_IMAGE2_SRGB )
113  {
114  v2 = XMVectorPow( v2, g_Gamma22 );
115  }
116  if ( flags & CMSE_IMAGE2_X2_BIAS )
117  {
118  v1 = XMVectorMultiplyAdd( v2, two, g_XMNegativeOne );
119  }
120 
121  // sum[ (I1 - I2)^2 ]
122  XMVECTOR v = XMVectorSubtract( v1, v2 );
123  if ( flags & CMSE_IGNORE_RED )
124  {
125  v = XMVectorSelect( v, g_XMZero, g_XMMaskX );
126  }
127  if ( flags & CMSE_IGNORE_GREEN )
128  {
129  v = XMVectorSelect( v, g_XMZero, g_XMMaskY );
130  }
131  if ( flags & CMSE_IGNORE_BLUE )
132  {
133  v = XMVectorSelect( v, g_XMZero, g_XMMaskZ );
134  }
135  if ( flags & CMSE_IGNORE_ALPHA )
136  {
137  v = XMVectorSelect( v, g_XMZero, g_XMMaskW );
138  }
139 
140  acc = XMVectorMultiplyAdd( v, v, acc );
141  }
142 
143  pSrc1 += rowPitch1;
144  pSrc2 += rowPitch2;
145  }
146 
147  // MSE = sum[ (I1 - I2)^2 ] / w*h
148  XMVECTOR d = XMVectorReplicate( float(image1.width * image1.height) );
149  XMVECTOR v = XMVectorDivide( acc, d );
150  if ( mseV )
151  {
152  XMStoreFloat4( reinterpret_cast<XMFLOAT4*>( mseV ), v );
153  mse = mseV[0] + mseV[1] + mseV[2] + mseV[3];
154  }
155  else
156  {
157  XMFLOAT4 _mseV;
158  XMStoreFloat4( &_mseV, v );
159  mse = _mseV.x + _mseV.y + _mseV.z + _mseV.w;
160  }
161 
162  return S_OK;
163 }
164 
165 
166 //=====================================================================================
167 // Entry points
168 //=====================================================================================
169 
170 //-------------------------------------------------------------------------------------
171 // Copies a rectangle from one image into another
172 //-------------------------------------------------------------------------------------
173 _Use_decl_annotations_
174 HRESULT CopyRectangle( const Image& srcImage, const Rect& srcRect, const Image& dstImage, DWORD filter, size_t xOffset, size_t yOffset )
175 {
176  if ( !srcImage.pixels || !dstImage.pixels )
177  return E_POINTER;
178 
179  if ( IsCompressed( srcImage.format ) || IsCompressed( dstImage.format )
180  || IsPlanar( srcImage.format ) || IsPlanar( dstImage.format )
181  || IsPalettized( srcImage.format ) || IsPalettized( dstImage.format ) )
182  return HRESULT_FROM_WIN32( ERROR_NOT_SUPPORTED );
183 
184  // Validate rectangle/offset
185  if ( !srcRect.w || !srcRect.h || ( (srcRect.x + srcRect.w) > srcImage.width ) || ( (srcRect.y + srcRect.h) > srcImage.height ) )
186  {
187  return E_INVALIDARG;
188  }
189 
190  if ( ( (xOffset + srcRect.w) > dstImage.width ) || ( (yOffset + srcRect.h) > dstImage.height ) )
191  {
192  return E_INVALIDARG;
193  }
194 
195  // Compute source bytes-per-pixel
196  size_t sbpp = BitsPerPixel( srcImage.format );
197  if ( !sbpp )
198  return E_FAIL;
199 
200  if ( sbpp < 8 )
201  {
202  // We don't support monochrome (DXGI_FORMAT_R1_UNORM)
203  return HRESULT_FROM_WIN32( ERROR_NOT_SUPPORTED );
204  }
205 
206  const uint8_t* pEndSrc = srcImage.pixels + srcImage.rowPitch*srcImage.height;
207  const uint8_t* pEndDest = dstImage.pixels + dstImage.rowPitch*dstImage.height;
208 
209  // Round to bytes
210  sbpp = ( sbpp + 7 ) / 8;
211 
212  const uint8_t* pSrc = srcImage.pixels + (srcRect.y * srcImage.rowPitch) + (srcRect.x * sbpp);
213 
214  if ( srcImage.format == dstImage.format )
215  {
216  // Direct copy case (avoid intermediate conversions)
217  uint8_t* pDest = dstImage.pixels + (yOffset * dstImage.rowPitch) + (xOffset * sbpp);
218  const size_t copyW = srcRect.w * sbpp;
219  for( size_t h=0; h < srcRect.h; ++h )
220  {
221  if ( ( (pSrc+copyW) > pEndSrc ) || (pDest > pEndDest) )
222  return E_FAIL;
223 
224  memcpy_s( pDest, pEndDest - pDest, pSrc, copyW );
225 
226  pSrc += srcImage.rowPitch;
227  pDest += dstImage.rowPitch;
228  }
229 
230  return S_OK;
231  }
232 
233  // Compute destination bytes-per-pixel (not the same format as source)
234  size_t dbpp = BitsPerPixel( dstImage.format );
235  if ( !dbpp )
236  return E_FAIL;
237 
238  if ( dbpp < 8 )
239  {
240  // We don't support monochrome (DXGI_FORMAT_R1_UNORM)
241  return HRESULT_FROM_WIN32( ERROR_NOT_SUPPORTED );
242  }
243 
244  // Round to bytes
245  dbpp = ( dbpp + 7 ) / 8;
246 
247  uint8_t* pDest = dstImage.pixels + (yOffset * dstImage.rowPitch) + (xOffset * dbpp);
248 
249  ScopedAlignedArrayXMVECTOR scanline( reinterpret_cast<XMVECTOR*>( _aligned_malloc( (sizeof(XMVECTOR)*srcRect.w), 16 ) ) );
250  if ( !scanline )
251  return E_OUTOFMEMORY;
252 
253  const size_t copyS = srcRect.w * sbpp;
254  const size_t copyD = srcRect.w * dbpp;
255 
256  for( size_t h=0; h < srcRect.h; ++h )
257  {
258  if ( ( (pSrc+copyS) > pEndSrc) || ((pDest+copyD) > pEndDest) )
259  return E_FAIL;
260 
261  if ( !_LoadScanline( scanline.get(), srcRect.w, pSrc, copyS, srcImage.format ) )
262  return E_FAIL;
263 
264  _ConvertScanline( scanline.get(), srcRect.w, dstImage.format, srcImage.format, filter );
265 
266  if ( !_StoreScanline( pDest, copyD, dstImage.format, scanline.get(), srcRect.w ) )
267  return E_FAIL;
268 
269  pSrc += srcImage.rowPitch;
270  pDest += dstImage.rowPitch;
271  }
272 
273  return S_OK;
274 }
275 
276 
277 //-------------------------------------------------------------------------------------
278 // Computes the Mean-Squared-Error (MSE) between two images
279 //-------------------------------------------------------------------------------------
280 _Use_decl_annotations_
281 HRESULT ComputeMSE( const Image& image1, const Image& image2, float& mse, float* mseV, DWORD flags )
282 {
283  if ( !image1.pixels || !image2.pixels )
284  return E_POINTER;
285 
286  if ( image1.width != image2.width || image1.height != image2.height )
287  return E_INVALIDARG;
288 
289  if ( IsPlanar( image1.format ) || IsPlanar( image2.format )
290  || IsPalettized( image1.format ) || IsPalettized( image2.format ) )
291  return HRESULT_FROM_WIN32( ERROR_NOT_SUPPORTED );
292 
293  if ( IsCompressed(image1.format) )
294  {
295  if ( IsCompressed(image2.format) )
296  {
297  // Case 1: both images are compressed, expand to RGBA32F
298  ScratchImage temp1;
299  HRESULT hr = Decompress( image1, DXGI_FORMAT_R32G32B32A32_FLOAT, temp1 );
300  if ( FAILED(hr) )
301  return hr;
302 
303  ScratchImage temp2;
304  hr = Decompress( image2, DXGI_FORMAT_R32G32B32A32_FLOAT, temp2 );
305  if ( FAILED(hr) )
306  return hr;
307 
308  const Image* img1 = temp1.GetImage(0,0,0);
309  const Image* img2 = temp2.GetImage(0,0,0);
310  if ( !img1 || !img2 )
311  return E_POINTER;
312 
313  return _ComputeMSE( *img1, *img2, mse, mseV, flags );
314  }
315  else
316  {
317  // Case 2: image1 is compressed, expand to RGBA32F
318  ScratchImage temp;
319  HRESULT hr = Decompress( image1, DXGI_FORMAT_R32G32B32A32_FLOAT, temp );
320  if ( FAILED(hr) )
321  return hr;
322 
323  const Image* img = temp.GetImage(0,0,0);
324  if ( !img )
325  return E_POINTER;
326 
327  return _ComputeMSE( *img, image2, mse, mseV, flags );
328  }
329  }
330  else
331  {
332  if ( IsCompressed(image2.format) )
333  {
334  // Case 3: image2 is compressed, expand to RGBA32F
335  ScratchImage temp;
336  HRESULT hr = Decompress( image2, DXGI_FORMAT_R32G32B32A32_FLOAT, temp );
337  if ( FAILED(hr) )
338  return hr;
339 
340  const Image* img = temp.GetImage(0,0,0);
341  if ( !img )
342  return E_POINTER;
343 
344  return _ComputeMSE( image1, *img, mse, mseV, flags );
345  }
346  else
347  {
348  // Case 4: neither image is compressed
349  return _ComputeMSE( image1, image2, mse, mseV, flags );
350  }
351  }
352 }
353 
354 }; // 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
uint8_t * pixels
Definition: DirectXTex.h:230
bool IsPlanar(_In_ DXGI_FORMAT fmt)
_In_ size_t _In_ DXGI_FORMAT _In_ size_t _In_ DXGI_FORMAT _In_ DWORD flags
Definition: DirectXTexP.h:170
size_t _In_ DXGI_FORMAT size_t _In_ TEXP_LEGACY_FORMAT _In_ DWORD flags assert(pDestination &&outSize > 0)
_Use_decl_annotations_ bool _LoadScanline(XMVECTOR *pDestination, size_t count, LPCVOID pSource, size_t size, DXGI_FORMAT format)
bool IsCompressed(_In_ DXGI_FORMAT fmt)
size_t rowPitch
Definition: DirectXTex.h:228
static const XMVECTORF32 g_Gamma22
_Use_decl_annotations_ void _ConvertScanline(XMVECTOR *pBuffer, size_t count, DXGI_FORMAT outFormat, DXGI_FORMAT inFormat, DWORD flags)
bool IsPalettized(_In_ DXGI_FORMAT fmt)
HRESULT ComputeMSE(_In_ const Image &image1, _In_ const Image &image2, _Out_ float &mse, _Out_writes_opt_(4) float *mseV, _In_ DWORD flags=0)
HRESULT Decompress(_In_ const Image &cImage, _In_ DXGI_FORMAT format, _Out_ ScratchImage &image)
static HRESULT _ComputeMSE(_In_ const Image &image1, _In_ const Image &image2, _Out_ float &mse, _Out_writes_opt_(4) float *mseV, _In_ DWORD flags)
DXGI_FORMAT format
Definition: DirectXTex.h:227
HRESULT CopyRectangle(_In_ const Image &srcImage, _In_ const Rect &srcRect, _In_ const Image &dstImage, _In_ DWORD filter, _In_ size_t xOffset, _In_ size_t yOffset)
size_t BitsPerPixel(_In_ DXGI_FORMAT fmt)
_Use_decl_annotations_ bool _StoreScanline(LPVOID pDestination, size_t size, DXGI_FORMAT format, const XMVECTOR *pSource, size_t count, float threshold)