Browse Source

Final refactorings + bug fixes

af/merge-core
James Jackson-South 8 years ago
parent
commit
0688343241
  1. 2
      src/ImageSharp/Dithering/ErrorDiffusion/ErrorDiffuserBase.cs
  2. 56
      src/ImageSharp/Dithering/ErrorDiffusion/KnownDiffusers.cs
  3. 34
      src/ImageSharp/Dithering/ErrorDiffusion/StevensonArceDiffuser.cs
  4. 60
      src/ImageSharp/Dithering/Ordered/BayerDither.cs
  5. 8
      src/ImageSharp/Dithering/Ordered/BayerDither2x2.cs
  6. 8
      src/ImageSharp/Dithering/Ordered/BayerDither4x4.cs
  7. 8
      src/ImageSharp/Dithering/Ordered/BayerDither8x8.cs
  8. 31
      src/ImageSharp/Dithering/Ordered/KnownDitherers.cs
  9. 50
      src/ImageSharp/Dithering/Ordered/OrderedDither.cs
  10. 19
      src/ImageSharp/Dithering/Ordered/OrderedDither3x3.cs
  11. 48
      src/ImageSharp/Dithering/Ordered/OrderedDitherBase.cs
  12. 94
      src/ImageSharp/Dithering/Ordered/OrderedDitherFactory.cs
  13. 58
      src/ImageSharp/Dithering/error_diffusion.txt
  14. 11
      src/ImageSharp/Memory/Fast2DArray{T}.cs
  15. 2
      src/ImageSharp/Processing/Processors/Dithering/ErrorDiffusionPaletteProcessor.cs
  16. 2
      src/ImageSharp/Processing/Processors/Dithering/OrderedDitherPaletteProcessor.cs
  17. 3
      src/ImageSharp/Processing/Processors/Dithering/PaletteDitherProcessorBase.cs
  18. 2
      src/ImageSharp/Quantizers/QuantizerBase{TPixel}.cs
  19. 12
      tests/ImageSharp.Tests/Memory/Fast2DArrayTests.cs
  20. 6
      tests/ImageSharp.Tests/Processing/Binarization/DitherTests.cs
  21. 102
      tests/ImageSharp.Tests/Processing/Binarization/OrderedDitherFactoryTests.cs
  22. 34
      tests/ImageSharp.Tests/Processing/Processors/Binarization/DitherTests.cs

2
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);
}
}

56
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
{
/// <summary>
/// Contains reusable static instances of known error diffusion algorithms
/// </summary>
public static class KnownDiffusers
{
/// <summary>
/// Gets the error diffuser that implements the Atkinson algorithm.
/// </summary>
public static IErrorDiffuser Atkinson { get; } = new AtkinsonDiffuser();
/// <summary>
/// Gets the error diffuser that implements the Burks algorithm.
/// </summary>
public static IErrorDiffuser Burks { get; } = new BurksDiffuser();
/// <summary>
/// Gets the error diffuser that implements the Floyd-Steinberg algorithm.
/// </summary>
public static IErrorDiffuser FloydSteinberg { get; } = new FloydSteinbergDiffuser();
/// <summary>
/// Gets the error diffuser that implements the Jarvis-Judice-Ninke algorithm.
/// </summary>
public static IErrorDiffuser JarvisJudiceNinke { get; } = new JarvisJudiceNinkeDiffuser();
/// <summary>
/// Gets the error diffuser that implements the Sierra-2 algorithm.
/// </summary>
public static IErrorDiffuser Sierra2 { get; } = new Sierra2Diffuser();
/// <summary>
/// Gets the error diffuser that implements the Sierra-3 algorithm.
/// </summary>
public static IErrorDiffuser Sierra3 { get; } = new Sierra3Diffuser();
/// <summary>
/// Gets the error diffuser that implements the Sierra-Lite algorithm.
/// </summary>
public static IErrorDiffuser SierraLite { get; } = new SierraLiteDiffuser();
/// <summary>
/// Gets the error diffuser that implements the Stevenson-Arce algorithm.
/// </summary>
public static IErrorDiffuser StevensonArce { get; } = new StevensonArceDiffuser();
/// <summary>
/// Gets the error diffuser that implements the Stucki algorithm.
/// </summary>
public static IErrorDiffuser Stucki { get; } = new StuckiDiffuser();
}
}

34
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
{
/// <summary>
/// Applies error diffusion based dithering using the Stevenson-Arce image dithering algorithm.
/// </summary>
public sealed class StevensonArceDiffuser : ErrorDiffuserBase
{
/// <summary>
/// The diffusion matrix
/// </summary>
private static readonly Fast2DArray<float> 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 }
};
/// <summary>
/// Initializes a new instance of the <see cref="StevensonArceDiffuser"/> class.
/// </summary>
public StevensonArceDiffuser()
: base(StevensonArceMatrix, 200)
{
}
}
}

60
src/ImageSharp/Dithering/Ordered/BayerDither.cs

@ -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
{
/// <summary>
/// Applies order dithering using a Bayer dithering matrix of arbitrary length.
/// <see href="https://en.wikipedia.org/wiki/Ordered_dithering"/>
/// </summary>
public class BayerDither : OrderedDitherBase
{
/// <summary>
/// Initializes a new instance of the <see cref="BayerDither"/> class.
/// </summary>
/// <param name="exponent">
/// 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
/// </param>
public BayerDither(uint exponent)
: base(ComputeBayer(exponent))
{
}
private static Fast2DArray<uint> ComputeBayer(uint order)
{
uint dimension = (uint)(1 << (int)order);
var matrix = new Fast2DArray<uint>((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;
}
}
}

8
src/ImageSharp/Dithering/Ordered/Bayer2x2Dither.cs → src/ImageSharp/Dithering/Ordered/BayerDither2x2.cs

@ -6,13 +6,13 @@ namespace SixLabors.ImageSharp.Dithering
/// <summary>
/// Applies order dithering using the 2x2 Bayer dithering matrix.
/// </summary>
public sealed class Bayer2x2Dither : BayerDither
public sealed class BayerDither2x2 : OrderedDither
{
/// <summary>
/// Initializes a new instance of the <see cref="Bayer2x2Dither"/> class.
/// Initializes a new instance of the <see cref="BayerDither2x2"/> class.
/// </summary>
public Bayer2x2Dither()
: base(1)
public BayerDither2x2()
: base(2)
{
}
}

8
src/ImageSharp/Dithering/Ordered/Bayer4x4Dither.cs → src/ImageSharp/Dithering/Ordered/BayerDither4x4.cs

@ -6,13 +6,13 @@ namespace SixLabors.ImageSharp.Dithering
/// <summary>
/// Applies order dithering using the 4x4 Bayer dithering matrix.
/// </summary>
public sealed class Bayer4x4Dither : BayerDither
public sealed class BayerDither4x4 : OrderedDither
{
/// <summary>
/// Initializes a new instance of the <see cref="Bayer4x4Dither"/> class.
/// Initializes a new instance of the <see cref="BayerDither4x4"/> class.
/// </summary>
public Bayer4x4Dither()
: base(2)
public BayerDither4x4()
: base(4)
{
}
}

8
src/ImageSharp/Dithering/Ordered/Bayer8x8Dither.cs → src/ImageSharp/Dithering/Ordered/BayerDither8x8.cs

@ -6,13 +6,13 @@ namespace SixLabors.ImageSharp.Dithering
/// <summary>
/// Applies order dithering using the 8x8 Bayer dithering matrix.
/// </summary>
public sealed class Bayer8x8Dither : BayerDither
public sealed class BayerDither8x8 : OrderedDither
{
/// <summary>
/// Initializes a new instance of the <see cref="Bayer8x8Dither"/> class.
/// Initializes a new instance of the <see cref="BayerDither8x8"/> class.
/// </summary>
public Bayer8x8Dither()
: base(3)
public BayerDither8x8()
: base(8)
{
}
}

31
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
{
/// <summary>
/// Contains reusable static instances of known ordered dither matrices
/// </summary>
public class KnownDitherers
{
/// <summary>
/// Gets the order ditherer using the 2x2 Bayer dithering matrix
/// </summary>
public static IOrderedDither BayerDither2x2 { get; } = new BayerDither2x2();
/// <summary>
/// Gets the order ditherer using the 3x3 dithering matrix
/// </summary>
public static IOrderedDither OrderedDither3x3 { get; } = new OrderedDither3x3();
/// <summary>
/// Gets the order ditherer using the 4x4 Bayer dithering matrix
/// </summary>
public static IOrderedDither BayerDither4x4 { get; } = new BayerDither4x4();
/// <summary>
/// Gets the order ditherer using the 8x8 Bayer dithering matrix
/// </summary>
public static IOrderedDither BayerDither8x8 { get; } = new BayerDither8x8();
}
}

50
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
{
/// <summary>
/// An ordered dithering matrix with equal sides of arbitrary length
/// </summary>
public class OrderedDither : IOrderedDither
{
private readonly Fast2DArray<uint> thresholdMatrix;
private readonly int modulusX;
private readonly int modulusY;
/// <summary>
/// Initializes a new instance of the <see cref="OrderedDither"/> class.
/// </summary>
/// <param name="length">The length of the matrix sides</param>
public OrderedDither(uint length)
{
Fast2DArray<uint> 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;
}
/// <inheritdoc />
public void Dither<TPixel>(ImageFrame<TPixel> image, TPixel source, TPixel upper, TPixel lower, byte threshold, int x, int y)
where TPixel : struct, IPixel<TPixel>
{
image[x, y] = this.thresholdMatrix[y % this.modulusY, x % this.modulusX] >= threshold ? lower : upper;
}
}
}

19
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
{
/// <summary>
/// Applies order dithering using the 3x3 dithering matrix.
/// </summary>
public sealed class OrderedDither3x3 : OrderedDither
{
/// <summary>
/// Initializes a new instance of the <see cref="OrderedDither3x3"/> class.
/// </summary>
public OrderedDither3x3()
: base(3)
{
}
}
}

48
src/ImageSharp/Dithering/Ordered/OrderedDitherBase.cs

@ -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
{
/// <summary>
/// The base class for performing ordered dithering using a dither matrix.
/// </summary>
public abstract class OrderedDitherBase : IOrderedDither
{
private readonly Fast2DArray<uint> matrix;
private readonly Fast2DArray<uint> thresholdMatrix;
private readonly int modulusX;
private readonly int modulusY;
/// <summary>
/// Initializes a new instance of the <see cref="OrderedDitherBase"/> class.
/// </summary>
/// <param name="matrix">The thresholding matrix. </param>
internal OrderedDitherBase(Fast2DArray<uint> matrix)
{
this.matrix = matrix;
this.modulusX = matrix.Width;
this.modulusY = matrix.Height;
this.thresholdMatrix = new Fast2DArray<uint>(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;
}
}
}
/// <inheritdoc />
public void Dither<TPixel>(ImageFrame<TPixel> image, TPixel source, TPixel upper, TPixel lower, byte threshold, int x, int y)
where TPixel : struct, IPixel<TPixel>
{
image[x, y] = this.thresholdMatrix[y % this.modulusY, x % this.modulusX] >= threshold ? lower : upper;
}
}
}

94
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
{
/// <summary>
/// A factory for creating ordered dither matrices.
/// </summary>
internal static class OrderedDitherFactory
{
/// <summary>
/// Creates an ordered dithering matrix with equal sides of arbitrary length.
/// <see href="https://en.wikipedia.org/wiki/Ordered_dithering"/>
/// </summary>
/// <param name="length">The length of the matrix sides</param>
/// <returns>The <see cref="Fast2DArray{T}"/></returns>
public static Fast2DArray<uint> 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<uint>((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;
}
}
}

58
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

11
src/ImageSharp/Memory/Fast2DArray{T}.cs

@ -28,6 +28,11 @@ namespace SixLabors.ImageSharp.Memory
/// </summary>
public int Height;
/// <summary>
/// Gets the number of items in the 2D array
/// </summary>
public int Count;
/// <summary>
/// Initializes a new instance of the <see cref="Fast2DArray{T}" /> struct.
/// </summary>
@ -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];
}
/// <summary>
@ -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++)
{

2
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);
}
}

2
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);
}
}
}

3
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)
{

2
src/ImageSharp/Quantizers/QuantizerBase{TPixel}.cs

@ -43,7 +43,7 @@ namespace SixLabors.ImageSharp.Quantizers.Base
public bool Dither { get; set; } = true;
/// <inheritdoc />
public IErrorDiffuser DitherType { get; set; } = new FloydSteinbergDiffuser();
public IErrorDiffuser DitherType { get; set; } = KnownDiffusers.FloydSteinberg;
/// <inheritdoc/>
public virtual QuantizedImage<TPixel> Quantize(ImageFrame<TPixel> image, int maxColors)

12
tests/ImageSharp.Tests/Memory/Fast2DArrayTests.cs

@ -22,7 +22,7 @@ namespace SixLabors.ImageSharp.Tests.Memory
{
Assert.Throws<ArgumentNullException>(() =>
{
Fast2DArray<float> fast = new Fast2DArray<float>(null);
var fast = new Fast2DArray<float>(null);
});
}
@ -31,7 +31,7 @@ namespace SixLabors.ImageSharp.Tests.Memory
{
Assert.Throws<ArgumentOutOfRangeException>(() =>
{
Fast2DArray<float> fast = new Fast2DArray<float>(0, 10);
var fast = new Fast2DArray<float>(0, 10);
});
}
@ -40,7 +40,7 @@ namespace SixLabors.ImageSharp.Tests.Memory
{
Assert.Throws<ArgumentOutOfRangeException>(() =>
{
Fast2DArray<float> fast = new Fast2DArray<float>(10, 0);
var fast = new Fast2DArray<float>(10, 0);
});
}
@ -49,14 +49,14 @@ namespace SixLabors.ImageSharp.Tests.Memory
{
Assert.Throws<ArgumentOutOfRangeException>(() =>
{
Fast2DArray<float> fast = new Fast2DArray<float>(new float[0, 0]);
var fast = new Fast2DArray<float>(new float[0, 0]);
});
}
[Fact]
public void Fast2DArrayReturnsCorrectDimensions()
{
Fast2DArray<float> fast = new Fast2DArray<float>(FloydSteinbergMatrix);
var fast = new Fast2DArray<float>(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<float> fast = new Fast2DArray<float>(4, 4);
var fast = new Fast2DArray<float>(4, 4);
const float Val = 5F;
fast[3, 3] = Val;

6
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<IOrderedDither>().Object;
this.errorDiffuser = new Mock<IErrorDiffuser>().Object;
this.orderedDither = KnownDitherers.BayerDither4x4;
this.errorDiffuser = KnownDiffusers.FloydSteinberg;
}
[Fact]
public void Dither_CorrectProcessor()
{

102
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<uint> Expected2x2Matrix = new Fast2DArray<uint>(
new uint[2, 2]
{
{ 0, 2 },
{ 3, 1 }
});
private static readonly Fast2DArray<uint> Expected3x3Matrix = new Fast2DArray<uint>(
new uint[3, 3]
{
{ 0, 5, 2 },
{ 7, 4, 8 },
{ 3, 6, 1 }
});
private static readonly Fast2DArray<uint> Expected4x4Matrix = new Fast2DArray<uint>(
new uint[4, 4]
{
{ 0, 8, 2, 10 },
{ 12, 4, 14, 6 },
{ 3, 11, 1, 9 },
{ 15, 7, 13, 5 }
});
private static readonly Fast2DArray<uint> Expected8x8Matrix = new Fast2DArray<uint>(
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<uint> 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<uint> 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<uint> 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<uint> 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]);
}
}
}
}
}

34
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<string, IOrderedDither> Ditherers = new TheoryData<string, IOrderedDither>
{
{ "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<string, IErrorDiffuser> ErrorDiffusers = new TheoryData<string, IErrorDiffuser>
{
{ "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<TPixel>(TestImageProvider<TPixel> provider)

Loading…
Cancel
Save