Paradox Game Engine  v1.0.0 beta06
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Events Macros Pages
Filters.h
Go to the documentation of this file.
1 //-------------------------------------------------------------------------------------
2 // filters.h
3 //
4 // Utility header with helpers for implementing image filters
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 
14 #if defined(_MSC_VER) && (_MSC_VER > 1000)
15 #pragma once
16 #endif
17 
18 #include <directxmath.h>
19 #include <directxpackedvector.h>
20 
21 #include <memory>
22 
23 #include "scoped.h"
24 
25 namespace DirectX
26 {
27 
28 //-------------------------------------------------------------------------------------
29 // Box filtering helpers
30 //-------------------------------------------------------------------------------------
31 
32 XMGLOBALCONST XMVECTORF32 g_boxScale = { 0.25f, 0.25f, 0.25f, 0.25f };
33 XMGLOBALCONST XMVECTORF32 g_boxScale3D = { 0.125f, 0.125f, 0.125f, 0.125f };
34 
35 #define AVERAGE4( res, p0, p1, p2, p3 ) \
36 { \
37  XMVECTOR v = XMVectorAdd( (p0), (p1) ); \
38  v = XMVectorAdd( v, (p2) ); \
39  v = XMVectorAdd( v, (p3) ); \
40  res = XMVectorMultiply( v, g_boxScale ); \
41 }
42 
43 #define AVERAGE8( res, p0, p1, p2, p3, p4, p5, p6, p7) \
44 { \
45  XMVECTOR v = XMVectorAdd( (p0), (p1) ); \
46  v = XMVectorAdd( v, (p2) ); \
47  v = XMVectorAdd( v, (p3) ); \
48  v = XMVectorAdd( v, (p4) ); \
49  v = XMVectorAdd( v, (p5) ); \
50  v = XMVectorAdd( v, (p6) ); \
51  v = XMVectorAdd( v, (p7) ); \
52  res = XMVectorMultiply( v, g_boxScale3D ); \
53 }
54 
55 
56 //-------------------------------------------------------------------------------------
57 // Linear filtering helpers
58 //-------------------------------------------------------------------------------------
59 
61 {
62  size_t u0;
63  float weight0;
64  size_t u1;
65  float weight1;
66 };
67 
68 inline void _CreateLinearFilter( _In_ size_t source, _In_ size_t dest, _In_ bool wrap, _Out_writes_(dest) LinearFilter* lf )
69 {
70  assert( source > 0 );
71  assert( dest > 0 );
72  assert( lf != 0 );
73 
74  float scale = float(source) / float(dest);
75 
76  // Mirror is the same case as clamp for linear
77 
78  for( size_t u = 0; u < dest; ++u )
79  {
80  float srcB = ( float(u) + 0.5f ) * scale + 0.5f;
81 
82  ptrdiff_t isrcB = ptrdiff_t(srcB);
83  ptrdiff_t isrcA = isrcB - 1;
84 
85  if ( isrcA < 0 )
86  {
87  isrcA = ( wrap ) ? ( source - 1) : 0;
88  }
89 
90  if ( size_t(isrcB) >= source )
91  {
92  isrcB = ( wrap ) ? 0 : ( source - 1);
93  }
94 
95  float weight = 1.0f + float(isrcB) - srcB;
96 
97  auto& entry = lf[ u ];
98  entry.u0 = size_t(isrcA);
99  entry.weight0 = weight;
100 
101  entry.u1 = size_t(isrcB);
102  entry.weight1 = 1.0f - weight;
103  }
104 }
105 
106 #define BILINEAR_INTERPOLATE( res, x, y, r0, r1 ) \
107  res = ( y.weight0 * ( (r0)[ x.u0 ] * x.weight0 + (r0)[ x.u1 ] * x.weight1 ) ) \
108  + ( y.weight1 * ( (r1)[ x.u0 ] * x.weight0 + (r1)[ x.u1 ] * x.weight1 ) )
109 
110 #define TRILINEAR_INTERPOLATE( res, x, y, z, r0, r1, r2, r3 ) \
111  res = ( z.weight0 * ( ( y.weight0 * ( (r0)[ x.u0 ] * x.weight0 + (r0)[ x.u1 ] * x.weight1 ) ) \
112  + ( y.weight1 * ( (r1)[ x.u0 ] * x.weight0 + (r1)[ x.u1 ] * x.weight1 ) ) ) ) \
113  + ( z.weight1 * ( ( y.weight0 * ( (r2)[ x.u0 ] * x.weight0 + (r2)[ x.u1 ] * x.weight1 ) ) \
114  + ( y.weight1 * ( (r3)[ x.u0 ] * x.weight0 + (r3)[ x.u1 ] * x.weight1 ) ) ) )
115 
116 
117 //-------------------------------------------------------------------------------------
118 // Cubic filtering helpers
119 //-------------------------------------------------------------------------------------
120 
121 XMGLOBALCONST XMVECTORF32 g_cubicThird = { 1.f/3.f, 1.f/3.f, 1.f/3.f, 1.f/3.f };
122 XMGLOBALCONST XMVECTORF32 g_cubicSixth = { 1.f/6.f, 1.f/6.f, 1.f/6.f, 1.f/6.f };
123 XMGLOBALCONST XMVECTORF32 g_cubicHalf = { 1.f/2.f, 1.f/2.f, 1.f/2.f, 1.f/2.f };
124 
125 inline ptrdiff_t bounduvw( ptrdiff_t u, ptrdiff_t maxu, bool wrap, bool mirror )
126 {
127  if ( wrap )
128  {
129  if ( u < 0 )
130  {
131  u = maxu + u + 1;
132  }
133  else if ( u > maxu )
134  {
135  u = u - maxu - 1;
136  }
137  }
138  else if ( mirror )
139  {
140  if ( u < 0 )
141  {
142  u = ( -u ) - 1;
143  }
144  else if ( u > maxu )
145  {
146  u = maxu - (u - maxu - 1);
147  }
148  }
149 
150  // Handles clamp, but also a safety factor for degenerate images for wrap/mirror
151  u = std::min<ptrdiff_t>( u, maxu );
152  u = std::max<ptrdiff_t>( u, 0 );
153 
154  return u;
155 }
156 
158 {
159  size_t u0;
160  size_t u1;
161  size_t u2;
162  size_t u3;
163  float x;
164 };
165 
166 inline void _CreateCubicFilter( _In_ size_t source, _In_ size_t dest, _In_ bool wrap, _In_ bool mirror, _Out_writes_(dest) CubicFilter* cf )
167 {
168  assert( source > 0 );
169  assert( dest > 0 );
170  assert( cf != 0 );
171 
172  float scale = float(source) / float(dest);
173 
174  for( size_t u = 0; u < dest; ++u )
175  {
176  float srcB = ( float(u) + 0.5f ) * scale - 0.5f;
177 
178  ptrdiff_t isrcB = bounduvw( ptrdiff_t(srcB), source - 1, wrap, mirror );
179  ptrdiff_t isrcA = bounduvw( isrcB - 1, source - 1, wrap, mirror );
180  ptrdiff_t isrcC = bounduvw( isrcB + 1, source - 1, wrap, mirror );
181  ptrdiff_t isrcD = bounduvw( isrcB + 2, source - 1, wrap, mirror );
182 
183  auto& entry = cf[ u ];
184  entry.u0 = size_t(isrcA);
185  entry.u1 = size_t(isrcB);
186  entry.u2 = size_t(isrcC);
187  entry.u3 = size_t(isrcD);
188 
189  float x = srcB - float(isrcB);
190  entry.x = x;
191  }
192 }
193 
194 #define CUBIC_INTERPOLATE( res, dx, p0, p1, p2, p3 ) \
195 { \
196  XMVECTOR a0 = (p1); \
197  XMVECTOR d0 = (p0) - a0; \
198  XMVECTOR d2 = (p2) - a0; \
199  XMVECTOR d3 = (p3) - a0; \
200  XMVECTOR a1 = d2 - g_cubicThird*d0 - g_cubicSixth*d3; \
201  XMVECTOR a2 = g_cubicHalf*d0 + g_cubicHalf*d2; \
202  XMVECTOR a3 = g_cubicSixth*d3 - g_cubicSixth*d0 - g_cubicHalf*d2; \
203  XMVECTOR vdx = XMVectorReplicate( dx ); \
204  XMVECTOR vdx2 = vdx * vdx; \
205  XMVECTOR vdx3 = vdx2 * vdx; \
206  res = a0 + a1*vdx + a2*vdx2 + a3*vdx3; \
207 }
208 
209 
210 //-------------------------------------------------------------------------------------
211 // Triangle filtering helpers
212 //-------------------------------------------------------------------------------------
213 
214 namespace TriangleFilter
215 {
216  struct FilterTo
217  {
218  size_t u;
219  float weight;
220  };
221 
222  struct FilterFrom
223  {
224  size_t count;
225  size_t sizeInBytes;
226  FilterTo to[1]; // variable-sized array
227  };
228 
229  struct Filter
230  {
231  size_t sizeInBytes;
232  size_t totalSize;
233  FilterFrom from[1]; // variable-sized array
234  };
235 
236  struct TriangleRow
237  {
238  size_t remaining;
241 
242  TriangleRow() : remaining(0), next(nullptr) {}
243  };
244 
245  static const size_t TF_FILTER_SIZE = sizeof(Filter) - sizeof(FilterFrom);
246  static const size_t TF_FROM_SIZE = sizeof(FilterFrom) - sizeof(FilterTo);
247  static const size_t TF_TO_SIZE = sizeof(FilterTo);
248 
249  static const float TF_EPSILON = 0.00001f;
250 
251  inline HRESULT _Create( _In_ size_t source, _In_ size_t dest, _In_ bool wrap, _Inout_ std::unique_ptr<Filter>& tf )
252  {
253  assert( source > 0 );
254  assert( dest > 0 );
255 
256  float scale = float(dest) / float(source);
257  float scaleInv = 0.5f / scale;
258 
259  // Determine storage required for filter and allocate memory if needed
260  size_t totalSize = TF_FILTER_SIZE + TF_FROM_SIZE + TF_TO_SIZE;
261  float repeat = (wrap) ? 1.f : 0.f;
262 
263  for( size_t u = 0; u < source; ++u )
264  {
265  float src = float(u) - 0.5f;
266  float destMin = src * scale;
267  float destMax = destMin + scale;
268 
269  totalSize += TF_FROM_SIZE + TF_TO_SIZE + size_t( destMax - destMin + repeat + 1.f ) * TF_TO_SIZE * 2;
270  }
271 
272  uint8_t* pFilter = nullptr;
273 
274  if ( tf )
275  {
276  // See if existing filter memory block is large enough to reuse
277  if ( tf->totalSize >= totalSize )
278  {
279  pFilter = reinterpret_cast<uint8_t*>( tf.get() );
280  }
281  else
282  {
283  // Need to reallocate filter memory block
284  tf.reset( nullptr );
285  }
286  }
287 
288  if ( !tf )
289  {
290  // Allocate filter memory block
291  pFilter = new (std::nothrow) uint8_t[ totalSize ];
292  if ( !pFilter )
293  return E_OUTOFMEMORY;
294 
295  tf.reset( reinterpret_cast<Filter*>( pFilter ) );
296  tf->totalSize = totalSize;
297  }
298 
299  assert( pFilter != 0 );
300 
301  // Filter setup
302  size_t sizeInBytes = TF_FILTER_SIZE;
303  size_t accumU = 0;
304  float accumWeight = 0.f;
305 
306  for( size_t u = 0; u < source; ++u )
307  {
308  // Setup from entry
309  size_t sizeFrom = sizeInBytes;
310  auto pFrom = reinterpret_cast<FilterFrom*>( pFilter + sizeInBytes );
311  sizeInBytes += TF_FROM_SIZE;
312 
313  if ( sizeInBytes > totalSize )
314  return E_FAIL;
315 
316  size_t toCount = 0;
317 
318  // Perform two passes to capture the influences from both sides
319  for( size_t j = 0; j < 2; ++j )
320  {
321  float src = float( u + j ) - 0.5f;
322 
323  float destMin = src * scale;
324  float destMax = destMin + scale;
325 
326  if ( !wrap )
327  {
328  // Clamp
329  if ( destMin < 0.f )
330  destMin = 0.f;
331  if ( destMax > float(dest) )
332  destMax = float(dest);
333  }
334 
335  for( auto k = static_cast<ptrdiff_t>( floorf( destMin ) ); float(k) < destMax; ++k )
336  {
337  float d0 = float(k);
338  float d1 = d0 + 1.f;
339 
340  size_t u0;
341  if ( k < 0 )
342  {
343  // Handle wrap
344  u0 = size_t( k + ptrdiff_t(dest) );
345  }
346  else if ( k >= ptrdiff_t(dest) )
347  {
348  // Handle wrap
349  u0 = size_t( k - ptrdiff_t(dest) );
350  }
351  else
352  {
353  u0 = size_t( k );
354  }
355 
356  // Save previous accumulated weight (if any)
357  if ( u0 != accumU )
358  {
359  if ( accumWeight > TF_EPSILON )
360  {
361  auto pTo = reinterpret_cast<FilterTo*>( pFilter + sizeInBytes );
362  sizeInBytes += TF_TO_SIZE;
363  ++toCount;
364 
365  if ( sizeInBytes > totalSize )
366  return E_FAIL;
367 
368  pTo->u = accumU;
369  pTo->weight = accumWeight;
370  }
371 
372  accumWeight = 0.f;
373  accumU = u0;
374  }
375 
376  // Clip destination
377  if ( d0 < destMin )
378  d0 = destMin;
379  if ( d1 > destMax )
380  d1 = destMax;
381 
382  // Calculate average weight over destination pixel
383 
384  float weight;
385  if ( !wrap && src < 0.f )
386  weight = 1.f;
387  else if ( !wrap && ( ( src + 1.f ) >= float(source) ) )
388  weight = 0.f;
389  else
390  weight = (d0 + d1) * scaleInv - src;
391 
392  accumWeight += (d1 - d0) * ( j ? (1.f - weight) : weight );
393  }
394  }
395 
396  // Store accumulated weight
397  if ( accumWeight > TF_EPSILON )
398  {
399  auto pTo = reinterpret_cast<FilterTo*>( pFilter + sizeInBytes );
400  sizeInBytes += TF_TO_SIZE;
401  ++toCount;
402 
403  if ( sizeInBytes > totalSize )
404  return E_FAIL;
405 
406  pTo->u = accumU;
407  pTo->weight = accumWeight;
408  }
409 
410  accumWeight = 0.f;
411 
412  // Finalize from entry
413  pFrom->count = toCount;
414  pFrom->sizeInBytes = sizeInBytes - sizeFrom;
415  }
416 
417  tf->sizeInBytes = sizeInBytes;
418 
419  return S_OK;
420  }
421 
422 }; // namespace
423 
424 }; // namespace
std::unique_ptr< DirectX::XMVECTOR, aligned_deleter > ScopedAlignedArrayXMVECTOR
Definition: scoped.h:27
static const size_t TF_TO_SIZE
Definition: Filters.h:247
static const float TF_EPSILON
Definition: Filters.h:249
size_t _In_ DXGI_FORMAT size_t _In_ TEXP_LEGACY_FORMAT _In_ DWORD flags assert(pDestination &&outSize > 0)
static const size_t TF_FROM_SIZE
Definition: Filters.h:246
XMGLOBALCONST XMVECTORF32 g_cubicSixth
Definition: Filters.h:122
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
XMGLOBALCONST XMVECTORF32 g_boxScale
Definition: Filters.h:32
XMGLOBALCONST XMVECTORF32 g_cubicThird
Definition: Filters.h:121
HRESULT _Create(_In_ size_t source, _In_ size_t dest, _In_ bool wrap, _Inout_ std::unique_ptr< Filter > &tf)
Definition: Filters.h:251
XMGLOBALCONST XMVECTORF32 g_boxScale3D
Definition: Filters.h:33
_In_ size_t _In_ const TexMetadata _In_ DWORD _Out_writes_(nImages) Image *images
char * dest
Definition: lz4.h:61
XMGLOBALCONST XMVECTORF32 g_cubicHalf
Definition: Filters.h:123
static const size_t TF_FILTER_SIZE
Definition: Filters.h:245
ScopedAlignedArrayXMVECTOR scanline
Definition: Filters.h:240
ptrdiff_t bounduvw(ptrdiff_t u, ptrdiff_t maxu, bool wrap, bool mirror)
Definition: Filters.h:125
void _CreateLinearFilter(_In_ size_t source, _In_ size_t dest, _In_ bool wrap, _Out_writes_(dest) LinearFilter *lf)
Definition: Filters.h:68