1+ // DarthAffe 07.09.2023: Copied from https://github.com/DarthAffe/RGB.NET/blob/2e0754f474b82ed4d0cae5c6c44378d234f1321b/RGB.NET.Presets/Textures/Sampler/AverageByteSampler.cs
2+
3+ using System ;
4+ using System . Numerics ;
5+ using System . Runtime . CompilerServices ;
6+
7+ namespace ScreenCapture . NET . Downscale ;
8+
9+ /// <summary>
10+ /// Represents a sampled that averages multiple byte-data entries.
11+ /// </summary>
12+ internal static class AverageByteSampler
13+ {
14+ #region Constants
15+
16+ private static readonly int INT_VECTOR_LENGTH = Vector < uint > . Count ;
17+
18+ #endregion
19+
20+ #region Methods
21+
22+ public static unsafe void Sample ( in SamplerInfo < byte > info , in Span < byte > pixelData )
23+ {
24+ int count = info . Width * info . Height ;
25+ if ( count == 0 ) return ;
26+
27+ int dataLength = pixelData . Length ;
28+ Span < uint > sums = stackalloc uint [ dataLength ] ;
29+
30+ int elementsPerVector = Vector < byte > . Count / dataLength ;
31+ int valuesPerVector = elementsPerVector * dataLength ;
32+ if ( Vector . IsHardwareAccelerated && ( info . Height > 1 ) && ( info . Width >= valuesPerVector ) && ( dataLength <= Vector < byte > . Count ) )
33+ {
34+ int chunks = info . Width / elementsPerVector ;
35+
36+ Vector < uint > sum1 = Vector < uint > . Zero ;
37+ Vector < uint > sum2 = Vector < uint > . Zero ;
38+ Vector < uint > sum3 = Vector < uint > . Zero ;
39+ Vector < uint > sum4 = Vector < uint > . Zero ;
40+
41+ for ( int y = 0 ; y < info . Height ; y ++ )
42+ {
43+ ReadOnlySpan < byte > data = info [ y ] ;
44+
45+ fixed ( byte * colorPtr = data )
46+ {
47+ byte * current = colorPtr ;
48+ for ( int i = 0 ; i < chunks ; i ++ )
49+ {
50+ Vector < byte > bytes = * ( Vector < byte > * ) current ;
51+ Vector. Widen ( bytes , out Vector < ushort > short1 , out Vector < ushort > short2 ) ;
52+ Vector. Widen ( short1 , out Vector < uint > int1 , out Vector < uint > int2 ) ;
53+ Vector . Widen ( short2 , out Vector < uint > int3 , out Vector < uint > int4 ) ;
54+
55+ sum1 = Vector . Add ( sum1 , int1 ) ;
56+ sum2 = Vector . Add ( sum2 , int2 ) ;
57+ sum3 = Vector . Add ( sum3 , int3 ) ;
58+ sum4 = Vector . Add ( sum4 , int4 ) ;
59+
60+ current += valuesPerVector ;
61+ }
62+ }
63+
64+ int missingElements = data . Length - ( chunks * valuesPerVector ) ;
65+ int offset = chunks * valuesPerVector ;
66+ for ( int i = 0 ; i < missingElements ; i += dataLength )
67+ for ( int j = 0 ; j < sums . Length ; j ++ )
68+ sums [ j ] += data [ offset + i + j ] ;
69+ }
70+
71+ int value = 0 ;
72+ int sumIndex = 0 ;
73+ for ( int j = 0 ; ( j < INT_VECTOR_LENGTH ) && ( value < valuesPerVector ) ; j ++ )
74+ {
75+ sums [ sumIndex ] += sum1 [ j ] ;
76+ ++ sumIndex ;
77+ ++ value ;
78+
79+ if ( sumIndex >= dataLength )
80+ sumIndex = 0 ;
81+ }
82+
83+ for ( int j = 0 ; ( j < INT_VECTOR_LENGTH ) && ( value < valuesPerVector ) ; j ++ )
84+ {
85+ sums [ sumIndex ] += sum2 [ j ] ;
86+ ++ sumIndex ;
87+ ++ value ;
88+
89+ if ( sumIndex >= dataLength )
90+ sumIndex = 0 ;
91+ }
92+
93+ for ( int j = 0 ; ( j < INT_VECTOR_LENGTH ) && ( value < valuesPerVector ) ; j ++ )
94+ {
95+ sums [ sumIndex ] += sum3 [ j ] ;
96+ ++ sumIndex ;
97+ ++ value ;
98+
99+ if ( sumIndex >= dataLength )
100+ sumIndex = 0 ;
101+ }
102+
103+ for ( int j = 0 ; ( j < INT_VECTOR_LENGTH ) && ( value < valuesPerVector ) ; j ++ )
104+ {
105+ sums [ sumIndex ] += sum4 [ j ] ;
106+ ++ sumIndex ;
107+ ++ value ;
108+
109+ if ( sumIndex >= dataLength )
110+ sumIndex = 0 ;
111+ }
112+ }
113+ else
114+ {
115+ for ( int y = 0 ; y < info . Height ; y ++ )
116+ {
117+ ReadOnlySpan < byte > data = info [ y ] ;
118+ for ( int i = 0 ; i < data . Length ; i += dataLength )
119+ for ( int j = 0 ; j < sums . Length ; j ++ )
120+ sums [ j ] += data [ i + j ] ;
121+ }
122+ }
123+
124+ float divisor = count * byte . MaxValue ;
125+ for ( int i = 0 ; i < pixelData . Length ; i ++ )
126+ pixelData [ i ] = ( sums [ i ] / divisor ) . GetByteValueFromPercentage ( ) ;
127+ }
128+
129+ [ MethodImpl ( MethodImplOptions . AggressiveInlining ) ]
130+ private static byte GetByteValueFromPercentage ( this float percentage )
131+ {
132+ if ( float . IsNaN ( percentage ) ) return 0 ;
133+
134+ percentage = percentage . Clamp ( 0 , 1.0f ) ;
135+ return ( byte ) ( percentage >= 1.0f ? 255 : percentage * 256.0f ) ;
136+ }
137+
138+ [ MethodImpl ( MethodImplOptions . AggressiveInlining ) ]
139+ private static float Clamp ( this float value , float min , float max )
140+ {
141+ // ReSharper disable ConvertIfStatementToReturnStatement - I'm not sure why, but inlining this statement reduces performance by ~10%
142+ if ( value < min ) return min ;
143+ if ( value > max ) return max ;
144+ return value ;
145+ // ReSharper restore ConvertIfStatementToReturnStatement
146+ }
147+
148+ #endregion
149+ }
0 commit comments