diff --git a/src/ImageSharp/Dithering/ErrorDiffusion/ErrorDiffuserBase.cs b/src/ImageSharp/Dithering/ErrorDiffusion/ErrorDiffuserBase.cs
index 9fde27908..8f448198b 100644
--- a/src/ImageSharp/Dithering/ErrorDiffusion/ErrorDiffuserBase.cs
+++ b/src/ImageSharp/Dithering/ErrorDiffusion/ErrorDiffuserBase.cs
@@ -105,8 +105,6 @@ namespace SixLabors.ImageSharp.Dithering.Base
var offsetColor = pixel.ToVector4();
Vector4 result = ((error * coefficient) / this.divisorVector) + offsetColor;
-
- // result.W = offsetColor.W;
pixel.PackFromVector4(result);
}
}
diff --git a/src/ImageSharp/Dithering/ErrorDiffusion/KnownDiffusers.cs b/src/ImageSharp/Dithering/ErrorDiffusion/KnownDiffusers.cs
new file mode 100644
index 000000000..c75530b8e
--- /dev/null
+++ b/src/ImageSharp/Dithering/ErrorDiffusion/KnownDiffusers.cs
@@ -0,0 +1,56 @@
+// Copyright (c) Six Labors and contributors.
+// Licensed under the Apache License, Version 2.0.
+
+namespace SixLabors.ImageSharp.Dithering
+{
+ ///
+ /// Contains reusable static instances of known error diffusion algorithms
+ ///
+ public static class KnownDiffusers
+ {
+ ///
+ /// Gets the error diffuser that implements the Atkinson algorithm.
+ ///
+ public static IErrorDiffuser Atkinson { get; } = new AtkinsonDiffuser();
+
+ ///
+ /// Gets the error diffuser that implements the Burks algorithm.
+ ///
+ public static IErrorDiffuser Burks { get; } = new BurksDiffuser();
+
+ ///
+ /// Gets the error diffuser that implements the Floyd-Steinberg algorithm.
+ ///
+ public static IErrorDiffuser FloydSteinberg { get; } = new FloydSteinbergDiffuser();
+
+ ///
+ /// Gets the error diffuser that implements the Jarvis-Judice-Ninke algorithm.
+ ///
+ public static IErrorDiffuser JarvisJudiceNinke { get; } = new JarvisJudiceNinkeDiffuser();
+
+ ///
+ /// Gets the error diffuser that implements the Sierra-2 algorithm.
+ ///
+ public static IErrorDiffuser Sierra2 { get; } = new Sierra2Diffuser();
+
+ ///
+ /// Gets the error diffuser that implements the Sierra-3 algorithm.
+ ///
+ public static IErrorDiffuser Sierra3 { get; } = new Sierra3Diffuser();
+
+ ///
+ /// Gets the error diffuser that implements the Sierra-Lite algorithm.
+ ///
+ public static IErrorDiffuser SierraLite { get; } = new SierraLiteDiffuser();
+
+ ///
+ /// Gets the error diffuser that implements the Stevenson-Arce algorithm.
+ ///
+ public static IErrorDiffuser StevensonArce { get; } = new StevensonArceDiffuser();
+
+ ///
+ /// Gets the error diffuser that implements the Stucki algorithm.
+ ///
+ public static IErrorDiffuser Stucki { get; } = new StuckiDiffuser();
+ }
+}
\ No newline at end of file
diff --git a/src/ImageSharp/Dithering/ErrorDiffusion/StevensonArceDiffuser.cs b/src/ImageSharp/Dithering/ErrorDiffusion/StevensonArceDiffuser.cs
new file mode 100644
index 000000000..0f0338ac7
--- /dev/null
+++ b/src/ImageSharp/Dithering/ErrorDiffusion/StevensonArceDiffuser.cs
@@ -0,0 +1,34 @@
+// Copyright (c) Six Labors and contributors.
+// Licensed under the Apache License, Version 2.0.
+
+using SixLabors.ImageSharp.Dithering.Base;
+using SixLabors.ImageSharp.Memory;
+
+namespace SixLabors.ImageSharp.Dithering
+{
+ ///
+ /// Applies error diffusion based dithering using the Stevenson-Arce image dithering algorithm.
+ ///
+ public sealed class StevensonArceDiffuser : ErrorDiffuserBase
+ {
+ ///
+ /// The diffusion matrix
+ ///
+ private static readonly Fast2DArray StevensonArceMatrix =
+ new float[,]
+ {
+ { 0, 0, 0, 0, 0, 32, 0 },
+ { 12, 0, 26, 0, 30, 0, 16 },
+ { 0, 12, 0, 26, 0, 12, 0 },
+ { 5, 0, 12, 0, 12, 0, 5 }
+ };
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public StevensonArceDiffuser()
+ : base(StevensonArceMatrix, 200)
+ {
+ }
+ }
+}
diff --git a/src/ImageSharp/Dithering/Ordered/BayerDither.cs b/src/ImageSharp/Dithering/Ordered/BayerDither.cs
deleted file mode 100644
index 3bac601ae..000000000
--- a/src/ImageSharp/Dithering/Ordered/BayerDither.cs
+++ /dev/null
@@ -1,60 +0,0 @@
-// Copyright (c) Six Labors and contributors.
-// Licensed under the Apache License, Version 2.0.
-
-using System.Runtime.CompilerServices;
-using SixLabors.ImageSharp.Memory;
-
-namespace SixLabors.ImageSharp.Dithering
-{
- ///
- /// Applies order dithering using a Bayer dithering matrix of arbitrary length.
- ///
- ///
- public class BayerDither : OrderedDitherBase
- {
- ///
- /// Initializes a new instance of the class.
- ///
- ///
- /// The exponent used to raise the base value (2).
- /// The value given determines the dimensions of the matrix with each dimension a power of 2. e.g 2^2 = 4, 2^3 = 8
- ///
- public BayerDither(uint exponent)
- : base(ComputeBayer(exponent))
- {
- }
-
- private static Fast2DArray ComputeBayer(uint order)
- {
- uint dimension = (uint)(1 << (int)order);
- var matrix = new Fast2DArray((int)dimension);
- uint i = 0;
- for (int y = 0; y < dimension; y++)
- {
- for (int x = 0; x < dimension; x++)
- {
- matrix[y, x] = Bayer(i / dimension, i % dimension, order);
- i++;
- }
- }
-
- return matrix;
- }
-
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- private static uint Bayer(uint x, uint y, uint order)
- {
- uint res = 0;
- for (uint i = 0; i < order; ++i)
- {
- uint xOdd_XOR_yOdd = (x & 1) ^ (y & 1);
- uint xOdd = x & 1;
- res = ((res << 1 | xOdd_XOR_yOdd) << 1) | xOdd;
- x >>= 1;
- y >>= 1;
- }
-
- return res;
- }
- }
-}
\ No newline at end of file
diff --git a/src/ImageSharp/Dithering/Ordered/Bayer2x2Dither.cs b/src/ImageSharp/Dithering/Ordered/BayerDither2x2.cs
similarity index 63%
rename from src/ImageSharp/Dithering/Ordered/Bayer2x2Dither.cs
rename to src/ImageSharp/Dithering/Ordered/BayerDither2x2.cs
index e96a9c4d3..1d844c8a7 100644
--- a/src/ImageSharp/Dithering/Ordered/Bayer2x2Dither.cs
+++ b/src/ImageSharp/Dithering/Ordered/BayerDither2x2.cs
@@ -6,13 +6,13 @@ namespace SixLabors.ImageSharp.Dithering
///
/// Applies order dithering using the 2x2 Bayer dithering matrix.
///
- public sealed class Bayer2x2Dither : BayerDither
+ public sealed class BayerDither2x2 : OrderedDither
{
///
- /// Initializes a new instance of the class.
+ /// Initializes a new instance of the class.
///
- public Bayer2x2Dither()
- : base(1)
+ public BayerDither2x2()
+ : base(2)
{
}
}
diff --git a/src/ImageSharp/Dithering/Ordered/Bayer4x4Dither.cs b/src/ImageSharp/Dithering/Ordered/BayerDither4x4.cs
similarity index 63%
rename from src/ImageSharp/Dithering/Ordered/Bayer4x4Dither.cs
rename to src/ImageSharp/Dithering/Ordered/BayerDither4x4.cs
index ad72c164f..4e9f20beb 100644
--- a/src/ImageSharp/Dithering/Ordered/Bayer4x4Dither.cs
+++ b/src/ImageSharp/Dithering/Ordered/BayerDither4x4.cs
@@ -6,13 +6,13 @@ namespace SixLabors.ImageSharp.Dithering
///
/// Applies order dithering using the 4x4 Bayer dithering matrix.
///
- public sealed class Bayer4x4Dither : BayerDither
+ public sealed class BayerDither4x4 : OrderedDither
{
///
- /// Initializes a new instance of the class.
+ /// Initializes a new instance of the class.
///
- public Bayer4x4Dither()
- : base(2)
+ public BayerDither4x4()
+ : base(4)
{
}
}
diff --git a/src/ImageSharp/Dithering/Ordered/Bayer8x8Dither.cs b/src/ImageSharp/Dithering/Ordered/BayerDither8x8.cs
similarity index 63%
rename from src/ImageSharp/Dithering/Ordered/Bayer8x8Dither.cs
rename to src/ImageSharp/Dithering/Ordered/BayerDither8x8.cs
index 9077dc2cc..3ff179a06 100644
--- a/src/ImageSharp/Dithering/Ordered/Bayer8x8Dither.cs
+++ b/src/ImageSharp/Dithering/Ordered/BayerDither8x8.cs
@@ -6,13 +6,13 @@ namespace SixLabors.ImageSharp.Dithering
///
/// Applies order dithering using the 8x8 Bayer dithering matrix.
///
- public sealed class Bayer8x8Dither : BayerDither
+ public sealed class BayerDither8x8 : OrderedDither
{
///
- /// Initializes a new instance of the class.
+ /// Initializes a new instance of the class.
///
- public Bayer8x8Dither()
- : base(3)
+ public BayerDither8x8()
+ : base(8)
{
}
}
diff --git a/src/ImageSharp/Dithering/Ordered/KnownDitherers.cs b/src/ImageSharp/Dithering/Ordered/KnownDitherers.cs
new file mode 100644
index 000000000..e58cbad8a
--- /dev/null
+++ b/src/ImageSharp/Dithering/Ordered/KnownDitherers.cs
@@ -0,0 +1,31 @@
+// Copyright (c) Six Labors and contributors.
+// Licensed under the Apache License, Version 2.0.
+
+namespace SixLabors.ImageSharp.Dithering
+{
+ ///
+ /// Contains reusable static instances of known ordered dither matrices
+ ///
+ public class KnownDitherers
+ {
+ ///
+ /// Gets the order ditherer using the 2x2 Bayer dithering matrix
+ ///
+ public static IOrderedDither BayerDither2x2 { get; } = new BayerDither2x2();
+
+ ///
+ /// Gets the order ditherer using the 3x3 dithering matrix
+ ///
+ public static IOrderedDither OrderedDither3x3 { get; } = new OrderedDither3x3();
+
+ ///
+ /// Gets the order ditherer using the 4x4 Bayer dithering matrix
+ ///
+ public static IOrderedDither BayerDither4x4 { get; } = new BayerDither4x4();
+
+ ///
+ /// Gets the order ditherer using the 8x8 Bayer dithering matrix
+ ///
+ public static IOrderedDither BayerDither8x8 { get; } = new BayerDither8x8();
+ }
+}
\ No newline at end of file
diff --git a/src/ImageSharp/Dithering/Ordered/OrderedDither.cs b/src/ImageSharp/Dithering/Ordered/OrderedDither.cs
new file mode 100644
index 000000000..8f8210a8b
--- /dev/null
+++ b/src/ImageSharp/Dithering/Ordered/OrderedDither.cs
@@ -0,0 +1,50 @@
+// Copyright (c) Six Labors and contributors.
+// Licensed under the Apache License, Version 2.0.
+
+using SixLabors.ImageSharp.Memory;
+using SixLabors.ImageSharp.PixelFormats;
+
+namespace SixLabors.ImageSharp.Dithering
+{
+ ///
+ /// An ordered dithering matrix with equal sides of arbitrary length
+ ///
+ public class OrderedDither : IOrderedDither
+ {
+ private readonly Fast2DArray thresholdMatrix;
+ private readonly int modulusX;
+ private readonly int modulusY;
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The length of the matrix sides
+ public OrderedDither(uint length)
+ {
+ Fast2DArray ditherMatrix = OrderedDitherFactory.CreateDitherMatrix(length);
+ this.modulusX = ditherMatrix.Width;
+ this.modulusY = ditherMatrix.Height;
+
+ // Adjust the matrix range for 0-255
+ // It looks like it's actually possible to dither an image using it's own colors. We should investigate for V2
+ // https://stackoverflow.com/questions/12422407/monochrome-dithering-in-javascript-bayer-atkinson-floyd-steinberg
+ int multiplier = 256 / ditherMatrix.Count;
+ for (int y = 0; y < ditherMatrix.Height; y++)
+ {
+ for (int x = 0; x < ditherMatrix.Width; x++)
+ {
+ ditherMatrix[y, x] = (uint)((ditherMatrix[y, x] + 1) * multiplier) - 1;
+ }
+ }
+
+ this.thresholdMatrix = ditherMatrix;
+ }
+
+ ///
+ public void Dither(ImageFrame image, TPixel source, TPixel upper, TPixel lower, byte threshold, int x, int y)
+ where TPixel : struct, IPixel
+ {
+ image[x, y] = this.thresholdMatrix[y % this.modulusY, x % this.modulusX] >= threshold ? lower : upper;
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/ImageSharp/Dithering/Ordered/OrderedDither3x3.cs b/src/ImageSharp/Dithering/Ordered/OrderedDither3x3.cs
new file mode 100644
index 000000000..0436b35e9
--- /dev/null
+++ b/src/ImageSharp/Dithering/Ordered/OrderedDither3x3.cs
@@ -0,0 +1,19 @@
+// Copyright (c) Six Labors and contributors.
+// Licensed under the Apache License, Version 2.0.
+
+namespace SixLabors.ImageSharp.Dithering
+{
+ ///
+ /// Applies order dithering using the 3x3 dithering matrix.
+ ///
+ public sealed class OrderedDither3x3 : OrderedDither
+ {
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public OrderedDither3x3()
+ : base(3)
+ {
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/ImageSharp/Dithering/Ordered/OrderedDitherBase.cs b/src/ImageSharp/Dithering/Ordered/OrderedDitherBase.cs
deleted file mode 100644
index cf7a14239..000000000
--- a/src/ImageSharp/Dithering/Ordered/OrderedDitherBase.cs
+++ /dev/null
@@ -1,48 +0,0 @@
-// Copyright (c) Six Labors and contributors.
-// Licensed under the Apache License, Version 2.0.
-
-using SixLabors.ImageSharp.Memory;
-using SixLabors.ImageSharp.PixelFormats;
-
-namespace SixLabors.ImageSharp.Dithering
-{
- ///
- /// The base class for performing ordered dithering using a dither matrix.
- ///
- public abstract class OrderedDitherBase : IOrderedDither
- {
- private readonly Fast2DArray matrix;
- private readonly Fast2DArray thresholdMatrix;
- private readonly int modulusX;
- private readonly int modulusY;
-
- ///
- /// Initializes a new instance of the class.
- ///
- /// The thresholding matrix.
- internal OrderedDitherBase(Fast2DArray matrix)
- {
- this.matrix = matrix;
- this.modulusX = matrix.Width;
- this.modulusY = matrix.Height;
- this.thresholdMatrix = new Fast2DArray(matrix.Width, matrix.Height);
-
- // Adjust the matrix range for 0-255
- int multiplier = 256 / (this.modulusX * this.modulusY);
- for (int y = 0; y < matrix.Height; y++)
- {
- for (int x = 0; x < matrix.Width; x++)
- {
- this.thresholdMatrix[y, x] = (uint)((matrix[y, x] + 1) * multiplier) - 1;
- }
- }
- }
-
- ///
- public void Dither(ImageFrame image, TPixel source, TPixel upper, TPixel lower, byte threshold, int x, int y)
- where TPixel : struct, IPixel
- {
- image[x, y] = this.thresholdMatrix[y % this.modulusY, x % this.modulusX] >= threshold ? lower : upper;
- }
- }
-}
\ No newline at end of file
diff --git a/src/ImageSharp/Dithering/Ordered/OrderedDitherFactory.cs b/src/ImageSharp/Dithering/Ordered/OrderedDitherFactory.cs
new file mode 100644
index 000000000..fc9ac2551
--- /dev/null
+++ b/src/ImageSharp/Dithering/Ordered/OrderedDitherFactory.cs
@@ -0,0 +1,94 @@
+// Copyright (c) Six Labors and contributors.
+// Licensed under the Apache License, Version 2.0.
+
+using System.Runtime.CompilerServices;
+using SixLabors.ImageSharp.Memory;
+
+namespace SixLabors.ImageSharp.Dithering
+{
+ ///
+ /// A factory for creating ordered dither matrices.
+ ///
+ internal static class OrderedDitherFactory
+ {
+ ///
+ /// Creates an ordered dithering matrix with equal sides of arbitrary length.
+ ///
+ ///
+ /// The length of the matrix sides
+ /// The
+ public static Fast2DArray CreateDitherMatrix(uint length)
+ {
+ // Calculate the the logarithm of length to the base 2
+ uint exponent = 0;
+ uint bayerLength = 0;
+ do
+ {
+ exponent++;
+ bayerLength = (uint)(1 << (int)exponent);
+ }
+ while (length > bayerLength);
+
+ // Create our Bayer matrix that matches the given exponent and dimensions
+ var matrix = new Fast2DArray((int)length);
+ uint i = 0;
+ for (int y = 0; y < length; y++)
+ {
+ for (int x = 0; x < length; x++)
+ {
+ matrix[y, x] = Bayer(i / length, i % length, exponent);
+ i++;
+ }
+ }
+
+ // If the user requested a matrix with a non-power-of-2 length e.g. 3x3 and we used 4x4 algorithm,
+ // we need to convert the numbers so that the resulting range is un-gapped.
+ // We generated: We saved: We compress the number range:
+ // 0 8 2 10 0 8 2 0 5 2
+ // 12 4 14 6 12 4 14 7 4 8
+ // 3 11 1 9 3 11 1 3 6 1
+ // 15 7 13 5
+ uint maxValue = bayerLength * bayerLength;
+ uint missing = 0;
+ for (uint v = 0; v < maxValue; ++v)
+ {
+ bool found = false;
+ for (int y = 0; y < length; ++y)
+ {
+ for (int x = 0; x < length; x++)
+ {
+ if (matrix[y, x] == v)
+ {
+ matrix[y, x] -= missing;
+ found = true;
+ break;
+ }
+ }
+ }
+
+ if (!found)
+ {
+ ++missing;
+ }
+ }
+
+ return matrix;
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ private static uint Bayer(uint x, uint y, uint order)
+ {
+ uint result = 0;
+ for (uint i = 0; i < order; ++i)
+ {
+ uint xOdd_XOR_yOdd = (x & 1) ^ (y & 1);
+ uint xOdd = x & 1;
+ result = ((result << 1 | xOdd_XOR_yOdd) << 1) | xOdd;
+ x >>= 1;
+ y >>= 1;
+ }
+
+ return result;
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/ImageSharp/Dithering/error_diffusion.txt b/src/ImageSharp/Dithering/error_diffusion.txt
new file mode 100644
index 000000000..ea412f635
--- /dev/null
+++ b/src/ImageSharp/Dithering/error_diffusion.txt
@@ -0,0 +1,58 @@
+List of error diffusion schemes.
+
+Quantization error of *current* pixel is added to the pixels
+on the right and below according to the formulas below.
+This works nicely for most static pictures, but causes
+an avalanche of jittering artifacts if used in animation.
+
+Floyd-Steinberg:
+
+ * 7
+ 3 5 1 / 16
+
+Jarvis-Judice-Ninke:
+
+ * 7 5
+ 3 5 7 5 3
+ 1 3 5 3 1 / 48
+
+Stucki:
+
+ * 8 4
+ 2 4 8 4 2
+ 1 2 4 2 1 / 42
+
+Burkes:
+
+ * 8 4
+ 2 4 8 4 2 / 32
+
+
+Sierra3:
+
+ * 5 3
+ 2 4 5 4 2
+ 2 3 2 / 32
+
+Sierra2:
+
+ * 4 3
+ 1 2 3 2 1 / 16
+
+Sierra-2-4A:
+
+ * 2
+ 1 1 / 4
+
+Stevenson-Arce:
+
+ * . 32
+ 12 . 26 . 30 . 16
+ . 12 . 26 . 12 .
+ 5 . 12 . 12 . 5 / 200
+
+Atkinson:
+
+ * 1 1 / 8
+ 1 1 1
+ 1
diff --git a/src/ImageSharp/Memory/Fast2DArray{T}.cs b/src/ImageSharp/Memory/Fast2DArray{T}.cs
index e0384d208..38ccdd279 100644
--- a/src/ImageSharp/Memory/Fast2DArray{T}.cs
+++ b/src/ImageSharp/Memory/Fast2DArray{T}.cs
@@ -28,6 +28,11 @@ namespace SixLabors.ImageSharp.Memory
///
public int Height;
+ ///
+ /// Gets the number of items in the 2D array
+ ///
+ public int Count;
+
///
/// Initializes a new instance of the struct.
///
@@ -50,7 +55,8 @@ namespace SixLabors.ImageSharp.Memory
Guard.MustBeGreaterThan(width, 0, nameof(width));
Guard.MustBeGreaterThan(height, 0, nameof(height));
- this.Data = new T[this.Width * this.Height];
+ this.Count = width * height;
+ this.Data = new T[this.Count];
}
///
@@ -66,7 +72,8 @@ namespace SixLabors.ImageSharp.Memory
Guard.MustBeGreaterThan(this.Width, 0, nameof(this.Width));
Guard.MustBeGreaterThan(this.Height, 0, nameof(this.Height));
- this.Data = new T[this.Width * this.Height];
+ this.Count = this.Width * this.Height;
+ this.Data = new T[this.Count];
for (int y = 0; y < this.Height; y++)
{
diff --git a/src/ImageSharp/Processing/Processors/Dithering/ErrorDiffusionPaletteProcessor.cs b/src/ImageSharp/Processing/Processors/Dithering/ErrorDiffusionPaletteProcessor.cs
index f8ff475d1..1102a48e4 100644
--- a/src/ImageSharp/Processing/Processors/Dithering/ErrorDiffusionPaletteProcessor.cs
+++ b/src/ImageSharp/Processing/Processors/Dithering/ErrorDiffusionPaletteProcessor.cs
@@ -104,7 +104,7 @@ namespace SixLabors.ImageSharp.Processing.Processors
previousPixel = sourcePixel;
}
- TPixel transformedPixel = luminance >= threshold ? pair.First : pair.Second;
+ TPixel transformedPixel = luminance >= threshold ? pair.Second : pair.First;
this.Diffuser.Dither(source, sourcePixel, transformedPixel, x, y, startX, startY, endX, endY);
}
}
diff --git a/src/ImageSharp/Processing/Processors/Dithering/OrderedDitherPaletteProcessor.cs b/src/ImageSharp/Processing/Processors/Dithering/OrderedDitherPaletteProcessor.cs
index 49455928a..0a49f99cf 100644
--- a/src/ImageSharp/Processing/Processors/Dithering/OrderedDitherPaletteProcessor.cs
+++ b/src/ImageSharp/Processing/Processors/Dithering/OrderedDitherPaletteProcessor.cs
@@ -85,7 +85,7 @@ namespace SixLabors.ImageSharp.Processing.Processors
previousPixel = sourcePixel;
}
- this.Dither.Dither(source, sourcePixel, pair.First, pair.Second, luminance, x, y);
+ this.Dither.Dither(source, sourcePixel, pair.Second, pair.First, luminance, x, y);
}
}
}
diff --git a/src/ImageSharp/Processing/Processors/Dithering/PaletteDitherProcessorBase.cs b/src/ImageSharp/Processing/Processors/Dithering/PaletteDitherProcessorBase.cs
index c6b80293c..b3c564edb 100644
--- a/src/ImageSharp/Processing/Processors/Dithering/PaletteDitherProcessorBase.cs
+++ b/src/ImageSharp/Processing/Processors/Dithering/PaletteDitherProcessorBase.cs
@@ -50,8 +50,7 @@ namespace SixLabors.ImageSharp.Processing.Processors
for (int index = 0; index < colorPalette.Length; index++)
{
TPixel temp = colorPalette[index];
- var tempVector = temp.ToVector4();
- float distance = Vector4.Distance(vector, tempVector);
+ float distance = Vector4.Distance(vector, temp.ToVector4());
if (distance < leastDistance)
{
diff --git a/src/ImageSharp/Quantizers/QuantizerBase{TPixel}.cs b/src/ImageSharp/Quantizers/QuantizerBase{TPixel}.cs
index 20ba2e637..31e424060 100644
--- a/src/ImageSharp/Quantizers/QuantizerBase{TPixel}.cs
+++ b/src/ImageSharp/Quantizers/QuantizerBase{TPixel}.cs
@@ -43,7 +43,7 @@ namespace SixLabors.ImageSharp.Quantizers.Base
public bool Dither { get; set; } = true;
///
- public IErrorDiffuser DitherType { get; set; } = new FloydSteinbergDiffuser();
+ public IErrorDiffuser DitherType { get; set; } = KnownDiffusers.FloydSteinberg;
///
public virtual QuantizedImage Quantize(ImageFrame image, int maxColors)
diff --git a/tests/ImageSharp.Tests/Memory/Fast2DArrayTests.cs b/tests/ImageSharp.Tests/Memory/Fast2DArrayTests.cs
index 5cdbe638a..a5364db72 100644
--- a/tests/ImageSharp.Tests/Memory/Fast2DArrayTests.cs
+++ b/tests/ImageSharp.Tests/Memory/Fast2DArrayTests.cs
@@ -22,7 +22,7 @@ namespace SixLabors.ImageSharp.Tests.Memory
{
Assert.Throws(() =>
{
- Fast2DArray fast = new Fast2DArray(null);
+ var fast = new Fast2DArray(null);
});
}
@@ -31,7 +31,7 @@ namespace SixLabors.ImageSharp.Tests.Memory
{
Assert.Throws(() =>
{
- Fast2DArray fast = new Fast2DArray(0, 10);
+ var fast = new Fast2DArray(0, 10);
});
}
@@ -40,7 +40,7 @@ namespace SixLabors.ImageSharp.Tests.Memory
{
Assert.Throws(() =>
{
- Fast2DArray fast = new Fast2DArray(10, 0);
+ var fast = new Fast2DArray(10, 0);
});
}
@@ -49,14 +49,14 @@ namespace SixLabors.ImageSharp.Tests.Memory
{
Assert.Throws(() =>
{
- Fast2DArray fast = new Fast2DArray(new float[0, 0]);
+ var fast = new Fast2DArray(new float[0, 0]);
});
}
[Fact]
public void Fast2DArrayReturnsCorrectDimensions()
{
- Fast2DArray fast = new Fast2DArray(FloydSteinbergMatrix);
+ var fast = new Fast2DArray(FloydSteinbergMatrix);
Assert.True(fast.Width == FloydSteinbergMatrix.GetLength(1));
Assert.True(fast.Height == FloydSteinbergMatrix.GetLength(0));
}
@@ -78,7 +78,7 @@ namespace SixLabors.ImageSharp.Tests.Memory
[Fact]
public void Fast2DArrayGetSetReturnsCorrectResults()
{
- Fast2DArray fast = new Fast2DArray(4, 4);
+ var fast = new Fast2DArray(4, 4);
const float Val = 5F;
fast[3, 3] = Val;
diff --git a/tests/ImageSharp.Tests/Processing/Binarization/DitherTests.cs b/tests/ImageSharp.Tests/Processing/Binarization/DitherTests.cs
index ba5cb0cf3..f801b2031 100644
--- a/tests/ImageSharp.Tests/Processing/Binarization/DitherTests.cs
+++ b/tests/ImageSharp.Tests/Processing/Binarization/DitherTests.cs
@@ -4,7 +4,6 @@
using SixLabors.ImageSharp.Dithering;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Processing.Processors;
-using Moq;
using Xunit;
namespace SixLabors.ImageSharp.Tests.Processing.Binarization
@@ -16,9 +15,10 @@ namespace SixLabors.ImageSharp.Tests.Processing.Binarization
public DitherTest()
{
- this.orderedDither = new Mock().Object;
- this.errorDiffuser = new Mock().Object;
+ this.orderedDither = KnownDitherers.BayerDither4x4;
+ this.errorDiffuser = KnownDiffusers.FloydSteinberg;
}
+
[Fact]
public void Dither_CorrectProcessor()
{
diff --git a/tests/ImageSharp.Tests/Processing/Binarization/OrderedDitherFactoryTests.cs b/tests/ImageSharp.Tests/Processing/Binarization/OrderedDitherFactoryTests.cs
new file mode 100644
index 000000000..a0ddc2c7c
--- /dev/null
+++ b/tests/ImageSharp.Tests/Processing/Binarization/OrderedDitherFactoryTests.cs
@@ -0,0 +1,102 @@
+// Copyright (c) Six Labors and contributors.
+// Licensed under the Apache License, Version 2.0.
+
+using SixLabors.ImageSharp.Dithering;
+using SixLabors.ImageSharp.Memory;
+using Xunit;
+
+namespace SixLabors.ImageSharp.Tests.Processing.Binarization
+{
+ public class OrderedDitherFactoryTests
+ {
+ private static readonly Fast2DArray Expected2x2Matrix = new Fast2DArray(
+ new uint[2, 2]
+ {
+ { 0, 2 },
+ { 3, 1 }
+ });
+
+ private static readonly Fast2DArray Expected3x3Matrix = new Fast2DArray(
+ new uint[3, 3]
+ {
+ { 0, 5, 2 },
+ { 7, 4, 8 },
+ { 3, 6, 1 }
+ });
+
+ private static readonly Fast2DArray Expected4x4Matrix = new Fast2DArray(
+ new uint[4, 4]
+ {
+ { 0, 8, 2, 10 },
+ { 12, 4, 14, 6 },
+ { 3, 11, 1, 9 },
+ { 15, 7, 13, 5 }
+ });
+
+ private static readonly Fast2DArray Expected8x8Matrix = new Fast2DArray(
+ new uint[8, 8]
+ {
+ { 0, 32, 8, 40, 2, 34, 10, 42 },
+ { 48, 16, 56, 24, 50, 18, 58, 26 },
+ { 12, 44, 4, 36, 14, 46, 6, 38 },
+ { 60, 28, 52, 20, 62, 30, 54, 22 },
+ { 3, 35, 11, 43, 1, 33, 9, 41 },
+ { 51, 19, 59, 27, 49, 17, 57, 25 },
+ { 15, 47, 7, 39, 13, 45, 5, 37 },
+ { 63, 31, 55, 23, 61, 29, 53, 21 }
+ });
+
+
+ [Fact]
+ public void OrderedDitherFactoryCreatesCorrect2x2Matrix()
+ {
+ Fast2DArray actual = OrderedDitherFactory.CreateDitherMatrix(2);
+ for (int y = 0; y < actual.Height; y++)
+ {
+ for (int x = 0; x < actual.Width; x++)
+ {
+ Assert.Equal(Expected2x2Matrix[y, x], actual[y, x]);
+ }
+ }
+ }
+
+ [Fact]
+ public void OrderedDitherFactoryCreatesCorrect3x3Matrix()
+ {
+ Fast2DArray actual = OrderedDitherFactory.CreateDitherMatrix(3);
+ for (int y = 0; y < actual.Height; y++)
+ {
+ for (int x = 0; x < actual.Width; x++)
+ {
+ Assert.Equal(Expected3x3Matrix[y, x], actual[y, x]);
+ }
+ }
+ }
+
+ [Fact]
+ public void OrderedDitherFactoryCreatesCorrect4x4Matrix()
+ {
+ Fast2DArray actual = OrderedDitherFactory.CreateDitherMatrix(4);
+ for (int y = 0; y < actual.Height; y++)
+ {
+ for (int x = 0; x < actual.Width; x++)
+ {
+ Assert.Equal(Expected4x4Matrix[y, x], actual[y, x]);
+ }
+ }
+ }
+
+ [Fact]
+ public void OrderedDitherFactoryCreatesCorrect8x8Matrix()
+ {
+ Fast2DArray actual = OrderedDitherFactory.CreateDitherMatrix(8);
+ for (int y = 0; y < actual.Height; y++)
+ {
+ for (int x = 0; x < actual.Width; x++)
+ {
+ Assert.Equal(Expected8x8Matrix[y, x], actual[y, x]);
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/tests/ImageSharp.Tests/Processing/Processors/Binarization/DitherTests.cs b/tests/ImageSharp.Tests/Processing/Processors/Binarization/DitherTests.cs
index 3ddf9d0fe..6db1434c6 100644
--- a/tests/ImageSharp.Tests/Processing/Processors/Binarization/DitherTests.cs
+++ b/tests/ImageSharp.Tests/Processing/Processors/Binarization/DitherTests.cs
@@ -1,7 +1,7 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
-using SixLabors.ImageSharp.Dithering;
+using SixLabors.ImageSharp.Dithering;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison;
@@ -11,8 +11,6 @@ using Xunit;
namespace SixLabors.ImageSharp.Tests.Processing.Processors.Binarization
{
- using System.Linq;
-
public class DitherTests : FileTestBase
{
public static readonly string[] CommonTestImages =
@@ -22,27 +20,29 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Binarization
public static readonly TheoryData Ditherers = new TheoryData
{
- { "Bayer8x8", new Bayer8x8Dither() },
- { "Bayer4x4", new Bayer4x4Dither() },
- { "Bayer2x2", new Bayer2x2Dither() }
+ { "Bayer8x8", KnownDitherers.BayerDither8x8 },
+ { "Bayer4x4", KnownDitherers.BayerDither4x4 },
+ { "Ordered3x3", KnownDitherers.OrderedDither3x3 },
+ { "Bayer2x2", KnownDitherers.BayerDither2x2 }
};
public static readonly TheoryData ErrorDiffusers = new TheoryData
{
- { "Atkinson", new AtkinsonDiffuser() },
- { "Burks", new BurksDiffuser() },
- { "FloydSteinberg", new FloydSteinbergDiffuser() },
- { "JarvisJudiceNinke", new JarvisJudiceNinkeDiffuser() },
- { "Sierra2", new Sierra2Diffuser() },
- { "Sierra3", new Sierra3Diffuser() },
- { "SierraLite", new SierraLiteDiffuser() },
- { "Stucki", new StuckiDiffuser() },
+ { "Atkinson", KnownDiffusers.Atkinson },
+ { "Burks", KnownDiffusers.Burks },
+ { "FloydSteinberg", KnownDiffusers.FloydSteinberg },
+ { "JarvisJudiceNinke", KnownDiffusers.JarvisJudiceNinke },
+ { "Sierra2", KnownDiffusers.Sierra2 },
+ { "Sierra3", KnownDiffusers.Sierra3 },
+ { "SierraLite", KnownDiffusers.SierraLite },
+ { "StevensonArce", KnownDiffusers.StevensonArce },
+ { "Stucki", KnownDiffusers.Stucki },
};
- private static IOrderedDither DefaultDitherer => new Bayer4x4Dither();
+ private static IOrderedDither DefaultDitherer => KnownDitherers.BayerDither4x4;
- private static IErrorDiffuser DefaultErrorDiffuser => new AtkinsonDiffuser();
+ private static IErrorDiffuser DefaultErrorDiffuser => KnownDiffusers.Atkinson;
[Theory]
[WithFileCollection(nameof(CommonTestImages), nameof(Ditherers), DefaultPixelType)]
@@ -81,7 +81,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Binarization
image.DebugSave(provider);
}
}
-
+
[Theory]
[WithFile(TestImages.Png.Bike, CommonNonDefaultPixelTypes)]
public void DiffusionFilter_ShouldNotDependOnSinglePixelType(TestImageProvider provider)