diff --git a/HPPH.Generators/Features/Quantize.cs b/HPPH.Generators/Features/Quantize.cs index c0b6224..7f37d7c 100644 --- a/HPPH.Generators/Features/Quantize.cs +++ b/HPPH.Generators/Features/Quantize.cs @@ -37,6 +37,17 @@ internal class Quantize : IGeneratorFeature return result; } + unsafe IColor[] IColorFormat.CreateSimpleColorPalette(ReadOnlySpan data, int paletteSize) + { + Color{{colorFormat.Format}}[] colors = PixelHelper.CreateSimpleColorPalette(MemoryMarshal.Cast(data), paletteSize); + + IColor[] result = new IColor[colors.Length]; + for(int i = 0; i < colors.Length; i++) + result[i] = colors[i]; + + return result; + } + #endregion } """; @@ -50,6 +61,7 @@ internal class Quantize : IGeneratorFeature public partial interface IColorFormat { internal IColor[] CreateColorPalette(ReadOnlySpan data, int paletteSize); + internal IColor[] CreateSimpleColorPalette(ReadOnlySpan data, int paletteSize); } """; } diff --git a/HPPH.Reference/PixelHelper.Quantize.cs b/HPPH.Reference/PixelHelper.Quantize.cs index c6e92ae..6d56d4d 100644 --- a/HPPH.Reference/PixelHelper.Quantize.cs +++ b/HPPH.Reference/PixelHelper.Quantize.cs @@ -6,19 +6,19 @@ public static partial class ReferencePixelHelper { #region Methods - public static T[] CreateColorPalette(Image image, int paletteSize) + public static T[] CreateSimpleColorPalette(Image image, int paletteSize) where T : unmanaged, IColor - => CreateColorPalette(image.ToArray(), paletteSize); + => CreateSimpleColorPalette(image.ToArray(), paletteSize); - public static T[] CreateColorPalette(RefImage image, int paletteSize) + public static T[] CreateSimpleColorPalette(RefImage image, int paletteSize) where T : unmanaged, IColor - => CreateColorPalette(image.ToArray(), paletteSize); + => CreateSimpleColorPalette(image.ToArray(), paletteSize); - public static T[] CreateColorPalette(Span colors, int paletteSize) + public static T[] CreateSimpleColorPalette(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(); + CreateSimpleColorPalette(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[] CreateSimpleColorPalette(IColor[] colors, int paletteSize) where T : struct, IColor { if (paletteSize == 0) return []; diff --git a/HPPH.Test/PixelHelper/QuantizeTests.cs b/HPPH.Test/PixelHelper/QuantizeTests.cs index 08bba5b..6f0fa72 100644 --- a/HPPH.Test/PixelHelper/QuantizeTests.cs +++ b/HPPH.Test/PixelHelper/QuantizeTests.cs @@ -3,68 +3,73 @@ using HPPH.Reference; namespace HPPH.Test.PixelHelper; [TestClass] -public class CreateColorPaletteTests +public class CreateSimpleColorPaletteTests { private static IEnumerable GetTestImages() => Directory.EnumerateFiles(@"..\..\..\..\sample_data", "*.png", SearchOption.AllDirectories); - private static int[] Sizes => [0, 1, 2, 16, 64]; + private static int[] SimpleSizes => [0, 1, 2, 16, 64]; + private static int[] Sizes => [0, 1, 2, 9, 16, 53]; + private static readonly Dictionary>> _simpleReference = []; private static readonly Dictionary>> _reference = []; [ClassInitialize] public static void Initialize(TestContext context) { - ReadOnlySpan sizes = Sizes; foreach (string image in GetTestImages()) { - _reference[image] = []; + _simpleReference[image] = []; - Initialize(image, sizes); - Initialize(image, sizes); - Initialize(image, sizes); - Initialize(image, sizes); - Initialize(image, sizes); - Initialize(image, sizes); + Initialize(image); + Initialize(image); + Initialize(image); + Initialize(image); + Initialize(image); + Initialize(image); } } - private static void Initialize(string image, ReadOnlySpan sizes) + private static void Initialize(string image) where T : unmanaged, IColor { - _reference[image][typeof(T)] = []; + _simpleReference[image][typeof(T)] = []; Image img = ImageHelper.GetImage(image); - foreach (int size in sizes) + + foreach (int size in SimpleSizes) + _simpleReference[image][typeof(T)][size] = [.. ReferencePixelHelper.CreateSimpleColorPalette(img, size).OrderBy(x => x.R).ThenBy(x => x.G).ThenBy(x => x.B).ThenBy(x => x.A)]; + + 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 CreateColorPaletteReadOnlySpan() + public void CreateSimpleColorPaletteReadOnlySpan() { - ReadOnlySpan sizes = Sizes; + ReadOnlySpan sizes = SimpleSizes; foreach (string image in GetTestImages()) { foreach (int size in sizes) { - CreateColorPaletteReadOnlySpan(image, size); - CreateColorPaletteReadOnlySpan(image, size); - CreateColorPaletteReadOnlySpan(image, size); - CreateColorPaletteReadOnlySpan(image, size); - CreateColorPaletteReadOnlySpan(image, size); - CreateColorPaletteReadOnlySpan(image, size); + CreateSimpleColorPaletteReadOnlySpan(image, size); + CreateSimpleColorPaletteReadOnlySpan(image, size); + CreateSimpleColorPaletteReadOnlySpan(image, size); + CreateSimpleColorPaletteReadOnlySpan(image, size); + CreateSimpleColorPaletteReadOnlySpan(image, size); + CreateSimpleColorPaletteReadOnlySpan(image, size); } } } - private void CreateColorPaletteReadOnlySpan(string image, int size) + private void CreateSimpleColorPaletteReadOnlySpan(string image, int size) where T : unmanaged, IColor { T[] data = ImageHelper.GetColorsFromImage(image); ReadOnlySpan span = data; - T[] test = [.. span.CreateColorPalette(size).OrderBy(x => x.R).ThenBy(x => x.G).ThenBy(x => x.B).ThenBy(x => x.A)]; + T[] test = [.. span.CreateSimpleColorPalette(size).OrderBy(x => x.R).ThenBy(x => x.G).ThenBy(x => x.B).ThenBy(x => x.A)]; - IColor[] reference = _reference[image][typeof(T)][size]; + IColor[] reference = _simpleReference[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++) @@ -72,32 +77,32 @@ public class CreateColorPaletteTests } [TestMethod] - public void CreateColorPaletteSpan() + public void CreateSimpleColorPaletteSpan() { - ReadOnlySpan sizes = Sizes; + ReadOnlySpan sizes = SimpleSizes; foreach (string image in GetTestImages()) { foreach (int size in sizes) { - CreateColorPaletteSpan(image, size); - CreateColorPaletteSpan(image, size); - CreateColorPaletteSpan(image, size); - CreateColorPaletteSpan(image, size); - CreateColorPaletteSpan(image, size); - CreateColorPaletteSpan(image, size); + CreateSimpleColorPaletteSpan(image, size); + CreateSimpleColorPaletteSpan(image, size); + CreateSimpleColorPaletteSpan(image, size); + CreateSimpleColorPaletteSpan(image, size); + CreateSimpleColorPaletteSpan(image, size); + CreateSimpleColorPaletteSpan(image, size); } } } - private void CreateColorPaletteSpan(string image, int size) + private void CreateSimpleColorPaletteSpan(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)]; + T[] test = [.. span.CreateSimpleColorPalette(size).OrderBy(x => x.R).ThenBy(x => x.G).ThenBy(x => x.B).ThenBy(x => x.A)]; - IColor[] reference = _reference[image][typeof(T)][size]; + IColor[] reference = _simpleReference[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++) @@ -105,31 +110,31 @@ public class CreateColorPaletteTests } [TestMethod] - public void CreateColorPaletteImage() + public void CreateSimpleColorPaletteImage() { - ReadOnlySpan sizes = Sizes; + ReadOnlySpan sizes = SimpleSizes; foreach (string image in GetTestImages()) { foreach (int size in sizes) { - CreateColorPaletteImage(image, size); - CreateColorPaletteImage(image, size); - CreateColorPaletteImage(image, size); - CreateColorPaletteImage(image, size); - CreateColorPaletteImage(image, size); - CreateColorPaletteImage(image, size); + CreateSimpleColorPaletteImage(image, size); + CreateSimpleColorPaletteImage(image, size); + CreateSimpleColorPaletteImage(image, size); + CreateSimpleColorPaletteImage(image, size); + CreateSimpleColorPaletteImage(image, size); + CreateSimpleColorPaletteImage(image, size); } } } - private void CreateColorPaletteImage(string image, int size) + private void CreateSimpleColorPaletteImage(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[] test = [.. data.CreateSimpleColorPalette(size).OrderBy(x => x.R).ThenBy(x => x.G).ThenBy(x => x.B).ThenBy(x => x.A)]; - IColor[] reference = _reference[image][typeof(T)][size]; + IColor[] reference = _simpleReference[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++) @@ -137,31 +142,31 @@ public class CreateColorPaletteTests } [TestMethod] - public void CreateColorPaletteGenericImage() + public void CreateSimpleColorPaletteGenericImage() { - ReadOnlySpan sizes = Sizes; + ReadOnlySpan sizes = SimpleSizes; 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); + CreateSimpleColorPaletteGenericImage(image, size); + CreateSimpleColorPaletteGenericImage(image, size); + CreateSimpleColorPaletteGenericImage(image, size); + CreateSimpleColorPaletteGenericImage(image, size); + CreateSimpleColorPaletteGenericImage(image, size); + CreateSimpleColorPaletteGenericImage(image, size); } } } - private void CreateColorPaletteGenericImage(string image, int size) + private void CreateSimpleColorPaletteGenericImage(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)]; + T[] test = [.. data.CreateSimpleColorPalette(size).OrderBy(x => x.R).ThenBy(x => x.G).ThenBy(x => x.B).ThenBy(x => x.A)]; - IColor[] reference = _reference[image][typeof(T)][size]; + IColor[] reference = _simpleReference[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++) @@ -169,34 +174,196 @@ public class CreateColorPaletteTests } [TestMethod] - public void CreateColorPaletteRefImage() + public void CreateSimpleColorPaletteRefImage() { - ReadOnlySpan sizes = Sizes; + ReadOnlySpan sizes = SimpleSizes; 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); + CreateSimpleColorPaletteRefImage(image, size); + CreateSimpleColorPaletteRefImage(image, size); + CreateSimpleColorPaletteRefImage(image, size); + CreateSimpleColorPaletteRefImage(image, size); + CreateSimpleColorPaletteRefImage(image, size); + CreateSimpleColorPaletteRefImage(image, size); } } } - private void CreateColorPaletteRefImage(string image, int size) + private void CreateSimpleColorPaletteRefImage(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)]; + T[] test = [.. data.CreateSimpleColorPalette(size).OrderBy(x => x.R).ThenBy(x => x.G).ThenBy(x => x.B).ThenBy(x => x.A)]; - IColor[] reference = _reference[image][typeof(T)][size]; + IColor[] reference = _simpleReference[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 CreateColorPaletteReadOnlySpan() + //{ + // ReadOnlySpan sizes = SimpleSizes; + // foreach (string image in GetTestImages()) + // { + // foreach (int size in sizes) + // { + // CreateColorPaletteReadOnlySpan(image, size); + // CreateColorPaletteReadOnlySpan(image, size); + // CreateColorPaletteReadOnlySpan(image, size); + // CreateColorPaletteReadOnlySpan(image, size); + // CreateColorPaletteReadOnlySpan(image, size); + // CreateColorPaletteReadOnlySpan(image, size); + // } + // } + //} + + //private void CreateColorPaletteReadOnlySpan(string image, int size) + // where T : unmanaged, IColor + //{ + // T[] data = ImageHelper.GetColorsFromImage(image); + // ReadOnlySpan 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 = _simpleReference[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 CreateColorPaletteSpan() + //{ + // ReadOnlySpan sizes = SimpleSizes; + // foreach (string image in GetTestImages()) + // { + // foreach (int size in sizes) + // { + // CreateColorPaletteSpan(image, size); + // CreateColorPaletteSpan(image, size); + // CreateColorPaletteSpan(image, size); + // CreateColorPaletteSpan(image, size); + // CreateColorPaletteSpan(image, size); + // CreateColorPaletteSpan(image, size); + // } + // } + //} + + //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 = _simpleReference[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 = SimpleSizes; + // foreach (string image in GetTestImages()) + // { + // 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 = _simpleReference[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 = SimpleSizes; + // 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 = _simpleReference[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 = SimpleSizes; + // 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 = _simpleReference[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/Generated/HPPH.Generators/HPPH.Generators.ColorSourceGenerator/ColorFormatABGR.Quantize.g.cs b/HPPH/Generated/HPPH.Generators/HPPH.Generators.ColorSourceGenerator/ColorFormatABGR.Quantize.g.cs index d8c4820..9625456 100644 --- a/HPPH/Generated/HPPH.Generators/HPPH.Generators.ColorSourceGenerator/ColorFormatABGR.Quantize.g.cs +++ b/HPPH/Generated/HPPH.Generators/HPPH.Generators.ColorSourceGenerator/ColorFormatABGR.Quantize.g.cs @@ -17,5 +17,16 @@ public sealed partial class ColorFormatABGR return result; } + unsafe IColor[] IColorFormat.CreateSimpleColorPalette(ReadOnlySpan data, int paletteSize) + { + ColorABGR[] colors = PixelHelper.CreateSimpleColorPalette(MemoryMarshal.Cast(data), paletteSize); + + IColor[] result = new IColor[colors.Length]; + for(int i = 0; i < colors.Length; i++) + result[i] = colors[i]; + + return result; + } + #endregion } \ No newline at end of file diff --git a/HPPH/Generated/HPPH.Generators/HPPH.Generators.ColorSourceGenerator/ColorFormatARGB.Quantize.g.cs b/HPPH/Generated/HPPH.Generators/HPPH.Generators.ColorSourceGenerator/ColorFormatARGB.Quantize.g.cs index 411e19c..c6be434 100644 --- a/HPPH/Generated/HPPH.Generators/HPPH.Generators.ColorSourceGenerator/ColorFormatARGB.Quantize.g.cs +++ b/HPPH/Generated/HPPH.Generators/HPPH.Generators.ColorSourceGenerator/ColorFormatARGB.Quantize.g.cs @@ -17,5 +17,16 @@ public sealed partial class ColorFormatARGB return result; } + unsafe IColor[] IColorFormat.CreateSimpleColorPalette(ReadOnlySpan data, int paletteSize) + { + ColorARGB[] colors = PixelHelper.CreateSimpleColorPalette(MemoryMarshal.Cast(data), paletteSize); + + IColor[] result = new IColor[colors.Length]; + for(int i = 0; i < colors.Length; i++) + result[i] = colors[i]; + + return result; + } + #endregion } \ No newline at end of file diff --git a/HPPH/Generated/HPPH.Generators/HPPH.Generators.ColorSourceGenerator/ColorFormatBGR.Quantize.g.cs b/HPPH/Generated/HPPH.Generators/HPPH.Generators.ColorSourceGenerator/ColorFormatBGR.Quantize.g.cs index 0f82830..093b3db 100644 --- a/HPPH/Generated/HPPH.Generators/HPPH.Generators.ColorSourceGenerator/ColorFormatBGR.Quantize.g.cs +++ b/HPPH/Generated/HPPH.Generators/HPPH.Generators.ColorSourceGenerator/ColorFormatBGR.Quantize.g.cs @@ -17,5 +17,16 @@ public sealed partial class ColorFormatBGR return result; } + unsafe IColor[] IColorFormat.CreateSimpleColorPalette(ReadOnlySpan data, int paletteSize) + { + ColorBGR[] colors = PixelHelper.CreateSimpleColorPalette(MemoryMarshal.Cast(data), paletteSize); + + IColor[] result = new IColor[colors.Length]; + for(int i = 0; i < colors.Length; i++) + result[i] = colors[i]; + + return result; + } + #endregion } \ No newline at end of file diff --git a/HPPH/Generated/HPPH.Generators/HPPH.Generators.ColorSourceGenerator/ColorFormatBGRA.Quantize.g.cs b/HPPH/Generated/HPPH.Generators/HPPH.Generators.ColorSourceGenerator/ColorFormatBGRA.Quantize.g.cs index 9c1e1e6..5c1e92d 100644 --- a/HPPH/Generated/HPPH.Generators/HPPH.Generators.ColorSourceGenerator/ColorFormatBGRA.Quantize.g.cs +++ b/HPPH/Generated/HPPH.Generators/HPPH.Generators.ColorSourceGenerator/ColorFormatBGRA.Quantize.g.cs @@ -17,5 +17,16 @@ public sealed partial class ColorFormatBGRA return result; } + unsafe IColor[] IColorFormat.CreateSimpleColorPalette(ReadOnlySpan data, int paletteSize) + { + ColorBGRA[] colors = PixelHelper.CreateSimpleColorPalette(MemoryMarshal.Cast(data), paletteSize); + + IColor[] result = new IColor[colors.Length]; + for(int i = 0; i < colors.Length; i++) + result[i] = colors[i]; + + return result; + } + #endregion } \ No newline at end of file diff --git a/HPPH/Generated/HPPH.Generators/HPPH.Generators.ColorSourceGenerator/ColorFormatRGB.Quantize.g.cs b/HPPH/Generated/HPPH.Generators/HPPH.Generators.ColorSourceGenerator/ColorFormatRGB.Quantize.g.cs index 3a7daca..fd1e93d 100644 --- a/HPPH/Generated/HPPH.Generators/HPPH.Generators.ColorSourceGenerator/ColorFormatRGB.Quantize.g.cs +++ b/HPPH/Generated/HPPH.Generators/HPPH.Generators.ColorSourceGenerator/ColorFormatRGB.Quantize.g.cs @@ -17,5 +17,16 @@ public sealed partial class ColorFormatRGB return result; } + unsafe IColor[] IColorFormat.CreateSimpleColorPalette(ReadOnlySpan data, int paletteSize) + { + ColorRGB[] colors = PixelHelper.CreateSimpleColorPalette(MemoryMarshal.Cast(data), paletteSize); + + IColor[] result = new IColor[colors.Length]; + for(int i = 0; i < colors.Length; i++) + result[i] = colors[i]; + + return result; + } + #endregion } \ No newline at end of file diff --git a/HPPH/Generated/HPPH.Generators/HPPH.Generators.ColorSourceGenerator/ColorFormatRGBA.Quantize.g.cs b/HPPH/Generated/HPPH.Generators/HPPH.Generators.ColorSourceGenerator/ColorFormatRGBA.Quantize.g.cs index 43a36dd..4e2514f 100644 --- a/HPPH/Generated/HPPH.Generators/HPPH.Generators.ColorSourceGenerator/ColorFormatRGBA.Quantize.g.cs +++ b/HPPH/Generated/HPPH.Generators/HPPH.Generators.ColorSourceGenerator/ColorFormatRGBA.Quantize.g.cs @@ -17,5 +17,16 @@ public sealed partial class ColorFormatRGBA return result; } + unsafe IColor[] IColorFormat.CreateSimpleColorPalette(ReadOnlySpan data, int paletteSize) + { + ColorRGBA[] colors = PixelHelper.CreateSimpleColorPalette(MemoryMarshal.Cast(data), paletteSize); + + IColor[] result = new IColor[colors.Length]; + for(int i = 0; i < colors.Length; i++) + result[i] = colors[i]; + + return result; + } + #endregion } \ No newline at end of file diff --git a/HPPH/Generated/HPPH.Generators/HPPH.Generators.ColorSourceGenerator/IColorFormat.Quantize.g.cs b/HPPH/Generated/HPPH.Generators/HPPH.Generators.ColorSourceGenerator/IColorFormat.Quantize.g.cs index 2bb8aca..a17d940 100644 --- a/HPPH/Generated/HPPH.Generators/HPPH.Generators.ColorSourceGenerator/IColorFormat.Quantize.g.cs +++ b/HPPH/Generated/HPPH.Generators/HPPH.Generators.ColorSourceGenerator/IColorFormat.Quantize.g.cs @@ -3,4 +3,5 @@ public partial interface IColorFormat { internal IColor[] CreateColorPalette(ReadOnlySpan data, int paletteSize); + internal IColor[] CreateSimpleColorPalette(ReadOnlySpan data, int paletteSize); } \ No newline at end of file diff --git a/HPPH/PixelHelper.Quantize.cs b/HPPH/PixelHelper.Quantize.cs index beb83ce..b26feef 100644 --- a/HPPH/PixelHelper.Quantize.cs +++ b/HPPH/PixelHelper.Quantize.cs @@ -66,24 +66,126 @@ 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)); + throw new NotImplementedException("This requires some more research and will be implemented later"); + + //TODO DarthAffe 21.07.2024: Run some tests how the result to performance ratio is with the options described at http://blog.pkh.me/p/39-improving-color-quantization-heuristics.html + + //if (paletteSize < 0) throw new ArgumentException("PaletteSize can't be < 0", nameof(paletteSize)); + //if (paletteSize == 0) return []; + + //Span> cubes = new ColorCube[paletteSize]; + //cubes[0] = new ColorCube(0, colors.Length, SortTarget.None); + + //for (int i = 0; i < paletteSize; i++) + //{ + // int splitCubeIndex = 0; + + // for (int j = 0; j < paletteSize; j++) + // { + + // } + + // cubes[splitCubeIndex].Split(colors, out ColorCube a, out ColorCube b); + // cubes[splitCubeIndex] = a; + // cubes[i + 1] = b; + //} + + //T[] result = new T[cubes.Length]; + //for (int i = 0; i < cubes.Length; i++) + // result[i] = Average(cubes[i].Slice(colors)); + + //return result; + } + + public static IColor[] CreateSimpleColorPalette(this IImage image, int paletteSize) + { + ArgumentNullException.ThrowIfNull(image); + + int dataLength = image.SizeInBytes; + byte[] array = ArrayPool.Shared.Rent(dataLength); + Span buffer = array.AsSpan()[..dataLength]; + try + { + image.CopyTo(buffer); + return image.ColorFormat.CreateSimpleColorPalette(buffer, paletteSize); + } + finally + { + ArrayPool.Shared.Return(array); + } + } + + public static T[] CreateSimpleColorPalette(this IImage image, int paletteSize) + where T : unmanaged, IColor + => image.AsRefImage().CreateSimpleColorPalette(paletteSize); + + public static T[] CreateSimpleColorPalette(this RefImage image, int paletteSize) + where T : unmanaged, IColor + { + int dataLength = image.Width * image.Height; + T[] array = ArrayPool.Shared.Rent(dataLength); + Span buffer = array.AsSpan()[..(dataLength)]; + try + { + image.CopyTo(buffer); + return CreateSimpleColorPalette(buffer, paletteSize); + } + finally + { + ArrayPool.Shared.Return(array); + } + } + + public static T[] CreateSimpleColorPalette(this ReadOnlySpan colors, int paletteSize) + where T : unmanaged, IColor + { + T[] buffer = ArrayPool.Shared.Rent(colors.Length); + try + { + Span colorBuffer = buffer.AsSpan()[..colors.Length]; + colors.CopyTo(colorBuffer); + + return CreateSimpleColorPalette(colorBuffer, paletteSize); + } + finally + { + ArrayPool.Shared.Return(buffer); + } + } + + public static unsafe T[] CreateSimpleColorPalette(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 []; + if (!BitOperations.IsPow2(paletteSize)) throw new ArgumentException("PaletteSize has to be a power of 2", nameof(paletteSize)); + int splits = BitOperations.Log2((uint)paletteSize); - Span> cubes = new ColorCube[1 << splits]; + ColorCube[] cubes = new ColorCube[1 << splits]; cubes[0] = new ColorCube(0, colors.Length, SortTarget.None); - int currentIndex = 0; - for (int i = 0; i < splits; i++) + ParallelOptions parallelOptions = new() { MaxDegreeOfParallelism = Environment.ProcessorCount }; + + int colorsLength = colors.Length; + fixed (T* colorsPtr = colors) { - int currentCubeCount = 1 << i; - Span> currentCubes = cubes[..currentCubeCount]; - for (int j = 0; j < currentCubes.Length; j++) + //HACK DarthAffe 21.07.2024: This is dangerous and needs to be treated carefully! It's used in the anonymous method used to run the splits in parallel and absolutely can't be used outside this fixed block! + T* unsafeColorsPtr = colorsPtr; + + for (int i = 0; i < splits; i++) { - currentCubes[j].Split(colors, out ColorCube a, out ColorCube b); - currentCubes[j] = a; - cubes[++currentIndex] = b; + int currentCubeCount = 1 << i; + + Parallel.For(0, currentCubeCount, parallelOptions, CreateCubes); + + void CreateCubes(int index) + { + cubes[index].Split(new Span(unsafeColorsPtr, colorsLength), out ColorCube a, out ColorCube b); + cubes[index] = a; + cubes[currentCubeCount + index] = b; + } } } diff --git a/HPPH/PixelHelper.Sort.cs b/HPPH/PixelHelper.Sort.cs index e19fc1a..f1287e9 100644 --- a/HPPH/PixelHelper.Sort.cs +++ b/HPPH/PixelHelper.Sort.cs @@ -21,5 +21,7 @@ public static unsafe partial class PixelHelper public static partial void SortByAlpha(this Span colors) where T : unmanaged, IColor; + //TODO DarthAffe 21.07.2024: Add CIE-Sorting + #endregion } diff --git a/HPPH/PixelHelper.Sum.cs b/HPPH/PixelHelper.Sum.cs index a33e5ee..34b0fa0 100644 --- a/HPPH/PixelHelper.Sum.cs +++ b/HPPH/PixelHelper.Sum.cs @@ -65,7 +65,7 @@ public static unsafe partial class PixelHelper return T.ColorFormat.BytesPerPixel switch { - // DarthAffe 05.07.2024: Important: The sum of 3-byte colors are result in 4 byte data! + // DarthAffe 05.07.2024: Important: The sum of 3-byte colors result in 4 byte data! 3 => Unsafe.BitCast(Sum(MemoryMarshal.Cast(colors))), 4 => Unsafe.BitCast(Sum(MemoryMarshal.Cast(colors))), _ => throw new NotSupportedException("Data is not of a supported valid color-type.")