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