22// Licensed under the Apache License, Version 2.0.
33
44using System ;
5- using System . Collections . Concurrent ;
65using System . Collections . Generic ;
76using System . Numerics ;
87using System . Runtime . CompilerServices ;
@@ -22,26 +21,11 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution
2221 internal class BokehBlurProcessor < TPixel > : ImageProcessor < TPixel >
2322 where TPixel : struct , IPixel < TPixel >
2423 {
25- /// <summary>
26- /// The kernel radius.
27- /// </summary>
28- private readonly int radius ;
29-
3024 /// <summary>
3125 /// The gamma highlight factor to use when applying the effect
3226 /// </summary>
3327 private readonly float gamma ;
3428
35- /// <summary>
36- /// The maximum size of the kernel in either direction
37- /// </summary>
38- private readonly int kernelSize ;
39-
40- /// <summary>
41- /// The number of components to use when applying the bokeh blur
42- /// </summary>
43- private readonly int componentsCount ;
44-
4529 /// <summary>
4630 /// The kernel parameters to use for the current instance (a: X, b: Y, A: Z, B: W)
4731 /// </summary>
@@ -52,16 +36,6 @@ internal class BokehBlurProcessor<TPixel> : ImageProcessor<TPixel>
5236 /// </summary>
5337 private readonly Complex64 [ ] [ ] kernels ;
5438
55- /// <summary>
56- /// The scaling factor for kernel values
57- /// </summary>
58- private readonly float kernelsScale ;
59-
60- /// <summary>
61- /// The mapping of initialized complex kernels and parameters, to speed up the initialization of new <see cref="BokehBlurProcessor{TPixel}"/> instances
62- /// </summary>
63- private static readonly ConcurrentDictionary < BokehBlurParameters , BokehBlurKernelData > Cache = new ConcurrentDictionary < BokehBlurParameters , BokehBlurKernelData > ( ) ;
64-
6539 /// <summary>
6640 /// Initializes a new instance of the <see cref="BokehBlurProcessor{TPixel}"/> class.
6741 /// </summary>
@@ -72,29 +46,16 @@ internal class BokehBlurProcessor<TPixel> : ImageProcessor<TPixel>
7246 public BokehBlurProcessor ( Configuration configuration , BokehBlurProcessor definition , Image < TPixel > source , Rectangle sourceRectangle )
7347 : base ( configuration , source , sourceRectangle )
7448 {
75- this . radius = definition . Radius ;
76- this . kernelSize = ( this . radius * 2 ) + 1 ;
77- this . componentsCount = definition . Components ;
7849 this . gamma = definition . Gamma ;
7950
80- // Reuse the initialized values from the cache, if possible
81- var parameters = new BokehBlurParameters ( this . radius , this . componentsCount ) ;
82- if ( Cache . TryGetValue ( parameters , out BokehBlurKernelData info ) )
83- {
84- this . kernelParameters = info . Parameters ;
85- this . kernelsScale = info . Scale ;
86- this . kernels = info . Kernels ;
87- }
88- else
89- {
90- // Initialize the complex kernels and parameters with the current arguments
91- ( this . kernelParameters , this . kernelsScale ) = this . GetParameters ( ) ;
92- this . kernels = this . CreateComplexKernels ( ) ;
93- this . NormalizeKernels ( ) ;
51+ // Get the bokeh blur data
52+ BokehBlurKernelData data = BokehBlurKernelDataProvider . GetBokehBlurKernelData (
53+ definition . Radius ,
54+ ( definition . Radius * 2 ) + 1 ,
55+ definition . Components ) ;
9456
95- // Store them in the cache for future use
96- Cache . TryAdd ( parameters , new BokehBlurKernelData ( this . kernelParameters , this . kernelsScale , this . kernels ) ) ;
97- }
57+ this . kernelParameters = data . Parameters ;
58+ this . kernels = data . Kernels ;
9859 }
9960
10061 /// <summary>
@@ -107,163 +68,6 @@ public BokehBlurProcessor(Configuration configuration, BokehBlurProcessor defini
10768 /// </summary>
10869 public IReadOnlyList < Vector4 > KernelParameters => this . kernelParameters ;
10970
110- /// <summary>
111- /// Gets the kernel scales to adjust the component values in each kernel
112- /// </summary>
113- private static IReadOnlyList < float > KernelScales { get ; } = new [ ] { 1.4f , 1.2f , 1.2f , 1.2f , 1.2f , 1.2f } ;
114-
115- /// <summary>
116- /// Gets the available bokeh blur kernel parameters
117- /// </summary>
118- private static IReadOnlyList < Vector4 [ ] > KernelComponents { get ; } = new [ ]
119- {
120- // 1 component
121- new [ ] { new Vector4 ( 0.862325f , 1.624835f , 0.767583f , 1.862321f ) } ,
122-
123- // 2 components
124- new [ ]
125- {
126- new Vector4 ( 0.886528f , 5.268909f , 0.411259f , - 0.548794f ) ,
127- new Vector4 ( 1.960518f , 1.558213f , 0.513282f , 4.56111f )
128- } ,
129-
130- // 3 components
131- new [ ]
132- {
133- new Vector4 ( 2.17649f , 5.043495f , 1.621035f , - 2.105439f ) ,
134- new Vector4 ( 1.019306f , 9.027613f , - 0.28086f , - 0.162882f ) ,
135- new Vector4 ( 2.81511f , 1.597273f , - 0.366471f , 10.300301f )
136- } ,
137-
138- // 4 components
139- new [ ]
140- {
141- new Vector4 ( 4.338459f , 1.553635f , - 5.767909f , 46.164397f ) ,
142- new Vector4 ( 3.839993f , 4.693183f , 9.795391f , - 15.227561f ) ,
143- new Vector4 ( 2.791880f , 8.178137f , - 3.048324f , 0.302959f ) ,
144- new Vector4 ( 1.342190f , 12.328289f , 0.010001f , 0.244650f )
145- } ,
146-
147- // 5 components
148- new [ ]
149- {
150- new Vector4 ( 4.892608f , 1.685979f , - 22.356787f , 85.91246f ) ,
151- new Vector4 ( 4.71187f , 4.998496f , 35.918936f , - 28.875618f ) ,
152- new Vector4 ( 4.052795f , 8.244168f , - 13.212253f , - 1.578428f ) ,
153- new Vector4 ( 2.929212f , 11.900859f , 0.507991f , 1.816328f ) ,
154- new Vector4 ( 1.512961f , 16.116382f , 0.138051f , - 0.01f )
155- } ,
156-
157- // 6 components
158- new [ ]
159- {
160- new Vector4 ( 5.143778f , 2.079813f , - 82.326596f , 111.231024f ) ,
161- new Vector4 ( 5.612426f , 6.153387f , 113.878661f , 58.004879f ) ,
162- new Vector4 ( 5.982921f , 9.802895f , 39.479083f , - 162.028887f ) ,
163- new Vector4 ( 6.505167f , 11.059237f , - 71.286026f , 95.027069f ) ,
164- new Vector4 ( 3.869579f , 14.81052f , 1.405746f , - 3.704914f ) ,
165- new Vector4 ( 2.201904f , 19.032909f , - 0.152784f , - 0.107988f )
166- }
167- } ;
168-
169- /// <summary>
170- /// Gets the kernel parameters and scaling factor for the current count value in the current instance
171- /// </summary>
172- private ( Vector4 [ ] Parameters , float Scale ) GetParameters ( )
173- {
174- // Prepare the kernel components
175- int index = Math . Max ( 0 , Math . Min ( this . componentsCount - 1 , KernelComponents . Count ) ) ;
176- return ( KernelComponents [ index ] , KernelScales [ index ] ) ;
177- }
178-
179- /// <summary>
180- /// Creates the collection of complex 1D kernels with the specified parameters
181- /// </summary>
182- private Complex64 [ ] [ ] CreateComplexKernels ( )
183- {
184- var kernels = new Complex64 [ this . kernelParameters . Length ] [ ] ;
185- ref Vector4 baseRef = ref MemoryMarshal . GetReference ( this . kernelParameters . AsSpan ( ) ) ;
186- for ( int i = 0 ; i < this . kernelParameters . Length ; i ++ )
187- {
188- ref Vector4 paramsRef = ref Unsafe . Add ( ref baseRef , i ) ;
189- kernels [ i ] = this . CreateComplex1DKernel ( paramsRef . X , paramsRef . Y ) ;
190- }
191-
192- return kernels ;
193- }
194-
195- /// <summary>
196- /// Creates a complex 1D kernel with the specified parameters
197- /// </summary>
198- /// <param name="a">The exponential parameter for each complex component</param>
199- /// <param name="b">The angle component for each complex component</param>
200- private Complex64 [ ] CreateComplex1DKernel ( float a , float b )
201- {
202- var kernel = new Complex64 [ this . kernelSize ] ;
203- ref Complex64 baseRef = ref MemoryMarshal . GetReference ( kernel . AsSpan ( ) ) ;
204- int r = this . radius , n = - r ;
205-
206- for ( int i = 0 ; i < this . kernelSize ; i ++ , n ++ )
207- {
208- // Incrementally compute the range values
209- float value = n * this . kernelsScale * ( 1f / r ) ;
210- value *= value ;
211-
212- // Fill in the complex kernel values
213- Unsafe . Add ( ref baseRef , i ) = new Complex64(
214- MathF . Exp ( - a * value ) * MathF . Cos ( b * value ) ,
215- MathF . Exp ( - a * value ) * MathF . Sin ( b * value ) ) ;
216- }
217-
218- return kernel ;
219- }
220-
221- /// <summary>
222- /// Normalizes the kernels with respect to A * real + B * imaginary
223- /// </summary>
224- private void NormalizeKernels ( )
225- {
226- // Calculate the complex weighted sum
227- float total = 0 ;
228- Span < Complex64 [ ] > kernelsSpan = this . kernels . AsSpan ( ) ;
229- ref Complex64 [ ] baseKernelsRef = ref MemoryMarshal . GetReference ( kernelsSpan ) ;
230- ref Vector4 baseParamsRef = ref MemoryMarshal . GetReference ( this . kernelParameters . AsSpan ( ) ) ;
231-
232- for ( int i = 0 ; i < this . kernelParameters . Length ; i ++ )
233- {
234- ref Complex64 [ ] kernelRef = ref Unsafe . Add ( ref baseKernelsRef , i ) ;
235- int length = kernelRef . Length ;
236- ref Complex64 valueRef = ref kernelRef [ 0 ] ;
237- ref Vector4 paramsRef = ref Unsafe . Add ( ref baseParamsRef , i ) ;
238-
239- for ( int j = 0 ; j < length ; j ++ )
240- {
241- for ( int k = 0 ; k < length ; k ++ )
242- {
243- ref Complex64 jRef = ref Unsafe . Add ( ref valueRef , j ) ;
244- ref Complex64 kRef = ref Unsafe . Add ( ref valueRef , k ) ;
245- total +=
246- ( paramsRef . Z * ( ( jRef . Real * kRef . Real ) - ( jRef . Imaginary * kRef . Imaginary ) ) )
247- + ( paramsRef . W * ( ( jRef . Real * kRef . Imaginary ) + ( jRef . Imaginary * kRef . Real ) ) ) ;
248- }
249- }
250- }
251-
252- // Normalize the kernels
253- float scalar = 1f / MathF . Sqrt ( total ) ;
254- for ( int i = 0 ; i < kernelsSpan . Length ; i ++ )
255- {
256- ref Complex64 [ ] kernelsRef = ref Unsafe . Add ( ref baseKernelsRef , i ) ;
257- int length = kernelsRef . Length ;
258- ref Complex64 valueRef = ref kernelsRef [ 0 ] ;
259-
260- for ( int j = 0 ; j < length ; j ++ )
261- {
262- Unsafe . Add ( ref valueRef , j ) * = scalar ;
263- }
264- }
265- }
266-
26771 /// <inheritdoc/>
26872 protected override void OnFrameApply ( ImageFrame < TPixel > source )
26973 {
0 commit comments