AVIR
High-quality pro image resizing library
 
Loading...
Searching...
No Matches
avir.h
Go to the documentation of this file.
1
50
51#ifndef AVIR_CIMAGERESIZER_INCLUDED
52#define AVIR_CIMAGERESIZER_INCLUDED
53
54#include <cstring>
55#include <cmath>
56
57#if __cplusplus >= 201103L
58
59 #include <cstdint>
60
61#else // __cplusplus >= 201103L
62
63 #include <stdint.h>
64
65#endif // __cplusplus >= 201103L
66
72
73namespace avir {
74
75using std :: memcpy;
76using std :: memset;
77using std :: floor;
78using std :: ceil;
79using std :: sin;
80using std :: cos;
81using std :: size_t;
82
83#if __cplusplus >= 201103L
84
85 using std :: uintptr_t;
86
87#else // __cplusplus >= 201103L
88
89 // Workaround for pre-C++11 compilers. `nullptr` is a keyword, and not a
90 // macro, but check if such workaround is already in place.
91
92 #if !defined( nullptr )
93 #define nullptr NULL
94 #define AVIR_NULLPTR
95 #endif // !defined( nullptr )
96
97#endif // __cplusplus >= 201103L
98
99#define AVIR_VERSION "3.1"
100
101#define AVIR_PI 3.1415926535897932
103
104#define AVIR_PId2 1.5707963267948966
107
115
116#define AVIR_NOCTOR( ClassName ) \
117 private: \
118 ClassName( const ClassName& ) { } \
119 ClassName& operator = ( const ClassName& ) { return( *this ); }
120
129
130template< typename T >
131inline T round( const T d )
132{
133 return( d < (T) 0 ? -(T) (int) ( (T) 0.5 - d ) :
134 (T) (int) ( d + (T) 0.5 ));
135}
136
147
148template< typename T >
149inline T clamp( const T& Value, const T minv, const T maxv )
150{
151 return( Value < minv ? minv : ( Value > maxv ? maxv : Value ));
152}
153
161
162template< typename T >
163inline T pow24_sRGB( const T x0 )
164{
165 const double x = (double) x0;
166 const double x2 = x * x;
167 const double x3 = x2 * x;
168 const double x4 = x2 * x2;
169
170 return( (T) ( 0.0985766365536824 + 0.839474952656502 * x2 +
171 0.363287814061725 * x3 - 0.0125559718896615 /
172 ( 0.12758338921578 + 0.290283465468235 * x ) -
173 0.231757513261358 * x - 0.0395365717969074 * x4 ));
174}
175
184
185template< typename T >
186inline T pow24i_sRGB( const T x0 )
187{
188 const double x = (double) x0;
189 const double sx = sqrt( x );
190 const double ssx = sqrt( sx );
191 const double sssx = sqrt( ssx );
192
193 return( (T) ( 0.000213364515060263 + 0.0149409239419218 * x +
194 0.433973412731747 * sx + ssx * ( 0.659628181609715 * sssx -
195 0.0380957908841466 - 0.0706476137208521 * sx )));
196}
197
207
208template< typename T, typename Tin >
209inline T convertSRGB2Lin( const Tin& s0, const T m )
210{
211 const T s = (T) s0 * m;
212 const T a = (T) 0.055;
213
214 if( s <= (T) 0.04045 )
215 {
216 return( s / (T) 12.92 );
217 }
218
219 return( pow24_sRGB(( s + a ) / ( (T) 1 + a )));
220}
221
230
231template< typename T >
232inline T convertSRGB2Lin( const unsigned char& s0, const T )
233{
234 static const float tbl[ 256 ] = {
235 0.0f, 0.000303527f, 0.000607054f, 0.000910581f, 0.001214108f,
236 0.001517635f, 0.001821162f, 0.002124689f, 0.002428216f, 0.002731743f,
237 0.00303527f, 0.003348383f, 0.003678029f, 0.004025973f, 0.004392482f,
238 0.004777817f, 0.005182236f, 0.005605992f, 0.006049334f, 0.006512507f,
239 0.006995751f, 0.007499306f, 0.008023403f, 0.008568275f, 0.009134147f,
240 0.009721244f, 0.01032979f, 0.01095999f, 0.01161207f, 0.01228625f,
241 0.01298271f, 0.01370169f, 0.01444337f, 0.01520795f, 0.01599565f,
242 0.01680664f, 0.01764113f, 0.01849931f, 0.01938136f, 0.02028748f,
243 0.02121784f, 0.02217263f, 0.02315203f, 0.02415622f, 0.02518537f,
244 0.02623966f, 0.02731927f, 0.02842436f, 0.0295551f, 0.03071166f,
245 0.0318942f, 0.0331029f, 0.03433791f, 0.03559939f, 0.03688751f,
246 0.03820243f, 0.03954429f, 0.04091326f, 0.04230949f, 0.04373313f,
247 0.04518433f, 0.04666325f, 0.04817003f, 0.04970482f, 0.05126777f,
248 0.05285903f, 0.05447873f, 0.05612702f, 0.05780404f, 0.05950994f,
249 0.06124485f, 0.06300892f, 0.06480227f, 0.06662506f, 0.0684774f,
250 0.07035945f, 0.07227132f, 0.07421317f, 0.07618511f, 0.07818728f,
251 0.08021981f, 0.08228283f, 0.08437647f, 0.08650086f, 0.08865612f,
252 0.09084239f, 0.09305977f, 0.09530841f, 0.09758843f, 0.09989995f,
253 0.1022431f, 0.104618f, 0.1070247f, 0.1094634f, 0.1119343f,
254 0.1144373f, 0.1169728f, 0.1195406f, 0.1221411f, 0.1247742f,
255 0.1274402f, 0.1301391f, 0.132871f, 0.1356361f, 0.1384344f,
256 0.1412662f, 0.1441314f, 0.1470303f, 0.1499628f, 0.1529292f,
257 0.1559296f, 0.158964f, 0.1620326f, 0.1651354f, 0.1682726f,
258 0.1714443f, 0.1746506f, 0.1778916f, 0.1811674f, 0.1844781f,
259 0.1878239f, 0.1912047f, 0.1946208f, 0.1980722f, 0.2015591f,
260 0.2050815f, 0.2086396f, 0.2122334f, 0.215863f, 0.2195286f,
261 0.2232303f, 0.2269681f, 0.2307422f, 0.2345526f, 0.2383994f,
262 0.2422828f, 0.2462029f, 0.2501597f, 0.2541534f, 0.258184f,
263 0.2622517f, 0.2663564f, 0.2704985f, 0.2746778f, 0.2788946f,
264 0.2831489f, 0.2874409f, 0.2917705f, 0.296138f, 0.3005433f,
265 0.3049867f, 0.3094681f, 0.3139877f, 0.3185456f, 0.3231419f,
266 0.3277766f, 0.3324498f, 0.3371618f, 0.3419124f, 0.3467019f,
267 0.3515303f, 0.3563977f, 0.3613041f, 0.3662498f, 0.3712348f,
268 0.3762591f, 0.3813228f, 0.3864261f, 0.391569f, 0.3967517f,
269 0.4019741f, 0.4072364f, 0.4125387f, 0.4178811f, 0.4232636f,
270 0.4286864f, 0.4341495f, 0.4396529f, 0.4451969f, 0.4507815f,
271 0.4564067f, 0.4620726f, 0.4677794f, 0.4735271f, 0.4793158f,
272 0.4851456f, 0.4910165f, 0.4969287f, 0.5028822f, 0.5088771f,
273 0.5149135f, 0.5209915f, 0.5271111f, 0.5332725f, 0.5394757f,
274 0.5457208f, 0.5520078f, 0.558337f, 0.5647082f, 0.5711217f,
275 0.5775775f, 0.5840756f, 0.5906162f, 0.5971993f, 0.6038251f,
276 0.6104935f, 0.6172047f, 0.6239587f, 0.6307557f, 0.6375956f,
277 0.6444787f, 0.6514048f, 0.6583742f, 0.665387f, 0.6724431f,
278 0.6795426f, 0.6866857f, 0.6938724f, 0.7011027f, 0.7083769f,
279 0.7156948f, 0.7230567f, 0.7304625f, 0.7379124f, 0.7454064f,
280 0.7529446f, 0.7605271f, 0.7681539f, 0.7758252f, 0.7835409f,
281 0.7913012f, 0.7991061f, 0.8069558f, 0.8148502f, 0.8227894f,
282 0.8307736f, 0.8388028f, 0.846877f, 0.8549964f, 0.8631609f,
283 0.8713707f, 0.8796259f, 0.8879265f, 0.8962726f, 0.9046642f,
284 0.9131014f, 0.9215843f, 0.930113f, 0.9386874f, 0.9473078f,
285 0.9559742f, 0.9646865f, 0.973445f, 0.9822496f, 0.9911004f,
286 0.9999975f };
287
288 return( tbl[ (size_t) s0 ]);
289}
290
298
299template< typename T >
300inline T convertLin2SRGB( const T s )
301{
302 const T a = (T) 0.055;
303
304 if( s <= (T) 0.0031308 )
305 {
306 return( (T) 12.92 * s );
307 }
308
309 return(( (T) 1 + a ) * pow24i_sRGB( s ) - a );
310}
311
325
326template< typename T1, typename T2 >
327inline void copyArray( const T1* ip, T2* op, int l,
328 const int ipinc = 1, const int opinc = 1 )
329{
330 while( l > 0 )
331 {
332 *op = (T2) *ip;
333 op += opinc;
334 ip += ipinc;
335 l--;
336 }
337}
338
348
349template< typename T1, typename T2 >
350inline void addArray( const T1* ip, T2* op, int l,
351 const int ipinc = 1, const int opinc = 1 )
352{
353 while( l > 0 )
354 {
355 *op += *ip;
356 op += opinc;
357 ip += ipinc;
358 l--;
359 }
360}
361
376
377template< typename T1, typename T2 >
378inline void replicateArray( const T1* const ip, const int ipl, T2* op, int l,
379 const int opinc )
380{
381 if( ipl == 1 )
382 {
383 while( l > 0 )
384 {
385 op[ 0 ] = (T2) ip[ 0 ];
386 op += opinc;
387 l--;
388 }
389 }
390 else
391 if( ipl == 4 )
392 {
393 while( l > 0 )
394 {
395 op[ 0 ] = (T2) ip[ 0 ];
396 op[ 1 ] = (T2) ip[ 1 ];
397 op[ 2 ] = (T2) ip[ 2 ];
398 op[ 3 ] = (T2) ip[ 3 ];
399 op += opinc;
400 l--;
401 }
402 }
403 else
404 if( ipl == 3 )
405 {
406 while( l > 0 )
407 {
408 op[ 0 ] = (T2) ip[ 0 ];
409 op[ 1 ] = (T2) ip[ 1 ];
410 op[ 2 ] = (T2) ip[ 2 ];
411 op += opinc;
412 l--;
413 }
414 }
415 else
416 if( ipl == 2 )
417 {
418 while( l > 0 )
419 {
420 op[ 0 ] = (T2) ip[ 0 ];
421 op[ 1 ] = (T2) ip[ 1 ];
422 op += opinc;
423 l--;
424 }
425 }
426 else
427 {
428 while( l > 0 )
429 {
430 int i;
431
432 for( i = 0; i < ipl; i++ )
433 {
434 op[ i ] = (T2) ip[ i ];
435 }
436
437 op += opinc;
438 l--;
439 }
440 }
441}
442
459
460template< typename T >
461inline void calcFIRFilterResponse( const T* flt, int fltlen,
462 const double th, double& re0, double& im0, const int fltlat = 0 )
463{
464 const double sincr = 2.0 * cos( th );
465 double cvalue1;
466 double svalue1;
467
468 if( fltlat == 0 )
469 {
470 cvalue1 = 1.0;
471 svalue1 = 0.0;
472 }
473 else
474 {
475 cvalue1 = cos( -fltlat * th );
476 svalue1 = sin( -fltlat * th );
477 }
478
479 double cvalue2 = cos( -( fltlat + 1 ) * th );
480 double svalue2 = sin( -( fltlat + 1 ) * th );
481
482 double re = 0.0;
483 double im = 0.0;
484
485 while( fltlen > 0 )
486 {
487 re += cvalue1 * (double) flt[ 0 ];
488 im += svalue1 * (double) flt[ 0 ];
489 flt++;
490 fltlen--;
491
492 double tmp = cvalue1;
493 cvalue1 = sincr * cvalue1 - cvalue2;
494 cvalue2 = tmp;
495
496 tmp = svalue1;
497 svalue1 = sincr * svalue1 - svalue2;
498 svalue2 = tmp;
499 }
500
501 re0 = re;
502 im0 = im;
503}
504
515
516template< typename T >
517inline void normalizeFIRFilter( T* const p, const int l, const double DCGain,
518 const int pstep = 1 )
519{
520 double s = 0.0;
521 T* pp = p;
522 int i = l;
523
524 while( i > 0 )
525 {
526 s += *pp;
527 pp += pstep;
528 i--;
529 }
530
531 s = DCGain / s;
532 pp = p;
533 i = l;
534
535 while( i > 0 )
536 {
537 *pp = (T) ( *pp * s );
538 pp += pstep;
539 i--;
540 }
541}
542
563
564template< typename T, typename capint = int >
565class CBuffer
566{
567public:
568 CBuffer()
569 : Data( nullptr )
570 , DataAligned( nullptr )
571 , Capacity( 0 )
572 , Alignment( 0 )
573 {
574 }
575
583
584 CBuffer( const capint aCapacity, const int aAlignment = 0 )
585 {
586 allocinit( aCapacity, aAlignment );
587 }
588
594
595 CBuffer( const CBuffer& Source )
596 {
597 allocinit( Source.Capacity, Source.Alignment );
598
599 if( Capacity > 0 )
600 {
601 memcpy( DataAligned, Source.DataAligned,
602 (size_t) Capacity * sizeof( T ));
603 }
604 }
605
606 ~CBuffer()
607 {
608 freeData();
609 }
610
617
618 CBuffer& operator = ( const CBuffer& Source )
619 {
620 alloc( Source.Capacity, Source.Alignment );
621
622 if( Capacity > 0 )
623 {
624 memcpy( DataAligned, Source.DataAligned,
625 (size_t) Capacity * sizeof( T ));
626 }
627
628 return( *this );
629 }
630
634
635 operator T* () const
636 {
637 return( DataAligned );
638 }
639
648
649 void alloc( const capint aCapacity, const int aAlignment = 0 )
650 {
651 freeData();
652 allocinit( aCapacity, aAlignment );
653 }
654
658
659 void free()
660 {
661 freeData();
662 DataAligned = nullptr;
663 Capacity = 0;
664 Alignment = 0;
665 }
666
670
671 capint getCapacity() const
672 {
673 return( Capacity );
674 }
675
686
687 void forceCapacity( const capint NewCapacity )
688 {
689 Capacity = NewCapacity;
690 }
691
701
702 void increaseCapacity( const capint NewCapacity,
703 const bool DoDataCopy = true )
704 {
705 if( NewCapacity < Capacity )
706 {
707 return;
708 }
709
710 if( DoDataCopy )
711 {
712 const capint PrevCapacity = Capacity;
713 char* const PrevData = Data;
714 T* const PrevDataAligned = DataAligned;
715
716 allocinit( NewCapacity, Alignment );
717
718 if( PrevCapacity > 0 )
719 {
720 memcpy( DataAligned, PrevDataAligned,
721 (size_t) PrevCapacity * sizeof( T ));
722 }
723
724 delete[] PrevData;
725 }
726 else
727 {
728 freeData();
729 allocinit( NewCapacity, Alignment );
730 }
731 }
732
741
742 void truncateCapacity( const capint NewCapacity )
743 {
744 if( NewCapacity >= Capacity )
745 {
746 return;
747 }
748
749 Capacity = NewCapacity;
750 }
751
762
763 void updateCapacity( const capint ReqCapacity )
764 {
765 if( ReqCapacity <= Capacity )
766 {
767 return;
768 }
769
770 capint NewCapacity = Capacity;
771
772 while( NewCapacity < ReqCapacity )
773 {
774 NewCapacity += NewCapacity / 3 + 1;
775 }
776
777 increaseCapacity( NewCapacity );
778 }
779
780private:
781 char* Data;
782 T* DataAligned;
783 capint Capacity;
784 int Alignment;
786
795
796 void allocinit( const capint aCapacity, const int aAlignment )
797 {
798 if( aAlignment == 0 )
799 {
800 Data = new char[ (size_t) aCapacity * sizeof( T )];
801 DataAligned = (T*) Data;
802 Alignment = 0;
803 }
804 else
805 {
806 Data = new char[ (size_t) aCapacity * sizeof( T ) +
807 (size_t) aAlignment ];
808
809 DataAligned = (T*) (( (uintptr_t) Data +
810 (uintptr_t) aAlignment ) & ~(uintptr_t) ( aAlignment - 1 ));
811
812 Alignment = aAlignment;
813 }
814
815 Capacity = aCapacity;
816 }
817
821
822 void freeData()
823 {
824 delete[] Data;
825 Data = nullptr;
826 }
827};
828
840
841template< class T >
842class CStructArray
843{
844public:
845 CStructArray()
846 : ItemCount( 0 )
847 {
848 }
849
855
856 CStructArray( const CStructArray& Source )
857 : ItemCount( 0 )
858 , Items( Source.getItemCount() )
859 {
860 while( ItemCount < Source.getItemCount() )
861 {
862 Items[ ItemCount ] = new T( Source[ ItemCount ]);
863 ItemCount++;
864 }
865 }
866
868 {
869 clear();
870 }
871
878
879 CStructArray& operator = ( const CStructArray& Source )
880 {
881 clear();
882
883 const int NewCount = Source.ItemCount;
884 Items.updateCapacity( NewCount );
885
886 while( ItemCount < NewCount )
887 {
888 Items[ ItemCount ] = new T( Source[ ItemCount ]);
889 ItemCount++;
890 }
891
892 return( *this );
893 }
894
900
901 T& operator []( const int Index )
902 {
903 return( *Items[ Index ]);
904 }
905
911
912 const T& operator []( const int Index ) const
913 {
914 return( *Items[ Index ]);
915 }
916
923
924 T& add()
925 {
926 if( ItemCount == Items.getCapacity() )
927 {
928 Items.increaseCapacity( ItemCount * 3 / 2 + 1 );
929 }
930
931 Items[ ItemCount ] = new T();
932 ItemCount++;
933
934 return( (*this)[ ItemCount - 1 ]);
935 }
936
946
947 void setItemCount( const int NewCount )
948 {
949 if( NewCount > ItemCount )
950 {
951 Items.increaseCapacity( NewCount );
952
953 while( ItemCount < NewCount )
954 {
955 Items[ ItemCount ] = new T();
956 ItemCount++;
957 }
958 }
959 else
960 {
961 while( ItemCount > NewCount )
962 {
963 ItemCount--;
964 delete Items[ ItemCount ];
965 }
966 }
967 }
968
972
973 void clear()
974 {
975 while( ItemCount > 0 )
976 {
977 ItemCount--;
978 delete Items[ ItemCount ];
979 }
980 }
981
985
986 int getItemCount() const
987 {
988 return( ItemCount );
989 }
990
991private:
992 CBuffer< T* > Items;
993 int ItemCount;
994};
995
1003
1005{
1006public:
1014
1015 CSineGen( const double si, const double ph )
1016 : svalue1( sin( ph ))
1017 , svalue2( sin( ph - si ))
1018 , sincr( 2.0 * cos( si ))
1019 {
1020 }
1021
1025
1026 double generate()
1027 {
1028 const double res = svalue1;
1029
1030 svalue1 = sincr * res - svalue2;
1031 svalue2 = res;
1032
1033 return( res );
1034 }
1035
1036private:
1037 double svalue1;
1038 double svalue2;
1039 double sincr;
1040};
1041
1053
1055{
1056public:
1064
1065 CDSPWindowGenPeakedCosine( const double aAlpha, const double aLen2 )
1066 : Alpha( aAlpha )
1067 , Len2( aLen2 )
1068 , Len2i( 1.0 / aLen2 )
1069 , wn( 0.0 )
1070 , w1( AVIR_PId2 / Len2, AVIR_PI * 0.5 )
1071 {
1072 }
1073
1077
1078 double generate()
1079 {
1080 const double h = pow( wn * Len2i, Alpha );
1081 wn += 1.0;
1082
1083 return( w1.generate() * ( 1.0 - h ));
1084 }
1085
1086private:
1087 double Alpha;
1088 double Len2;
1089 double Len2i;
1090 double wn;
1092 CSineGen w1;
1093};
1094
1115
1117{
1118public:
1136
1137 void init( const double SampleRate, const double aFilterLength,
1138 const int aBandCount, const double MinFreq, const double MaxFreq,
1139 const bool IsLogBands, const double WFAlpha )
1140 {
1141 FilterLength = aFilterLength;
1142 BandCount = aBandCount;
1143
1144 CenterFreqs.alloc( BandCount );
1145
1146 z = (int) ceil( FilterLength * 0.5 );
1147 zi = z + ( z & 1 );
1148 z2 = z * 2;
1149
1150 CBuffer< double > oscbuf( z2 );
1151 initOscBuf( oscbuf );
1152
1153 CBuffer< double > winbuf( z );
1154 initWinBuf( winbuf, WFAlpha );
1155
1156 UseFirstVirtBand = ( MinFreq > 0.0 );
1157 const int k = zi * ( BandCount + ( UseFirstVirtBand ? 1 : 0 ));
1158 Kernels1.alloc( k );
1159 Kernels2.alloc( k );
1160
1161 double m; // Frequency step multiplier.
1162 double mo; // Frequency step offset (addition).
1163
1164 if( IsLogBands )
1165 {
1166 m = exp( log( MaxFreq / MinFreq ) / ( BandCount - 1 ));
1167 mo = 0.0;
1168 }
1169 else
1170 {
1171 m = 1.0;
1172 mo = ( MaxFreq - MinFreq ) / ( BandCount - 1 );
1173 }
1174
1175 double f = MinFreq;
1176 double x1 = 0.0;
1177 double x2;
1178 int si;
1179
1180 if( UseFirstVirtBand )
1181 {
1182 si = 0;
1183 }
1184 else
1185 {
1186 si = 1;
1187 CenterFreqs[ 0 ] = 0.0;
1188 f = f * m + mo;
1189 }
1190
1191 double* kernbuf1 = &Kernels1[ 0 ];
1192 double* kernbuf2 = &Kernels2[ 0 ];
1193 int i;
1194
1195 for( i = si; i < BandCount; i++ )
1196 {
1197 x2 = f * 2.0 / SampleRate;
1198 CenterFreqs[ i ] = x2;
1199
1200 fillBandKernel( x1, x2, kernbuf1, kernbuf2, oscbuf, winbuf );
1201
1202 kernbuf1 += zi;
1203 kernbuf2 += zi;
1204 x1 = x2;
1205 f = f * m + mo;
1206 }
1207
1208 if( x1 < 1.0 )
1209 {
1210 UseLastVirtBand = true;
1211 fillBandKernel( x1, 1.0, kernbuf1, kernbuf2, oscbuf, winbuf );
1212 }
1213 else
1214 {
1215 UseLastVirtBand = false;
1216 }
1217 }
1218
1222
1224 {
1225 return( z2 - 1 );
1226 }
1227
1231
1233 {
1234 return( z - 1 );
1235 }
1236
1246
1247 void buildFilter( const double* const BandGains, double* const Filter )
1248 {
1249 const double* kernbuf1 = &Kernels1[ 0 ];
1250 const double* kernbuf2 = &Kernels2[ 0 ];
1251 double x1 = 0.0;
1252 double y1 = BandGains[ 0 ];
1253 double x2;
1254 double y2;
1255
1256 int i;
1257 int si;
1258
1259 if( UseFirstVirtBand )
1260 {
1261 si = 1;
1262 x2 = CenterFreqs[ 0 ];
1263 y2 = y1;
1264 }
1265 else
1266 {
1267 si = 2;
1268 x2 = CenterFreqs[ 1 ];
1269 y2 = BandGains[ 1 ];
1270 }
1271
1272 copyBandKernel( Filter, kernbuf1, kernbuf2, y1 - y2,
1273 x1 * y2 - x2 * y1 );
1274
1275 kernbuf1 += zi;
1276 kernbuf2 += zi;
1277 x1 = x2;
1278 y1 = y2;
1279
1280 for( i = si; i < BandCount; i++ )
1281 {
1282 x2 = CenterFreqs[ i ];
1283 y2 = BandGains[ i ];
1284
1285 addBandKernel( Filter, kernbuf1, kernbuf2, y1 - y2,
1286 x1 * y2 - x2 * y1 );
1287
1288 kernbuf1 += zi;
1289 kernbuf2 += zi;
1290 x1 = x2;
1291 y1 = y2;
1292 }
1293
1294 if( UseLastVirtBand )
1295 {
1296 addBandKernel( Filter, kernbuf1, kernbuf2, y1 - y2,
1297 x1 * y2 - y1 );
1298 }
1299
1300 for( i = 0; i < z - 1; i++ )
1301 {
1302 Filter[ z + i ] = Filter[ z - 2 - i ];
1303 }
1304 }
1305
1315
1316 static int calcFilterLength( const double aFilterLength, int& Latency )
1317 {
1318 const int l = (int) ceil( aFilterLength * 0.5 );
1319 Latency = l - 1;
1320
1321 return( l * 2 - 1 );
1322 }
1323
1324private:
1325 double FilterLength;
1326 int z;
1327 int zi;
1330 int z2;
1331 int BandCount;
1332 CBuffer< double > CenterFreqs;
1334 CBuffer< double > Kernels1;
1336 CBuffer< double > Kernels2;
1338 bool UseFirstVirtBand;
1341 bool UseLastVirtBand;
1345
1351
1352 void initOscBuf( double* oscbuf ) const
1353 {
1354 int i = z;
1355
1356 while( i > 0 )
1357 {
1358 oscbuf[ 0 ] = 0.0;
1359 oscbuf[ 1 ] = 1.0;
1360 oscbuf += 2;
1361 i--;
1362 }
1363 }
1364
1373
1374 void initWinBuf( double* winbuf, const double Alpha ) const
1375 {
1376 CDSPWindowGenPeakedCosine wf( Alpha, FilterLength * 0.5 );
1377 int i;
1378
1379 for( i = 1; i <= z; i++ )
1380 {
1381 winbuf[ z - i ] = wf.generate();
1382 }
1383 }
1384
1401
1402 void fillBandKernel( const double x1, const double x2, double* kernbuf1,
1403 double* kernbuf2, double* oscbuf, const double* const winbuf )
1404 {
1405 const double s2_incr = AVIR_PI * x2;
1406 const double s2_coeff = 2.0 * cos( s2_incr );
1407
1408 double s2_value1 = sin( s2_incr * ( -z + 1 ));
1409 double c2_value1 = sin( s2_incr * ( -z + 1 ) + AVIR_PI * 0.5 );
1410 oscbuf[ 0 ] = sin( s2_incr * -z );
1411 oscbuf[ 1 ] = sin( s2_incr * -z + AVIR_PI * 0.5 );
1412
1413 int ks;
1414
1415 for( ks = 1; ks < z; ks++ )
1416 {
1417 const int ks2 = ks * 2;
1418 const double s1_value1 = oscbuf[ ks2 ];
1419 const double c1_value1 = oscbuf[ ks2 + 1 ];
1420 oscbuf[ ks2 ] = s2_value1;
1421 oscbuf[ ks2 + 1 ] = c2_value1;
1422
1423 const double x = AVIR_PI * ( ks - z );
1424 const double v0 = winbuf[ ks - 1 ] / (( x1 - x2 ) * x );
1425
1426 kernbuf1[ ks - 1 ] = ( x2 * s2_value1 - x1 * s1_value1 +
1427 ( c2_value1 - c1_value1 ) / x ) * v0;
1428
1429 kernbuf2[ ks - 1 ] = ( s2_value1 - s1_value1 ) * v0;
1430
1431 s2_value1 = s2_coeff * s2_value1 - oscbuf[ ks2 - 2 ];
1432 c2_value1 = s2_coeff * c2_value1 - oscbuf[ ks2 - 1 ];
1433 }
1434
1435 kernbuf1[ z - 1 ] = ( x2 * x2 - x1 * x1 ) / ( x1 - x2 ) * 0.5;
1436 kernbuf2[ z - 1 ] = -1.0;
1437 }
1438
1448
1449 void copyBandKernel( double* outbuf, const double* const kernbuf1,
1450 const double* const kernbuf2, const double c, const double d ) const
1451 {
1452 int ks;
1453
1454 for( ks = 0; ks < z; ks++ )
1455 {
1456 outbuf[ ks ] = c * kernbuf1[ ks ] + d * kernbuf2[ ks ];
1457 }
1458 }
1459
1469
1470 void addBandKernel( double* outbuf, const double* const kernbuf1,
1471 const double* const kernbuf2, const double c, const double d ) const
1472 {
1473 int ks;
1474
1475 for( ks = 0; ks < z; ks++ )
1476 {
1477 outbuf[ ks ] += c * kernbuf1[ ks ] + d * kernbuf2[ ks ];
1478 }
1479 }
1480};
1481
1489
1491{
1492public:
1493 int fl2;
1496
1505
1506 CDSPPeakedCosineLPF( const double aLen2, const double aFreq2,
1507 const double aAlpha )
1508 : fl2( (int) ceil( aLen2 ) - 1 )
1509 , FilterLen( fl2 + fl2 + 1 )
1510 , Len2( aLen2 )
1511 , Freq2( aFreq2 )
1512 , Alpha( aAlpha )
1513 {
1514 }
1515
1527
1528 template< typename T >
1529 void generateLPF( T* op, const double DCGain )
1530 {
1531 CDSPWindowGenPeakedCosine wf( Alpha, Len2 );
1532 CSineGen f2( Freq2, 0.0 );
1533
1534 op += fl2;
1535 T* op2 = op;
1536 f2.generate();
1537
1538 if( DCGain > 0.0 )
1539 {
1540 int t = 1;
1541
1542 *op = (T) ( Freq2 * wf.generate() );
1543 double s = *op;
1544
1545 while( t <= fl2 )
1546 {
1547 const T v = (T) ( f2.generate() * wf.generate() / t );
1548 op++;
1549 op2--;
1550 *op = v;
1551 *op2 = v;
1552 s += v + v;
1553 t++;
1554 }
1555
1556 t = FilterLen;
1557 s = DCGain / s;
1558
1559 while( t > 0 )
1560 {
1561 *op2 = (T) ( *op2 * s );
1562 op2++;
1563 t--;
1564 }
1565 }
1566 else
1567 {
1568 int t = 1;
1569
1570 *op = (T) ( Freq2 * wf.generate() );
1571
1572 while( t <= fl2 )
1573 {
1574 const T v = (T) ( f2.generate() * wf.generate() / t );
1575 op++;
1576 op2--;
1577 *op = v;
1578 *op2 = v;
1579 t++;
1580 }
1581 }
1582 }
1583
1584private:
1585 double Len2;
1587 double Freq2;
1588 double Alpha;
1589};
1590
1599
1600class CFltBuffer : public CBuffer< double >
1601{
1602public:
1603 double Len2;
1605 double Freq;
1606 double Alpha;
1607 double DCGain;
1608
1609 CFltBuffer()
1610 : CBuffer< double >()
1611 , Len2( 0.0 )
1612 , Freq( 0.0 )
1613 , Alpha( 0.0 )
1614 , DCGain( 0.0 )
1615 {
1616 }
1617
1623
1624 bool operator == ( const CFltBuffer& b2 ) const
1625 {
1626 return( Len2 == b2.Len2 && Freq == b2.Freq && Alpha == b2.Alpha &&
1627 DCGain == b2.DCGain );
1628 }
1629};
1630
1646
1647template< typename fptype >
1648class CDSPFracFilterBankLin
1649{
1650 AVIR_NOCTOR( CDSPFracFilterBankLin )
1651
1652public:
1653 CDSPFracFilterBankLin()
1654 : Order( -1 )
1655 {
1656 }
1657
1667
1668 void copyInitParams( const CDSPFracFilterBankLin& s )
1669 {
1670 WFLen2 = s.WFLen2;
1671 WFFreq = s.WFFreq;
1672 WFAlpha = s.WFAlpha;
1673 FracCount = s.FracCount;
1674 Order = s.Order;
1675 Alignment = s.Alignment;
1676 SrcFilterLen = s.SrcFilterLen;
1677 FilterLen = s.FilterLen;
1678 FilterSize = s.FilterSize;
1679 IsSrcTableBuilt = false;
1680 ExtFilter = s.ExtFilter;
1681 TableFillFlags.alloc( s.TableFillFlags.getCapacity() );
1682 int i;
1683
1684 // Copy table fill flags, but shifted so that further initialization
1685 // is still possible (such feature should not be used, though).
1686
1687 for( i = 0; i < TableFillFlags.getCapacity(); i++ )
1688 {
1689 TableFillFlags[ i ] = (char) ( s.TableFillFlags[ i ] << 2 );
1690 }
1691 }
1692
1701
1702 bool operator == ( const CDSPFracFilterBankLin& s ) const
1703 {
1704 return( Order == s.Order && WFLen2 == s.WFLen2 &&
1705 WFFreq == s.WFFreq && WFAlpha == s.WFAlpha &&
1706 FracCount == s.FracCount && ExtFilter == s.ExtFilter );
1707 }
1708
1731
1732 void init( const int ReqFracCount, const int ReqOrder,
1733 const double BaseLen, const double Cutoff, const double aWFAlpha,
1734 const CFltBuffer& aExtFilter, const int aAlignment = 0,
1735 const int FltLenAlign = 1 )
1736 {
1737 double NewWFLen2 = 0.5 * BaseLen * ReqFracCount;
1738 double NewWFFreq = AVIR_PI * Cutoff / ReqFracCount;
1739 double NewWFAlpha = aWFAlpha;
1740
1741 if( ReqOrder == Order && NewWFLen2 == WFLen2 && NewWFFreq == WFFreq &&
1742 NewWFAlpha == WFAlpha && ReqFracCount == FracCount &&
1743 aExtFilter == ExtFilter )
1744 {
1745 IsInitRequired = false;
1746 return;
1747 }
1748
1749 WFLen2 = NewWFLen2;
1750 WFFreq = NewWFFreq;
1751 WFAlpha = NewWFAlpha;
1752 FracCount = ReqFracCount;
1753 Order = ReqOrder;
1754 Alignment = aAlignment;
1755 ExtFilter = aExtFilter;
1756
1757 CDSPPeakedCosineLPF p( WFLen2, WFFreq, WFAlpha );
1758 SrcFilterLen = ( p.fl2 / ReqFracCount + 1 ) * 2;
1759
1760 const int ElementSize = ReqOrder + 1;
1761 FilterLen = SrcFilterLen;
1762
1763 if( ExtFilter.getCapacity() > 0 )
1764 {
1765 FilterLen += ExtFilter.getCapacity() - 1;
1766 }
1767
1768 FilterLen = ( FilterLen + FltLenAlign - 1 ) & ~( FltLenAlign - 1 );
1769 FilterSize = FilterLen * ElementSize;
1770 IsSrcTableBuilt = false;
1771 IsInitRequired = true;
1772 }
1773
1778
1779 int getFilterLen() const
1780 {
1781 return( FilterLen );
1782 }
1783
1787
1788 int getFracCount() const
1789 {
1790 return( FracCount );
1791 }
1792
1796
1797 int getOrder() const
1798 {
1799 return( Order );
1800 }
1801
1813
1814 const fptype* getFilter( const int i )
1815 {
1816 if( !IsSrcTableBuilt )
1817 {
1818 buildSrcTable();
1819 }
1820
1821 fptype* const Res = &Table[ i * FilterSize ];
1822
1823 if(( TableFillFlags[ i ] & 2 ) == 0 )
1824 {
1825 createFilter( i );
1826 TableFillFlags[ i ] |= 2;
1827
1828 if( Order > 0 )
1829 {
1830 createFilter( i + 1 );
1831 const fptype* const Res2 = Res + FilterSize;
1832 fptype* const op = Res + FilterLen;
1833 int j;
1834
1835 // Create higher-order interpolation coefficients (linear
1836 // interpolation).
1837
1838 for( j = 0; j < FilterLen; j++ )
1839 {
1840 op[ j ] = Res2[ j ] - Res[ j ];
1841 }
1842 }
1843 }
1844
1845 return( Res );
1846 }
1847
1860
1861 const fptype* getFilterConst( const int i ) const
1862 {
1863 return( &Table[ i * FilterSize ]);
1864 }
1865
1869
1871 {
1872 int i;
1873
1874 for( i = 0; i < FracCount; i++ )
1875 {
1876 getFilter( i );
1877 }
1878 }
1879
1894
1895 int calcInitComplexity( const CBuffer< char >& FracUseMap ) const
1896 {
1897 const int FltInitCost = 65; // Cost to initialize a single sample
1898 // of the fractional delay filter.
1899 const int FltUseCost = FilterLen * Order +
1900 SrcFilterLen * ExtFilter.getCapacity(); // Cost to use a single
1901 // fractional delay filter.
1902 const int ucb[ 2 ] = { 0, FltUseCost };
1903 int ic;
1904 int i;
1905
1906 if( IsInitRequired )
1907 {
1908 ic = FracCount * SrcFilterLen * FltInitCost;
1909
1910 for( i = 0; i < FracCount; i++ )
1911 {
1912 ic += ucb[ (size_t) FracUseMap[ i ]];
1913 }
1914 }
1915 else
1916 {
1917 ic = 0;
1918
1919 for( i = 0; i < FracCount; i++ )
1920 {
1921 if( FracUseMap[ i ] != 0 )
1922 {
1923 ic += ucb[ TableFillFlags[ i ] == 0 ? 1 : 0 ];
1924 }
1925 }
1926 }
1927
1928 return( ic );
1929 }
1930
1931private:
1932 static const int InterpPoints = 2;
1934 double WFLen2;
1935 double WFFreq;
1936 double WFAlpha;
1937 int FracCount;
1938 int Order;
1939 int Alignment;
1940 int SrcFilterLen;
1942 int FilterLen;
1945 int FilterSize;
1947 bool IsInitRequired;
1950 CBuffer< fptype > Table;
1952 CBuffer< char > TableFillFlags;
1957 CFltBuffer ExtFilter;
1960 CBuffer< double > SrcTable;
1963 bool IsSrcTableBuilt;
1965
1969
1970 void buildSrcTable()
1971 {
1972 IsSrcTableBuilt = true;
1973 IsInitRequired = false;
1974
1975 CDSPPeakedCosineLPF p( WFLen2, WFFreq, WFAlpha );
1976
1977 const int BufLen = SrcFilterLen * FracCount + InterpPoints - 1;
1978 const int BufOffs = InterpPoints / 2 - 1;
1979 const int BufCenter = SrcFilterLen * FracCount / 2 + BufOffs;
1980
1981 CBuffer< double > Buf( BufLen );
1982 memset( Buf, 0, (size_t) ( BufCenter - p.fl2 ) * sizeof( double ));
1983 int i = BufLen - BufCenter - p.fl2 - 1;
1984 memset( &Buf[ BufLen - i ], 0, (size_t) i * sizeof( double ));
1985
1986 p.generateLPF( &Buf[ BufCenter - p.fl2 ], 0.0 );
1987
1988 SrcTable.alloc(( FracCount + 1 ) * SrcFilterLen );
1989 TableFillFlags.alloc( FracCount + 1 );
1990 int j;
1991 double* op0 = SrcTable;
1992
1993 for( i = FracCount; i >= 0; i-- )
1994 {
1995 TableFillFlags[ i ] = 0;
1996 double* ip = Buf + BufOffs + i;
1997
1998 for( j = 0; j < SrcFilterLen; j++ )
1999 {
2000 op0[ 0 ] = ip[ 0 ];
2001 op0++;
2002 ip += FracCount;
2003 }
2004
2005 normalizeFIRFilter( op0 - SrcFilterLen, SrcFilterLen, 1.0 );
2006 }
2007
2008 Table.alloc(( FracCount + 1 ) * FilterSize, Alignment );
2009 }
2010
2020
2021 void createFilter( const int n )
2022 {
2023 if( TableFillFlags[ n ] != 0 )
2024 {
2025 return;
2026 }
2027
2028 TableFillFlags[ n ] |= 1;
2029 const int ExtFilterLatency = ExtFilter.getCapacity() / 2;
2030 const int ResLatency = ExtFilterLatency + SrcFilterLen / 2;
2031 int ResLen = SrcFilterLen;
2032
2033 if( ExtFilter.getCapacity() > 0 )
2034 {
2035 ResLen += ExtFilter.getCapacity() - 1;
2036 }
2037
2038 const int ResOffs = FilterLen / 2 - ResLatency;
2039 fptype* op = &Table[ n * FilterSize ];
2040 int i;
2041
2042 for( i = 0; i < ResOffs; i++ )
2043 {
2044 op[ i ] = 0;
2045 }
2046
2047 for( i = ResOffs + ResLen; i < FilterLen; i++ )
2048 {
2049 op[ i ] = 0;
2050 }
2051
2052 op += ResOffs;
2053 const double* const srcflt = &SrcTable[ n * SrcFilterLen ];
2054
2055 if( ExtFilter.getCapacity() == 0 )
2056 {
2057 for( i = 0; i < ResLen; i++ )
2058 {
2059 op[ i ] = (fptype) srcflt[ i ];
2060 }
2061
2062 return;
2063 }
2064
2065 // Perform convolution of `extflt` and `srcflt`.
2066
2067 const double* const extflt = &ExtFilter[ 0 ];
2068 int j;
2069
2070 for( j = 0; j < ResLen; j++ )
2071 {
2072 int k = 0;
2073 int l = j - ExtFilter.getCapacity() + 1;
2074 int r = l + ExtFilter.getCapacity();
2075
2076 if( l < 0 )
2077 {
2078 k -= l;
2079 l = 0;
2080 }
2081
2082 if( r > SrcFilterLen )
2083 {
2084 r = SrcFilterLen;
2085 }
2086
2087 const double* const extfltb = extflt + k;
2088 const double* const srcfltb = srcflt + l;
2089 double s = 0.0;
2090 l = r - l;
2091
2092 for( i = 0; i < l; i++ )
2093 {
2094 s += extfltb[ i ] * srcfltb[ i ];
2095 }
2096
2097 op[ j ] = (fptype) s;
2098 }
2099 }
2100};
2101
2118
2119class CImageResizerThreadPool
2120{
2121public:
2122 CImageResizerThreadPool()
2123 {
2124 }
2125
2126 virtual ~CImageResizerThreadPool()
2127 {
2128 }
2129
2136
2138 {
2139 public:
2140 virtual ~CWorkload()
2141 {
2142 }
2143
2148
2149 virtual void process() = 0;
2150 };
2151
2162
2163 virtual int getSuggestedWorkloadCount() const
2164 {
2165 return( 1 );
2166 }
2167
2187
2188 virtual void addWorkload( CWorkload* const Workload )
2189 {
2190 }
2191
2201
2202 virtual void startAllWorkloads()
2203 {
2204 }
2205
2209
2211 {
2212 }
2213
2221
2222 virtual void removeAllWorkloads()
2223 {
2224 }
2225};
2226
2261
2262struct CImageResizerParams
2263{
2267 double CorrFltLen;
2276 double IntFltLen;
2283 double LPFltAlpha;
2299
2300 CImageResizerParams()
2301 : HBFltAlpha( 1.94609 )
2302 , HBFltCutoff( 0.46437 )
2303 , HBFltLen( 24 )
2304 {
2305 }
2306
2307 double HBFltAlpha;
2310 double HBFltLen;
2317};
2318
2327
2328struct CImageResizerParamsDef : public CImageResizerParams
2329{
2330 CImageResizerParamsDef()
2331 {
2332 CorrFltAlpha = 0.97946;//10.06/1.88/1.029(256064.90)/0.000039:258649,447179
2333 CorrFltLen = 6.4262;
2334 IntFltAlpha = 6.41341;
2335 IntFltCutoff = 0.7372;
2336 IntFltLen = 18;
2337 LPFltAlpha = 4.76449;
2338 LPFltBaseLen = 7.55999999999998;
2339 LPFltCutoffMult = 0.79285;
2340 }
2341};
2342
2352
2353struct CImageResizerParamsULR : public CImageResizerParams
2354{
2355 CImageResizerParamsULR()
2356 {
2357 CorrFltAlpha = 0.95521;//7.50/2.01/1.083(11568559.86)/0.000001:258649,434609
2358 CorrFltLen = 5.70774;
2359 IntFltAlpha = 1.00766;
2360 IntFltCutoff = 0.74202;
2361 IntFltLen = 18;
2362 LPFltAlpha = 1.6801;
2363 LPFltBaseLen = 6.62;
2364 LPFltCutoffMult = 0.67821;
2365 }
2366};
2367
2376
2377struct CImageResizerParamsLR : public CImageResizerParams
2378{
2379 CImageResizerParamsLR()
2380 {
2381 CorrFltAlpha = 1;//7.91/1.96/1.065(1980857.66)/0.000004:258649,437578
2382 CorrFltLen = 5.865;
2383 IntFltAlpha = 1.79529;
2384 IntFltCutoff = 0.74325;
2385 IntFltLen = 18;
2386 LPFltAlpha = 1.87597;
2387 LPFltBaseLen = 6.89999999999999;
2388 LPFltCutoffMult = 0.69326;
2389 }
2390};
2391
2400
2401struct CImageResizerParamsLow : public CImageResizerParams
2402{
2403 CImageResizerParamsLow()
2404 {
2405 CorrFltAlpha = 0.99739;//9.21/1.91/1.040(391960.71)/0.000023:258649,444105
2406 CorrFltLen = 6.20326;
2407 IntFltAlpha = 4.6836;
2408 IntFltCutoff = 0.73879;
2409 IntFltLen = 18;
2410 LPFltAlpha = 7.86565;
2411 LPFltBaseLen = 6.91999999999999;
2412 LPFltCutoffMult = 0.78379;
2413 }
2414};
2415
2425
2426struct CImageResizerParamsHigh : public CImageResizerParams
2427{
2428 CImageResizerParamsHigh()
2429 {
2430 CorrFltAlpha = 0.97433;//11.59/1.84/1.015(73054.59)/0.000159:258649,451830
2431 CorrFltLen = 6.87893;
2432 IntFltAlpha = 7.74731;
2433 IntFltCutoff = 0.73844;
2434 IntFltLen = 18;
2435 LPFltAlpha = 4.8149;
2436 LPFltBaseLen = 8.07999999999996;
2437 LPFltCutoffMult = 0.79335;
2438 }
2439};
2440
2450
2451struct CImageResizerParamsUltra : public CImageResizerParams
2452{
2453 CImageResizerParamsUltra()
2454 {
2455 CorrFltAlpha = 0.99705;//13.68/1.79/1.000(521792.07)/0.000026:258649,457973
2456 CorrFltLen = 7.42695;
2457 IntFltAlpha = 1.71985;
2458 IntFltCutoff = 0.7571;
2459 IntFltLen = 18;
2460 LPFltAlpha = 6.71313;
2461 LPFltBaseLen = 8.27999999999996;
2462 LPFltCutoffMult = 0.78413;
2463 }
2464};
2465
2472
2474{
2475public:
2490 int BufLen[ 2 ];
2491 int BufOffs[ 2 ];
2495 double k;
2497 double o;
2506};
2507
2515
2516class CImageResizerVars : public CImageResizerVarsBase
2517{
2518public:
2519 double ox;
2521 double oy;
2536
2537 CImageResizerVars()
2538 : ox( 0.0 )
2539 , oy( 0.0 )
2540 , ThreadPool( nullptr )
2541 , UseSRGBGamma( false )
2542 , AlphaIndex( -1 )
2543 , BuildMode( -1 )
2544 , RndSeed( 0 )
2545 {
2546 }
2547};
2548
2567
2568template< typename fptype, typename fptypeatom >
2569class CImageResizerFilterStep
2570{
2571 AVIR_NOCTOR( CImageResizerFilterStep )
2572
2573public:
2587 double DCGain;
2591 int InLen;
2592 int InBuf;
2629 static const int EdgePixelCountDef = 3;
2632
2639
2641 {
2643 int fti;
2644 const fptype* ftp;
2645 fptypeatom x;
2647 int fl;
2648 };
2649
2656
2657 class CRPosBuf : public CBuffer< CResizePos >
2658 {
2659 public:
2660 double k;
2661 double o;
2664 };
2665
2673
2674 class CRPosBufArray : public CStructArray< CRPosBuf >
2675 {
2676 public:
2677 using CStructArray< CRPosBuf > :: add;
2678 using CStructArray< CRPosBuf > :: getItemCount;
2679
2692
2693 CRPosBuf& getRPosBuf( const double k, const double o,
2694 const int FracCount )
2695 {
2696 int i;
2697
2698 for( i = 0; i < getItemCount(); i++ )
2699 {
2700 CRPosBuf& Buf = (*this)[ i ];
2701
2702 if( Buf.k == k && Buf.o == o && Buf.FracCount == FracCount )
2703 {
2704 return( Buf );
2705 }
2706 }
2707
2708 CRPosBuf& NewBuf = add();
2709 NewBuf.k = k;
2710 NewBuf.o = o;
2711 NewBuf.FracCount = FracCount;
2712
2713 return( NewBuf );
2714 }
2715 };
2716
2724
2725 CImageResizerFilterStep()
2726 {
2727 }
2728};
2729
2740
2741template< typename fptype, typename fptypeatom >
2743 public CImageResizerFilterStep< fptype, fptypeatom >
2744{
2745public:
2746 using CImageResizerFilterStep< fptype, fptypeatom > :: IsUpsample;
2747 using CImageResizerFilterStep< fptype, fptypeatom > :: ResampleFactor;
2748 using CImageResizerFilterStep< fptype, fptypeatom > :: Flt;
2749 using CImageResizerFilterStep< fptype, fptypeatom > :: FltOrig;
2750 using CImageResizerFilterStep< fptype, fptypeatom > :: FltLatency;
2751 using CImageResizerFilterStep< fptype, fptypeatom > :: Vars;
2752 using CImageResizerFilterStep< fptype, fptypeatom > :: InLen;
2753 using CImageResizerFilterStep< fptype, fptypeatom > :: InPrefix;
2754 using CImageResizerFilterStep< fptype, fptypeatom > :: InSuffix;
2755 using CImageResizerFilterStep< fptype, fptypeatom > :: OutLen;
2756 using CImageResizerFilterStep< fptype, fptypeatom > :: OutPrefix;
2757 using CImageResizerFilterStep< fptype, fptypeatom > :: OutSuffix;
2758 using CImageResizerFilterStep< fptype, fptypeatom > :: PrefixDC;
2759 using CImageResizerFilterStep< fptype, fptypeatom > :: SuffixDC;
2760 using CImageResizerFilterStep< fptype, fptypeatom > :: RPosBuf;
2761 using CImageResizerFilterStep< fptype, fptypeatom > :: FltBank;
2762 using CImageResizerFilterStep< fptype, fptypeatom > :: EdgePixelCount;
2763
2776
2777 template< typename Tin >
2778 void packScanline( const Tin* ip, fptype* const op0, const int l0 ) const
2779 {
2780 const int ElCount = Vars -> ElCount;
2781 const int ElCountIO = Vars -> ElCountIO;
2782 fptype* op = op0;
2783 int l = l0;
2784
2785 if( !Vars -> UseSRGBGamma )
2786 {
2787 if( ElCountIO == 1 )
2788 {
2789 while( l > 0 )
2790 {
2791 fptypeatom* v = (fptypeatom*) op;
2792 v[ 0 ] = (fptypeatom) ip[ 0 ];
2793 op += ElCount;
2794 ip++;
2795 l--;
2796 }
2797 }
2798 else
2799 if( ElCountIO == 4 )
2800 {
2801 while( l > 0 )
2802 {
2803 fptypeatom* v = (fptypeatom*) op;
2804 v[ 0 ] = (fptypeatom) ip[ 0 ];
2805 v[ 1 ] = (fptypeatom) ip[ 1 ];
2806 v[ 2 ] = (fptypeatom) ip[ 2 ];
2807 v[ 3 ] = (fptypeatom) ip[ 3 ];
2808 op += ElCount;
2809 ip += 4;
2810 l--;
2811 }
2812 }
2813 else
2814 if( ElCountIO == 3 )
2815 {
2816 while( l > 0 )
2817 {
2818 fptypeatom* v = (fptypeatom*) op;
2819 v[ 0 ] = (fptypeatom) ip[ 0 ];
2820 v[ 1 ] = (fptypeatom) ip[ 1 ];
2821 v[ 2 ] = (fptypeatom) ip[ 2 ];
2822 op += ElCount;
2823 ip += 3;
2824 l--;
2825 }
2826 }
2827 else
2828 if( ElCountIO == 2 )
2829 {
2830 while( l > 0 )
2831 {
2832 fptypeatom* v = (fptypeatom*) op;
2833 v[ 0 ] = (fptypeatom) ip[ 0 ];
2834 v[ 1 ] = (fptypeatom) ip[ 1 ];
2835 op += ElCount;
2836 ip += 2;
2837 l--;
2838 }
2839 }
2840 }
2841 else
2842 {
2843 const fptypeatom gm = (fptypeatom) Vars -> InGammaMult;
2844
2845 if( ElCountIO == 1 )
2846 {
2847 while( l > 0 )
2848 {
2849 fptypeatom* v = (fptypeatom*) op;
2850 v[ 0 ] = convertSRGB2Lin( ip[ 0 ], gm );
2851 op += ElCount;
2852 ip++;
2853 l--;
2854 }
2855 }
2856 else
2857 if( ElCountIO == 4 )
2858 {
2859 if( Vars -> AlphaIndex == 0 )
2860 {
2861 while( l > 0 )
2862 {
2863 fptypeatom* v = (fptypeatom*) op;
2864 v[ 0 ] = (fptypeatom) ip[ 0 ] * gm;
2865 v[ 1 ] = convertSRGB2Lin( ip[ 1 ], gm );
2866 v[ 2 ] = convertSRGB2Lin( ip[ 2 ], gm );
2867 v[ 3 ] = convertSRGB2Lin( ip[ 3 ], gm );
2868 op += ElCount;
2869 ip += 4;
2870 l--;
2871 }
2872 }
2873 else
2874 if( Vars -> AlphaIndex == 3 )
2875 {
2876 while( l > 0 )
2877 {
2878 fptypeatom* v = (fptypeatom*) op;
2879 v[ 0 ] = convertSRGB2Lin( ip[ 0 ], gm );
2880 v[ 1 ] = convertSRGB2Lin( ip[ 1 ], gm );
2881 v[ 2 ] = convertSRGB2Lin( ip[ 2 ], gm );
2882 v[ 3 ] = (fptypeatom) ip[ 3 ] * gm;
2883 op += ElCount;
2884 ip += 4;
2885 l--;
2886 }
2887 }
2888 else
2889 {
2890 while( l > 0 )
2891 {
2892 fptypeatom* v = (fptypeatom*) op;
2893 v[ 0 ] = convertSRGB2Lin( ip[ 0 ], gm );
2894 v[ 1 ] = convertSRGB2Lin( ip[ 1 ], gm );
2895 v[ 2 ] = convertSRGB2Lin( ip[ 2 ], gm );
2896 v[ 3 ] = convertSRGB2Lin( ip[ 3 ], gm );
2897 op += ElCount;
2898 ip += 4;
2899 l--;
2900 }
2901 }
2902 }
2903 else
2904 if( ElCountIO == 3 )
2905 {
2906 while( l > 0 )
2907 {
2908 fptypeatom* v = (fptypeatom*) op;
2909 v[ 0 ] = convertSRGB2Lin( ip[ 0 ], gm );
2910 v[ 1 ] = convertSRGB2Lin( ip[ 1 ], gm );
2911 v[ 2 ] = convertSRGB2Lin( ip[ 2 ], gm );
2912 op += ElCount;
2913 ip += 3;
2914 l--;
2915 }
2916 }
2917 else
2918 if( ElCountIO == 2 )
2919 {
2920 while( l > 0 )
2921 {
2922 fptypeatom* v = (fptypeatom*) op;
2923 v[ 0 ] = convertSRGB2Lin( ip[ 0 ], gm );
2924 v[ 1 ] = convertSRGB2Lin( ip[ 1 ], gm );
2925 op += ElCount;
2926 ip += 2;
2927 l--;
2928 }
2929 }
2930 }
2931
2932 const int ZeroCount = ElCount * Vars -> fppack - ElCountIO;
2933 op = (fptype*) ( (fptypeatom*) op0 + ElCountIO );
2934 l = l0;
2935
2936 if( ZeroCount == 1 )
2937 {
2938 while( l > 0 )
2939 {
2940 fptypeatom* v = (fptypeatom*) op;
2941 v[ 0 ] = (fptypeatom) 0;
2942 op += ElCount;
2943 l--;
2944 }
2945 }
2946 else
2947 if( ZeroCount == 2 )
2948 {
2949 while( l > 0 )
2950 {
2951 fptypeatom* v = (fptypeatom*) op;
2952 v[ 0 ] = (fptypeatom) 0;
2953 v[ 1 ] = (fptypeatom) 0;
2954 op += ElCount;
2955 l--;
2956 }
2957 }
2958 else
2959 if( ZeroCount == 3 )
2960 {
2961 while( l > 0 )
2962 {
2963 fptypeatom* v = (fptypeatom*) op;
2964 v[ 0 ] = (fptypeatom) 0;
2965 v[ 1 ] = (fptypeatom) 0;
2966 v[ 2 ] = (fptypeatom) 0;
2967 op += ElCount;
2968 l--;
2969 }
2970 }
2971 }
2972
2981
2982 static void applySRGBGamma( fptype* p, int l,
2983 const CImageResizerVars& Vars0 )
2984 {
2985 const int ElCount = Vars0.ElCount;
2986 const int ElCountIO = Vars0.ElCountIO;
2987 const fptypeatom gm = (fptypeatom) Vars0.OutGammaMult;
2988
2989 if( ElCountIO == 1 )
2990 {
2991 while( l > 0 )
2992 {
2993 fptypeatom* v = (fptypeatom*) p;
2994 v[ 0 ] = convertLin2SRGB( v[ 0 ]) * gm;
2995 p += ElCount;
2996 l--;
2997 }
2998 }
2999 else
3000 if( ElCountIO == 4 )
3001 {
3002 if( Vars0.AlphaIndex == 0 )
3003 {
3004 while( l > 0 )
3005 {
3006 fptypeatom* v = (fptypeatom*) p;
3007 v[ 0 ] *= gm;
3008 v[ 1 ] = convertLin2SRGB( v[ 1 ]) * gm;
3009 v[ 2 ] = convertLin2SRGB( v[ 2 ]) * gm;
3010 v[ 3 ] = convertLin2SRGB( v[ 3 ]) * gm;
3011 p += ElCount;
3012 l--;
3013 }
3014 }
3015 else
3016 if( Vars0.AlphaIndex == 3 )
3017 {
3018 while( l > 0 )
3019 {
3020 fptypeatom* v = (fptypeatom*) p;
3021 v[ 0 ] = convertLin2SRGB( v[ 0 ]) * gm;
3022 v[ 1 ] = convertLin2SRGB( v[ 1 ]) * gm;
3023 v[ 2 ] = convertLin2SRGB( v[ 2 ]) * gm;
3024 v[ 3 ] *= gm;
3025 p += ElCount;
3026 l--;
3027 }
3028 }
3029 else
3030 {
3031 while( l > 0 )
3032 {
3033 fptypeatom* v = (fptypeatom*) p;
3034 v[ 0 ] = convertLin2SRGB( v[ 0 ]) * gm;
3035 v[ 1 ] = convertLin2SRGB( v[ 1 ]) * gm;
3036 v[ 2 ] = convertLin2SRGB( v[ 2 ]) * gm;
3037 v[ 3 ] = convertLin2SRGB( v[ 3 ]) * gm;
3038 p += ElCount;
3039 l--;
3040 }
3041 }
3042 }
3043 else
3044 if( ElCountIO == 3 )
3045 {
3046 while( l > 0 )
3047 {
3048 fptypeatom* v = (fptypeatom*) p;
3049 v[ 0 ] = convertLin2SRGB( v[ 0 ]) * gm;
3050 v[ 1 ] = convertLin2SRGB( v[ 1 ]) * gm;
3051 v[ 2 ] = convertLin2SRGB( v[ 2 ]) * gm;
3052 p += ElCount;
3053 l--;
3054 }
3055 }
3056 else
3057 if( ElCountIO == 2 )
3058 {
3059 while( l > 0 )
3060 {
3061 fptypeatom* v = (fptypeatom*) p;
3062 v[ 0 ] = convertLin2SRGB( v[ 0 ]) * gm;
3063 v[ 1 ] = convertLin2SRGB( v[ 1 ]) * gm;
3064 p += ElCount;
3065 l--;
3066 }
3067 }
3068 }
3069
3084
3085 void convertVtoH( const fptype* ip, fptype* op, const int SrcLen,
3086 const int SrcIncr ) const
3087 {
3088 const int ElCount = Vars -> ElCount;
3089 int j;
3090
3091 if( ElCount == 1 )
3092 {
3093 for( j = 0; j < SrcLen; j++ )
3094 {
3095 op[ 0 ] = ip[ 0 ];
3096 ip += SrcIncr;
3097 op++;
3098 }
3099 }
3100 else
3101 if( ElCount == 4 )
3102 {
3103 for( j = 0; j < SrcLen; j++ )
3104 {
3105 op[ 0 ] = ip[ 0 ];
3106 op[ 1 ] = ip[ 1 ];
3107 op[ 2 ] = ip[ 2 ];
3108 op[ 3 ] = ip[ 3 ];
3109 ip += SrcIncr;
3110 op += 4;
3111 }
3112 }
3113 else
3114 if( ElCount == 3 )
3115 {
3116 for( j = 0; j < SrcLen; j++ )
3117 {
3118 op[ 0 ] = ip[ 0 ];
3119 op[ 1 ] = ip[ 1 ];
3120 op[ 2 ] = ip[ 2 ];
3121 ip += SrcIncr;
3122 op += 3;
3123 }
3124 }
3125 else
3126 if( ElCount == 2 )
3127 {
3128 for( j = 0; j < SrcLen; j++ )
3129 {
3130 op[ 0 ] = ip[ 0 ];
3131 op[ 1 ] = ip[ 1 ];
3132 ip += SrcIncr;
3133 op += 2;
3134 }
3135 }
3136 }
3137
3154
3155 template< typename Tout >
3156 static void unpackScanline( const fptype* ip, Tout* op, int l,
3157 const CImageResizerVars& Vars0 )
3158 {
3159 const int ElCount = Vars0.ElCount;
3160 const int ElCountIO = Vars0.ElCountIO;
3161
3162 if( ElCountIO == 1 )
3163 {
3164 while( l > 0 )
3165 {
3166 const fptypeatom* v = (const fptypeatom*) ip;
3167 op[ 0 ] = (Tout) v[ 0 ];
3168 ip += ElCount;
3169 op++;
3170 l--;
3171 }
3172 }
3173 else
3174 if( ElCountIO == 4 )
3175 {
3176 while( l > 0 )
3177 {
3178 const fptypeatom* v = (const fptypeatom*) ip;
3179 op[ 0 ] = (Tout) v[ 0 ];
3180 op[ 1 ] = (Tout) v[ 1 ];
3181 op[ 2 ] = (Tout) v[ 2 ];
3182 op[ 3 ] = (Tout) v[ 3 ];
3183 ip += ElCount;
3184 op += 4;
3185 l--;
3186 }
3187 }
3188 else
3189 if( ElCountIO == 3 )
3190 {
3191 while( l > 0 )
3192 {
3193 const fptypeatom* v = (const fptypeatom*) ip;
3194 op[ 0 ] = (Tout) v[ 0 ];
3195 op[ 1 ] = (Tout) v[ 1 ];
3196 op[ 2 ] = (Tout) v[ 2 ];
3197 ip += ElCount;
3198 op += 3;
3199 l--;
3200 }
3201 }
3202 else
3203 if( ElCountIO == 2 )
3204 {
3205 while( l > 0 )
3206 {
3207 const fptypeatom* v = (const fptypeatom*) ip;
3208 op[ 0 ] = (Tout) v[ 0 ];
3209 op[ 1 ] = (Tout) v[ 1 ];
3210 ip += ElCount;
3211 op += 2;
3212 l--;
3213 }
3214 }
3215 }
3216
3226
3227 void prepareInBuf( fptype* Src ) const
3228 {
3229 if( IsUpsample || InPrefix + InSuffix == 0 )
3230 {
3231 return;
3232 }
3233
3234 const int ElCount = Vars -> ElCount;
3235 replicateArray( Src, ElCount, Src - ElCount, InPrefix, -ElCount );
3236
3237 Src += ( InLen - 1 ) * ElCount;
3238 replicateArray( Src, ElCount, Src + ElCount, InSuffix, ElCount );
3239 }
3240
3248
3249 void doUpsample( const fptype* const Src, fptype* const Dst ) const
3250 {
3251 const int ElCount = Vars -> ElCount;
3252 fptype* op0 = &Dst[ -OutPrefix * ElCount ];
3253 memset( op0, 0, (size_t) ( OutPrefix + OutLen + OutSuffix ) *
3254 (size_t) ElCount * sizeof( fptype ));
3255
3256 const fptype* ip = Src;
3257 const int opstep = ElCount * ResampleFactor;
3258 int l;
3259
3260 if( FltOrig.getCapacity() > 0 )
3261 {
3262 // Do not perform filtering, only upsample.
3263
3264 op0 += ( OutPrefix % ResampleFactor ) * ElCount;
3266
3267 if( ElCount == 1 )
3268 {
3269 while( l > 0 )
3270 {
3271 op0[ 0 ] = ip[ 0 ];
3272 op0 += opstep;
3273 l--;
3274 }
3275
3276 l = InLen - 1;
3277
3278 while( l > 0 )
3279 {
3280 op0[ 0 ] = ip[ 0 ];
3281 op0 += opstep;
3282 ip += ElCount;
3283 l--;
3284 }
3285
3287
3288 while( l >= 0 )
3289 {
3290 op0[ 0 ] = ip[ 0 ];
3291 op0 += opstep;
3292 l--;
3293 }
3294 }
3295 else
3296 if( ElCount == 4 )
3297 {
3298 while( l > 0 )
3299 {
3300 op0[ 0 ] = ip[ 0 ];
3301 op0[ 1 ] = ip[ 1 ];
3302 op0[ 2 ] = ip[ 2 ];
3303 op0[ 3 ] = ip[ 3 ];
3304 op0 += opstep;
3305 l--;
3306 }
3307
3308 l = InLen - 1;
3309
3310 while( l > 0 )
3311 {
3312 op0[ 0 ] = ip[ 0 ];
3313 op0[ 1 ] = ip[ 1 ];
3314 op0[ 2 ] = ip[ 2 ];
3315 op0[ 3 ] = ip[ 3 ];
3316 op0 += opstep;
3317 ip += ElCount;
3318 l--;
3319 }
3320
3322
3323 while( l >= 0 )
3324 {
3325 op0[ 0 ] = ip[ 0 ];
3326 op0[ 1 ] = ip[ 1 ];
3327 op0[ 2 ] = ip[ 2 ];
3328 op0[ 3 ] = ip[ 3 ];
3329 op0 += opstep;
3330 l--;
3331 }
3332 }
3333 else
3334 if( ElCount == 3 )
3335 {
3336 while( l > 0 )
3337 {
3338 op0[ 0 ] = ip[ 0 ];
3339 op0[ 1 ] = ip[ 1 ];
3340 op0[ 2 ] = ip[ 2 ];
3341 op0 += opstep;
3342 l--;
3343 }
3344
3345 l = InLen - 1;
3346
3347 while( l > 0 )
3348 {
3349 op0[ 0 ] = ip[ 0 ];
3350 op0[ 1 ] = ip[ 1 ];
3351 op0[ 2 ] = ip[ 2 ];
3352 op0 += opstep;
3353 ip += ElCount;
3354 l--;
3355 }
3356
3358
3359 while( l >= 0 )
3360 {
3361 op0[ 0 ] = ip[ 0 ];
3362 op0[ 1 ] = ip[ 1 ];
3363 op0[ 2 ] = ip[ 2 ];
3364 op0 += opstep;
3365 l--;
3366 }
3367 }
3368 else
3369 if( ElCount == 2 )
3370 {
3371 while( l > 0 )
3372 {
3373 op0[ 0 ] = ip[ 0 ];
3374 op0[ 1 ] = ip[ 1 ];
3375 op0 += opstep;
3376 l--;
3377 }
3378
3379 l = InLen - 1;
3380
3381 while( l > 0 )
3382 {
3383 op0[ 0 ] = ip[ 0 ];
3384 op0[ 1 ] = ip[ 1 ];
3385 op0 += opstep;
3386 ip += ElCount;
3387 l--;
3388 }
3389
3391
3392 while( l >= 0 )
3393 {
3394 op0[ 0 ] = ip[ 0 ];
3395 op0[ 1 ] = ip[ 1 ];
3396 op0 += opstep;
3397 l--;
3398 }
3399 }
3400
3401 return;
3402 }
3403
3404 const fptype* const f = Flt;
3405 const int flen = Flt.getCapacity();
3406 fptype* op;
3407 int i;
3408
3409 if( ElCount == 1 )
3410 {
3411 l = InPrefix;
3412
3413 while( l > 0 )
3414 {
3415 op = op0;
3416
3417 for( i = 0; i < flen; i++ )
3418 {
3419 op[ i ] += f[ i ] * ip[ 0 ];
3420 }
3421
3422 op0 += opstep;
3423 l--;
3424 }
3425
3426 l = InLen - 1;
3427
3428 while( l > 0 )
3429 {
3430 op = op0;
3431
3432 for( i = 0; i < flen; i++ )
3433 {
3434 op[ i ] += f[ i ] * ip[ 0 ];
3435 }
3436
3437 ip += ElCount;
3438 op0 += opstep;
3439 l--;
3440 }
3441
3442 l = InSuffix;
3443
3444 while( l >= 0 )
3445 {
3446 op = op0;
3447
3448 for( i = 0; i < flen; i++ )
3449 {
3450 op[ i ] += f[ i ] * ip[ 0 ];
3451 }
3452
3453 op0 += opstep;
3454 l--;
3455 }
3456 }
3457 else
3458 if( ElCount == 4 )
3459 {
3460 l = InPrefix;
3461
3462 while( l > 0 )
3463 {
3464 op = op0;
3465
3466 for( i = 0; i < flen; i++ )
3467 {
3468 op[ 0 ] += f[ i ] * ip[ 0 ];
3469 op[ 1 ] += f[ i ] * ip[ 1 ];
3470 op[ 2 ] += f[ i ] * ip[ 2 ];
3471 op[ 3 ] += f[ i ] * ip[ 3 ];
3472 op += 4;
3473 }
3474
3475 op0 += opstep;
3476 l--;
3477 }
3478
3479 l = InLen - 1;
3480
3481 while( l > 0 )
3482 {
3483 op = op0;
3484
3485 for( i = 0; i < flen; i++ )
3486 {
3487 op[ 0 ] += f[ i ] * ip[ 0 ];
3488 op[ 1 ] += f[ i ] * ip[ 1 ];
3489 op[ 2 ] += f[ i ] * ip[ 2 ];
3490 op[ 3 ] += f[ i ] * ip[ 3 ];
3491 op += 4;
3492 }
3493
3494 ip += ElCount;
3495 op0 += opstep;
3496 l--;
3497 }
3498
3499 l = InSuffix;
3500
3501 while( l >= 0 )
3502 {
3503 op = op0;
3504
3505 for( i = 0; i < flen; i++ )
3506 {
3507 op[ 0 ] += f[ i ] * ip[ 0 ];
3508 op[ 1 ] += f[ i ] * ip[ 1 ];
3509 op[ 2 ] += f[ i ] * ip[ 2 ];
3510 op[ 3 ] += f[ i ] * ip[ 3 ];
3511 op += 4;
3512 }
3513
3514 op0 += opstep;
3515 l--;
3516 }
3517 }
3518 else
3519 if( ElCount == 3 )
3520 {
3521 l = InPrefix;
3522
3523 while( l > 0 )
3524 {
3525 op = op0;
3526
3527 for( i = 0; i < flen; i++ )
3528 {
3529 op[ 0 ] += f[ i ] * ip[ 0 ];
3530 op[ 1 ] += f[ i ] * ip[ 1 ];
3531 op[ 2 ] += f[ i ] * ip[ 2 ];
3532 op += 3;
3533 }
3534
3535 op0 += opstep;
3536 l--;
3537 }
3538
3539 l = InLen - 1;
3540
3541 while( l > 0 )
3542 {
3543 op = op0;
3544
3545 for( i = 0; i < flen; i++ )
3546 {
3547 op[ 0 ] += f[ i ] * ip[ 0 ];
3548 op[ 1 ] += f[ i ] * ip[ 1 ];
3549 op[ 2 ] += f[ i ] * ip[ 2 ];
3550 op += 3;
3551 }
3552
3553 ip += ElCount;
3554 op0 += opstep;
3555 l--;
3556 }
3557
3558 l = InSuffix;
3559
3560 while( l >= 0 )
3561 {
3562 op = op0;
3563
3564 for( i = 0; i < flen; i++ )
3565 {
3566 op[ 0 ] += f[ i ] * ip[ 0 ];
3567 op[ 1 ] += f[ i ] * ip[ 1 ];
3568 op[ 2 ] += f[ i ] * ip[ 2 ];
3569 op += 3;
3570 }
3571
3572 op0 += opstep;
3573 l--;
3574 }
3575 }
3576 else
3577 if( ElCount == 2 )
3578 {
3579 l = InPrefix;
3580
3581 while( l > 0 )
3582 {
3583 op = op0;
3584
3585 for( i = 0; i < flen; i++ )
3586 {
3587 op[ 0 ] += f[ i ] * ip[ 0 ];
3588 op[ 1 ] += f[ i ] * ip[ 1 ];
3589 op += 2;
3590 }
3591
3592 op0 += opstep;
3593 l--;
3594 }
3595
3596 l = InLen - 1;
3597
3598 while( l > 0 )
3599 {
3600 op = op0;
3601
3602 for( i = 0; i < flen; i++ )
3603 {
3604 op[ 0 ] += f[ i ] * ip[ 0 ];
3605 op[ 1 ] += f[ i ] * ip[ 1 ];
3606 op += 2;
3607 }
3608
3609 ip += ElCount;
3610 op0 += opstep;
3611 l--;
3612 }
3613
3614 l = InSuffix;
3615
3616 while( l >= 0 )
3617 {
3618 op = op0;
3619
3620 for( i = 0; i < flen; i++ )
3621 {
3622 op[ 0 ] += f[ i ] * ip[ 0 ];
3623 op[ 1 ] += f[ i ] * ip[ 1 ];
3624 op += 2;
3625 }
3626
3627 op0 += opstep;
3628 l--;
3629 }
3630 }
3631
3632 op = op0;
3633 const fptype* dc = SuffixDC;
3634 l = SuffixDC.getCapacity();
3635
3636 if( ElCount == 1 )
3637 {
3638 for( i = 0; i < l; i++ )
3639 {
3640 op[ i ] += ip[ 0 ] * dc[ i ];
3641 }
3642 }
3643 else
3644 if( ElCount == 4 )
3645 {
3646 while( l > 0 )
3647 {
3648 op[ 0 ] += ip[ 0 ] * dc[ 0 ];
3649 op[ 1 ] += ip[ 1 ] * dc[ 0 ];
3650 op[ 2 ] += ip[ 2 ] * dc[ 0 ];
3651 op[ 3 ] += ip[ 3 ] * dc[ 0 ];
3652 dc++;
3653 op += 4;
3654 l--;
3655 }
3656 }
3657 else
3658 if( ElCount == 3 )
3659 {
3660 while( l > 0 )
3661 {
3662 op[ 0 ] += ip[ 0 ] * dc[ 0 ];
3663 op[ 1 ] += ip[ 1 ] * dc[ 0 ];
3664 op[ 2 ] += ip[ 2 ] * dc[ 0 ];
3665 dc++;
3666 op += 3;
3667 l--;
3668 }
3669 }
3670 else
3671 if( ElCount == 2 )
3672 {
3673 while( l > 0 )
3674 {
3675 op[ 0 ] += ip[ 0 ] * dc[ 0 ];
3676 op[ 1 ] += ip[ 1 ] * dc[ 0 ];
3677 dc++;
3678 op += 2;
3679 l--;
3680 }
3681 }
3682
3683 ip = Src;
3684 op = Dst - InPrefix * opstep;
3685 dc = PrefixDC;
3686 l = PrefixDC.getCapacity();
3687
3688 if( ElCount == 1 )
3689 {
3690 for( i = 0; i < l; i++ )
3691 {
3692 op[ i ] += ip[ 0 ] * dc[ i ];
3693 }
3694 }
3695 else
3696 if( ElCount == 4 )
3697 {
3698 while( l > 0 )
3699 {
3700 op[ 0 ] += ip[ 0 ] * dc[ 0 ];
3701 op[ 1 ] += ip[ 1 ] * dc[ 0 ];
3702 op[ 2 ] += ip[ 2 ] * dc[ 0 ];
3703 op[ 3 ] += ip[ 3 ] * dc[ 0 ];
3704 dc++;
3705 op += 4;
3706 l--;
3707 }
3708 }
3709 else
3710 if( ElCount == 3 )
3711 {
3712 while( l > 0 )
3713 {
3714 op[ 0 ] += ip[ 0 ] * dc[ 0 ];
3715 op[ 1 ] += ip[ 1 ] * dc[ 0 ];
3716 op[ 2 ] += ip[ 2 ] * dc[ 0 ];
3717 dc++;
3718 op += 3;
3719 l--;
3720 }
3721 }
3722 else
3723 if( ElCount == 2 )
3724 {
3725 while( l > 0 )
3726 {
3727 op[ 0 ] += ip[ 0 ] * dc[ 0 ];
3728 op[ 1 ] += ip[ 1 ] * dc[ 0 ];
3729 dc++;
3730 op += 2;
3731 l--;
3732 }
3733 }
3734 }
3735
3747
3748 void doFilter( const fptype* const Src, fptype* Dst,
3749 const int DstIncr ) const
3750 {
3751 const int ElCount = Vars -> ElCount;
3752 const fptype* const f = &Flt[ FltLatency ];
3753 const int flen = FltLatency + 1;
3754 const int ipstep = ElCount * ResampleFactor;
3755 const fptype* ip = Src - EdgePixelCount * ipstep;
3756 const fptype* ip1;
3757 const fptype* ip2;
3758 int l = OutLen;
3759 int i;
3760
3761 if( ElCount == 1 )
3762 {
3763 while( l > 0 )
3764 {
3765 fptype s = f[ 0 ] * ip[ 0 ];
3766 ip1 = ip;
3767 ip2 = ip;
3768
3769 for( i = 1; i < flen; i++ )
3770 {
3771 ip1++;
3772 ip2--;
3773 s += f[ i ] * ( ip1[ 0 ] + ip2[ 0 ]);
3774 }
3775
3776 Dst[ 0 ] = s;
3777 Dst += DstIncr;
3778 ip += ipstep;
3779 l--;
3780 }
3781 }
3782 else
3783 if( ElCount == 4 )
3784 {
3785 while( l > 0 )
3786 {
3787 fptype s1 = f[ 0 ] * ip[ 0 ];
3788 fptype s2 = f[ 0 ] * ip[ 1 ];
3789 fptype s3 = f[ 0 ] * ip[ 2 ];
3790 fptype s4 = f[ 0 ] * ip[ 3 ];
3791 ip1 = ip;
3792 ip2 = ip;
3793
3794 for( i = 1; i < flen; i++ )
3795 {
3796 ip1 += 4;
3797 ip2 -= 4;
3798 s1 += f[ i ] * ( ip1[ 0 ] + ip2[ 0 ]);
3799 s2 += f[ i ] * ( ip1[ 1 ] + ip2[ 1 ]);
3800 s3 += f[ i ] * ( ip1[ 2 ] + ip2[ 2 ]);
3801 s4 += f[ i ] * ( ip1[ 3 ] + ip2[ 3 ]);
3802 }
3803
3804 Dst[ 0 ] = s1;
3805 Dst[ 1 ] = s2;
3806 Dst[ 2 ] = s3;
3807 Dst[ 3 ] = s4;
3808 Dst += DstIncr;
3809 ip += ipstep;
3810 l--;
3811 }
3812 }
3813 else
3814 if( ElCount == 3 )
3815 {
3816 while( l > 0 )
3817 {
3818 fptype s1 = f[ 0 ] * ip[ 0 ];
3819 fptype s2 = f[ 0 ] * ip[ 1 ];
3820 fptype s3 = f[ 0 ] * ip[ 2 ];
3821 ip1 = ip;
3822 ip2 = ip;
3823
3824 for( i = 1; i < flen; i++ )
3825 {
3826 ip1 += 3;
3827 ip2 -= 3;
3828 s1 += f[ i ] * ( ip1[ 0 ] + ip2[ 0 ]);
3829 s2 += f[ i ] * ( ip1[ 1 ] + ip2[ 1 ]);
3830 s3 += f[ i ] * ( ip1[ 2 ] + ip2[ 2 ]);
3831 }
3832
3833 Dst[ 0 ] = s1;
3834 Dst[ 1 ] = s2;
3835 Dst[ 2 ] = s3;
3836 Dst += DstIncr;
3837 ip += ipstep;
3838 l--;
3839 }
3840 }
3841 else
3842 if( ElCount == 2 )
3843 {
3844 while( l > 0 )
3845 {
3846 fptype s1 = f[ 0 ] * ip[ 0 ];
3847 fptype s2 = f[ 0 ] * ip[ 1 ];
3848 ip1 = ip;
3849 ip2 = ip;
3850
3851 for( i = 1; i < flen; i++ )
3852 {
3853 ip1 += 2;
3854 ip2 -= 2;
3855 s1 += f[ i ] * ( ip1[ 0 ] + ip2[ 0 ]);
3856 s2 += f[ i ] * ( ip1[ 1 ] + ip2[ 1 ]);
3857 }
3858
3859 Dst[ 0 ] = s1;
3860 Dst[ 1 ] = s2;
3861 Dst += DstIncr;
3862 ip += ipstep;
3863 l--;
3864 }
3865 }
3866 }
3867
3883
3884 void doResize( const fptype* SrcLine, fptype* DstLine,
3885 const int DstLineIncr, fptype* const ) const
3886 {
3887 const int IntFltLen = FltBank -> getFilterLen();
3888 const int ElCount = Vars -> ElCount;
3889 const typename CImageResizerFilterStep< fptype, fptypeatom > ::
3890 CResizePos* rpos = &(*RPosBuf)[ 0 ];
3891
3892 const typename CImageResizerFilterStep< fptype, fptypeatom > ::
3893 CResizePos* const rpose = rpos + OutLen;
3894
3895#define AVIR_RESIZE_PART1 \
3896 while( rpos < rpose ) \
3897 { \
3898 const fptype x = (fptype) rpos -> x; \
3899 const fptype* const ftp = rpos -> ftp; \
3900 const fptype* const ftp2 = ftp + IntFltLen; \
3901 const fptype* Src = SrcLine + rpos -> SrcOffs; \
3902 int i;
3903
3904#define AVIR_RESIZE_PART1nx \
3905 while( rpos < rpose ) \
3906 { \
3907 const fptype* const ftp = rpos -> ftp; \
3908 const fptype* Src = SrcLine + rpos -> SrcOffs; \
3909 int i;
3910
3911#define AVIR_RESIZE_PART2 \
3912 DstLine += DstLineIncr; \
3913 rpos++; \
3914 }
3915
3916 if( FltBank -> getOrder() == 1 )
3917 {
3918 if( ElCount == 1 )
3919 {
3920 AVIR_RESIZE_PART1
3921
3922 fptype sum0 = 0;
3923
3924 for( i = 0; i < IntFltLen; i++ )
3925 {
3926 sum0 += ( ftp[ i ] + ftp2[ i ] * x ) * Src[ i ];
3927 }
3928
3929 DstLine[ 0 ] = sum0;
3930
3931 AVIR_RESIZE_PART2
3932 }
3933 else
3934 if( ElCount == 4 )
3935 {
3936 AVIR_RESIZE_PART1
3937
3938 fptype sum0 = 0;
3939 fptype sum1 = 0;
3940 fptype sum2 = 0;
3941 fptype sum3 = 0;
3942
3943 for( i = 0; i < IntFltLen; i++ )
3944 {
3945 const fptype xx = ftp[ i ] + ftp2[ i ] * x;
3946 sum0 += xx * Src[ 0 ];
3947 sum1 += xx * Src[ 1 ];
3948 sum2 += xx * Src[ 2 ];
3949 sum3 += xx * Src[ 3 ];
3950 Src += 4;
3951 }
3952
3953 DstLine[ 0 ] = sum0;
3954 DstLine[ 1 ] = sum1;
3955 DstLine[ 2 ] = sum2;
3956 DstLine[ 3 ] = sum3;
3957
3958 AVIR_RESIZE_PART2
3959 }
3960 else
3961 if( ElCount == 3 )
3962 {
3963 AVIR_RESIZE_PART1
3964
3965 fptype sum0 = 0;
3966 fptype sum1 = 0;
3967 fptype sum2 = 0;
3968
3969 for( i = 0; i < IntFltLen; i++ )
3970 {
3971 const fptype xx = ftp[ i ] + ftp2[ i ] * x;
3972 sum0 += xx * Src[ 0 ];
3973 sum1 += xx * Src[ 1 ];
3974 sum2 += xx * Src[ 2 ];
3975 Src += 3;
3976 }
3977
3978 DstLine[ 0 ] = sum0;
3979 DstLine[ 1 ] = sum1;
3980 DstLine[ 2 ] = sum2;
3981
3982 AVIR_RESIZE_PART2
3983 }
3984 else
3985 if( ElCount == 2 )
3986 {
3987 AVIR_RESIZE_PART1
3988
3989 fptype sum0 = 0;
3990 fptype sum1 = 0;
3991
3992 for( i = 0; i < IntFltLen; i++ )
3993 {
3994 const fptype xx = ftp[ i ] + ftp2[ i ] * x;
3995 sum0 += xx * Src[ 0 ];
3996 sum1 += xx * Src[ 1 ];
3997 Src += 2;
3998 }
3999
4000 DstLine[ 0 ] = sum0;
4001 DstLine[ 1 ] = sum1;
4002
4003 AVIR_RESIZE_PART2
4004 }
4005 }
4006 else
4007 {
4008 if( ElCount == 1 )
4009 {
4010 AVIR_RESIZE_PART1nx
4011
4012 fptype sum0 = 0;
4013
4014 for( i = 0; i < IntFltLen; i++ )
4015 {
4016 sum0 += ftp[ i ] * Src[ i ];
4017 }
4018
4019 DstLine[ 0 ] = sum0;
4020
4021 AVIR_RESIZE_PART2
4022 }
4023 else
4024 if( ElCount == 4 )
4025 {
4026 AVIR_RESIZE_PART1nx
4027
4028 fptype sum0 = 0;
4029 fptype sum1 = 0;
4030 fptype sum2 = 0;
4031 fptype sum3 = 0;
4032
4033 for( i = 0; i < IntFltLen; i++ )
4034 {
4035 const fptype xx = ftp[ i ];
4036 sum0 += xx * Src[ 0 ];
4037 sum1 += xx * Src[ 1 ];
4038 sum2 += xx * Src[ 2 ];
4039 sum3 += xx * Src[ 3 ];
4040 Src += 4;
4041 }
4042
4043 DstLine[ 0 ] = sum0;
4044 DstLine[ 1 ] = sum1;
4045 DstLine[ 2 ] = sum2;
4046 DstLine[ 3 ] = sum3;
4047
4048 AVIR_RESIZE_PART2
4049 }
4050 else
4051 if( ElCount == 3 )
4052 {
4053 AVIR_RESIZE_PART1nx
4054
4055 fptype sum0 = 0;
4056 fptype sum1 = 0;
4057 fptype sum2 = 0;
4058
4059 for( i = 0; i < IntFltLen; i++ )
4060 {
4061 const fptype xx = ftp[ i ];
4062 sum0 += xx * Src[ 0 ];
4063 sum1 += xx * Src[ 1 ];
4064 sum2 += xx * Src[ 2 ];
4065 Src += 3;
4066 }
4067
4068 DstLine[ 0 ] = sum0;
4069 DstLine[ 1 ] = sum1;
4070 DstLine[ 2 ] = sum2;
4071
4072 AVIR_RESIZE_PART2
4073 }
4074 else
4075 if( ElCount == 2 )
4076 {
4077 AVIR_RESIZE_PART1nx
4078
4079 fptype sum0 = 0;
4080 fptype sum1 = 0;
4081
4082 for( i = 0; i < IntFltLen; i++ )
4083 {
4084 const fptype xx = ftp[ i ];
4085 sum0 += xx * Src[ 0 ];
4086 sum1 += xx * Src[ 1 ];
4087 Src += 2;
4088 }
4089
4090 DstLine[ 0 ] = sum0;
4091 DstLine[ 1 ] = sum1;
4092
4093 AVIR_RESIZE_PART2
4094 }
4095 }
4096 }
4097#undef AVIR_RESIZE_PART2
4098#undef AVIR_RESIZE_PART1nx
4099#undef AVIR_RESIZE_PART1
4100
4113
4114 void doResize2( const fptype* SrcLine, fptype* DstLine,
4115 const int DstLineIncr, fptype* const ) const
4116 {
4117 const int IntFltLen0 = FltBank -> getFilterLen();
4118 const int ElCount = Vars -> ElCount;
4119 const typename CImageResizerFilterStep< fptype, fptypeatom > ::
4120 CResizePos* rpos = &(*RPosBuf)[ 0 ];
4121
4122 const typename CImageResizerFilterStep< fptype, fptypeatom > ::
4123 CResizePos* const rpose = rpos + OutLen;
4124
4125#define AVIR_RESIZE_PART1 \
4126 while( rpos < rpose ) \
4127 { \
4128 const fptype x = (fptype) rpos -> x; \
4129 const fptype* const ftp = rpos -> ftp; \
4130 const fptype* const ftp2 = ftp + IntFltLen0; \
4131 const fptype* Src = SrcLine + rpos -> SrcOffs; \
4132 const int IntFltLen = rpos -> fl; \
4133 int i;
4134
4135#define AVIR_RESIZE_PART1nx \
4136 while( rpos < rpose ) \
4137 { \
4138 const fptype* const ftp = rpos -> ftp; \
4139 const fptype* Src = SrcLine + rpos -> SrcOffs; \
4140 const int IntFltLen = rpos -> fl; \
4141 int i;
4142
4143#define AVIR_RESIZE_PART2 \
4144 DstLine += DstLineIncr; \
4145 rpos++; \
4146 }
4147
4148 if( FltBank -> getOrder() == 1 )
4149 {
4150 if( ElCount == 1 )
4151 {
4152 AVIR_RESIZE_PART1
4153
4154 fptype sum0 = 0;
4155
4156 for( i = 0; i < IntFltLen; i += 2 )
4157 {
4158 sum0 += ( ftp[ i ] + ftp2[ i ] * x ) * Src[ i ];
4159 }
4160
4161 DstLine[ 0 ] = sum0;
4162
4163 AVIR_RESIZE_PART2
4164 }
4165 else
4166 if( ElCount == 4 )
4167 {
4168 AVIR_RESIZE_PART1
4169
4170 fptype sum0 = 0;
4171 fptype sum1 = 0;
4172 fptype sum2 = 0;
4173 fptype sum3 = 0;
4174
4175 for( i = 0; i < IntFltLen; i += 2 )
4176 {
4177 const fptype xx = ftp[ i ] + ftp2[ i ] * x;
4178 sum0 += xx * Src[ 0 ];
4179 sum1 += xx * Src[ 1 ];
4180 sum2 += xx * Src[ 2 ];
4181 sum3 += xx * Src[ 3 ];
4182 Src += 4 * 2;
4183 }
4184
4185 DstLine[ 0 ] = sum0;
4186 DstLine[ 1 ] = sum1;
4187 DstLine[ 2 ] = sum2;
4188 DstLine[ 3 ] = sum3;
4189
4190 AVIR_RESIZE_PART2
4191 }
4192 else
4193 if( ElCount == 3 )
4194 {
4195 AVIR_RESIZE_PART1
4196
4197 fptype sum0 = 0;
4198 fptype sum1 = 0;
4199 fptype sum2 = 0;
4200
4201 for( i = 0; i < IntFltLen; i += 2 )
4202 {
4203 const fptype xx = ftp[ i ] + ftp2[ i ] * x;
4204 sum0 += xx * Src[ 0 ];
4205 sum1 += xx * Src[ 1 ];
4206 sum2 += xx * Src[ 2 ];
4207 Src += 3 * 2;
4208 }
4209
4210 DstLine[ 0 ] = sum0;
4211 DstLine[ 1 ] = sum1;
4212 DstLine[ 2 ] = sum2;
4213
4214 AVIR_RESIZE_PART2
4215 }
4216 else
4217 if( ElCount == 2 )
4218 {
4219 AVIR_RESIZE_PART1
4220
4221 fptype sum0 = 0;
4222 fptype sum1 = 0;
4223
4224 for( i = 0; i < IntFltLen; i += 2 )
4225 {
4226 const fptype xx = ftp[ i ] + ftp2[ i ] * x;
4227 sum0 += xx * Src[ 0 ];
4228 sum1 += xx * Src[ 1 ];
4229 Src += 2 * 2;
4230 }
4231
4232 DstLine[ 0 ] = sum0;
4233 DstLine[ 1 ] = sum1;
4234
4235 AVIR_RESIZE_PART2
4236 }
4237 }
4238 else
4239 {
4240 if( ElCount == 1 )
4241 {
4242 AVIR_RESIZE_PART1nx
4243
4244 fptype sum0 = 0;
4245
4246 for( i = 0; i < IntFltLen; i += 2 )
4247 {
4248 sum0 += ftp[ i ] * Src[ i ];
4249 }
4250
4251 DstLine[ 0 ] = sum0;
4252
4253 AVIR_RESIZE_PART2
4254 }
4255 else
4256 if( ElCount == 4 )
4257 {
4258 AVIR_RESIZE_PART1nx
4259
4260 fptype sum0 = 0;
4261 fptype sum1 = 0;
4262 fptype sum2 = 0;
4263 fptype sum3 = 0;
4264
4265 for( i = 0; i < IntFltLen; i += 2 )
4266 {
4267 const fptype xx = ftp[ i ];
4268 sum0 += xx * Src[ 0 ];
4269 sum1 += xx * Src[ 1 ];
4270 sum2 += xx * Src[ 2 ];
4271 sum3 += xx * Src[ 3 ];
4272 Src += 4 * 2;
4273 }
4274
4275 DstLine[ 0 ] = sum0;
4276 DstLine[ 1 ] = sum1;
4277 DstLine[ 2 ] = sum2;
4278 DstLine[ 3 ] = sum3;
4279
4280 AVIR_RESIZE_PART2
4281 }
4282 else
4283 if( ElCount == 3 )
4284 {
4285 AVIR_RESIZE_PART1nx
4286
4287 fptype sum0 = 0;
4288 fptype sum1 = 0;
4289 fptype sum2 = 0;
4290
4291 for( i = 0; i < IntFltLen; i += 2 )
4292 {
4293 const fptype xx = ftp[ i ];
4294 sum0 += xx * Src[ 0 ];
4295 sum1 += xx * Src[ 1 ];
4296 sum2 += xx * Src[ 2 ];
4297 Src += 3 * 2;
4298 }
4299
4300 DstLine[ 0 ] = sum0;
4301 DstLine[ 1 ] = sum1;
4302 DstLine[ 2 ] = sum2;
4303
4304 AVIR_RESIZE_PART2
4305 }
4306 else
4307 if( ElCount == 2 )
4308 {
4309 AVIR_RESIZE_PART1nx
4310
4311 fptype sum0 = 0;
4312 fptype sum1 = 0;
4313
4314 for( i = 0; i < IntFltLen; i += 2 )
4315 {
4316 const fptype xx = ftp[ i ];
4317 sum0 += xx * Src[ 0 ];
4318 sum1 += xx * Src[ 1 ];
4319 Src += 2 * 2;
4320 }
4321
4322 DstLine[ 0 ] = sum0;
4323 DstLine[ 1 ] = sum1;
4324
4325 AVIR_RESIZE_PART2
4326 }
4327 }
4328 }
4329#undef AVIR_RESIZE_PART2
4330#undef AVIR_RESIZE_PART1nx
4331#undef AVIR_RESIZE_PART1
4332};
4333
4350
4351template< typename fptype >
4353{
4354public:
4364
4365 void init( const int aLen, const CImageResizerVars& aVars,
4366 const double aTrMul, const double aPkOut )
4367 {
4368 Len = aLen;
4369 Vars = &aVars;
4370 LenE = aLen * Vars -> ElCount;
4371 TrMul0 = aTrMul;
4372 PkOut0 = aPkOut;
4373 }
4374
4380
4381 static bool isRecursive()
4382 {
4383 return( false );
4384 }
4385
4391
4392 void dither( fptype* const ResScanline ) const
4393 {
4394 const fptype c0 = 0;
4395 const fptype PkOut = (fptype) PkOut0;
4396 int j;
4397
4398 if( TrMul0 == 1.0 )
4399 {
4400 // Optimization - do not perform bit depth truncation.
4401
4402 for( j = 0; j < LenE; j++ )
4403 {
4404 ResScanline[ j ] = clamp( round( ResScanline[ j ]), c0,
4405 PkOut );
4406 }
4407 }
4408 else
4409 {
4410 const fptype TrMul = (fptype) TrMul0;
4411 const fptype TrMulI = (fptype) ( 1.0 / TrMul0 );
4412
4413 for( j = 0; j < LenE; j++ )
4414 {
4415 const fptype z0 = round( ResScanline[ j ] * TrMulI ) * TrMul;
4416 ResScanline[ j ] = clamp( z0, c0, PkOut );
4417 }
4418 }
4419 }
4420
4421protected:
4422 int Len;
4424 int LenE;
4425 double TrMul0;
4426 double PkOut0;
4427};
4428
4440
4441template< typename fptype >
4443 public CImageResizerDithererDefINL< fptype >
4444{
4445public:
4455
4456 void init( const int aLen, const CImageResizerVars& aVars,
4457 const double aTrMul, const double aPkOut )
4458 {
4460 aPkOut );
4461
4462 ResScanlineDith0.alloc( LenE + Vars -> ElCount, sizeof( fptype ));
4463 ResScanlineDith = ResScanlineDith0 + Vars -> ElCount;
4464 int i;
4465
4466 for( i = 0; i < LenE + Vars -> ElCount; i++ )
4467 {
4468 ResScanlineDith0[ i ] = 0;
4469 }
4470 }
4471
4475
4476 static bool isRecursive()
4477 {
4478 return( true );
4479 }
4480
4484
4485 void dither( fptype* const ResScanline )
4486 {
4487 const int ElCount = Vars -> ElCount;
4488 const fptype c0 = 0;
4489 const fptype TrMul = (fptype) TrMul0;
4490 const fptype TrMulI = (fptype) ( 1.0 / TrMul0 );
4491 const fptype PkOut = (fptype) PkOut0;
4492 int j;
4493
4494 for( j = 0; j < LenE; j++ )
4495 {
4496 ResScanline[ j ] += ResScanlineDith[ j ];
4497 ResScanlineDith[ j ] = 0;
4498 }
4499
4500 for( j = 0; j < LenE - ElCount; j++ )
4501 {
4502 // Perform rounding, noise estimation and saturation.
4503
4504 const fptype z0 = round( ResScanline[ j ] * TrMulI ) * TrMul;
4505 const fptype Noise = ResScanline[ j ] - z0;
4506 ResScanline[ j ] = clamp( z0, c0, PkOut );
4507
4508 const fptype NoiseM1 = Noise * (fptype) 0.364842;
4509 ResScanline[ j + ElCount ] += NoiseM1;
4510 ResScanlineDith[ j - ElCount ] += Noise * (fptype) 0.207305;
4511 ResScanlineDith[ j ] += NoiseM1;
4512 ResScanlineDith[ j + ElCount ] += Noise * (fptype) 0.063011;
4513 }
4514
4515 while( j < LenE )
4516 {
4517 const fptype z0 = round( ResScanline[ j ] * TrMulI ) * TrMul;
4518 const fptype Noise = ResScanline[ j ] - z0;
4519 ResScanline[ j ] = clamp( z0, c0, PkOut );
4520
4521 ResScanlineDith[ j - ElCount ] += Noise * (fptype) 0.207305;
4522 ResScanlineDith[ j ] += Noise * (fptype) 0.364842;
4523 j++;
4524 }
4525 }
4526
4527protected:
4528 using CImageResizerDithererDefINL< fptype > :: Len;
4529 using CImageResizerDithererDefINL< fptype > :: Vars;
4530 using CImageResizerDithererDefINL< fptype > :: LenE;
4531 using CImageResizerDithererDefINL< fptype > :: TrMul0;
4532 using CImageResizerDithererDefINL< fptype > :: PkOut0;
4533
4537};
4538
4568
4569template< typename afptype, typename afptypeatom = afptype,
4572{
4573public:
4574 typedef afptype fptype;
4575 typedef afptypeatom fptypeatom;
4576 static const int fppack = sizeof( fptype ) / sizeof( fptypeatom );
4578 static const int fpalign = sizeof( fptype );
4583 static const int elalign = 1;
4587 static const int packmode = 0;
4591 typedef adith CDitherer;
4592};
4593
4608
4609template< class fpclass = fpclass_def< float > >
4611{
4613
4614public:
4629
4630 CImageResizer( const int aResBitDepth = 8, const int aSrcBitDepth = 0,
4631 const CImageResizerParams& aParams = CImageResizerParamsDef() )
4632 : Params( aParams )
4633 , ResBitDepth( aResBitDepth )
4634 {
4635 SrcBitDepth = ( aSrcBitDepth == 0 ? ResBitDepth : aSrcBitDepth );
4636
4637 initFilterBank( FixedFilterBank, 1.0, false, CFltBuffer() );
4638 FixedFilterBank.createAllFilters();
4639 }
4640
4679
4680 template< typename Tin, typename Tout >
4681 void resizeImage( const Tin* const SrcBuf, const int SrcWidth,
4682 const int SrcHeight, int SrcScanlineSize, Tout* const NewBuf,
4683 const int NewWidth, const int NewHeight, const int ElCountIO,
4684 const double k, CImageResizerVars* const aVars = nullptr ) const
4685 {
4686 if( SrcWidth == 0 || SrcHeight == 0 )
4687 {
4688 memset( NewBuf, 0, (size_t) NewWidth * (size_t) NewHeight *
4689 sizeof( Tout ));
4690
4691 return;
4692 }
4693 else
4694 if( NewWidth == 0 || NewHeight == 0 )
4695 {
4696 return;
4697 }
4698
4699 CImageResizerVars DefVars;
4700 CImageResizerVars& Vars = ( aVars == nullptr ? DefVars : *aVars );
4701
4702 CImageResizerThreadPool DefThreadPool;
4703 CImageResizerThreadPool& ThreadPool = ( Vars.ThreadPool == nullptr ?
4704 DefThreadPool : *Vars.ThreadPool );
4705
4706 // Define resizing steps, also optionally modify offsets so that
4707 // resizing produces a "centered" image.
4708
4709 double kx;
4710 double ky;
4711 double ox = Vars.ox;
4712 double oy = Vars.oy;
4713
4714 if( k == 0.0 )
4715 {
4716 kx = (double) SrcWidth / NewWidth;
4717 ox += ( kx - 1.0 ) * 0.5;
4718
4719 ky = (double) SrcHeight / NewHeight;
4720 oy += ( ky - 1.0 ) * 0.5;
4721 }
4722 else
4723 if( k > 0.0 )
4724 {
4725 kx = k;
4726 ky = k;
4727
4728 const double ko = ( k - 1.0 ) * 0.5;
4729 ox += ko;
4730 oy += ko;
4731 }
4732 else
4733 {
4734 kx = -k;
4735 ky = -k;
4736 }
4737
4738 // Evaluate pre-multipliers used on the output stage.
4739
4740 const bool IsInFloat = ( (Tin) 0.25 != 0 );
4741 const bool IsOutFloat = ( (Tout) 0.25 != 0 );
4742 double OutMul; // Output multiplier.
4743
4744 if( Vars.UseSRGBGamma )
4745 {
4746 if( IsInFloat )
4747 {
4748 Vars.InGammaMult = 1.0;
4749 }
4750 else
4751 {
4752 Vars.InGammaMult =
4753 1.0 / ( sizeof( Tin ) == 1 ? 255.0 : 65535.0 );
4754 }
4755
4756 if( IsOutFloat )
4757 {
4758 Vars.OutGammaMult = 1.0;
4759 }
4760 else
4761 {
4762 Vars.OutGammaMult = ( sizeof( Tout ) == 1 ? 255.0 : 65535.0 );
4763 }
4764
4765 OutMul = 1.0;
4766 }
4767 else
4768 {
4769 if( IsOutFloat )
4770 {
4771 OutMul = 1.0;
4772 }
4773 else
4774 {
4775 OutMul = ( sizeof( Tout ) == 1 ? 255.0 : 65535.0 );
4776 }
4777
4778 if( !IsInFloat )
4779 {
4780 OutMul /= ( sizeof( Tin ) == 1 ? 255.0 : 65535.0 );
4781 }
4782 }
4783
4784 // Fill widely-used variables.
4785
4786 const int ElCount = ( ElCountIO + fpclass :: fppack - 1 ) /
4787 fpclass :: fppack;
4788
4789 const int NewWidthE = NewWidth * ElCount;
4790
4791 if( SrcScanlineSize < 1 )
4792 {
4793 SrcScanlineSize = SrcWidth * ElCountIO;
4794 }
4795
4796 Vars.ElCount = ElCount;
4797 Vars.ElCountIO = ElCountIO;
4798 Vars.fppack = fpclass :: fppack;
4799 Vars.fpalign = fpclass :: fpalign;
4800 Vars.elalign = fpclass :: elalign;
4801 Vars.packmode = fpclass :: packmode;
4802
4803 // Horizontal scanline filtering and resizing.
4804
4806 CFilterSteps FltSteps;
4807 typename CFilterStep :: CRPosBufArray RPosBufArray;
4808 CBuffer< char > UsedFracMap;
4809
4810 // Perform the filtering steps modeling at various modes, find the
4811 // most efficient mode for both horizontal and vertical resizing.
4812
4813 int UseBuildMode = 1;
4814 const int BuildModeCount =
4815 ( FixedFilterBank.getOrder() == 0 ? 4 : 2 );
4816
4817 int m;
4818
4819 if( Vars.BuildMode >= 0 )
4820 {
4821 UseBuildMode = Vars.BuildMode;
4822 }
4823 else
4824 {
4825 int BestScore = 0x7FFFFFFF;
4826
4827 for( m = 0; m < BuildModeCount; m++ )
4828 {
4830 CFilterSteps TmpSteps;
4831 Vars.k = kx;
4832 Vars.o = ox;
4833 buildFilterSteps( TmpSteps, Vars, TmpBank, OutMul, m, true );
4834 updateFilterStepBuffers( TmpSteps, Vars, RPosBufArray,
4835 SrcWidth, NewWidth );
4836
4837 fillUsedFracMap( TmpSteps[ Vars.ResizeStep ], UsedFracMap );
4838 const int c = calcComplexity( TmpSteps, Vars, UsedFracMap,
4839 SrcHeight );
4840
4841 if( c < BestScore )
4842 {
4843 UseBuildMode = m;
4844 BestScore = c;
4845 }
4846 }
4847 }
4848
4849 // Perform the actual filtering steps building.
4850
4851 Vars.k = kx;
4852 Vars.o = ox;
4853 buildFilterSteps( FltSteps, Vars, FltBank, OutMul, UseBuildMode,
4854 false );
4855
4856 updateFilterStepBuffers( FltSteps, Vars, RPosBufArray, SrcWidth,
4857 NewWidth );
4858
4859 updateBufLenAndRPosPtrs( FltSteps, Vars, NewWidth );
4860
4861 const int ThreadCount = ThreadPool.getSuggestedWorkloadCount();
4862 // Includes the current thread.
4863
4865 td.setItemCount( ThreadCount );
4866 int i;
4867
4868 for( i = 0; i < ThreadCount; i++ )
4869 {
4870 if( i > 0 )
4871 {
4872 ThreadPool.addWorkload( &td[ i ]);
4873 }
4874
4875 td[ i ].init( i, ThreadCount, FltSteps, Vars );
4876
4877 td[ i ].initScanlineQueue( td[ i ].sopResizeH, SrcHeight,
4878 SrcWidth );
4879 }
4880
4881 CBuffer< fptype, size_t > FltBuf( (size_t) NewWidthE *
4882 (size_t) SrcHeight, fpclass :: fpalign ); // Temporary buffer that
4883 // receives horizontally-filtered and resized image.
4884
4885 for( i = 0; i < SrcHeight; i++ )
4886 {
4887 td[ i % ThreadCount ].addScanlineToQueue(
4888 (void*) &SrcBuf[ (size_t) i * (size_t) SrcScanlineSize ],
4889 &FltBuf[ (size_t) i * (size_t) NewWidthE ]);
4890 }
4891
4892 ThreadPool.startAllWorkloads();
4893 td[ 0 ].processScanlineQueue();
4894 ThreadPool.waitAllWorkloadsToFinish();
4895
4896 // Vertical scanline filtering and resizing, reuse previously defined
4897 // filtering steps if possible.
4898
4899 const int PrevUseBuildMode = UseBuildMode;
4900
4901 if( Vars.BuildMode >= 0 )
4902 {
4903 UseBuildMode = Vars.BuildMode;
4904 }
4905 else
4906 {
4907 CImageResizerVars TmpVars( Vars );
4908 int BestScore = 0x7FFFFFFF;
4909
4910 for( m = 0; m < BuildModeCount; m++ )
4911 {
4913 TmpBank.copyInitParams( FltBank );
4914 CFilterSteps TmpSteps;
4915 TmpVars.k = ky;
4916 TmpVars.o = oy;
4917 buildFilterSteps( TmpSteps, TmpVars, TmpBank, 1.0, m, true );
4918 updateFilterStepBuffers( TmpSteps, TmpVars, RPosBufArray,
4919 SrcHeight, NewHeight );
4920
4921 fillUsedFracMap( TmpSteps[ TmpVars.ResizeStep ],
4922 UsedFracMap );
4923
4924 const int c = calcComplexity( TmpSteps, TmpVars, UsedFracMap,
4925 NewWidth );
4926
4927 if( c < BestScore )
4928 {
4929 UseBuildMode = m;
4930 BestScore = c;
4931 }
4932 }
4933 }
4934
4935 Vars.k = ky;
4936 Vars.o = oy;
4937
4938 if( UseBuildMode == PrevUseBuildMode && ky == kx )
4939 {
4940 if( OutMul != 1.0 )
4941 {
4942 modifyCorrFilterDCGain( FltSteps, 1.0 / OutMul );
4943 }
4944 }
4945 else
4946 {
4947 buildFilterSteps( FltSteps, Vars, FltBank, 1.0, UseBuildMode,
4948 false );
4949 }
4950
4951 updateFilterStepBuffers( FltSteps, Vars, RPosBufArray, SrcHeight,
4952 NewHeight );
4953
4954 updateBufLenAndRPosPtrs( FltSteps, Vars, NewWidth );
4955
4956 if( IsOutFloat && sizeof( FltBuf[ 0 ]) == sizeof( Tout ) &&
4957 fpclass :: packmode == 0 )
4958 {
4959 // In-place output.
4960
4961 for( i = 0; i < ThreadCount; i++ )
4962 {
4963 td[ i ].initScanlineQueue( td[ i ].sopResizeV, NewWidth,
4964 SrcHeight, NewWidthE, NewWidthE );
4965 }
4966
4967 for( i = 0; i < NewWidth; i++ )
4968 {
4969 td[ i % ThreadCount ].addScanlineToQueue(
4970 &FltBuf[ i * ElCount ], (fptype*) &NewBuf[ i * ElCount ]);
4971 }
4972
4973 ThreadPool.startAllWorkloads();
4974 td[ 0 ].processScanlineQueue();
4975 ThreadPool.waitAllWorkloadsToFinish();
4976 ThreadPool.removeAllWorkloads();
4977
4978 return;
4979 }
4980
4981 CBuffer< fptype, size_t > ResBuf( (size_t) NewWidthE *
4982 (size_t) NewHeight, fpclass :: fpalign );
4983
4984 for( i = 0; i < ThreadCount; i++ )
4985 {
4986 td[ i ].initScanlineQueue( td[ i ].sopResizeV, NewWidth,
4987 SrcHeight, NewWidthE, NewWidthE );
4988 }
4989
4990 const int im = ( fpclass :: packmode == 0 ? ElCount : 1 );
4991
4992 for( i = 0; i < NewWidth; i++ )
4993 {
4994 td[ i % ThreadCount ].addScanlineToQueue(
4995 &FltBuf[ i * im ], &ResBuf[ i * im ]);
4996 }
4997
4998 ThreadPool.startAllWorkloads();
4999 td[ 0 ].processScanlineQueue();
5000 ThreadPool.waitAllWorkloadsToFinish();
5001
5002 if( IsOutFloat )
5003 {
5004 // Perform output, but skip dithering.
5005
5006 for( i = 0; i < ThreadCount; i++ )
5007 {
5008 td[ i ].initScanlineQueue( td[ i ].sopUnpackH,
5009 NewHeight, NewWidth );
5010 }
5011
5012 for( i = 0; i < NewHeight; i++ )
5013 {
5014 td[ i % ThreadCount ].addScanlineToQueue(
5015 &ResBuf[ (size_t) i * (size_t) NewWidthE ],
5016 &NewBuf[ (size_t) i * (size_t) ( NewWidth * ElCountIO )]);
5017 }
5018
5019 ThreadPool.startAllWorkloads();
5020 td[ 0 ].processScanlineQueue();
5021 ThreadPool.waitAllWorkloadsToFinish();
5022 ThreadPool.removeAllWorkloads();
5023
5024 return;
5025 }
5026
5027 // Perform output with dithering (for integer output only).
5028
5029 int TruncBits; // The number of lower bits to truncate and dither.
5030 int OutRange; // Output range.
5031
5032 if( sizeof( Tout ) == 1 )
5033 {
5034 TruncBits = 8 - ResBitDepth;
5035 OutRange = 255;
5036 }
5037 else
5038 {
5039 TruncBits = 16 - ResBitDepth;
5040 OutRange = 65535;
5041 }
5042
5043 const double PkOut = OutRange;
5044 const double TrMul = ( TruncBits > 0 ?
5045 PkOut / ( OutRange >> TruncBits ) : 1.0 );
5046
5047 if( CDitherer :: isRecursive() )
5048 {
5049 td[ 0 ].getDitherer().init( NewWidth, Vars, TrMul, PkOut );
5050
5051 for( i = 0; i < NewHeight; i++ )
5052 {
5053 fptype* const ResScanline =
5054 &ResBuf[ (size_t) i * (size_t) NewWidthE ];
5055
5056 if( Vars.UseSRGBGamma )
5057 {
5058 CFilterStep :: applySRGBGamma( ResScanline, NewWidth,
5059 Vars );
5060 }
5061
5062 td[ 0 ].getDitherer().dither( ResScanline );
5063
5064 CFilterStep :: unpackScanline( ResScanline,
5065 &NewBuf[ (size_t) i * (size_t) ( NewWidth * ElCountIO )],
5066 NewWidth, Vars );
5067 }
5068 }
5069 else
5070 {
5071 for( i = 0; i < ThreadCount; i++ )
5072 {
5073 td[ i ].initScanlineQueue( td[ i ].sopDitherAndUnpackH,
5074 NewHeight, NewWidth );
5075
5076 td[ i ].getDitherer().init( NewWidth, Vars, TrMul, PkOut );
5077 }
5078
5079 for( i = 0; i < NewHeight; i++ )
5080 {
5081 td[ i % ThreadCount ].addScanlineToQueue(
5082 &ResBuf[ (size_t) i * (size_t) NewWidthE ],
5083 &NewBuf[ (size_t) i * (size_t) ( NewWidth * ElCountIO )]);
5084 }
5085
5086 ThreadPool.startAllWorkloads();
5087 td[ 0 ].processScanlineQueue();
5088 ThreadPool.waitAllWorkloadsToFinish();
5089 }
5090
5091 ThreadPool.removeAllWorkloads();
5092 }
5093
5094private:
5095 typedef typename fpclass :: fptype fptype;
5097 typedef typename fpclass :: CFilterStep CFilterStep;
5099 typedef typename fpclass :: CDitherer CDitherer;
5101 CImageResizerParams Params;
5102 int SrcBitDepth;
5103 int ResBitDepth;
5104 CDSPFracFilterBankLin< fptype > FixedFilterBank;
5107
5113
5114 typedef CStructArray< CFilterStep > CFilterSteps;
5115
5127
5128 void initFilterBank( CDSPFracFilterBankLin< fptype >& FltBank,
5129 const double CutoffMult, const bool ForceHiOrder,
5130 const CFltBuffer& ExtFilter ) const
5131 {
5132 const int IntBitDepth = ( ResBitDepth > SrcBitDepth ? ResBitDepth :
5133 SrcBitDepth );
5134
5135 const double SNR = -6.02 * ( IntBitDepth + 3 );
5136 int UseOrder;
5137 int FracCount; // The number of fractional delay filters sampled by
5138 // the filter bank. This variable affects the signal-to-noise
5139 // ratio at interpolation stage. Theoretically, at `UseOrder`
5140 // equal to 1, 8-bit image resizing requires 66.2 dB SNR, or 11.
5141 // 16-bit resizing requires 114.4 dB SNR, or 150. At `UseOrder`
5142 // equal to 0, the required number of filters is exponentially
5143 // higher.
5144
5145 if( ForceHiOrder || IntBitDepth > 8 )
5146 {
5147 UseOrder = 1; // -146 dB max
5148 FracCount = (int) ceil( 0.23134052 * exp( -0.058062929 * SNR ));
5149 }
5150 else
5151 {
5152 UseOrder = 0; // -72 dB max
5153 FracCount = (int) ceil( 0.33287686 * exp( -0.11334583 * SNR ));
5154 }
5155
5156 if( FracCount < 2 )
5157 {
5158 FracCount = 2;
5159 }
5160
5161 FltBank.init( FracCount, UseOrder, Params.IntFltLen / CutoffMult,
5162 Params.IntFltCutoff * CutoffMult, Params.IntFltAlpha, ExtFilter,
5163 fpclass :: fpalign, fpclass :: elalign );
5164 }
5165
5180
5181 static void allocFilter( CBuffer< fptype >& Flt, const int ReqCapacity,
5182 const bool IsModel = false, int* const FltExt = nullptr )
5183 {
5184 int UseCapacity = ( ReqCapacity + fpclass :: elalign - 1 ) &
5185 ~( fpclass :: elalign - 1 );
5186
5187 int Ext = UseCapacity - ReqCapacity;
5188
5189 if( FltExt != nullptr )
5190 {
5191 *FltExt = Ext;
5192 }
5193
5194 if( IsModel )
5195 {
5196 Flt.forceCapacity( UseCapacity );
5197 return;
5198 }
5199
5200 Flt.alloc( UseCapacity, fpclass :: fpalign );
5201
5202 while( Ext > 0 )
5203 {
5204 Ext--;
5205 Flt[ ReqCapacity + Ext ] = 0;
5206 }
5207 }
5208
5230
5231 void assignFilterParams( CFilterStep& fs, const bool IsUpsample,
5232 const int ResampleFactor, const double FltCutoff, const double DCGain,
5233 const bool UseFltOrig, const bool IsModel ) const
5234 {
5235 double FltAlpha;
5236 double Len2;
5237 double Freq;
5238
5239 if( FltCutoff == 0.0 )
5240 {
5241 const double m = 2.0 / ResampleFactor;
5242 FltAlpha = Params.HBFltAlpha;
5243 Len2 = 0.5 * Params.HBFltLen / m;
5244 Freq = AVIR_PI * Params.HBFltCutoff * m;
5245 }
5246 else
5247 {
5248 FltAlpha = Params.LPFltAlpha;
5249 Len2 = 0.25 * Params.LPFltBaseLen / FltCutoff;
5250 Freq = AVIR_PI * Params.LPFltCutoffMult * FltCutoff;
5251 }
5252
5253 if( IsUpsample )
5254 {
5255 Len2 *= ResampleFactor;
5256 Freq /= ResampleFactor;
5257 fs.DCGain = DCGain * ResampleFactor;
5258 }
5259 else
5260 {
5261 fs.DCGain = DCGain;
5262 }
5263
5264 fs.FltOrig.Len2 = Len2;
5265 fs.FltOrig.Freq = Freq;
5266 fs.FltOrig.Alpha = FltAlpha;
5267 fs.FltOrig.DCGain = fs.DCGain;
5268
5269 CDSPPeakedCosineLPF w( Len2, Freq, FltAlpha );
5270
5271 fs.IsUpsample = IsUpsample;
5272 fs.ResampleFactor = ResampleFactor;
5273 fs.FltLatency = w.fl2;
5274
5275 int FltExt; // Filter's extension due to fpclass :: elalign.
5276
5277 if( IsModel )
5278 {
5279 allocFilter( fs.Flt, w.FilterLen, true, &FltExt );
5280
5281 if( UseFltOrig )
5282 {
5283 // Allocate a real buffer even in modeling mode since this
5284 // filter may be copied by the filter bank.
5285
5286 fs.FltOrig.alloc( w.FilterLen );
5287 memset( &fs.FltOrig[ 0 ], 0,
5288 (size_t) w.FilterLen * sizeof( fs.FltOrig[ 0 ]));
5289 }
5290 }
5291 else
5292 {
5293 fs.FltOrig.alloc( w.FilterLen );
5294
5295 w.generateLPF( &fs.FltOrig[ 0 ], fs.DCGain );
5296
5297 allocFilter( fs.Flt, fs.FltOrig.getCapacity(), false, &FltExt );
5298 copyArray( &fs.FltOrig[ 0 ], &fs.Flt[ 0 ],
5299 fs.FltOrig.getCapacity() );
5300
5301 if( !UseFltOrig )
5302 {
5303 fs.FltOrig.free();
5304 }
5305 }
5306
5307 if( IsUpsample )
5308 {
5309 int l = fs.Flt.getCapacity() - fs.FltLatency - ResampleFactor -
5310 FltExt;
5311
5312 allocFilter( fs.PrefixDC, l, IsModel );
5313 allocFilter( fs.SuffixDC, fs.FltLatency, IsModel );
5314
5315 if( IsModel )
5316 {
5317 return;
5318 }
5319
5320 // Create prefix and suffix "tails" used during upsampling.
5321
5322 const fptype* ip = &fs.Flt[ fs.FltLatency + ResampleFactor ];
5323 copyArray( ip, &fs.PrefixDC[ 0 ], l );
5324
5325 while( true )
5326 {
5327 ip += ResampleFactor;
5328 l -= ResampleFactor;
5329
5330 if( l <= 0 )
5331 {
5332 break;
5333 }
5334
5335 addArray( ip, &fs.PrefixDC[ 0 ], l );
5336 }
5337
5338 l = fs.FltLatency;
5339 fptype* op = &fs.SuffixDC[ 0 ];
5340 copyArray( &fs.Flt[ 0 ], op, l );
5341
5342 while( true )
5343 {
5344 op += ResampleFactor;
5345 l -= ResampleFactor;
5346
5347 if( l <= 0 )
5348 {
5349 break;
5350 }
5351
5352 addArray( &fs.Flt[ 0 ], op, l );
5353 }
5354 }
5355 else
5356 if( !UseFltOrig )
5357 {
5358 fs.EdgePixelCount = fs.EdgePixelCountDef;
5359 }
5360 }
5361
5383
5384 void addCorrectionFilter( CFilterSteps& Steps, const double bw,
5385 const bool IsPreCorrection, const bool IsModel ) const
5386 {
5387 CFilterStep& nfs = ( IsPreCorrection ? Steps[ 0 ] : Steps.add() );
5388 nfs.IsUpsample = false;
5389 nfs.ResampleFactor = 1;
5390 nfs.DCGain = 1.0;
5391 nfs.EdgePixelCount = ( IsPreCorrection ? nfs.EdgePixelCountDef : 0 );
5392
5393 if( IsModel )
5394 {
5395 allocFilter( nfs.Flt, CDSPFIREQ :: calcFilterLength(
5396 Params.CorrFltLen, nfs.FltLatency ), true );
5397
5398 return;
5399 }
5400
5401 const int BinCount = 65; // Frequency response bins to control.
5402 const int BinCount1 = BinCount - 1;
5403 double curbw = 1.0; // Bandwidth of the filter at the current step.
5404 int i;
5405 int j;
5406 double re;
5407 double im;
5408
5409 CBuffer< double > Bins( BinCount ); // Adjustment introduced by all
5410 // steps at all frequencies of interest.
5411
5412 for( j = 0; j < BinCount; j++ )
5413 {
5414 Bins[ j ] = 1.0;
5415 }
5416
5417 const int si = ( IsPreCorrection ? 1 : 0 );
5418
5419 for( i = si; i < Steps.getItemCount() - ( si ^ 1 ); i++ )
5420 {
5421 const CFilterStep& fs = Steps[ i ];
5422
5423 if( fs.IsUpsample )
5424 {
5425 curbw *= fs.ResampleFactor;
5426
5427 if( fs.FltOrig.getCapacity() > 0 )
5428 {
5429 continue;
5430 }
5431 }
5432
5433 const fptype* Flt;
5434 int FltLen;
5435
5436 if( fs.ResampleFactor == 0 )
5437 {
5438 if( fs.FltBankDyn == nullptr )
5439 {
5440 Flt = fs.FltBank -> getFilterConst( 0 );
5441 FltLen = fs.FltBank -> getFilterLen();
5442 }
5443 else
5444 {
5445 Flt = fs.FltBankDyn -> getFilter( 0 );
5446 FltLen = fs.FltBankDyn -> getFilterLen();
5447 }
5448 }
5449 else
5450 {
5451 Flt = &fs.Flt[ 0 ];
5452 FltLen = fs.Flt.getCapacity();
5453 }
5454
5455 // Calculate frequency response adjustment introduced by the
5456 // filter at this step, within the bounds of bandwidth of
5457 // interest.
5458
5459 const double thm = AVIR_PI * bw / ( curbw * BinCount1 );
5460
5461 for( j = 0; j < BinCount; j++ )
5462 {
5463 calcFIRFilterResponse( Flt, FltLen, j * thm, re, im );
5464
5465 Bins[ j ] *= fs.DCGain / sqrt( re * re + im * im );
5466 }
5467
5468 if( !fs.IsUpsample && fs.ResampleFactor > 1 )
5469 {
5470 curbw /= fs.ResampleFactor;
5471 }
5472 }
5473
5474 // Calculate filter.
5475
5476 CDSPFIREQ EQ;
5477 EQ.init( bw * 2.0, Params.CorrFltLen, BinCount, 0.0, bw, false,
5478 Params.CorrFltAlpha );
5479
5480 nfs.FltLatency = EQ.getFilterLatency();
5481
5482 CBuffer< double > Filter( EQ.getFilterLength() );
5483 EQ.buildFilter( Bins, &Filter[ 0 ]);
5484 normalizeFIRFilter( &Filter[ 0 ], Filter.getCapacity(), 1.0 );
5485
5486 allocFilter( nfs.Flt, Filter.getCapacity() );
5487 copyArray( &Filter[ 0 ], &nfs.Flt[ 0 ], Filter.getCapacity() );
5488
5489 // Print a theoretically achieved final frequency response at various
5490 // feature sizes (from DC to 1 pixel). Values above 255 means features
5491 // become brighter, values below 255 means features become dimmer.
5492
5493/* const double sbw = ( bw > 1.0 ? 1.0 / bw : 1.0 );
5494
5495 for( j = 0; j < BinCount; j++ )
5496 {
5497 const double th = AVIR_PI * sbw * j / BinCount1;
5498
5499 calcFIRFilterResponse( &nfs.Flt[ 0 ], nfs.Flt.getCapacity(),
5500 th, re, im );
5501
5502 printf( "%f\n", sqrt( re * re + im * im ) / Bins[ j ] * 255 );
5503 }
5504
5505 printf( "***\n" );*/
5506 }
5507
5525
5526 static void addSharpenTest( CFilterSteps& Steps, const double bw,
5527 const bool IsModel )
5528 {
5529 if( bw <= 1.0 )
5530 {
5531 return;
5532 }
5533
5534 const double FltLen = 10.0 * bw;
5535
5536 CFilterStep& fs = Steps.add();
5537 fs.IsUpsample = false;
5538 fs.ResampleFactor = 1;
5539 fs.DCGain = 1.0;
5540 fs.EdgePixelCount = 0;
5541
5542 if( IsModel )
5543 {
5544 allocFilter( fs.Flt, CDSPFIREQ :: calcFilterLength( FltLen,
5545 fs.FltLatency ), true );
5546
5547 return;
5548 }
5549
5550 const int BinCount = 200;
5551 CBuffer< double > Bins( BinCount );
5552 int Thresh = (int) round( BinCount / bw * 1.75 );
5553
5554 if( Thresh > BinCount )
5555 {
5556 Thresh = BinCount;
5557 }
5558
5559 int j;
5560
5561 for( j = 0; j < Thresh; j++ )
5562 {
5563 Bins[ j ] = 1.0;
5564 }
5565
5566 for( j = Thresh; j < BinCount; j++ )
5567 {
5568 Bins[ j ] = 256.0;
5569 }
5570
5571 CDSPFIREQ EQ;
5572 EQ.init( bw * 2.0, FltLen, BinCount, 0.0, bw, false, 1.7 );
5573
5574 fs.FltLatency = EQ.getFilterLatency();
5575
5576 CBuffer< double > Filter( EQ.getFilterLength() );
5577 EQ.buildFilter( Bins, &Filter[ 0 ]);
5578 normalizeFIRFilter( &Filter[ 0 ], Filter.getCapacity(), 1.0 );
5579
5580 allocFilter( fs.Flt, Filter.getCapacity() );
5581 copyArray( &Filter[ 0 ], &fs.Flt[ 0 ], Filter.getCapacity() );
5582
5583/* for( j = 0; j < BinCount; j++ )
5584 {
5585 const double th = AVIR_PI * j / ( BinCount - 1 );
5586 double re;
5587 double im;
5588
5589 calcFIRFilterResponse( &fs.Flt[ 0 ], fs.Flt.getCapacity(),
5590 th, re, im );
5591
5592 printf( "%f\n", sqrt( re * re + im * im ));
5593 }
5594
5595 printf( "***\n" );*/
5596 }
5597
5615
5616 void buildFilterSteps( CFilterSteps& Steps, CImageResizerVars& Vars,
5617 CDSPFracFilterBankLin< fptype >& FltBank, const double DCGain,
5618 const int ModeFlags, const bool IsModel ) const
5619 {
5620 Steps.clear();
5621
5622 const bool DoFltAndIntCombo = (( ModeFlags & 1 ) != 0 ); // Do filter
5623 // and interpolator combining.
5624 const bool ForceHiOrderInt = (( ModeFlags & 2 ) != 0 ); // Force use
5625 // of a higher-order interpolation.
5626 const bool UseHalfband = (( ModeFlags & 4 ) != 0 ); // Use half-band
5627 // filter.
5628
5629 const double bw = 1.0 / Vars.k; // Resulting bandwidth.
5630 const int UpsampleFactor = ( (int) floor( Vars.k ) < 2 ? 2 : 1 );
5631 double IntCutoffMult; // Interpolation filter cutoff multiplier.
5632 CFilterStep* ReuseStep; // If not `nullptr`, resizing step should use
5633 // this step object instead of creating a new one.
5634 CFilterStep* ExtFltStep; // Use `FltOrig` of this step as the external
5635 // filter to applied to the interpolator.
5636 bool IsPreCorrection; // `true`, if the correction filter is applied
5637 // first.
5638 double FltCutoff; // Cutoff frequency of the first filtering step.
5639 double corrbw; // Bandwidth at the correction step.
5640
5641 if( Vars.k <= 1.0 )
5642 {
5643 IsPreCorrection = true;
5644 FltCutoff = 1.0;
5645 corrbw = 1.0;
5646 Steps.add();
5647 }
5648 else
5649 {
5650 IsPreCorrection = false;
5651 FltCutoff = bw;
5652 corrbw = bw;
5653 }
5654
5655 // Add 1 upsampling or several downsampling filters.
5656
5657 if( UpsampleFactor > 1 )
5658 {
5659 CFilterStep& fs = Steps.add();
5660 assignFilterParams( fs, true, UpsampleFactor, FltCutoff, DCGain,
5661 DoFltAndIntCombo, IsModel );
5662
5663 IntCutoffMult = FltCutoff * 2.0 / UpsampleFactor;
5664 ReuseStep = nullptr;
5665 ExtFltStep = ( DoFltAndIntCombo ? &fs : nullptr );
5666 }
5667 else
5668 {
5669 int DownsampleFactor;
5670
5671 while( true )
5672 {
5673 DownsampleFactor = (int) floor( 0.5 / FltCutoff );
5674 bool DoHBFltAdd = ( UseHalfband && DownsampleFactor > 1 );
5675
5676 if( DoHBFltAdd )
5677 {
5678 assignFilterParams( Steps.add(), false, DownsampleFactor,
5679 0.0, 1.0, false, IsModel );
5680
5681 FltCutoff *= DownsampleFactor;
5682 }
5683 else
5684 {
5685 if( DownsampleFactor < 1 )
5686 {
5687 DownsampleFactor = 1;
5688 }
5689
5690 break;
5691 }
5692 }
5693
5694 CFilterStep& fs = Steps.add();
5695 assignFilterParams( fs, false, DownsampleFactor, FltCutoff,
5696 DCGain, DoFltAndIntCombo, IsModel );
5697
5698 IntCutoffMult = FltCutoff / 0.5;
5699
5700 if( DoFltAndIntCombo )
5701 {
5702 ReuseStep = &fs;
5703 ExtFltStep = &fs;
5704 }
5705 else
5706 {
5707 IntCutoffMult *= DownsampleFactor;
5708 ReuseStep = nullptr;
5709 ExtFltStep = nullptr;
5710 }
5711 }
5712
5713 // Insert resizing and correction steps.
5714
5715 CFilterStep& fs = ( ReuseStep == nullptr ? Steps.add() : *ReuseStep );
5716
5717 Vars.ResizeStep = Steps.getItemCount() - 1;
5718 fs.IsUpsample = false;
5719 fs.ResampleFactor = 0;
5720 fs.DCGain = ( ExtFltStep == nullptr ? 1.0 : ExtFltStep -> DCGain );
5721
5722 initFilterBank( FltBank, IntCutoffMult, ForceHiOrderInt,
5723 ( ExtFltStep == nullptr ? fs.FltOrig : ExtFltStep -> FltOrig ));
5724
5725 if( FltBank == FixedFilterBank )
5726 {
5727 fs.FltBank = &FixedFilterBank;
5728 fs.FltBankDyn = nullptr;
5729 }
5730 else
5731 {
5732 fs.FltBank = &FltBank;
5733 fs.FltBankDyn = &FltBank;
5734 }
5735
5736 addCorrectionFilter( Steps, corrbw, IsPreCorrection, IsModel );
5737
5738 //addSharpenTest( Steps, bw, IsModel );
5739 }
5740
5752
5753 static void extendUpsample( CFilterStep& fs, CFilterStep& NextStep )
5754 {
5755 fs.InPrefix = ( NextStep.InPrefix + fs.ResampleFactor - 1 ) /
5756 fs.ResampleFactor;
5757
5758 fs.OutPrefix += fs.InPrefix * fs.ResampleFactor;
5759 NextStep.InPrefix = 0;
5760
5761 fs.InSuffix = ( NextStep.InSuffix + fs.ResampleFactor - 1 ) /
5762 fs.ResampleFactor;
5763
5764 fs.OutSuffix += fs.InSuffix * fs.ResampleFactor;
5765 NextStep.InSuffix = 0;
5766 }
5767
5781
5782 static void fillRPosBuf( CFilterStep& fs, const CImageResizerVars& Vars )
5783 {
5784 const int PrevLen = fs.RPosBuf -> getCapacity();
5785
5786 if( fs.OutLen > PrevLen )
5787 {
5788 fs.RPosBuf -> increaseCapacity( fs.OutLen );
5789 }
5790
5791 typename CFilterStep :: CResizePos* rpos = &(*fs.RPosBuf)[ PrevLen ];
5792 const int FracCount = fs.FltBank -> getFracCount();
5793 const double o = Vars.o;
5794 const double k = Vars.k;
5795 int i;
5796
5797 for( i = PrevLen; i < fs.OutLen; i++ )
5798 {
5799 const double SrcPos = o + k * i;
5800 const int SrcPosInt = (int) floor( SrcPos );
5801 const double x = ( SrcPos - SrcPosInt ) * FracCount;
5802 const int fti = (int) x;
5803 rpos -> x = (typename fpclass :: fptypeatom) ( x - fti );
5804 rpos -> fti = fti;
5805 rpos -> SrcPosInt = SrcPosInt;
5806 rpos++;
5807 }
5808 }
5809
5826
5827 static void updateFilterStepBuffers( CFilterSteps& Steps,
5828 CImageResizerVars& Vars,
5829 typename CFilterStep :: CRPosBufArray& RPosBufArray, int SrcLen,
5830 const int NewLen )
5831 {
5832 int upstep = -1;
5833 int InBuf = 0;
5834 int i;
5835
5836 for( i = 0; i < Steps.getItemCount(); i++ )
5837 {
5838 CFilterStep& fs = Steps[ i ];
5839
5840 fs.Vars = &Vars;
5841 fs.InLen = SrcLen;
5842 fs.InBuf = InBuf;
5843 fs.OutBuf = ( InBuf + 1 ) & 1;
5844
5845 if( fs.IsUpsample )
5846 {
5847 upstep = i;
5848 Vars.k *= fs.ResampleFactor;
5849 Vars.o *= fs.ResampleFactor;
5850 fs.InPrefix = 0;
5851 fs.InSuffix = 0;
5852 fs.OutLen = fs.InLen * fs.ResampleFactor;
5853 fs.OutPrefix = fs.FltLatency;
5854 fs.OutSuffix = fs.Flt.getCapacity() - fs.FltLatency -
5855 fs.ResampleFactor;
5856
5857 int l0 = fs.OutPrefix + fs.OutLen + fs.OutSuffix;
5858 int l = fs.InLen * fs.ResampleFactor +
5859 fs.SuffixDC.getCapacity();
5860
5861 if( l > l0 )
5862 {
5863 fs.OutSuffix += l - l0;
5864 }
5865
5866 l0 = fs.OutLen + fs.OutSuffix;
5867
5868 if( fs.PrefixDC.getCapacity() > l0 )
5869 {
5870 fs.OutSuffix += fs.PrefixDC.getCapacity() - l0;
5871 }
5872 }
5873 else
5874 if( fs.ResampleFactor == 0 )
5875 {
5876 const int FilterLenD2 = fs.FltBank -> getFilterLen() / 2;
5877 const int FilterLenD21 = FilterLenD2 - 1;
5878
5879 const int ResizeLPix = (int) floor( Vars.o ) - FilterLenD21;
5880 fs.InPrefix = ( ResizeLPix < 0 ? -ResizeLPix : 0 );
5881 const int ResizeRPix = (int) floor( Vars.o +
5882 ( NewLen - 1 ) * Vars.k ) + FilterLenD2 + 1;
5883
5884 fs.InSuffix = ( ResizeRPix > fs.InLen ?
5885 ResizeRPix - fs.InLen : 0 );
5886
5887 fs.OutLen = NewLen;
5888 fs.RPosBuf = &RPosBufArray.getRPosBuf( Vars.k, Vars.o,
5889 fs.FltBank -> getFracCount() );
5890
5891 fillRPosBuf( fs, Vars );
5892 }
5893 else
5894 {
5895 Vars.k /= fs.ResampleFactor;
5896 Vars.o /= fs.ResampleFactor;
5897 Vars.o += fs.EdgePixelCount;
5898
5899 fs.InPrefix = fs.FltLatency;
5900 fs.InSuffix = fs.Flt.getCapacity() - fs.FltLatency - 1;
5901
5902 // Additionally extend `OutLen`, to produce more precise edge
5903 // pixels.
5904
5905 fs.OutLen = ( fs.InLen + fs.ResampleFactor - 1 ) /
5906 fs.ResampleFactor + fs.EdgePixelCount;
5907
5908 fs.InSuffix += ( fs.OutLen - 1 ) * fs.ResampleFactor + 1 -
5909 fs.InLen;
5910
5911 fs.InPrefix += fs.EdgePixelCount * fs.ResampleFactor;
5912 fs.OutLen += fs.EdgePixelCount;
5913 }
5914
5915 InBuf = fs.OutBuf;
5916 SrcLen = fs.OutLen;
5917 }
5918
5919 Steps[ Steps.getItemCount() - 1 ].OutBuf = 2;
5920 Vars.IsResize2 = false;
5921
5922 if( upstep != -1 )
5923 {
5924 extendUpsample( Steps[ upstep ], Steps[ upstep + 1 ]);
5925
5926 if( Steps[ upstep ].ResampleFactor == 2 &&
5927 Vars.ResizeStep == upstep + 1 &&
5928 fpclass :: packmode == 0 &&
5929 Steps[ upstep ].FltOrig.getCapacity() > 0 )
5930 {
5931 // Interpolation with preceeding 2x filterless upsample,
5932 // interleaved resizing only.
5933
5934 Vars.IsResize2 = true;
5935 }
5936 }
5937 }
5938
5956
5957 static void updateBufLenAndRPosPtrs( CFilterSteps& Steps,
5958 CImageResizerVars& Vars, const int ResElIncr )
5959 {
5960 int MaxPrefix[ 2 ] = { 0, 0 };
5961 int MaxLen[ 2 ] = { 0, 0 };
5962 int i;
5963
5964 for( i = 0; i < Steps.getItemCount(); i++ )
5965 {
5966 CFilterStep& fs = Steps[ i ];
5967 const int ib = fs.InBuf;
5968
5969 if( fs.InPrefix > MaxPrefix[ ib ])
5970 {
5971 MaxPrefix[ ib ] = fs.InPrefix;
5972 }
5973
5974 int l = fs.InLen + fs.InSuffix;
5975
5976 if( l > MaxLen[ ib ])
5977 {
5978 MaxLen[ ib ] = l;
5979 }
5980
5981 fs.InElIncr = fs.InPrefix + l;
5982
5983 if( fs.OutBuf == 2 )
5984 {
5985 break;
5986 }
5987
5988 const int ob = fs.OutBuf;
5989
5990 if( fs.IsUpsample )
5991 {
5992 if( fs.OutPrefix > MaxPrefix[ ob ])
5993 {
5994 MaxPrefix[ ob ] = fs.OutPrefix;
5995 }
5996
5997 l = fs.OutLen + fs.OutSuffix;
5998
5999 if( l > MaxLen[ ob ])
6000 {
6001 MaxLen[ ob ] = l;
6002 }
6003 }
6004 else
6005 {
6006 if( fs.OutLen > MaxLen[ ob ])
6007 {
6008 MaxLen[ ob ] = fs.OutLen;
6009 }
6010 }
6011 }
6012
6013 // Update OutElIncr values of all steps.
6014
6015 for( i = 0; i < Steps.getItemCount(); i++ )
6016 {
6017 CFilterStep& fs = Steps[ i ];
6018
6019 if( fs.OutBuf == 2 )
6020 {
6021 fs.OutElIncr = ResElIncr;
6022 break;
6023 }
6024
6025 CFilterStep& fs2 = Steps[ i + 1 ];
6026
6027 if( fs.IsUpsample )
6028 {
6029 fs.OutElIncr = fs.OutPrefix + fs.OutLen + fs.OutSuffix;
6030
6031 if( fs.OutElIncr > fs2.InElIncr )
6032 {
6033 fs2.InElIncr = fs.OutElIncr;
6034 }
6035 else
6036 {
6037 fs.OutElIncr = fs2.InElIncr;
6038 }
6039 }
6040 else
6041 {
6042 fs.OutElIncr = fs2.InElIncr;
6043 }
6044 }
6045
6046 // Update temporary buffer's length.
6047
6048 for( i = 0; i < 2; i++ )
6049 {
6050 Vars.BufLen[ i ] = MaxPrefix[ i ] + MaxLen[ i ];
6051 Vars.BufOffs[ i ] = MaxPrefix[ i ];
6052
6053 if( Vars.packmode == 0 )
6054 {
6055 Vars.BufOffs[ i ] *= Vars.ElCount;
6056 }
6057
6058 Vars.BufLen[ i ] *= Vars.ElCount;
6059 }
6060
6061 // Update `RPosBuf` pointers, and `SrcOffs`.
6062
6063 CFilterStep& fs = Steps[ Vars.ResizeStep ];
6064 typename CFilterStep :: CResizePos* rpos = &(*fs.RPosBuf)[ 0 ];
6065 const int em = ( fpclass :: packmode == 0 ? Vars.ElCount : 1 );
6066 const int fl = ( fs.FltBankDyn == nullptr ?
6067 fs.FltBank -> getFilterLen() : fs.FltBankDyn -> getFilterLen() );
6068
6069 const int FilterLenD21 = fl / 2 - 1;
6070
6071 if( Vars.IsResize2 )
6072 {
6073 if( fs.FltBankDyn == nullptr )
6074 {
6075 for( i = 0; i < fs.OutLen; i++ )
6076 {
6077 const int p = rpos -> SrcPosInt - FilterLenD21;
6078 const int fo = p & 1;
6079 rpos -> SrcOffs = ( p + fo ) * em;
6080 rpos -> ftp = fs.FltBank -> getFilterConst(
6081 rpos -> fti ) + fo;
6082
6083 rpos -> fl = fl - fo;
6084 rpos++;
6085 }
6086 }
6087 else
6088 {
6089 for( i = 0; i < fs.OutLen; i++ )
6090 {
6091 const int p = rpos -> SrcPosInt - FilterLenD21;
6092 const int fo = p & 1;
6093 rpos -> SrcOffs = ( p + fo ) * em;
6094 rpos -> ftp = fs.FltBankDyn -> getFilter(
6095 rpos -> fti ) + fo;
6096
6097 rpos -> fl = fl - fo;
6098 rpos++;
6099 }
6100 }
6101 }
6102 else
6103 {
6104 if( fs.FltBankDyn == nullptr )
6105 {
6106 for( i = 0; i < fs.OutLen; i++ )
6107 {
6108 rpos -> SrcOffs = ( rpos -> SrcPosInt -
6109 FilterLenD21 ) * em;
6110
6111 rpos -> ftp = fs.FltBank -> getFilterConst( rpos -> fti );
6112 rpos++;
6113 }
6114 }
6115 else
6116 {
6117 for( i = 0; i < fs.OutLen; i++ )
6118 {
6119 rpos -> SrcOffs = ( rpos -> SrcPosInt -
6120 FilterLenD21 ) * em;
6121
6122 rpos -> ftp = fs.FltBankDyn -> getFilter( rpos -> fti );
6123 rpos++;
6124 }
6125 }
6126 }
6127 }
6128
6136
6137 void modifyCorrFilterDCGain( CFilterSteps& Steps, const double m ) const
6138 {
6139 CBuffer< fptype >* Flt;
6140 const int z = Steps.getItemCount() - 1;
6141
6142 if( !Steps[ z ].IsUpsample && Steps[ z ].ResampleFactor == 1 )
6143 {
6144 Flt = &Steps[ z ].Flt;
6145 }
6146 else
6147 {
6148 Flt = &Steps[ 0 ].Flt;
6149 }
6150
6151 int i;
6152
6153 for( i = 0; i < Flt -> getCapacity(); i++ )
6154 {
6155 (*Flt)[ i ] = (fptype) ( (double) (*Flt)[ i ] * m );
6156 }
6157 }
6158
6166
6167 static void fillUsedFracMap( const CFilterStep& fs,
6168 CBuffer< char >& UsedFracMap )
6169 {
6170 const int FracCount = fs.FltBank -> getFracCount();
6171 UsedFracMap.increaseCapacity( FracCount, false );
6172 memset( &UsedFracMap[ 0 ], 0,
6173 (size_t) FracCount * sizeof( UsedFracMap[ 0 ]));
6174
6175 typename CFilterStep :: CResizePos* rpos = &(*fs.RPosBuf)[ 0 ];
6176 int i;
6177
6178 for( i = 0; i < fs.OutLen; i++ )
6179 {
6180 UsedFracMap[ rpos -> fti ] |= 1;
6181 rpos++;
6182 }
6183 }
6184
6205
6206 static int calcComplexity( const CFilterSteps& Steps,
6207 const CImageResizerVars& Vars, const CBuffer< char >& UsedFracMap,
6208 const int ScanlineCount )
6209 {
6210 int fcnum; // Filter complexity multiplier numerator.
6211 int fcdenom; // Filter complexity multiplier denominator.
6212
6213 if( Vars.packmode != 0 )
6214 {
6215 fcnum = 1;
6216 fcdenom = 1;
6217 }
6218 else
6219 {
6220 // In interleaved processing mode, filters require 1 less
6221 // multiplication per 2 multiply-add instructions.
6222
6223 fcnum = 3;
6224 fcdenom = 4;
6225 }
6226
6227 int s = 0; // Complexity per one scanline.
6228 int s2 = 0; // Complexity per all scanlines.
6229 int i;
6230
6231 for( i = 0; i < Steps.getItemCount(); i++ )
6232 {
6233 const CFilterStep& fs = Steps[ i ];
6234
6235 s2 += 65 * fs.Flt.getCapacity(); // Filter creation complexity.
6236
6237 if( fs.IsUpsample )
6238 {
6239 if( fs.FltOrig.getCapacity() > 0 )
6240 {
6241 continue;
6242 }
6243
6244 s += ( fs.Flt.getCapacity() *
6245 ( fs.InPrefix + fs.InLen + fs.InSuffix ) +
6246 fs.SuffixDC.getCapacity() + fs.PrefixDC.getCapacity() ) *
6247 Vars.ElCount;
6248 }
6249 else
6250 if( fs.ResampleFactor == 0 )
6251 {
6252 s += fs.FltBank -> getFilterLen() *
6253 ( fs.FltBank -> getOrder() + Vars.ElCount ) * fs.OutLen;
6254
6255 if( i == Vars.ResizeStep && Vars.IsResize2 )
6256 {
6257 s >>= 1;
6258 }
6259
6260 s2 += fs.FltBank -> calcInitComplexity( UsedFracMap );
6261 }
6262 else
6263 {
6264 s += fs.Flt.getCapacity() * Vars.ElCount * fs.OutLen *
6265 fcnum / fcdenom;
6266 }
6267 }
6268
6269 return( s + s2 / ScanlineCount );
6270 }
6271
6283
6284 template< typename Tin, typename Tout >
6285 class CThreadData : public CImageResizerThreadPool :: CWorkload
6286 {
6287 public:
6288 virtual void process()
6289 #if __cplusplus >= 201103L
6290 override
6291 #endif // __cplusplus >= 201103L
6292 {
6293 processScanlineQueue();
6294 }
6295
6299
6300 enum EScanlineOperation
6301 {
6302 sopResizeH,
6303 sopResizeV,
6304 sopDitherAndUnpackH,
6305 sopUnpackH
6306 };
6307
6317
6318 void init( const int aThreadIndex, const int aThreadCount,
6319 const CFilterSteps& aSteps, const CImageResizerVars& aVars )
6320 {
6321 ThreadIndex = aThreadIndex;
6322 ThreadCount = aThreadCount;
6323 Steps = &aSteps;
6324 Vars = &aVars;
6325 }
6326
6340
6341 void initScanlineQueue( const EScanlineOperation aOp,
6342 const int TotalLines, const int aSrcLen, const int aSrcIncr = 0,
6343 const int aResIncr = 0 )
6344 {
6345 const int l = Vars -> BufLen[ 0 ] + Vars -> BufLen[ 1 ];
6346
6347 if( Bufs.getCapacity() < l )
6348 {
6349 Bufs.alloc( l, fpclass :: fpalign );
6350 }
6351
6352 BufPtrs[ 0 ] = Bufs + Vars -> BufOffs[ 0 ];
6353 BufPtrs[ 1 ] = Bufs + Vars -> BufLen[ 0 ] + Vars -> BufOffs[ 1 ];
6354
6355 int j;
6356 int ml = 0;
6357
6358 for( j = 0; j < Steps -> getItemCount(); j++ )
6359 {
6360 const CFilterStep& fs = (*Steps)[ j ];
6361
6362 if( fs.ResampleFactor == 0 &&
6363 ml < fs.FltBank -> getFilterLen() )
6364 {
6365 ml = fs.FltBank -> getFilterLen();
6366 }
6367 }
6368
6369 TmpFltBuf.alloc( ml, fpclass :: fpalign );
6370 ScanlineOp = aOp;
6371 SrcLen = aSrcLen;
6372 SrcIncr = aSrcIncr;
6373 ResIncr = aResIncr;
6374 QueueLen = 0;
6375 Queue.increaseCapacity(( TotalLines + ThreadCount - 1 ) /
6376 ThreadCount, false );
6377 }
6378
6389
6390 void addScanlineToQueue( void* const SrcBuf, void* const ResBuf )
6391 {
6392 Queue[ QueueLen ].SrcBuf = SrcBuf;
6393 Queue[ QueueLen ].ResBuf = ResBuf;
6394 QueueLen++;
6395 }
6396
6400
6401 void processScanlineQueue()
6402 {
6403 int i;
6404
6405 switch( ScanlineOp )
6406 {
6407 case sopResizeH:
6408 {
6409 for( i = 0; i < QueueLen; i++ )
6410 {
6411 resizeScanlineH( (Tin*) Queue[ i ].SrcBuf,
6412 (fptype*) Queue[ i ].ResBuf );
6413 }
6414
6415 break;
6416 }
6417
6418 case sopResizeV:
6419 {
6420 for( i = 0; i < QueueLen; i++ )
6421 {
6422 resizeScanlineV( (fptype*) Queue[ i ].SrcBuf,
6423 (fptype*) Queue[ i ].ResBuf );
6424 }
6425
6426 break;
6427 }
6428
6429 case sopDitherAndUnpackH:
6430 {
6431 for( i = 0; i < QueueLen; i++ )
6432 {
6433 if( Vars -> UseSRGBGamma )
6434 {
6435 CFilterStep :: applySRGBGamma(
6436 (fptype*) Queue[ i ].SrcBuf, SrcLen, *Vars );
6437 }
6438
6439 Ditherer.dither( (fptype*) Queue[ i ].SrcBuf );
6440
6441 CFilterStep :: unpackScanline(
6442 (fptype*) Queue[ i ].SrcBuf,
6443 (Tout*) Queue[ i ].ResBuf, SrcLen, *Vars );
6444 }
6445
6446 break;
6447 }
6448
6449 case sopUnpackH:
6450 {
6451 for( i = 0; i < QueueLen; i++ )
6452 {
6453 if( Vars -> UseSRGBGamma )
6454 {
6455 CFilterStep :: applySRGBGamma(
6456 (fptype*) Queue[ i ].SrcBuf, SrcLen, *Vars );
6457 }
6458
6459 CFilterStep :: unpackScanline(
6460 (fptype*) Queue[ i ].SrcBuf,
6461 (Tout*) Queue[ i ].ResBuf, SrcLen, *Vars );
6462 }
6463
6464 break;
6465 }
6466 }
6467 }
6468
6473
6474 CDitherer& getDitherer()
6475 {
6476 return( Ditherer );
6477 }
6478
6479 private:
6480 int ThreadIndex;
6481 int ThreadCount;
6482 const CFilterSteps* Steps;
6483 const CImageResizerVars* Vars;
6484 CBuffer< fptype > Bufs;
6485 fptype* BufPtrs[ 3 ];
6487 CBuffer< fptype > TmpFltBuf;
6489 EScanlineOperation ScanlineOp;
6491 int SrcLen;
6492 int SrcIncr;
6493 int ResIncr;
6495 CDitherer Ditherer;
6496
6502
6503 struct CQueueItem
6504 {
6505 void* SrcBuf;
6507 void* ResBuf;
6509 };
6510
6511 CBuffer< CQueueItem > Queue;
6512 int QueueLen;
6513
6521
6522 void resizeScanlineH( const Tin* const SrcBuf, fptype* const ResBuf )
6523 {
6524 const CFilterStep& fs0 = (*Steps)[ 0 ];
6525
6526 fs0.packScanline( SrcBuf, BufPtrs[ 0 ], SrcLen );
6527 BufPtrs[ 2 ] = ResBuf;
6528
6529 int j;
6530
6531 for( j = 0; j < Steps -> getItemCount(); j++ )
6532 {
6533 const CFilterStep& fs = (*Steps)[ j ];
6534 fs.prepareInBuf( BufPtrs[ fs.InBuf ]);
6535 const int DstIncr =
6536 ( Vars -> packmode == 0 ? Vars -> ElCount : 1 );
6537
6538 if( fs.ResampleFactor != 0 )
6539 {
6540 if( fs.IsUpsample )
6541 {
6542 fs.doUpsample( BufPtrs[ fs.InBuf ],
6543 BufPtrs[ fs.OutBuf ]);
6544 }
6545 else
6546 {
6547 fs.doFilter( BufPtrs[ fs.InBuf ],
6548 BufPtrs[ fs.OutBuf ], DstIncr );
6549 }
6550 }
6551 else
6552 {
6553 if( Vars -> IsResize2 )
6554 {
6555 fs.doResize2( BufPtrs[ fs.InBuf ],
6556 BufPtrs[ fs.OutBuf ], DstIncr, TmpFltBuf );
6557 }
6558 else
6559 {
6560 fs.doResize( BufPtrs[ fs.InBuf ],
6561 BufPtrs[ fs.OutBuf ], DstIncr, TmpFltBuf );
6562 }
6563 }
6564 }
6565 }
6566
6574
6575 void resizeScanlineV( const fptype* const SrcBuf,
6576 fptype* const ResBuf )
6577 {
6578 const CFilterStep& fs0 = (*Steps)[ 0 ];
6579
6580 fs0.convertVtoH( SrcBuf, BufPtrs[ 0 ], SrcLen, SrcIncr );
6581 BufPtrs[ 2 ] = ResBuf;
6582
6583 int j;
6584
6585 for( j = 0; j < Steps -> getItemCount(); j++ )
6586 {
6587 const CFilterStep& fs = (*Steps)[ j ];
6588 fs.prepareInBuf( BufPtrs[ fs.InBuf ]);
6589 const int DstIncr = ( fs.OutBuf == 2 ? ResIncr :
6590 ( Vars -> packmode == 0 ? Vars -> ElCount : 1 ));
6591
6592 if( fs.ResampleFactor != 0 )
6593 {
6594 if( fs.IsUpsample )
6595 {
6596 fs.doUpsample( BufPtrs[ fs.InBuf ],
6597 BufPtrs[ fs.OutBuf ]);
6598 }
6599 else
6600 {
6601 fs.doFilter( BufPtrs[ fs.InBuf ],
6602 BufPtrs[ fs.OutBuf ], DstIncr );
6603 }
6604 }
6605 else
6606 {
6607 if( Vars -> IsResize2 )
6608 {
6609 fs.doResize2( BufPtrs[ fs.InBuf ],
6610 BufPtrs[ fs.OutBuf ], DstIncr, TmpFltBuf );
6611 }
6612 else
6613 {
6614 fs.doResize( BufPtrs[ fs.InBuf ],
6615 BufPtrs[ fs.OutBuf ], DstIncr, TmpFltBuf );
6616 }
6617 }
6618 }
6619 }
6620 };
6621};
6622
6623#undef AVIR_PI
6624#undef AVIR_PId2
6625#undef AVIR_NOCTOR
6626
6627#if defined( AVIR_NULLPTR )
6628 #undef nullptr
6629 #undef AVIR_NULLPTR
6630#endif // defined( AVIR_NULLPTR )
6631
6632} // namespace avir
6633
6634#endif // AVIR_CIMAGERESIZER_INCLUDED
T clamp(const T &Value, const T minv, const T maxv)
"Clamps" (clips) the specified value so that it is not lesser than minv, and not greater than maxv.
Definition avir.h:149
#define AVIR_PI
The macro equals to pi constant, fills 53-bit floating point mantissa. Undefined at the end of file.
Definition avir.h:101
#define AVIR_NOCTOR(ClassName)
Macro that defines empty copy-constructor and copy operator, with the private: prefix.
Definition avir.h:116
void calcFIRFilterResponse(const T *flt, int fltlen, const double th, double &re0, double &im0, const int fltlat=0)
Calculates frequency response of the specified FIR filter at the specified circular frequency.
Definition avir.h:461
T convertLin2SRGB(const T s)
Approximately de-linearizes the linear gamma value.
Definition avir.h:300
void replicateArray(const T1 *const ip, const int ipl, T2 *op, int l, const int opinc)
Replicates a set of adjacent elements several times in a row.
Definition avir.h:378
T pow24i_sRGB(const T x0)
Power 1/2.4 approximation function, designed for sRGB gamma correction.
Definition avir.h:186
void normalizeFIRFilter(T *const p, const int l, const double DCGain, const int pstep=1)
Normalizes FIR filter so that its frequency response at DC is equal to DCGain.
Definition avir.h:517
#define AVIR_PId2
The macro equals to pi divided by 2 constant, fills 53-bit floating point mantissa....
Definition avir.h:104
T round(const T d)
Rounding function, based on the (int) typecast. Biased result. Not suitable for numbers greater than ...
Definition avir.h:131
void addArray(const T1 *ip, T2 *op, int l, const int ipinc=1, const int opinc=1)
Adds values located in array ip, to array op.
Definition avir.h:350
T convertSRGB2Lin(const Tin &s0, const T m)
Approximately linearizes the sRGB gamma value.
Definition avir.h:209
void copyArray(const T1 *ip, T2 *op, int l, const int ipinc=1, const int opinc=1)
Converts (via typecast) specified array of type T1 values of length l into array of type T2 values.
Definition avir.h:327
T pow24_sRGB(const T x0)
Power 2.4 approximation, designed for sRGB gamma correction.
Definition avir.h:163
Memory buffer class for element array storage, with capacity tracking.
Definition avir.h:566
void truncateCapacity(const capint NewCapacity)
"Truncates" (reduces) capacity of this buffer, without reallocating it.
Definition avir.h:742
capint getCapacity() const
Returns the capacity of this element buffer.
Definition avir.h:671
CBuffer & operator=(const CBuffer &Source)
Completely copies the specified buffer.
Definition avir.h:618
CBuffer(const CBuffer &Source)
Completely copies the specified buffer.
Definition avir.h:595
void increaseCapacity(const capint NewCapacity, const bool DoDataCopy=true)
Reallocates this buffer to a larger size.
Definition avir.h:702
void forceCapacity(const capint NewCapacity)
"Forces" this buffer to have an arbitary capacity.
Definition avir.h:687
void free()
Deallocates any previously allocated buffer.
Definition avir.h:659
void updateCapacity(const capint ReqCapacity)
Increases capacity so that the specified number of elements can be stored.
Definition avir.h:763
void alloc(const capint aCapacity, const int aAlignment=0)
Allocates memory so that the specified number of elements can be stored in this buffer object.
Definition avir.h:649
CBuffer(const capint aCapacity, const int aAlignment=0)
Creates the buffer with the specified capacity.
Definition avir.h:584
Array of structured objects.
Definition avir.h:843
CStructArray & operator=(const CStructArray &Source)
Copies the specified array element-by-element.
Definition avir.h:879
T & add()
Creates a new object of type T with the default constructor, and adds this object to the array.
Definition avir.h:924
void clear()
Erases all items of this array.
Definition avir.h:973
CStructArray(const CStructArray &Source)
Copies the specified array element-by-element.
Definition avir.h:856
void setItemCount(const int NewCount)
Changes the number of allocated items.
Definition avir.h:947
int getItemCount() const
Definition avir.h:986
T & operator[](const int Index)
Returns writable reference to the specified element.
Definition avir.h:901
Sine signal generator class.
Definition avir.h:1005
double generate()
Returns the next value of the sine function, without biasing.
Definition avir.h:1026
CSineGen(const double si, const double ph)
Initializes this sine signal generator.
Definition avir.h:1015
Peaked Cosine window function generator class.
Definition avir.h:1055
double generate()
Returns the next Peaked Cosine window function coefficient.
Definition avir.h:1078
CDSPWindowGenPeakedCosine(const double aAlpha, const double aLen2)
Initializes this window function generator.
Definition avir.h:1065
FIR filter-based equalizer generator.
Definition avir.h:1117
static int calcFilterLength(const double aFilterLength, int &Latency)
Calculates filter's length (in samples) and latency, depending on the required non-truncated filter l...
Definition avir.h:1316
int getFilterLatency() const
Returns filter's latency (group delay), in samples (taps).
Definition avir.h:1232
int getFilterLength() const
Returns filter's length, in samples (taps).
Definition avir.h:1223
void buildFilter(const double *const BandGains, double *const Filter)
Creates symmetric-odd FIR filter with the specified gain levels at band crossover points.
Definition avir.h:1247
void init(const double SampleRate, const double aFilterLength, const int aBandCount, const double MinFreq, const double MaxFreq, const bool IsLogBands, const double WFAlpha)
Initializes this object with the required parameters.
Definition avir.h:1137
Low-pass filter windowed by Peaked Cosine window function.
Definition avir.h:1491
int fl2
Half filter's length, excluding the peak value. This value can be also used as filter's latency in sa...
Definition avir.h:1493
int FilterLen
Filter's length in samples (taps).
Definition avir.h:1495
void generateLPF(T *op, const double DCGain)
Generates a linear-phase low-pass filter windowed by Peaked Cosine window function.
Definition avir.h:1529
CDSPPeakedCosineLPF(const double aLen2, const double aFreq2, const double aAlpha)
Initalizes this object.
Definition avir.h:1506
Buffer class for parametrized low-pass filter.
Definition avir.h:1601
double Len2
Half-length (non-truncated) of low-pass filters, in samples (taps).
Definition avir.h:1603
double Freq
Low-pass filter's corner frequency.
Definition avir.h:1605
bool operator==(const CFltBuffer &b2) const
Returns true, if both filters have same parameters.
Definition avir.h:1624
double Alpha
Peaked Cosine window function Alpha parameter.
Definition avir.h:1606
double DCGain
DC gain applied to the filter.
Definition avir.h:1607
Sinc function-based fractional delay filter bank.
Definition avir.h:1649
int getFilterLen() const
Returns the length of each fractional delay filter, in samples (taps). Always an even value.
Definition avir.h:1779
void createAllFilters()
Makes sure all fractional delay filters were created.
Definition avir.h:1870
const fptype * getFilter(const int i)
Returns the pointer to the specified interpolation table filter.
Definition avir.h:1814
void init(const int ReqFracCount, const int ReqOrder, const double BaseLen, const double Cutoff, const double aWFAlpha, const CFltBuffer &aExtFilter, const int aAlignment=0, const int FltLenAlign=1)
Initializes (builds) the filter bank based on the supplied parameters.
Definition avir.h:1732
void copyInitParams(const CDSPFracFilterBankLin &s)
Copies a limited set of parameters of the source filter bank.
Definition avir.h:1668
int getOrder() const
Returns the order of the interpolation polynomial.
Definition avir.h:1797
bool operator==(const CDSPFracFilterBankLin &s) const
Compares this filter bank and another filter bank, and returns true, if their parameters are equal....
Definition avir.h:1702
const fptype * getFilterConst(const int i) const
Returns the pointer to the specified interpolation table filter.
Definition avir.h:1861
int calcInitComplexity(const CBuffer< char > &FracUseMap) const
Returns an approximate initialization complexity, expressed in the number of multiply-add operations.
Definition avir.h:1895
int getFracCount() const
Returns the number of fractional filters in use by this bank.
Definition avir.h:1788
Thread pool for multi-threaded image resizing operation.
Definition avir.h:2120
virtual int getSuggestedWorkloadCount() const
Returns the suggested number of workloads (and their associated threads) to add.
Definition avir.h:2163
virtual void startAllWorkloads()
Starts all workloads associated with threads previously added via the addWorkload() function.
Definition avir.h:2202
virtual void addWorkload(CWorkload *const Workload)
Adds a new workload (and possibly thread) to the thread pool.
Definition avir.h:2188
virtual void waitAllWorkloadsToFinish()
Waits for all workloads to finish.
Definition avir.h:2210
virtual void removeAllWorkloads()
Removes all workloads previously added via the addWorkload() function.
Definition avir.h:2222
Thread pool's workload object class.
Definition avir.h:2138
virtual void process()=0
Function that gets called from the thread when thread pool's startAllWorkloads() function is called.
Resizing algorithm parameters structure.
Definition avir.h:2263
double IntFltAlpha
Alpha parameter of the Peaked Cosine window function used on the interpolation low-pass filter....
Definition avir.h:2271
double HBFltCutoff
Half-band filter's cutoff point, [0; 1]. Assigned internally.
Definition avir.h:2308
double HBFltAlpha
Half-band filter's Alpha. Assigned internally.
Definition avir.h:2307
double LPFltBaseLen
Base length of the low-pass (aka anti-aliasing or reconstruction) filter, in samples (taps),...
Definition avir.h:2286
double IntFltCutoff
Interpolation low-pass filter's cutoff frequency (normalized, [0; 1]). The "usable" range is 0....
Definition avir.h:2274
double IntFltLen
Interpolation low-pass filter's length in samples (taps). The length value should be at least 18 or o...
Definition avir.h:2276
double CorrFltLen
Correction filter's length in samples (taps). The "usable" range is narrow, 5.5 to 8,...
Definition avir.h:2267
double CorrFltAlpha
Alpha parameter of the Peaked Cosine window function used on the correction filter....
Definition avir.h:2264
double LPFltAlpha
Alpha parameter of the Peaked Cosine window function used on the low-pass filter. The "usable" values...
Definition avir.h:2283
double LPFltCutoffMult
Low-pass filter's cutoff frequency multiplier. This value can be both below and above 1....
Definition avir.h:2290
double HBFltLen
Length of the half-band low-pass filter. Assigned internally. Internally used to perform 2X or higher...
Definition avir.h:2310
The default set of resizing algorithm parameters (10.06/1.88/1.029(256064.90)/0.000039).
Definition avir.h:2329
Image resizing variables (base class).
Definition avir.h:2474
int BufOffs[2]
Offsets into the intermediate buffers, used to provide prefix elements required during processing so ...
Definition avir.h:2491
int BufLen[2]
Intermediate buffers' lengths in fptype elements.
Definition avir.h:2490
int ResizeStep
Index of the resizing step in the latest filtering steps array.
Definition avir.h:2499
int packmode
0, if interleaved packing; 1, if de-interleaved.
Definition avir.h:2489
int ElCountIO
The number of source and destination image's elements used to store 1 pixel.
Definition avir.h:2477
bool IsResize2
Use optimized doResize2() function.
Definition avir.h:2501
int fppack
The number of atomic types stored in a single fptype element.
Definition avir.h:2479
double o
Starting pixel offset inside the source image, updated to reflect the actually used offset during res...
Definition avir.h:2497
double OutGammaMult
Output gamma multiplier, used to convert data to [0; 255/65535] range. 0.0, if no gamma is in use.
Definition avir.h:2504
double k
Resizing step coefficient, updated to reflect the actually used coefficient during resizing.
Definition avir.h:2495
int ElCount
The number of fptype elements used to store 1 pixel.
Definition avir.h:2476
double InGammaMult
Input gamma multiplier, used to convert input data to [0; 1] range. 0.0, if no gamma is in use.
Definition avir.h:2502
int fpalign
Suggested alignment size in bytes. This is not a required alignment, because image resizing algorithm...
Definition avir.h:2481
int elalign
Length alignment of arrays of elements. This applies to filters and intermediate buffers: this consta...
Definition avir.h:2485
Image resizing variables.
Definition avir.h:2517
int BuildMode
The build mode to use, for debugging purposes. Set to -1, to select a minimal-complexity mode automat...
Definition avir.h:2530
CImageResizerThreadPool * ThreadPool
Thread pool to be used by the image resizing function. Set to nullptr to use single-threaded processi...
Definition avir.h:2523
int AlphaIndex
Alpha-channel index, for 4-channel input. Default -1 applies gamma correction to all channels....
Definition avir.h:2527
bool UseSRGBGamma
Perform sRGB gamma linearization (correction).
Definition avir.h:2526
double oy
Start Y pixel offset within source image (can be negative). Positive offset moves image to the top.
Definition avir.h:2521
int RndSeed
Random seed parameter. This parameter may be incremented after each random generator initialization....
Definition avir.h:2533
double ox
Start X pixel offset within source image (can be negative). Positive offset moves image to the left.
Definition avir.h:2519
int OutLen
Length of the resulting scanline.
Definition avir.h:2606
int ResampleFactor
Resample factor (greater or equal to 1). If 0, this is a resizing step. This value should be greater ...
Definition avir.h:2577
int InElIncr
Pixel element increment within the input buffer, used during de-interleaved processing: in this case ...
Definition avir.h:2603
bool IsUpsample
true, if this step is an upsampling step; false, if downsampling step. Should be set to false,...
Definition avir.h:2574
int OutPrefix
Required output prefix pixels. These prefix pixels will not be pre-filled with any values....
Definition avir.h:2608
CBuffer< fptype > SuffixDC
DC component fluctuations added at the end of the resulting scanline, used when IsUpsample equals tru...
Definition avir.h:2620
int OutElIncr
Pixel element increment within the output buffer, used during de-interleaved processing....
Definition avir.h:2614
static const int EdgePixelCountDef
The default number of pixels additionally produced at scanline edges during filtering....
Definition avir.h:2629
int OutBuf
Output buffer index. 0 or 1; 2 for the last step.
Definition avir.h:2607
int EdgePixelCount
The number of edge pixels added. Affects the initial position within the input scanline,...
Definition avir.h:2623
int InPrefix
Required input prefix pixels. These prefix pixels will be filled with source scanline's first pixel v...
Definition avir.h:2593
int InLen
Input scanline's length in pixels.
Definition avir.h:2591
CFltBuffer FltOrig
Originally-designed filter. This buffer may not be assigned. Assigned by filters that precede the res...
Definition avir.h:2581
int FltLatency
Filter's latency (group delay, shift) in pixels.
Definition avir.h:2589
CBuffer< fptype > Flt
Filter to use at this step.
Definition avir.h:2580
const CDSPFracFilterBankLin< fptype > * FltBank
Filter bank in use by this resizing step, may be equal to FltBankDyn.
Definition avir.h:2719
int InBuf
Input buffer index, 0 or 1.
Definition avir.h:2592
CDSPFracFilterBankLin< fptype > * FltBankDyn
Dynamically-allocated filter bank in use by this resizing step. Equals nullptr, if a static FltBank f...
Definition avir.h:2721
int OutSuffix
Required input suffix pixels. These suffix pixels will not be pre-filled with any values....
Definition avir.h:2611
const CImageResizerVars * Vars
Image resizing-related variables.
Definition avir.h:2590
double DCGain
DC gain which was applied to the filter. Not defined, if ResampleFactor equals 0.
Definition avir.h:2587
CRPosBuf * RPosBuf
Resizing positions buffer. Used when ResampleFactor equals 0 (resizing step).
Definition avir.h:2717
int InSuffix
Required input suffix pixels. These suffix pixels will be filled with source scanline's last pixel va...
Definition avir.h:2598
CBuffer< fptype > PrefixDC
DC component fluctuations added at the start of the resulting scanline, used when IsUpsample equals t...
Definition avir.h:2617
Resizing position structure.
Definition avir.h:2641
const fptype * ftp
Fractional delay filter pointer.
Definition avir.h:2644
int SrcPosInt
Source scanline position.
Definition avir.h:2642
int SrcOffs
Source scanline offset.
Definition avir.h:2646
fptypeatom x
Interpolation coefficient between delay filters.
Definition avir.h:2645
int fti
Fractional delay filter index.
Definition avir.h:2643
int fl
Filter length to use, applies to doResize2() only.
Definition avir.h:2647
Resizing positions buffer class.
Definition avir.h:2658
int FracCount
The number of fractional delay filters in a filter bank used together with this buffer.
Definition avir.h:2662
double k
Resizing step.
Definition avir.h:2660
double o
Resizing offset.
Definition avir.h:2661
Resizing positions buffer array class.
Definition avir.h:2675
CRPosBuf & getRPosBuf(const double k, const double o, const int FracCount)
Returns the resizing positions buffer with the required stepping.
Definition avir.h:2693
Interleaved filtering steps implementation class.
Definition avir.h:2744
void doResize(const fptype *SrcLine, fptype *DstLine, const int DstLineIncr, fptype *const) const
Performs resizing of a single scanline.
Definition avir.h:3884
void packScanline(const Tin *ip, fptype *const op0, const int l0) const
Performs "packing" of a scanline, and type conversion.
Definition avir.h:2778
void doFilter(const fptype *const Src, fptype *Dst, const int DstIncr) const
Performs scanline filtering with optional downsampling.
Definition avir.h:3748
void prepareInBuf(fptype *Src) const
Prepares input scanline buffer for this filtering step.
Definition avir.h:3227
static void applySRGBGamma(fptype *p, int l, const CImageResizerVars &Vars0)
Applies Linear to sRGB gamma correction to the specified scanline.
Definition avir.h:2982
void doResize2(const fptype *SrcLine, fptype *DstLine, const int DstLineIncr, fptype *const) const
Performs resizing of a single scanline assuming that the input buffer consists of zero-padded element...
Definition avir.h:4114
static void unpackScanline(const fptype *ip, Tout *op, int l, const CImageResizerVars &Vars0)
Performs "unpacking" of a scanline, and type conversion.
Definition avir.h:3156
void convertVtoH(const fptype *ip, fptype *op, const int SrcLen, const int SrcIncr) const
Converts vertical scanline to horizontal scanline.
Definition avir.h:3085
void doUpsample(const fptype *const Src, fptype *const Dst) const
Performs scanline upsampling with filtering.
Definition avir.h:3249
Image resizer's default dithering class.
Definition avir.h:4353
double PkOut0
Peak output value allowed.
Definition avir.h:4426
static bool isRecursive()
Returns true, if dithering is recursive relative to scanlines, meaning multi-threaded execution is no...
Definition avir.h:4381
int Len
Scanline's length in pixels.
Definition avir.h:4422
const CImageResizerVars * Vars
Image resizing-related variables.
Definition avir.h:4423
int LenE
= LenE * ElCount.
Definition avir.h:4424
void dither(fptype *const ResScanline) const
Performs rounding and clipping operations, in-place.
Definition avir.h:4392
void init(const int aLen, const CImageResizerVars &aVars, const double aTrMul, const double aPkOut)
Initializes the ditherer object.
Definition avir.h:4365
double TrMul0
Bit-depth truncation multiplier.
Definition avir.h:4425
Image resizer's error-diffusion dithering class, interleaved mode.
Definition avir.h:4444
static bool isRecursive()
Returns true, if dithering is recursive relative to scanlines, meaning multi-threaded execution is no...
Definition avir.h:4476
CBuffer< fptype > ResScanlineDith0
Error diffusion buffer.
Definition avir.h:4534
void init(const int aLen, const CImageResizerVars &aVars, const double aTrMul, const double aPkOut)
Initializes the ditherer object.
Definition avir.h:4456
fptype * ResScanlineDith
Error diffusion buffer pointer which skips the first ElCount elements.
Definition avir.h:4535
void dither(fptype *const ResScanline)
Performs rounding and clipping operations, in-place.
Definition avir.h:4485
Floating-point processing definition and abstraction class.
Definition avir.h:4572
static const int fppack
Definition avir.h:4576
adith CDitherer
Ditherer class to use during processing.
Definition avir.h:4591
static const int elalign
Definition avir.h:4583
static const int packmode
Definition avir.h:4587
static const int fpalign
Definition avir.h:4578
CImageResizerFilterStepINL< fptype, fptypeatom > CFilterStep
Filtering step class to use during processing.
Definition avir.h:4589
afptypeatom fptypeatom
Atomic type fptype consists of.
Definition avir.h:4575
afptype fptype
Floating-point type to use during processing.
Definition avir.h:4574
void resizeImage(const Tin *const SrcBuf, const int SrcWidth, const int SrcHeight, int SrcScanlineSize, Tout *const NewBuf, const int NewWidth, const int NewHeight, const int ElCountIO, const double k, CImageResizerVars *const aVars=nullptr) const
Resizes the image.
Definition avir.h:4681
CImageResizer(const int aResBitDepth=8, const int aSrcBitDepth=0, const CImageResizerParams &aParams=CImageResizerParamsDef())
Initializes the resizer.
Definition avir.h:4630