diff --git a/HPPH.Generators/Features/Colors.cs b/HPPH.Generators/Features/Colors.cs index a091c78..9a6da84 100644 --- a/HPPH.Generators/Features/Colors.cs +++ b/HPPH.Generators/Features/Colors.cs @@ -74,6 +74,9 @@ internal class Colors : IGeneratorFeature #region Methods + /// + public bool Equals(IColor? other) => (other != null) && (R == other.R) && (G == other.G) && (B == other.B) && (A == other.A); + /// public override string ToString() => $"[A: {A}, R: {R}, G: {G}, B: {B}]"; @@ -137,6 +140,9 @@ internal class Colors : IGeneratorFeature #region Methods + /// + public bool Equals(IColor? other) => (other != null) && (R == other.R) && (G == other.G) && (B == other.B) && (A == other.A); + /// public override string ToString() => $"[A: {A}, R: {R}, G: {G}, B: {B}]"; diff --git a/HPPH.Reference/PixelHelper.Quantize.cs b/HPPH.Reference/PixelHelper.Quantize.cs index 8abeff2..c6e92ae 100644 --- a/HPPH.Reference/PixelHelper.Quantize.cs +++ b/HPPH.Reference/PixelHelper.Quantize.cs @@ -6,8 +6,9 @@ public static partial class ReferencePixelHelper { #region Methods - public static IColor[] CreateColorPalette(IImage image, int paletteSize) - => CreateColorPalette(image.ToArray(), paletteSize); + public static T[] CreateColorPalette(Image image, int paletteSize) + where T : unmanaged, IColor + => CreateColorPalette(image.ToArray(), paletteSize); public static T[] CreateColorPalette(RefImage image, int paletteSize) where T : unmanaged, IColor @@ -15,17 +16,20 @@ public static partial class ReferencePixelHelper public static T[] CreateColorPalette(Span colors, int paletteSize) where T : unmanaged, IColor => - CreateColorPalette(colors.ToArray().Cast().ToArray(), paletteSize).Select(x => T.Create(x.R, x.G, x.B, x.A)).Cast().ToArray(); + CreateColorPalette(colors.ToArray().Cast().ToArray(), paletteSize).Select(x => T.Create(x.R, x.G, x.B, x.A)).Cast().ToArray(); - private static IColor[] CreateColorPalette(IColor[] colors, int paletteSize) + private static IColor[] CreateColorPalette(IColor[] colors, int paletteSize) + where T : struct, IColor { + if (paletteSize == 0) return []; + int splits = BitOperations.Log2((uint)paletteSize); - List cubes = [new ColorCube(colors)]; + HashSet cubes = [new ColorCube(colors)]; for (int i = 0; i < splits; i++) { - List currentCubes = [.. cubes]; + HashSet currentCubes = [.. cubes]; foreach (ColorCube currentCube in currentCubes) { currentCube.Split(out ColorCube a, out ColorCube b); @@ -35,7 +39,7 @@ public static partial class ReferencePixelHelper } } - return cubes.Select(c => c.GetAverageColor()).ToArray(); + return cubes.Select(c => c.GetAverageColor()).ToArray(); } #endregion @@ -53,6 +57,12 @@ internal class ColorCube internal ColorCube(IList colors) { + if (colors.Count == 0) + { + _colors = []; + return; + } + int redRange = colors.Max(c => c.R) - colors.Min(c => c.R); int greenRange = colors.Max(c => c.G) - colors.Min(c => c.G); int blueRange = colors.Max(c => c.B) - colors.Min(c => c.B); @@ -77,17 +87,20 @@ internal class ColorCube b = new ColorCube(_colors.GetRange(median, _colors.Count - median)); } - internal IColor GetAverageColor() + internal IColor GetAverageColor() + where T : struct, IColor { + if (_colors.Count == 0) return T.Create(0, 0, 0, 0); + int r = _colors.Sum(x => x.R); int g = _colors.Sum(x => x.G); int b = _colors.Sum(x => x.B); int a = _colors.Sum(x => x.A); - return new ColorRGBA((byte)(r / _colors.Count), - (byte)(g / _colors.Count), - (byte)(b / _colors.Count), - (byte)(a / _colors.Count)); + return T.Create((byte)(r / _colors.Count), + (byte)(g / _colors.Count), + (byte)(b / _colors.Count), + (byte)(a / _colors.Count)); } #endregion diff --git a/HPPH.Test/Colors/MinMaxStructTests.cs b/HPPH.Test/Colors/MinMaxStructTests.cs index 81cdfe3..f3b0c41 100644 --- a/HPPH.Test/Colors/MinMaxStructTests.cs +++ b/HPPH.Test/Colors/MinMaxStructTests.cs @@ -1,6 +1,4 @@ -using System.Runtime.InteropServices; - -namespace HPPH.Test.Colors; +namespace HPPH.Test.Colors; [TestClass] public class MinMaxStructTests diff --git a/HPPH.Test/Image/ImageTest.cs b/HPPH.Test/Image/ImageTest.cs index 59d7ac9..b2ee5c1 100644 --- a/HPPH.Test/Image/ImageTest.cs +++ b/HPPH.Test/Image/ImageTest.cs @@ -15,7 +15,7 @@ public class ImageTest #region Methods [TestMethod] - public void TestImageCreation() + public void ImageCreation() { IImage image = TestDataHelper.CreateTestImage(TEST_WIDTH, TEST_HEIGHT); @@ -28,7 +28,7 @@ public class ImageTest } [TestMethod] - public void TestImageInnerFull() + public void ImageInnerFull() { IImage image = TestDataHelper.CreateTestImage(TEST_WIDTH, TEST_HEIGHT); image = image[0, 0, image.Width, image.Height]; @@ -39,7 +39,7 @@ public class ImageTest } [TestMethod] - public void TestImageEnumerator() + public void ImageEnumerator() { IImage image = TestDataHelper.CreateTestImage(TEST_WIDTH, TEST_HEIGHT); @@ -56,7 +56,7 @@ public class ImageTest } [TestMethod] - public void TestImageInnerPartial() + public void ImageInnerPartial() { IImage image = TestDataHelper.CreateTestImage(TEST_WIDTH, TEST_HEIGHT); image = image[163, 280, 720, 13]; @@ -70,7 +70,7 @@ public class ImageTest } [TestMethod] - public void TestImageInnerInnerPartial() + public void ImageInnerInnerPartial() { IImage image = TestDataHelper.CreateTestImage(TEST_WIDTH, TEST_HEIGHT); image = image[163, 280, 720, 13]; @@ -85,7 +85,7 @@ public class ImageTest } [TestMethod] - public void TestImageRowIndexer() + public void ImageRowIndexer() { IImage image = TestDataHelper.CreateTestImage(TEST_WIDTH, TEST_HEIGHT); @@ -101,7 +101,7 @@ public class ImageTest } [TestMethod] - public void TestImageRowEnumerator() + public void ImageRowEnumerator() { IImage image = TestDataHelper.CreateTestImage(TEST_WIDTH, TEST_HEIGHT); @@ -116,7 +116,7 @@ public class ImageTest } [TestMethod] - public void TestImageColumnIndexer() + public void ImageColumnIndexer() { IImage image = TestDataHelper.CreateTestImage(TEST_WIDTH, TEST_HEIGHT); @@ -132,7 +132,7 @@ public class ImageTest } [TestMethod] - public void TestImageColumnEnumerator() + public void ImageColumnEnumerator() { IImage image = TestDataHelper.CreateTestImage(TEST_WIDTH, TEST_HEIGHT); @@ -147,7 +147,7 @@ public class ImageTest } [TestMethod] - public void TestAsRefImage() + public void AsRefImage() { IImage image = TestDataHelper.CreateTestImage(TEST_WIDTH, TEST_HEIGHT); image = image[163, 280, 720, 13]; @@ -417,6 +417,16 @@ public class ImageTest y++; } + + y = 0; + foreach (IImageRow row in (IEnumerable)image.Rows) + { + int x = 0; + foreach (IColor color in (IEnumerable)row) + Assert.AreEqual(TestDataHelper.GetColorFromLocation(x++, y), color); + + y++; + } } [TestMethod] @@ -509,5 +519,53 @@ public class ImageTest } } + [TestMethod] + public void InterfaceRowsCopyTo() + { + IImage image = TestDataHelper.CreateTestImage(TEST_WIDTH, TEST_HEIGHT); + + int y = 0; + foreach (IImageRow row in image.Rows) + { + IColor[] colors = new IColor[TEST_WIDTH]; + byte[] bytes = new byte[TEST_WIDTH * 4]; + row.CopyTo(colors); + row.CopyTo(bytes); + + int x = 0; + foreach (IColor color in colors) + Assert.AreEqual(TestDataHelper.GetColorFromLocation(x++, y), color); + + for (x = 0; x < TEST_WIDTH; x++) + { + IColor reference = TestDataHelper.GetColorFromLocation(x, y); + + Assert.AreEqual(reference.A, bytes[(x * 4) + 0]); + Assert.AreEqual(reference.R, bytes[(x * 4) + 1]); + Assert.AreEqual(reference.G, bytes[(x * 4) + 2]); + Assert.AreEqual(reference.B, bytes[(x * 4) + 3]); + } + + y++; + } + } + + [TestMethod] + public void InterfaceRowsToArray() + { + IImage image = TestDataHelper.CreateTestImage(TEST_WIDTH, TEST_HEIGHT); + + int y = 0; + foreach (IImageRow row in image.Rows) + { + IColor[] data = row.ToArray(); + + for (int x = 0; x < TEST_WIDTH; x++) + Assert.AreEqual(TestDataHelper.GetColorFromLocation(x, y), data[x]); + + y++; + } + } + #endregion } \ No newline at end of file diff --git a/HPPH.Test/Image/RefImageTest.cs b/HPPH.Test/Image/RefImageTest.cs index cf043b3..6134657 100644 --- a/HPPH.Test/Image/RefImageTest.cs +++ b/HPPH.Test/Image/RefImageTest.cs @@ -15,7 +15,7 @@ public class RefImageTest #region Methods [TestMethod] - public void TestImageCreation() + public void ImageCreation() { RefImage image = TestDataHelper.CreateTestImage(TEST_WIDTH, TEST_HEIGHT).AsRefImage(); @@ -28,7 +28,7 @@ public class RefImageTest } [TestMethod] - public void TestImageInnerFull() + public void ImageInnerFull() { RefImage image = TestDataHelper.CreateTestImage(TEST_WIDTH, TEST_HEIGHT).AsRefImage(); image = image[0, 0, image.Width, image.Height]; @@ -39,7 +39,7 @@ public class RefImageTest } [TestMethod] - public void TestImageEnumerator() + public void ImageEnumerator() { RefImage image = TestDataHelper.CreateTestImage(TEST_WIDTH, TEST_HEIGHT).AsRefImage(); @@ -49,7 +49,7 @@ public class RefImageTest int x = counter % image.Width; int y = counter / image.Width; - if(y == 1) Debugger.Break(); + if (y == 1) Debugger.Break(); Assert.AreEqual(TestDataHelper.GetColorFromLocation(x, y), color); @@ -58,7 +58,7 @@ public class RefImageTest } [TestMethod] - public void TestImageInnerPartial() + public void ImageInnerPartial() { RefImage image = TestDataHelper.CreateTestImage(TEST_WIDTH, TEST_HEIGHT).AsRefImage(); image = image[163, 280, 720, 13]; @@ -72,7 +72,7 @@ public class RefImageTest } [TestMethod] - public void TestImageInnerInnerPartial() + public void ImageInnerInnerPartial() { RefImage image = TestDataHelper.CreateTestImage(TEST_WIDTH, TEST_HEIGHT).AsRefImage(); image = image[163, 280, 720, 13]; @@ -87,7 +87,7 @@ public class RefImageTest } [TestMethod] - public void TestImageRowIndexer() + public void ImageRowIndexer() { RefImage image = TestDataHelper.CreateTestImage(TEST_WIDTH, TEST_HEIGHT).AsRefImage(); @@ -103,7 +103,7 @@ public class RefImageTest } [TestMethod] - public void TestImageRowEnumerator() + public void ImageRowEnumerator() { RefImage image = TestDataHelper.CreateTestImage(TEST_WIDTH, TEST_HEIGHT).AsRefImage(); @@ -118,7 +118,7 @@ public class RefImageTest } [TestMethod] - public void TestImageColumnIndexer() + public void ImageColumnIndexer() { RefImage image = TestDataHelper.CreateTestImage(TEST_WIDTH, TEST_HEIGHT).AsRefImage(); @@ -134,7 +134,7 @@ public class RefImageTest } [TestMethod] - public void TestImageColumnEnumerator() + public void ImageColumnEnumerator() { RefImage image = TestDataHelper.CreateTestImage(TEST_WIDTH, TEST_HEIGHT).AsRefImage(); @@ -148,5 +148,57 @@ public class RefImageTest } } + [TestMethod] + public void ToArray() + { + RefImage image = TestDataHelper.CreateTestImage(TEST_WIDTH, TEST_HEIGHT).AsRefImage(); + ColorARGB[] testData = image.ToArray(); + + for (int y = 0; y < TEST_HEIGHT; y++) + for (int x = 0; x < TEST_WIDTH; x++) + Assert.AreEqual(TestDataHelper.GetColorFromLocation(x, y), testData[(y * TEST_WIDTH) + x]); + } + + [TestMethod] + public void CopyTo() + { + RefImage image = TestDataHelper.CreateTestImage(TEST_WIDTH, TEST_HEIGHT).AsRefImage(); + ColorARGB[] testData = new ColorARGB[TEST_WIDTH * TEST_HEIGHT]; + image.CopyTo(testData); + + for (int y = 0; y < TEST_HEIGHT; y++) + for (int x = 0; x < TEST_WIDTH; x++) + Assert.AreEqual(TestDataHelper.GetColorFromLocation(x, y), testData[(y * TEST_WIDTH) + x]); + } + + [TestMethod] + public void SubImage() + { + RefImage image = TestDataHelper.CreateTestImage(TEST_WIDTH, TEST_HEIGHT).AsRefImage(); + RefImage subImage = image[10, 20, 100, 200]; + + for (int y = 0; y < 200; y++) + for (int x = 0; x < 100; x++) + Assert.AreEqual(TestDataHelper.GetColorFromLocation(10 + x, 20 + y), subImage[x, y]); + } + + [TestMethod] + public unsafe void Pin() + { + RefImage image = TestDataHelper.CreateTestImage(TEST_WIDTH, TEST_HEIGHT).AsRefImage(); + ColorARGB[] reference = TestDataHelper.GetPixelData(TEST_WIDTH, TEST_HEIGHT); + + fixed (byte* ptr = image) + { + for (int i = 0; i < reference.Length; i++) + { + Assert.AreEqual(reference[i].A, ptr[(i * 4) + 0]); + Assert.AreEqual(reference[i].R, ptr[(i * 4) + 1]); + Assert.AreEqual(reference[i].G, ptr[(i * 4) + 2]); + Assert.AreEqual(reference[i].B, ptr[(i * 4) + 3]); + } + } + } + #endregion } \ No newline at end of file diff --git a/HPPH.Test/PixelHelper/ConvertTests.cs b/HPPH.Test/PixelHelper/ConvertTests.cs index 3a31004..948db7e 100644 --- a/HPPH.Test/PixelHelper/ConvertTests.cs +++ b/HPPH.Test/PixelHelper/ConvertTests.cs @@ -26,10 +26,10 @@ public class ConvertTests ColorRGB reference = referenceData[i]; ColorBGR test = result[i]; - Assert.AreEqual(reference.R, test.R, $"R differs at index {i}"); - Assert.AreEqual(reference.G, test.G, $"G differs at index {i}"); - Assert.AreEqual(reference.B, test.B, $"B differs at index {i}"); - Assert.AreEqual(reference.A, test.A, $"A differs at index {i}"); + Assert.AreEqual(reference.R, test.R, $"R differs at index {i}. Image: {image}, skip: {skip}"); + Assert.AreEqual(reference.G, test.G, $"G differs at index {i}. Image: {image}, skip: {skip}"); + Assert.AreEqual(reference.B, test.B, $"B differs at index {i}. Image: {image}, skip: {skip}"); + Assert.AreEqual(reference.A, test.A, $"A differs at index {i}. Image: {image}, skip: {skip}"); } } } @@ -56,10 +56,10 @@ public class ConvertTests ColorRGBA reference = referenceData[i]; ColorARGB test = result[i]; - Assert.AreEqual(reference.R, test.R, $"R differs at index {i}"); - Assert.AreEqual(reference.G, test.G, $"G differs at index {i}"); - Assert.AreEqual(reference.B, test.B, $"B differs at index {i}"); - Assert.AreEqual(reference.A, test.A, $"A differs at index {i}"); + Assert.AreEqual(reference.R, test.R, $"R differs at index {i}. Image: {image}, skip: {skip}"); + Assert.AreEqual(reference.G, test.G, $"G differs at index {i}. Image: {image}, skip: {skip}"); + Assert.AreEqual(reference.B, test.B, $"B differs at index {i}. Image: {image}, skip: {skip}"); + Assert.AreEqual(reference.A, test.A, $"A differs at index {i}. Image: {image}, skip: {skip}"); } } } @@ -86,10 +86,10 @@ public class ConvertTests ColorRGBA reference = referenceData[i]; ColorBGRA test = result[i]; - Assert.AreEqual(reference.R, test.R, $"R differs at index {i}"); - Assert.AreEqual(reference.G, test.G, $"G differs at index {i}"); - Assert.AreEqual(reference.B, test.B, $"B differs at index {i}"); - Assert.AreEqual(reference.A, test.A, $"A differs at index {i}"); + Assert.AreEqual(reference.R, test.R, $"R differs at index {i}. Image: {image}, skip: {skip}"); + Assert.AreEqual(reference.G, test.G, $"G differs at index {i}. Image: {image}, skip: {skip}"); + Assert.AreEqual(reference.B, test.B, $"B differs at index {i}. Image: {image}, skip: {skip}"); + Assert.AreEqual(reference.A, test.A, $"A differs at index {i}. Image: {image}, skip: {skip}"); } } } @@ -116,10 +116,10 @@ public class ConvertTests ColorRGBA reference = referenceData[i]; ColorRGB test = result[i]; - Assert.AreEqual(reference.R, test.R, $"R differs at index {i}"); - Assert.AreEqual(reference.G, test.G, $"G differs at index {i}"); - Assert.AreEqual(reference.B, test.B, $"B differs at index {i}"); - Assert.AreEqual(reference.A, test.A, $"A differs at index {i}"); + Assert.AreEqual(reference.R, test.R, $"R differs at index {i}. Image: {image}, skip: {skip}"); + Assert.AreEqual(reference.G, test.G, $"G differs at index {i}. Image: {image}, skip: {skip}"); + Assert.AreEqual(reference.B, test.B, $"B differs at index {i}. Image: {image}, skip: {skip}"); + Assert.AreEqual(255, test.A, $"A differs at index {i}. Image: {image}, skip: {skip}"); } } } @@ -146,10 +146,10 @@ public class ConvertTests ColorRGBA reference = referenceData[i]; ColorBGR test = result[i]; - Assert.AreEqual(reference.R, test.R, $"R differs at index {i}"); - Assert.AreEqual(reference.G, test.G, $"G differs at index {i}"); - Assert.AreEqual(reference.B, test.B, $"B differs at index {i}"); - Assert.AreEqual(reference.A, test.A, $"A differs at index {i}"); + Assert.AreEqual(reference.R, test.R, $"R differs at index {i}. Image: {image}, skip: {skip}"); + Assert.AreEqual(reference.G, test.G, $"G differs at index {i}. Image: {image}, skip: {skip}"); + Assert.AreEqual(reference.B, test.B, $"B differs at index {i}. Image: {image}, skip: {skip}"); + Assert.AreEqual(255, test.A, $"A differs at index {i}. Image: {image}, skip: {skip}"); } } } @@ -176,10 +176,10 @@ public class ConvertTests ColorRGB reference = referenceData[i]; ColorRGBA test = result[i]; - Assert.AreEqual(reference.R, test.R, $"R differs at index {i}"); - Assert.AreEqual(reference.G, test.G, $"G differs at index {i}"); - Assert.AreEqual(reference.B, test.B, $"B differs at index {i}"); - Assert.AreEqual(reference.A, test.A, $"A differs at index {i}"); + Assert.AreEqual(reference.R, test.R, $"R differs at index {i}. Image: {image}, skip: {skip}"); + Assert.AreEqual(reference.G, test.G, $"G differs at index {i}. Image: {image}, skip: {skip}"); + Assert.AreEqual(reference.B, test.B, $"B differs at index {i}. Image: {image}, skip: {skip}"); + Assert.AreEqual(reference.A, test.A, $"A differs at index {i}. Image: {image}, skip: {skip}"); } } } @@ -206,10 +206,10 @@ public class ConvertTests ColorRGB reference = referenceData[i]; ColorARGB test = result[i]; - Assert.AreEqual(reference.R, test.R, $"R differs at index {i}"); - Assert.AreEqual(reference.G, test.G, $"G differs at index {i}"); - Assert.AreEqual(reference.B, test.B, $"B differs at index {i}"); - Assert.AreEqual(reference.A, test.A, $"A differs at index {i}"); + Assert.AreEqual(reference.R, test.R, $"R differs at index {i}. Image: {image}, skip: {skip}"); + Assert.AreEqual(reference.G, test.G, $"G differs at index {i}. Image: {image}, skip: {skip}"); + Assert.AreEqual(reference.B, test.B, $"B differs at index {i}. Image: {image}, skip: {skip}"); + Assert.AreEqual(reference.A, test.A, $"A differs at index {i}. Image: {image}, skip: {skip}"); } } } @@ -236,10 +236,10 @@ public class ConvertTests ColorRGB reference = referenceData[i]; ColorBGRA test = result[i]; - Assert.AreEqual(reference.R, test.R, $"R differs at index {i}"); - Assert.AreEqual(reference.G, test.G, $"G differs at index {i}"); - Assert.AreEqual(reference.B, test.B, $"B differs at index {i}"); - Assert.AreEqual(reference.A, test.A, $"A differs at index {i}"); + Assert.AreEqual(reference.R, test.R, $"R differs at index {i}. Image: {image}, skip: {skip}"); + Assert.AreEqual(reference.G, test.G, $"G differs at index {i}. Image: {image}, skip: {skip}"); + Assert.AreEqual(reference.B, test.B, $"B differs at index {i}. Image: {image}, skip: {skip}"); + Assert.AreEqual(reference.A, test.A, $"A differs at index {i}. Image: {image}, skip: {skip}"); } } } @@ -266,10 +266,10 @@ public class ConvertTests ColorRGB reference = referenceData[i]; ColorABGR test = result[i]; - Assert.AreEqual(reference.R, test.R, $"R differs at index {i}"); - Assert.AreEqual(reference.G, test.G, $"G differs at index {i}"); - Assert.AreEqual(reference.B, test.B, $"B differs at index {i}"); - Assert.AreEqual(reference.A, test.A, $"A differs at index {i}"); + Assert.AreEqual(reference.R, test.R, $"R differs at index {i}. Image: {image}, skip: {skip}"); + Assert.AreEqual(reference.G, test.G, $"G differs at index {i}. Image: {image}, skip: {skip}"); + Assert.AreEqual(reference.B, test.B, $"B differs at index {i}. Image: {image}, skip: {skip}"); + Assert.AreEqual(reference.A, test.A, $"A differs at index {i}. Image: {image}, skip: {skip}"); } } } diff --git a/HPPH.Test/PixelHelper/QuantizeTests.cs b/HPPH.Test/PixelHelper/QuantizeTests.cs index d081189..08bba5b 100644 --- a/HPPH.Test/PixelHelper/QuantizeTests.cs +++ b/HPPH.Test/PixelHelper/QuantizeTests.cs @@ -7,111 +7,196 @@ public class CreateColorPaletteTests { private static IEnumerable GetTestImages() => Directory.EnumerateFiles(@"..\..\..\..\sample_data", "*.png", SearchOption.AllDirectories); - [TestMethod] - public void CreateColorPaletteReadOnlySpan3ByteSize1() + private static int[] Sizes => [0, 1, 2, 16, 64]; + + private static readonly Dictionary>> _reference = []; + + [ClassInitialize] + public static void Initialize(TestContext context) { + ReadOnlySpan sizes = Sizes; foreach (string image in GetTestImages()) { - ColorRGB[] data = ImageHelper.GetColorsFromImage(image); - Span span = data; + _reference[image] = []; - ColorRGB[] reference = [.. ReferencePixelHelper.CreateColorPalette(span, 1).OrderBy(x => x.R).ThenBy(x => x.G).ThenBy(x => x.B).ThenBy(x => x.A)]; - ColorRGB[] test = [.. span.CreateColorPalette(1).OrderBy(x => x.R).ThenBy(x => x.G).ThenBy(x => x.B).ThenBy(x => x.A)]; - - Assert.AreEqual(reference.Length, test.Length, "Palette Size differs"); - - for (int i = 0; i < reference.Length; i++) - Assert.AreEqual(reference[i], test[i], $"Index {i} differs"); + Initialize(image, sizes); + Initialize(image, sizes); + Initialize(image, sizes); + Initialize(image, sizes); + Initialize(image, sizes); + Initialize(image, sizes); } } - [TestMethod] - public void CreateColorPaletteReadOnlySpan3ByteSize4() + private static void Initialize(string image, ReadOnlySpan sizes) + where T : unmanaged, IColor { - foreach (string image in GetTestImages()) - { - ColorRGB[] data = ImageHelper.GetColorsFromImage(image); - Span span = data; + _reference[image][typeof(T)] = []; - ColorRGB[] reference = [.. ReferencePixelHelper.CreateColorPalette(span, 2).OrderBy(x => x.R).ThenBy(x => x.G).ThenBy(x => x.B).ThenBy(x => x.A)]; - ColorRGB[] test = [.. span.CreateColorPalette(2).OrderBy(x => x.R).ThenBy(x => x.G).ThenBy(x => x.B).ThenBy(x => x.A)]; - - Assert.AreEqual(reference.Length, test.Length, "Palette Size differs"); - - for (int i = 0; i < reference.Length; i++) - Assert.AreEqual(reference[i], test[i], $"Index {i} differs"); - } + Image img = ImageHelper.GetImage(image); + foreach (int size in sizes) + _reference[image][typeof(T)][size] = [.. ReferencePixelHelper.CreateColorPalette(img, size).OrderBy(x => x.R).ThenBy(x => x.G).ThenBy(x => x.B).ThenBy(x => x.A)]; } [TestMethod] - public void CreateColorPaletteReadOnlySpan3ByteSize16() + public void CreateColorPaletteReadOnlySpan() { + ReadOnlySpan sizes = Sizes; foreach (string image in GetTestImages()) { - ColorRGB[] data = ImageHelper.GetColorsFromImage(image); - Span span = data; - - ColorRGB[] reference = [.. ReferencePixelHelper.CreateColorPalette(span, 16).OrderBy(x => x.R).ThenBy(x => x.G).ThenBy(x => x.B).ThenBy(x => x.A)]; - ColorRGB[] test = [.. span.CreateColorPalette(16).OrderBy(x => x.R).ThenBy(x => x.G).ThenBy(x => x.B).ThenBy(x => x.A)]; - - Assert.AreEqual(reference.Length, test.Length, "Palette Size differs"); - - for (int i = 0; i < reference.Length; i++) - Assert.AreEqual(reference[i], test[i], $"Index {i} differs"); + foreach (int size in sizes) + { + CreateColorPaletteReadOnlySpan(image, size); + CreateColorPaletteReadOnlySpan(image, size); + CreateColorPaletteReadOnlySpan(image, size); + CreateColorPaletteReadOnlySpan(image, size); + CreateColorPaletteReadOnlySpan(image, size); + CreateColorPaletteReadOnlySpan(image, size); + } } } - [TestMethod] - public void CreateColorPaletteReadOnlySpan4ByteSize1() + private void CreateColorPaletteReadOnlySpan(string image, int size) + where T : unmanaged, IColor { - foreach (string image in GetTestImages()) - { - ColorRGBA[] data = ImageHelper.GetColorsFromImage(image); - Span span = data; + T[] data = ImageHelper.GetColorsFromImage(image); + ReadOnlySpan span = data; - ColorRGBA[] reference = [.. ReferencePixelHelper.CreateColorPalette(span, 1).OrderBy(x => x.R).ThenBy(x => x.G).ThenBy(x => x.B).ThenBy(x => x.A)]; - ColorRGBA[] test = [.. span.CreateColorPalette(1).OrderBy(x => x.R).ThenBy(x => x.G).ThenBy(x => x.B).ThenBy(x => x.A)]; + T[] test = [.. span.CreateColorPalette(size).OrderBy(x => x.R).ThenBy(x => x.G).ThenBy(x => x.B).ThenBy(x => x.A)]; - Assert.AreEqual(reference.Length, test.Length, "Palette Size differs"); + IColor[] reference = _reference[image][typeof(T)][size]; + Assert.AreEqual(reference.Length, test.Length, $"Palette Size differs for image {image}, size {size}"); - for (int i = 0; i < reference.Length; i++) - Assert.AreEqual(reference[i], test[i], $"Index {i} differs"); - } + for (int i = 0; i < reference.Length; i++) + Assert.AreEqual(reference[i], test[i], $"Index {i} differs for image {image}, size {size}"); } [TestMethod] - public void CreateColorPaletteReadOnlySpan4ByteSize4() + public void CreateColorPaletteSpan() { + ReadOnlySpan sizes = Sizes; foreach (string image in GetTestImages()) { - ColorRGBA[] data = ImageHelper.GetColorsFromImage(image); - Span span = data; - - ColorRGBA[] reference = [.. ReferencePixelHelper.CreateColorPalette(span, 2).OrderBy(x => x.R).ThenBy(x => x.G).ThenBy(x => x.B).ThenBy(x => x.A)]; - ColorRGBA[] test = [.. span.CreateColorPalette(2).OrderBy(x => x.R).ThenBy(x => x.G).ThenBy(x => x.B).ThenBy(x => x.A)]; - - Assert.AreEqual(reference.Length, test.Length, "Palette Size differs"); - - for (int i = 0; i < reference.Length; i++) - Assert.AreEqual(reference[i], test[i], $"Index {i} differs"); + foreach (int size in sizes) + { + CreateColorPaletteSpan(image, size); + CreateColorPaletteSpan(image, size); + CreateColorPaletteSpan(image, size); + CreateColorPaletteSpan(image, size); + CreateColorPaletteSpan(image, size); + CreateColorPaletteSpan(image, size); + } } } - [TestMethod] - public void CreateColorPaletteReadOnlySpan4ByteSize16() + private void CreateColorPaletteSpan(string image, int size) + where T : unmanaged, IColor { + T[] data = ImageHelper.GetColorsFromImage(image); + Span span = data; + + T[] test = [.. span.CreateColorPalette(size).OrderBy(x => x.R).ThenBy(x => x.G).ThenBy(x => x.B).ThenBy(x => x.A)]; + + IColor[] reference = _reference[image][typeof(T)][size]; + Assert.AreEqual(reference.Length, test.Length, $"Palette Size differs for image {image}, size {size}"); + + for (int i = 0; i < reference.Length; i++) + Assert.AreEqual(reference[i], test[i], $"Index {i} differs for image {image}, size {size}"); + } + + [TestMethod] + public void CreateColorPaletteImage() + { + ReadOnlySpan sizes = Sizes; foreach (string image in GetTestImages()) { - ColorRGBA[] data = ImageHelper.GetColorsFromImage(image); - Span span = data; - - ColorRGBA[] reference = [.. ReferencePixelHelper.CreateColorPalette(span, 16).OrderBy(x => x.R).ThenBy(x => x.G).ThenBy(x => x.B).ThenBy(x => x.A)]; - ColorRGBA[] test = [.. span.CreateColorPalette(16).OrderBy(x => x.R).ThenBy(x => x.G).ThenBy(x => x.B).ThenBy(x => x.A)]; - - Assert.AreEqual(reference.Length, test.Length, "Palette Size differs"); - - for (int i = 0; i < reference.Length; i++) - Assert.AreEqual(reference[i], test[i], $"Index {i} differs"); + foreach (int size in sizes) + { + CreateColorPaletteImage(image, size); + CreateColorPaletteImage(image, size); + CreateColorPaletteImage(image, size); + CreateColorPaletteImage(image, size); + CreateColorPaletteImage(image, size); + CreateColorPaletteImage(image, size); + } } } + + private void CreateColorPaletteImage(string image, int size) + where T : struct, IColor + { + IImage data = ImageHelper.GetImage(image); + + IColor[] test = [.. data.CreateColorPalette(size).OrderBy(x => x.R).ThenBy(x => x.G).ThenBy(x => x.B).ThenBy(x => x.A)]; + + IColor[] reference = _reference[image][typeof(T)][size]; + Assert.AreEqual(reference.Length, test.Length, $"Palette Size differs for image {image}, size {size}"); + + for (int i = 0; i < reference.Length; i++) + Assert.AreEqual(reference[i], test[i], $"Index {i} differs for image {image}, size {size}"); + } + + [TestMethod] + public void CreateColorPaletteGenericImage() + { + ReadOnlySpan sizes = Sizes; + foreach (string image in GetTestImages()) + { + foreach (int size in sizes) + { + CreateColorPaletteGenericImage(image, size); + CreateColorPaletteGenericImage(image, size); + CreateColorPaletteGenericImage(image, size); + CreateColorPaletteGenericImage(image, size); + CreateColorPaletteGenericImage(image, size); + CreateColorPaletteGenericImage(image, size); + } + } + } + + private void CreateColorPaletteGenericImage(string image, int size) + where T : unmanaged, IColor + { + Image data = ImageHelper.GetImage(image); + + T[] test = [.. data.CreateColorPalette(size).OrderBy(x => x.R).ThenBy(x => x.G).ThenBy(x => x.B).ThenBy(x => x.A)]; + + IColor[] reference = _reference[image][typeof(T)][size]; + Assert.AreEqual(reference.Length, test.Length, $"Palette Size differs for image {image}, size {size}"); + + for (int i = 0; i < reference.Length; i++) + Assert.AreEqual(reference[i], test[i], $"Index {i} differs for image {image}, size {size}"); + } + + [TestMethod] + public void CreateColorPaletteRefImage() + { + ReadOnlySpan sizes = Sizes; + foreach (string image in GetTestImages()) + { + foreach (int size in sizes) + { + CreateColorPaletteRefImage(image, size); + CreateColorPaletteRefImage(image, size); + CreateColorPaletteRefImage(image, size); + CreateColorPaletteRefImage(image, size); + CreateColorPaletteRefImage(image, size); + CreateColorPaletteRefImage(image, size); + } + } + } + + private void CreateColorPaletteRefImage(string image, int size) + where T : unmanaged, IColor + { + RefImage data = ImageHelper.GetImage(image).AsRefImage(); + + T[] test = [.. data.CreateColorPalette(size).OrderBy(x => x.R).ThenBy(x => x.G).ThenBy(x => x.B).ThenBy(x => x.A)]; + + IColor[] reference = _reference[image][typeof(T)][size]; + Assert.AreEqual(reference.Length, test.Length, $"Palette Size differs for image {image}, size {size}"); + + for (int i = 0; i < reference.Length; i++) + Assert.AreEqual(reference[i], test[i], $"Index {i} differs for image {image}, size {size}"); + } } \ No newline at end of file diff --git a/HPPH/Colors/IColor.cs b/HPPH/Colors/IColor.cs index 82d5a29..740a036 100644 --- a/HPPH/Colors/IColor.cs +++ b/HPPH/Colors/IColor.cs @@ -3,7 +3,7 @@ /// /// Represents a generic color made of 4 bytes (alpha, red, green and blue) /// -public interface IColor +public interface IColor : IEquatable { /// /// Gets the red-component of this color. diff --git a/HPPH/Data/Generic3ByteMinMax.cs b/HPPH/Data/Generic3ByteMinMax.cs index c4a226a..4e808b0 100644 --- a/HPPH/Data/Generic3ByteMinMax.cs +++ b/HPPH/Data/Generic3ByteMinMax.cs @@ -13,8 +13,4 @@ internal readonly struct Generic3ByteMinMax(byte b1Min, byte b1Max, byte b2Min, public readonly byte B3Min = b3Min; public readonly byte B3Max = b3Max; - - public byte B1Range => (byte)(B1Max - B1Min); - public byte B2Range => (byte)(B2Max - B2Min); - public byte B3Range => (byte)(B3Max - B3Min); } diff --git a/HPPH/Data/Generic4ByteMinMax.cs b/HPPH/Data/Generic4ByteMinMax.cs index 09b191f..d971c15 100644 --- a/HPPH/Data/Generic4ByteMinMax.cs +++ b/HPPH/Data/Generic4ByteMinMax.cs @@ -16,9 +16,4 @@ internal readonly struct Generic4ByteMinMax(byte b1Min, byte b1Max, byte b2Min, public readonly byte B4Min = b4Min; public readonly byte B4Max = b4Max; - - public byte B1Range => (byte)(B1Max - B1Min); - public byte B2Range => (byte)(B2Max - B2Min); - public byte B3Range => (byte)(B3Max - B3Min); - public byte B4Range => (byte)(B4Max - B4Min); } diff --git a/HPPH/Generated/HPPH.Generators/HPPH.Generators.ColorSourceGenerator/ColorABGR.g.cs b/HPPH/Generated/HPPH.Generators/HPPH.Generators.ColorSourceGenerator/ColorABGR.g.cs index a652165..749268b 100644 --- a/HPPH/Generated/HPPH.Generators/HPPH.Generators.ColorSourceGenerator/ColorABGR.g.cs +++ b/HPPH/Generated/HPPH.Generators/HPPH.Generators.ColorSourceGenerator/ColorABGR.g.cs @@ -49,6 +49,9 @@ public readonly partial struct ColorABGR(byte a, byte b, byte g, byte r) : IColo #region Methods + /// + public bool Equals(IColor? other) => (other != null) && (R == other.R) && (G == other.G) && (B == other.B) && (A == other.A); + /// public override string ToString() => $"[A: {A}, R: {R}, G: {G}, B: {B}]"; diff --git a/HPPH/Generated/HPPH.Generators/HPPH.Generators.ColorSourceGenerator/ColorARGB.g.cs b/HPPH/Generated/HPPH.Generators/HPPH.Generators.ColorSourceGenerator/ColorARGB.g.cs index 2c1950f..04eef35 100644 --- a/HPPH/Generated/HPPH.Generators/HPPH.Generators.ColorSourceGenerator/ColorARGB.g.cs +++ b/HPPH/Generated/HPPH.Generators/HPPH.Generators.ColorSourceGenerator/ColorARGB.g.cs @@ -49,6 +49,9 @@ public readonly partial struct ColorARGB(byte a, byte r, byte g, byte b) : IColo #region Methods + /// + public bool Equals(IColor? other) => (other != null) && (R == other.R) && (G == other.G) && (B == other.B) && (A == other.A); + /// public override string ToString() => $"[A: {A}, R: {R}, G: {G}, B: {B}]"; diff --git a/HPPH/Generated/HPPH.Generators/HPPH.Generators.ColorSourceGenerator/ColorBGR.g.cs b/HPPH/Generated/HPPH.Generators/HPPH.Generators.ColorSourceGenerator/ColorBGR.g.cs index 8d75a41..72080cc 100644 --- a/HPPH/Generated/HPPH.Generators/HPPH.Generators.ColorSourceGenerator/ColorBGR.g.cs +++ b/HPPH/Generated/HPPH.Generators/HPPH.Generators.ColorSourceGenerator/ColorBGR.g.cs @@ -47,6 +47,9 @@ public readonly partial struct ColorBGR(byte b, byte g, byte r): IColor #region Methods + /// + public bool Equals(IColor? other) => (other != null) && (R == other.R) && (G == other.G) && (B == other.B) && (A == other.A); + /// public override string ToString() => $"[A: {A}, R: {R}, G: {G}, B: {B}]"; diff --git a/HPPH/Generated/HPPH.Generators/HPPH.Generators.ColorSourceGenerator/ColorBGRA.g.cs b/HPPH/Generated/HPPH.Generators/HPPH.Generators.ColorSourceGenerator/ColorBGRA.g.cs index 70d5d72..8c449e5 100644 --- a/HPPH/Generated/HPPH.Generators/HPPH.Generators.ColorSourceGenerator/ColorBGRA.g.cs +++ b/HPPH/Generated/HPPH.Generators/HPPH.Generators.ColorSourceGenerator/ColorBGRA.g.cs @@ -49,6 +49,9 @@ public readonly partial struct ColorBGRA(byte b, byte g, byte r, byte a) : IColo #region Methods + /// + public bool Equals(IColor? other) => (other != null) && (R == other.R) && (G == other.G) && (B == other.B) && (A == other.A); + /// public override string ToString() => $"[A: {A}, R: {R}, G: {G}, B: {B}]"; diff --git a/HPPH/Generated/HPPH.Generators/HPPH.Generators.ColorSourceGenerator/ColorRGB.g.cs b/HPPH/Generated/HPPH.Generators/HPPH.Generators.ColorSourceGenerator/ColorRGB.g.cs index 6746521..0115749 100644 --- a/HPPH/Generated/HPPH.Generators/HPPH.Generators.ColorSourceGenerator/ColorRGB.g.cs +++ b/HPPH/Generated/HPPH.Generators/HPPH.Generators.ColorSourceGenerator/ColorRGB.g.cs @@ -47,6 +47,9 @@ public readonly partial struct ColorRGB(byte r, byte g, byte b): IColor #region Methods + /// + public bool Equals(IColor? other) => (other != null) && (R == other.R) && (G == other.G) && (B == other.B) && (A == other.A); + /// public override string ToString() => $"[A: {A}, R: {R}, G: {G}, B: {B}]"; diff --git a/HPPH/Generated/HPPH.Generators/HPPH.Generators.ColorSourceGenerator/ColorRGBA.g.cs b/HPPH/Generated/HPPH.Generators/HPPH.Generators.ColorSourceGenerator/ColorRGBA.g.cs index 07f9997..c30acf5 100644 --- a/HPPH/Generated/HPPH.Generators/HPPH.Generators.ColorSourceGenerator/ColorRGBA.g.cs +++ b/HPPH/Generated/HPPH.Generators/HPPH.Generators.ColorSourceGenerator/ColorRGBA.g.cs @@ -49,6 +49,9 @@ public readonly partial struct ColorRGBA(byte r, byte g, byte b, byte a) : IColo #region Methods + /// + public bool Equals(IColor? other) => (other != null) && (R == other.R) && (G == other.G) && (B == other.B) && (A == other.A); + /// public override string ToString() => $"[A: {A}, R: {R}, G: {G}, B: {B}]"; diff --git a/HPPH/Images/Interfaces/IImageRow.cs b/HPPH/Images/Interfaces/IImageRow.cs index 5e6b681..ae15708 100644 --- a/HPPH/Images/Interfaces/IImageRow.cs +++ b/HPPH/Images/Interfaces/IImageRow.cs @@ -22,6 +22,8 @@ public interface IImageRow : IEnumerable /// The at the specified location. IColor this[int x] { get; } + void CopyTo(Span destination); + /// /// Copies the contents of this into a destination instance. /// diff --git a/HPPH/PixelHelper.Quantize.cs b/HPPH/PixelHelper.Quantize.cs index 1bfdc12..beb83ce 100644 --- a/HPPH/PixelHelper.Quantize.cs +++ b/HPPH/PixelHelper.Quantize.cs @@ -66,6 +66,9 @@ public static partial class PixelHelper public static T[] CreateColorPalette(this Span colors, int paletteSize) where T : unmanaged, IColor { + if (paletteSize < 0) throw new ArgumentException("PaletteSize Can't be < 0", nameof(paletteSize)); + if (paletteSize == 0) return []; + int splits = BitOperations.Log2((uint)paletteSize); Span> cubes = new ColorCube[1 << splits]; diff --git a/sample_data/1.png b/sample_data/1.png index d366d84..b452e5c 100644 Binary files a/sample_data/1.png and b/sample_data/1.png differ diff --git a/sample_data/3.png b/sample_data/3.png index c6049ad..e851411 100644 Binary files a/sample_data/3.png and b/sample_data/3.png differ diff --git a/sample_data/4.png b/sample_data/4.png index a8c8a29..f0d0608 100644 Binary files a/sample_data/4.png and b/sample_data/4.png differ diff --git a/sample_data/5.png b/sample_data/5.png index 47637ad..7bcf1e8 100644 Binary files a/sample_data/5.png and b/sample_data/5.png differ diff --git a/sample_data/6.png b/sample_data/6.png new file mode 100644 index 0000000..9d45a6a Binary files /dev/null and b/sample_data/6.png differ diff --git a/sample_data/7.png b/sample_data/7.png new file mode 100644 index 0000000..b01460e Binary files /dev/null and b/sample_data/7.png differ diff --git a/sample_data/8.png b/sample_data/8.png new file mode 100644 index 0000000..19a9ac3 Binary files /dev/null and b/sample_data/8.png differ diff --git a/sample_data/9.png b/sample_data/9.png new file mode 100644 index 0000000..5514ad4 Binary files /dev/null and b/sample_data/9.png differ