1- // Copyright (c) Six Labors and contributors.
1+ // Copyright (c) Six Labors and contributors.
22// Licensed under the Apache License, Version 2.0.
33
44using System ;
@@ -15,112 +15,80 @@ namespace SixLabors.ImageSharp.Processing.Processors.Dithering
1515 /// </summary>
1616 public abstract class ErrorDiffuser : IErrorDiffuser
1717 {
18- /// <summary>
19- /// The vector to perform division.
20- /// </summary>
21- private readonly Vector4 divisorVector ;
22-
23- /// <summary>
24- /// The matrix width.
25- /// </summary>
26- private readonly int matrixHeight ;
27-
28- /// <summary>
29- /// The matrix height.
30- /// </summary>
31- private readonly int matrixWidth ;
32-
33- /// <summary>
34- /// The offset at which to start the dithering operation.
35- /// </summary>
36- private readonly int startingOffset ;
37-
38- /// <summary>
39- /// The diffusion matrix.
40- /// </summary>
18+ private readonly int offset ;
4119 private readonly DenseMatrix < float > matrix ;
4220
4321 /// <summary>
4422 /// Initializes a new instance of the <see cref="ErrorDiffuser"/> class.
4523 /// </summary>
4624 /// <param name="matrix">The dithering matrix.</param>
47- /// <param name="divisor">The divisor.</param>
48- internal ErrorDiffuser ( in DenseMatrix < float > matrix , byte divisor )
25+ internal ErrorDiffuser ( in DenseMatrix < float > matrix )
4926 {
50- Guard . MustBeGreaterThan ( divisor , 0 , nameof ( divisor ) ) ;
51-
52- this . matrix = matrix ;
53- this . matrixWidth = this . matrix . Columns ;
54- this . matrixHeight = this . matrix . Rows ;
55- this . divisorVector = new Vector4 ( divisor ) ;
27+ // Calculate the offset position of the pixel relative to
28+ // the diffusion matrix.
29+ this . offset = 0 ;
5630
57- this . startingOffset = 0 ;
58- for ( int i = 0 ; i < this . matrixWidth ; i ++ )
31+ for ( int col = 0 ; col < matrix . Columns ; col ++ )
5932 {
60- // Good to disable here as we are not comparing mathematical output.
61- // ReSharper disable once CompareOfFloatsByEqualityOperator
62- if ( matrix [ 0 , i ] != 0 )
33+ if ( matrix [ 0 , col ] != 0 )
6334 {
64- this . startingOffset = ( byte ) ( i - 1 ) ;
35+ this . offset = col - 1 ;
6536 break ;
6637 }
6738 }
39+
40+ this . matrix = matrix ;
6841 }
6942
7043 /// <inheritdoc />
71- [ MethodImpl ( MethodImplOptions . AggressiveInlining ) ]
72- public void Dither < TPixel > ( ImageFrame < TPixel > image , TPixel source , TPixel transformed , int x , int y , int minX , int minY , int maxX , int maxY )
44+ [ MethodImpl ( InliningOptions . ShortMethod ) ]
45+ public void Dither < TPixel > ( ImageFrame < TPixel > image , TPixel source , TPixel transformed , int x , int y , int minX , int maxX , int maxY )
7346 where TPixel : struct , IPixel < TPixel >
7447 {
7548 image [ x , y ] = transformed ;
7649
77- // Equal? Break out as there's nothing to pass.
50+ // Equal? Break out as there's no error to pass.
7851 if ( source . Equals ( transformed ) )
7952 {
8053 return ;
8154 }
8255
8356 // Calculate the error
8457 Vector4 error = source . ToVector4 ( ) - transformed . ToVector4 ( ) ;
85- this . DoDither ( image , x , y , minX , minY , maxX , maxY , error ) ;
58+ this . DoDither ( image , x , y , minX , maxX , maxY , error ) ;
8659 }
8760
88- [ MethodImpl ( MethodImplOptions . NoInlining ) ]
89- private void DoDither < TPixel > ( ImageFrame < TPixel > image , int x , int y , int minX , int minY , int maxX , int maxY , Vector4 error )
61+ [ MethodImpl ( InliningOptions . ShortMethod ) ]
62+ private void DoDither < TPixel > ( ImageFrame < TPixel > image , int x , int y , int minX , int maxX , int maxY , Vector4 error )
9063 where TPixel : struct , IPixel < TPixel >
9164 {
65+ int offset = this . offset ;
66+ DenseMatrix < float > matrix = this . matrix ;
67+
9268 // Loop through and distribute the error amongst neighboring pixels.
93- for ( int row = 0 ; row < this . matrixHeight ; row ++ )
69+ for ( int row = 0 , targetY = y ; row < matrix . Rows && targetY < maxY ; row ++ , targetY ++ )
9470 {
95- int matrixY = y + row ;
96- if ( matrixY > minY && matrixY < maxY )
97- {
98- Span < TPixel > rowSpan = image . GetPixelRowSpan ( matrixY ) ;
71+ Span < TPixel > rowSpan = image . GetPixelRowSpan ( targetY ) ;
9972
100- for ( int col = 0 ; col < this . matrixWidth ; col ++ )
73+ for ( int col = 0 ; col < matrix . Columns ; col ++ )
74+ {
75+ int targetX = x + ( col - offset ) ;
76+ if ( targetX >= minX && targetX < maxX )
10177 {
102- int matrixX = x + ( col - this . startingOffset ) ;
103-
104- if ( matrixX > minX && matrixX < maxX )
78+ float coefficient = matrix [ row , col ] ;
79+ if ( coefficient == 0 )
10580 {
106- float coefficient = this . matrix [ row , col ] ;
107-
108- // Good to disable here as we are not comparing mathematical output.
109- // ReSharper disable once CompareOfFloatsByEqualityOperator
110- if ( coefficient == 0 )
111- {
112- continue ;
113- }
81+ continue ;
82+ }
11483
115- ref TPixel pixel = ref rowSpan [ matrixX ] ;
116- var offsetColor = pixel . ToVector4 ( ) ;
84+ ref TPixel pixel = ref rowSpan [ targetX ] ;
85+ var result = pixel . ToVector4 ( ) ;
11786
118- Vector4 result = ( ( error * coefficient ) / this . divisorVector ) + offsetColor ;
119- pixel . FromVector4 ( result ) ;
120- }
87+ result += error * coefficient ;
88+ pixel . FromVector4 ( result ) ;
12189 }
12290 }
12391 }
12492 }
12593 }
126- }
94+ }
0 commit comments