diff --git a/FilterLib.Tests/FilterTests/DitherTests.cs b/FilterLib.Tests/FilterTests/DitherTests.cs index 5f9371b..847097c 100644 --- a/FilterLib.Tests/FilterTests/DitherTests.cs +++ b/FilterLib.Tests/FilterTests/DitherTests.cs @@ -25,6 +25,12 @@ internal static IEnumerable Data() yield return new TestCaseData("Grayscale_30_59_11.bmp", "BayerDither_2_5_bw.bmp", new BayerDitherFilter(2, 5), 1); yield return new TestCaseData("Grayscale_30_59_11.bmp", "BayerDither_4_4_bw.bmp", new BayerDitherFilter(4, 4), 1); + yield return new TestCaseData("_input.bmp", "ClusterDotDither_2.bmp", new ClusterDotDitherFilter(2), 1); + yield return new TestCaseData("_input.bmp", "ClusterDotDither_4.bmp", new ClusterDotDitherFilter(4), 1); + yield return new TestCaseData("_input.bmp", "_input.bmp", new ClusterDotDitherFilter(256), 1); + yield return new TestCaseData("Grayscale_30_59_11.bmp", "ClusterDotDither_2_bw.bmp", new ClusterDotDitherFilter(2), 1); + yield return new TestCaseData("Grayscale_30_59_11.bmp", "ClusterDotDither_4_bw.bmp", new ClusterDotDitherFilter(4), 1); + yield return new TestCaseData("_input.bmp", "AtkinsonDither_2.bmp", new AtkinsonDitherFilter(2), 1); yield return new TestCaseData("_input.bmp", "AtkinsonDither_4.bmp", new AtkinsonDitherFilter(4), 1); yield return new TestCaseData("_input.bmp", "_input.bmp", new AtkinsonDitherFilter(256), 1); diff --git a/FilterLib.Tests/FilterTests/ToStringTests.cs b/FilterLib.Tests/FilterTests/ToStringTests.cs index 1156b4a..070200d 100644 --- a/FilterLib.Tests/FilterTests/ToStringTests.cs +++ b/FilterLib.Tests/FilterTests/ToStringTests.cs @@ -153,6 +153,9 @@ internal static IEnumerable Dither() yield return new TestCaseData( new BurkesDitherFilter(123), "BurkesDitherFilter(Levels: 123)"); + yield return new TestCaseData( + new ClusterDotDitherFilter(123), + "ClusterDotDitherFilter(Levels: 123)"); yield return new TestCaseData( new FanDitherFilter(123), "FanDitherFilter(Levels: 123)"); diff --git a/FilterLib.Tests/ReflectiveApiTests/ApiTests.cs b/FilterLib.Tests/ReflectiveApiTests/ApiTests.cs index 6c1b6e7..fc34f4b 100644 --- a/FilterLib.Tests/ReflectiveApiTests/ApiTests.cs +++ b/FilterLib.Tests/ReflectiveApiTests/ApiTests.cs @@ -7,7 +7,7 @@ public class ApiTests { [Test] public void TestListFilters() => - Assert.That(ReflectiveApi.GetFilterTypes().Count(), Is.EqualTo(73)); + Assert.That(ReflectiveApi.GetFilterTypes().Count(), Is.EqualTo(74)); [Test] public void TestListBlends() => diff --git a/FilterLib.Tests/ReflectiveApiTests/DitherApiTests.cs b/FilterLib.Tests/ReflectiveApiTests/DitherApiTests.cs index 34bdc89..a9c066d 100644 --- a/FilterLib.Tests/ReflectiveApiTests/DitherApiTests.cs +++ b/FilterLib.Tests/ReflectiveApiTests/DitherApiTests.cs @@ -20,6 +20,18 @@ public void TestBayerDither() [Test] public void TestBayerDitherParCnt() => Assert.That(Common.ParamCount(typeof(BayerDitherFilter)), Is.EqualTo(2)); + [Test] + public void TestClusterDotDither() + { + IFilter f = ReflectiveApi.ConstructFilterByName("ClusterDotDither"); + Assert.That(f, Is.InstanceOf()); + ReflectiveApi.SetFilterPropertyByName(f, "Levels", "50"); + ClusterDotDitherFilter ff = f as ClusterDotDitherFilter; + Assert.That(ff.Levels, Is.EqualTo(50)); + } + + [Test] + public void TestClusterDotDitherParCnt() => Assert.That(Common.ParamCount(typeof(ClusterDotDitherFilter)), Is.EqualTo(1)); [Test] public void TestAtkinsonDither() diff --git a/FilterLib.Tests/TestImages/ClusterDotDither_2.bmp b/FilterLib.Tests/TestImages/ClusterDotDither_2.bmp new file mode 100644 index 0000000..55d5fff Binary files /dev/null and b/FilterLib.Tests/TestImages/ClusterDotDither_2.bmp differ diff --git a/FilterLib.Tests/TestImages/ClusterDotDither_2_bw.bmp b/FilterLib.Tests/TestImages/ClusterDotDither_2_bw.bmp new file mode 100644 index 0000000..4ac0b85 Binary files /dev/null and b/FilterLib.Tests/TestImages/ClusterDotDither_2_bw.bmp differ diff --git a/FilterLib.Tests/TestImages/ClusterDotDither_4.bmp b/FilterLib.Tests/TestImages/ClusterDotDither_4.bmp new file mode 100644 index 0000000..2fef77d Binary files /dev/null and b/FilterLib.Tests/TestImages/ClusterDotDither_4.bmp differ diff --git a/FilterLib.Tests/TestImages/ClusterDotDither_4_bw.bmp b/FilterLib.Tests/TestImages/ClusterDotDither_4_bw.bmp new file mode 100644 index 0000000..dfe5d35 Binary files /dev/null and b/FilterLib.Tests/TestImages/ClusterDotDither_4_bw.bmp differ diff --git a/FilterLib/Filters/Dither/ClusterDotDitherFilter.cs b/FilterLib/Filters/Dither/ClusterDotDitherFilter.cs new file mode 100644 index 0000000..6a8c3df --- /dev/null +++ b/FilterLib/Filters/Dither/ClusterDotDitherFilter.cs @@ -0,0 +1,15 @@ +namespace FilterLib.Filters.Dither +{ + /// + /// Cluster dot dither is an ordered dither with a specifically defined matrix + /// + [Filter] + public class ClusterDotDitherFilter : OrderedDitherFilterBase + { + /// + /// Constructor + /// + /// Number of levels [2:256] + public ClusterDotDitherFilter(int levels = 256) : base(levels, new ClusterDotDitherMatrix()) { } + } +} diff --git a/FilterLib/Filters/Dither/ClusterDotDitherMatrix.cs b/FilterLib/Filters/Dither/ClusterDotDitherMatrix.cs new file mode 100644 index 0000000..36e7a57 --- /dev/null +++ b/FilterLib/Filters/Dither/ClusterDotDitherMatrix.cs @@ -0,0 +1,40 @@ +namespace FilterLib.Filters.Dither +{ + /// + /// Cluster dot dither matrix + /// + public sealed class ClusterDotDitherMatrix : IOrderedDitherMatrix + { + private readonly float[,] matrix; + + /// + /// Constructor + /// + public ClusterDotDitherMatrix() + { + matrix = new float[8, 8] + { + { 24, 8, 22, 30, 34, 44, 42, 32 }, + { 10, 0, 6, 20, 46, 58, 56, 40 }, + { 12, 2, 4, 18, 48, 60, 62, 54 }, + { 26, 14, 16, 28, 36, 50, 52, 38 }, + { 35, 45, 43, 33, 25, 9, 23, 31 }, + { 47, 59, 57, 41, 11, 1, 7, 21 }, + { 49, 61, 63, 55, 13, 3, 5, 19 }, + { 37, 51, 53, 39, 27, 15, 17, 29 }, + }; + for (int x = 0; x < matrix.GetLength(0); x++) + for (int y = 0; y < matrix.GetLength(1); y++) + matrix[x, y] /= 64f; + } + + /// + public float this[int x, int y] => matrix[x, y]; + + /// + public int Width { get { return matrix.GetLength(0); } } + + /// + public int Height { get { return matrix.GetLength(1); } } + } +}