mirror of
https://github.com/DarthAffe/HPPH.git
synced 2025-12-12 13:28:37 +00:00
Compare commits
6 Commits
15981f82d6
...
bd7a2bca48
| Author | SHA1 | Date | |
|---|---|---|---|
| bd7a2bca48 | |||
| cfdd95f079 | |||
| 536a462540 | |||
| 44ff0bf5b0 | |||
| 10f0c8c5b7 | |||
| c897b34690 |
@ -37,6 +37,17 @@ internal class Quantize : IGeneratorFeature
|
||||
return result;
|
||||
}
|
||||
|
||||
unsafe IColor[] IColorFormat.CreateSimpleColorPalette(ReadOnlySpan<byte> data, int paletteSize)
|
||||
{
|
||||
Color{{colorFormat.Format}}[] colors = PixelHelper.CreateSimpleColorPalette<Color{{colorFormat.Format}}>(MemoryMarshal.Cast<byte, Color{{colorFormat.Format}}>(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<byte> data, int paletteSize);
|
||||
internal IColor[] CreateSimpleColorPalette(ReadOnlySpan<byte> data, int paletteSize);
|
||||
}
|
||||
""";
|
||||
}
|
||||
|
||||
@ -9,10 +9,10 @@ public static partial class ReferencePixelHelper
|
||||
float count = image.Width * image.Height;
|
||||
|
||||
ISum sum = Sum(image);
|
||||
return new ColorRGBA((byte)(sum.R / count),
|
||||
(byte)(sum.G / count),
|
||||
(byte)(sum.B / count),
|
||||
(byte)(sum.A / count));
|
||||
return new ColorRGBA((byte)MathF.Round(sum.R / count),
|
||||
(byte)MathF.Round(sum.G / count),
|
||||
(byte)MathF.Round(sum.B / count),
|
||||
(byte)MathF.Round(sum.A / count));
|
||||
}
|
||||
|
||||
public static T Average<T>(IImage<T> image)
|
||||
@ -21,10 +21,10 @@ public static partial class ReferencePixelHelper
|
||||
float count = image.Width * image.Height;
|
||||
|
||||
ISum sum = Sum(image);
|
||||
return (T)T.Create((byte)(sum.R / count),
|
||||
(byte)(sum.G / count),
|
||||
(byte)(sum.B / count),
|
||||
(byte)(sum.A / count));
|
||||
return (T)T.Create((byte)MathF.Round(sum.R / count),
|
||||
(byte)MathF.Round(sum.G / count),
|
||||
(byte)MathF.Round(sum.B / count),
|
||||
(byte)MathF.Round(sum.A / count));
|
||||
}
|
||||
|
||||
public static T Average<T>(RefImage<T> image)
|
||||
@ -33,10 +33,10 @@ public static partial class ReferencePixelHelper
|
||||
float count = image.Width * image.Height;
|
||||
|
||||
ISum sum = Sum(image);
|
||||
return (T)T.Create((byte)(sum.R / count),
|
||||
(byte)(sum.G / count),
|
||||
(byte)(sum.B / count),
|
||||
(byte)(sum.A / count));
|
||||
return (T)T.Create((byte)MathF.Round(sum.R / count),
|
||||
(byte)MathF.Round(sum.G / count),
|
||||
(byte)MathF.Round(sum.B / count),
|
||||
(byte)MathF.Round(sum.A / count));
|
||||
}
|
||||
|
||||
public static T Average<T>(Span<T> colors)
|
||||
@ -45,10 +45,10 @@ public static partial class ReferencePixelHelper
|
||||
float count = colors.Length;
|
||||
|
||||
ISum sum = Sum(colors);
|
||||
return (T)T.Create((byte)(sum.R / count),
|
||||
(byte)(sum.G / count),
|
||||
(byte)(sum.B / count),
|
||||
(byte)(sum.A / count));
|
||||
return (T)T.Create((byte)MathF.Round(sum.R / count),
|
||||
(byte)MathF.Round(sum.G / count),
|
||||
(byte)MathF.Round(sum.B / count),
|
||||
(byte)MathF.Round(sum.A / count));
|
||||
}
|
||||
|
||||
public static T Average<T>(ReadOnlySpan<T> colors)
|
||||
@ -57,10 +57,10 @@ public static partial class ReferencePixelHelper
|
||||
float count = colors.Length;
|
||||
|
||||
ISum sum = Sum(colors);
|
||||
return (T)T.Create((byte)(sum.R / count),
|
||||
(byte)(sum.G / count),
|
||||
(byte)(sum.B / count),
|
||||
(byte)(sum.A / count));
|
||||
return (T)T.Create((byte)MathF.Round(sum.R / count),
|
||||
(byte)MathF.Round(sum.G / count),
|
||||
(byte)MathF.Round(sum.B / count),
|
||||
(byte)MathF.Round(sum.A / count));
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
@ -6,19 +6,19 @@ public static partial class ReferencePixelHelper
|
||||
{
|
||||
#region Methods
|
||||
|
||||
public static T[] CreateColorPalette<T>(Image<T> image, int paletteSize)
|
||||
public static T[] CreateSimpleColorPalette<T>(Image<T> image, int paletteSize)
|
||||
where T : unmanaged, IColor
|
||||
=> CreateColorPalette<T>(image.ToArray(), paletteSize);
|
||||
=> CreateSimpleColorPalette<T>(image.ToArray(), paletteSize);
|
||||
|
||||
public static T[] CreateColorPalette<T>(RefImage<T> image, int paletteSize)
|
||||
public static T[] CreateSimpleColorPalette<T>(RefImage<T> image, int paletteSize)
|
||||
where T : unmanaged, IColor
|
||||
=> CreateColorPalette<T>(image.ToArray(), paletteSize);
|
||||
=> CreateSimpleColorPalette<T>(image.ToArray(), paletteSize);
|
||||
|
||||
public static T[] CreateColorPalette<T>(Span<T> colors, int paletteSize)
|
||||
public static T[] CreateSimpleColorPalette<T>(Span<T> colors, int paletteSize)
|
||||
where T : unmanaged, IColor =>
|
||||
CreateColorPalette<T>(colors.ToArray().Cast<IColor>().ToArray(), paletteSize).Select(x => T.Create(x.R, x.G, x.B, x.A)).Cast<T>().ToArray();
|
||||
CreateSimpleColorPalette<T>(colors.ToArray().Cast<IColor>().ToArray(), paletteSize).Select(x => T.Create(x.R, x.G, x.B, x.A)).Cast<T>().ToArray();
|
||||
|
||||
private static IColor[] CreateColorPalette<T>(IColor[] colors, int paletteSize)
|
||||
private static IColor[] CreateSimpleColorPalette<T>(IColor[] colors, int paletteSize)
|
||||
where T : struct, IColor
|
||||
{
|
||||
if (paletteSize == 0) return [];
|
||||
|
||||
@ -117,6 +117,25 @@ public class ImageTest
|
||||
}
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void ImageRowIndexerSubImage()
|
||||
{
|
||||
(int width, int height) = SIZES[0];
|
||||
|
||||
IImage image = TestDataHelper.CreateTestImage<ColorARGB>(width, height);
|
||||
image = image[163, 280, 720, 13];
|
||||
|
||||
Assert.AreEqual(image.Height, image.Rows.Count);
|
||||
|
||||
for (int y = 0; y < image.Height; y++)
|
||||
{
|
||||
IImageRow row = image.Rows[y];
|
||||
Assert.AreEqual(image.Width, row.Length);
|
||||
for (int x = 0; x < row.Length; x++)
|
||||
Assert.AreEqual(TestDataHelper.GetColorFromLocation<ColorARGB>(163 + x, 280 + y), row[x]);
|
||||
}
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void ImageRowEnumerator()
|
||||
{
|
||||
@ -154,6 +173,25 @@ public class ImageTest
|
||||
}
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void ImageColumnIndexerSubImage()
|
||||
{
|
||||
(int width, int height) = SIZES[0];
|
||||
|
||||
IImage image = TestDataHelper.CreateTestImage<ColorARGB>(width, height);
|
||||
image = image[163, 280, 720, 13];
|
||||
|
||||
Assert.AreEqual(image.Width, image.Columns.Count);
|
||||
|
||||
for (int x = 0; x < image.Width; x++)
|
||||
{
|
||||
IImageColumn column = image.Columns[x];
|
||||
Assert.AreEqual(image.Height, column.Length);
|
||||
for (int y = 0; y < column.Length; y++)
|
||||
Assert.AreEqual(TestDataHelper.GetColorFromLocation<ColorARGB>(163 + x, 280 + y), column[y]);
|
||||
}
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void ImageColumnEnumerator()
|
||||
{
|
||||
|
||||
@ -118,6 +118,25 @@ public class RefImageTest
|
||||
}
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void ImageRowIndexerSubImage()
|
||||
{
|
||||
(int width, int height) = SIZES[0];
|
||||
|
||||
RefImage<ColorARGB> image = TestDataHelper.CreateTestImage<ColorARGB>(width, height).AsRefImage();
|
||||
image = image[163, 280, 720, 13];
|
||||
|
||||
Assert.AreEqual(image.Height, image.Rows.Count);
|
||||
|
||||
for (int y = 0; y < image.Height; y++)
|
||||
{
|
||||
ImageRow<ColorARGB> row = image.Rows[y];
|
||||
Assert.AreEqual(image.Width, row.Length);
|
||||
for (int x = 0; x < row.Length; x++)
|
||||
Assert.AreEqual(TestDataHelper.GetColorFromLocation<ColorARGB>(163 + x, 280 + y), row[x]);
|
||||
}
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void ImageRowEnumerator()
|
||||
{
|
||||
@ -155,6 +174,25 @@ public class RefImageTest
|
||||
}
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void ImageColumnIndexerSubImage()
|
||||
{
|
||||
(int width, int height) = SIZES[0];
|
||||
|
||||
RefImage<ColorARGB> image = TestDataHelper.CreateTestImage<ColorARGB>(width, height).AsRefImage();
|
||||
image = image[163, 280, 720, 13];
|
||||
|
||||
Assert.AreEqual(image.Width, image.Columns.Count);
|
||||
|
||||
for (int x = 0; x < image.Width; x++)
|
||||
{
|
||||
ImageColumn<ColorARGB> column = image.Columns[x];
|
||||
Assert.AreEqual(image.Height, column.Length);
|
||||
for (int y = 0; y < column.Length; y++)
|
||||
Assert.AreEqual(TestDataHelper.GetColorFromLocation<ColorARGB>(163 + x, 280 + y), column[y]);
|
||||
}
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void ImageColumnEnumerator()
|
||||
{
|
||||
|
||||
@ -3,68 +3,73 @@ using HPPH.Reference;
|
||||
namespace HPPH.Test.PixelHelper;
|
||||
|
||||
[TestClass]
|
||||
public class CreateColorPaletteTests
|
||||
public class CreateSimpleColorPaletteTests
|
||||
{
|
||||
private static IEnumerable<string> 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<string, Dictionary<Type, Dictionary<int, IColor[]>>> _simpleReference = [];
|
||||
private static readonly Dictionary<string, Dictionary<Type, Dictionary<int, IColor[]>>> _reference = [];
|
||||
|
||||
[ClassInitialize]
|
||||
public static void Initialize(TestContext context)
|
||||
{
|
||||
ReadOnlySpan<int> sizes = Sizes;
|
||||
foreach (string image in GetTestImages())
|
||||
{
|
||||
_reference[image] = [];
|
||||
_simpleReference[image] = [];
|
||||
|
||||
Initialize<ColorRGB>(image, sizes);
|
||||
Initialize<ColorBGR>(image, sizes);
|
||||
Initialize<ColorRGBA>(image, sizes);
|
||||
Initialize<ColorBGRA>(image, sizes);
|
||||
Initialize<ColorARGB>(image, sizes);
|
||||
Initialize<ColorABGR>(image, sizes);
|
||||
Initialize<ColorRGB>(image);
|
||||
Initialize<ColorBGR>(image);
|
||||
Initialize<ColorRGBA>(image);
|
||||
Initialize<ColorBGRA>(image);
|
||||
Initialize<ColorARGB>(image);
|
||||
Initialize<ColorABGR>(image);
|
||||
}
|
||||
}
|
||||
|
||||
private static void Initialize<T>(string image, ReadOnlySpan<int> sizes)
|
||||
private static void Initialize<T>(string image)
|
||||
where T : unmanaged, IColor
|
||||
{
|
||||
_reference[image][typeof(T)] = [];
|
||||
_simpleReference[image][typeof(T)] = [];
|
||||
|
||||
Image<T> img = ImageHelper.GetImage<T>(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)];
|
||||
|
||||
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<int> sizes = Sizes;
|
||||
ReadOnlySpan<int> sizes = SimpleSizes;
|
||||
foreach (string image in GetTestImages())
|
||||
{
|
||||
foreach (int size in sizes)
|
||||
{
|
||||
CreateColorPaletteReadOnlySpan<ColorRGB>(image, size);
|
||||
CreateColorPaletteReadOnlySpan<ColorBGR>(image, size);
|
||||
CreateColorPaletteReadOnlySpan<ColorRGBA>(image, size);
|
||||
CreateColorPaletteReadOnlySpan<ColorBGRA>(image, size);
|
||||
CreateColorPaletteReadOnlySpan<ColorARGB>(image, size);
|
||||
CreateColorPaletteReadOnlySpan<ColorABGR>(image, size);
|
||||
CreateSimpleColorPaletteReadOnlySpan<ColorRGB>(image, size);
|
||||
CreateSimpleColorPaletteReadOnlySpan<ColorBGR>(image, size);
|
||||
CreateSimpleColorPaletteReadOnlySpan<ColorRGBA>(image, size);
|
||||
CreateSimpleColorPaletteReadOnlySpan<ColorBGRA>(image, size);
|
||||
CreateSimpleColorPaletteReadOnlySpan<ColorARGB>(image, size);
|
||||
CreateSimpleColorPaletteReadOnlySpan<ColorABGR>(image, size);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void CreateColorPaletteReadOnlySpan<T>(string image, int size)
|
||||
private void CreateSimpleColorPaletteReadOnlySpan<T>(string image, int size)
|
||||
where T : unmanaged, IColor
|
||||
{
|
||||
T[] data = ImageHelper.GetColorsFromImage<T>(image);
|
||||
ReadOnlySpan<T> 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<int> sizes = Sizes;
|
||||
ReadOnlySpan<int> sizes = SimpleSizes;
|
||||
foreach (string image in GetTestImages())
|
||||
{
|
||||
foreach (int size in sizes)
|
||||
{
|
||||
CreateColorPaletteSpan<ColorRGB>(image, size);
|
||||
CreateColorPaletteSpan<ColorBGR>(image, size);
|
||||
CreateColorPaletteSpan<ColorRGBA>(image, size);
|
||||
CreateColorPaletteSpan<ColorBGRA>(image, size);
|
||||
CreateColorPaletteSpan<ColorARGB>(image, size);
|
||||
CreateColorPaletteSpan<ColorABGR>(image, size);
|
||||
CreateSimpleColorPaletteSpan<ColorRGB>(image, size);
|
||||
CreateSimpleColorPaletteSpan<ColorBGR>(image, size);
|
||||
CreateSimpleColorPaletteSpan<ColorRGBA>(image, size);
|
||||
CreateSimpleColorPaletteSpan<ColorBGRA>(image, size);
|
||||
CreateSimpleColorPaletteSpan<ColorARGB>(image, size);
|
||||
CreateSimpleColorPaletteSpan<ColorABGR>(image, size);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void CreateColorPaletteSpan<T>(string image, int size)
|
||||
private void CreateSimpleColorPaletteSpan<T>(string image, int size)
|
||||
where T : unmanaged, IColor
|
||||
{
|
||||
T[] data = ImageHelper.GetColorsFromImage<T>(image);
|
||||
Span<T> 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<int> sizes = Sizes;
|
||||
ReadOnlySpan<int> sizes = SimpleSizes;
|
||||
foreach (string image in GetTestImages())
|
||||
{
|
||||
foreach (int size in sizes)
|
||||
{
|
||||
CreateColorPaletteImage<ColorRGB>(image, size);
|
||||
CreateColorPaletteImage<ColorBGR>(image, size);
|
||||
CreateColorPaletteImage<ColorRGBA>(image, size);
|
||||
CreateColorPaletteImage<ColorBGRA>(image, size);
|
||||
CreateColorPaletteImage<ColorARGB>(image, size);
|
||||
CreateColorPaletteImage<ColorABGR>(image, size);
|
||||
CreateSimpleColorPaletteImage<ColorRGB>(image, size);
|
||||
CreateSimpleColorPaletteImage<ColorBGR>(image, size);
|
||||
CreateSimpleColorPaletteImage<ColorRGBA>(image, size);
|
||||
CreateSimpleColorPaletteImage<ColorBGRA>(image, size);
|
||||
CreateSimpleColorPaletteImage<ColorARGB>(image, size);
|
||||
CreateSimpleColorPaletteImage<ColorABGR>(image, size);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void CreateColorPaletteImage<T>(string image, int size)
|
||||
private void CreateSimpleColorPaletteImage<T>(string image, int size)
|
||||
where T : struct, IColor
|
||||
{
|
||||
IImage data = ImageHelper.GetImage<T>(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<int> sizes = Sizes;
|
||||
ReadOnlySpan<int> sizes = SimpleSizes;
|
||||
foreach (string image in GetTestImages())
|
||||
{
|
||||
foreach (int size in sizes)
|
||||
{
|
||||
CreateColorPaletteGenericImage<ColorRGB>(image, size);
|
||||
CreateColorPaletteGenericImage<ColorBGR>(image, size);
|
||||
CreateColorPaletteGenericImage<ColorRGBA>(image, size);
|
||||
CreateColorPaletteGenericImage<ColorBGRA>(image, size);
|
||||
CreateColorPaletteGenericImage<ColorARGB>(image, size);
|
||||
CreateColorPaletteGenericImage<ColorABGR>(image, size);
|
||||
CreateSimpleColorPaletteGenericImage<ColorRGB>(image, size);
|
||||
CreateSimpleColorPaletteGenericImage<ColorBGR>(image, size);
|
||||
CreateSimpleColorPaletteGenericImage<ColorRGBA>(image, size);
|
||||
CreateSimpleColorPaletteGenericImage<ColorBGRA>(image, size);
|
||||
CreateSimpleColorPaletteGenericImage<ColorARGB>(image, size);
|
||||
CreateSimpleColorPaletteGenericImage<ColorABGR>(image, size);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void CreateColorPaletteGenericImage<T>(string image, int size)
|
||||
private void CreateSimpleColorPaletteGenericImage<T>(string image, int size)
|
||||
where T : unmanaged, IColor
|
||||
{
|
||||
Image<T> data = ImageHelper.GetImage<T>(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<int> sizes = Sizes;
|
||||
ReadOnlySpan<int> sizes = SimpleSizes;
|
||||
foreach (string image in GetTestImages())
|
||||
{
|
||||
foreach (int size in sizes)
|
||||
{
|
||||
CreateColorPaletteRefImage<ColorRGB>(image, size);
|
||||
CreateColorPaletteRefImage<ColorBGR>(image, size);
|
||||
CreateColorPaletteRefImage<ColorRGBA>(image, size);
|
||||
CreateColorPaletteRefImage<ColorBGRA>(image, size);
|
||||
CreateColorPaletteRefImage<ColorARGB>(image, size);
|
||||
CreateColorPaletteRefImage<ColorABGR>(image, size);
|
||||
CreateSimpleColorPaletteRefImage<ColorRGB>(image, size);
|
||||
CreateSimpleColorPaletteRefImage<ColorBGR>(image, size);
|
||||
CreateSimpleColorPaletteRefImage<ColorRGBA>(image, size);
|
||||
CreateSimpleColorPaletteRefImage<ColorBGRA>(image, size);
|
||||
CreateSimpleColorPaletteRefImage<ColorARGB>(image, size);
|
||||
CreateSimpleColorPaletteRefImage<ColorABGR>(image, size);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void CreateColorPaletteRefImage<T>(string image, int size)
|
||||
private void CreateSimpleColorPaletteRefImage<T>(string image, int size)
|
||||
where T : unmanaged, IColor
|
||||
{
|
||||
RefImage<T> data = ImageHelper.GetImage<T>(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<int> sizes = SimpleSizes;
|
||||
// foreach (string image in GetTestImages())
|
||||
// {
|
||||
// foreach (int size in sizes)
|
||||
// {
|
||||
// CreateColorPaletteReadOnlySpan<ColorRGB>(image, size);
|
||||
// CreateColorPaletteReadOnlySpan<ColorBGR>(image, size);
|
||||
// CreateColorPaletteReadOnlySpan<ColorRGBA>(image, size);
|
||||
// CreateColorPaletteReadOnlySpan<ColorBGRA>(image, size);
|
||||
// CreateColorPaletteReadOnlySpan<ColorARGB>(image, size);
|
||||
// CreateColorPaletteReadOnlySpan<ColorABGR>(image, size);
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
|
||||
//private void CreateColorPaletteReadOnlySpan<T>(string image, int size)
|
||||
// where T : unmanaged, IColor
|
||||
//{
|
||||
// T[] data = ImageHelper.GetColorsFromImage<T>(image);
|
||||
// ReadOnlySpan<T> 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<int> sizes = SimpleSizes;
|
||||
// foreach (string image in GetTestImages())
|
||||
// {
|
||||
// foreach (int size in sizes)
|
||||
// {
|
||||
// CreateColorPaletteSpan<ColorRGB>(image, size);
|
||||
// CreateColorPaletteSpan<ColorBGR>(image, size);
|
||||
// CreateColorPaletteSpan<ColorRGBA>(image, size);
|
||||
// CreateColorPaletteSpan<ColorBGRA>(image, size);
|
||||
// CreateColorPaletteSpan<ColorARGB>(image, size);
|
||||
// CreateColorPaletteSpan<ColorABGR>(image, size);
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
|
||||
//private void CreateColorPaletteSpan<T>(string image, int size)
|
||||
// where T : unmanaged, IColor
|
||||
//{
|
||||
// T[] data = ImageHelper.GetColorsFromImage<T>(image);
|
||||
// Span<T> 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<int> sizes = SimpleSizes;
|
||||
// foreach (string image in GetTestImages())
|
||||
// {
|
||||
// foreach (int size in sizes)
|
||||
// {
|
||||
// CreateColorPaletteImage<ColorRGB>(image, size);
|
||||
// CreateColorPaletteImage<ColorBGR>(image, size);
|
||||
// CreateColorPaletteImage<ColorRGBA>(image, size);
|
||||
// CreateColorPaletteImage<ColorBGRA>(image, size);
|
||||
// CreateColorPaletteImage<ColorARGB>(image, size);
|
||||
// CreateColorPaletteImage<ColorABGR>(image, size);
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
|
||||
//private void CreateColorPaletteImage<T>(string image, int size)
|
||||
// where T : struct, IColor
|
||||
//{
|
||||
// IImage data = ImageHelper.GetImage<T>(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<int> sizes = SimpleSizes;
|
||||
// foreach (string image in GetTestImages())
|
||||
// {
|
||||
// foreach (int size in sizes)
|
||||
// {
|
||||
// CreateColorPaletteGenericImage<ColorRGB>(image, size);
|
||||
// CreateColorPaletteGenericImage<ColorBGR>(image, size);
|
||||
// CreateColorPaletteGenericImage<ColorRGBA>(image, size);
|
||||
// CreateColorPaletteGenericImage<ColorBGRA>(image, size);
|
||||
// CreateColorPaletteGenericImage<ColorARGB>(image, size);
|
||||
// CreateColorPaletteGenericImage<ColorABGR>(image, size);
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
|
||||
//private void CreateColorPaletteGenericImage<T>(string image, int size)
|
||||
// where T : unmanaged, IColor
|
||||
//{
|
||||
// Image<T> data = ImageHelper.GetImage<T>(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<int> sizes = SimpleSizes;
|
||||
// foreach (string image in GetTestImages())
|
||||
// {
|
||||
// foreach (int size in sizes)
|
||||
// {
|
||||
// CreateColorPaletteRefImage<ColorRGB>(image, size);
|
||||
// CreateColorPaletteRefImage<ColorBGR>(image, size);
|
||||
// CreateColorPaletteRefImage<ColorRGBA>(image, size);
|
||||
// CreateColorPaletteRefImage<ColorBGRA>(image, size);
|
||||
// CreateColorPaletteRefImage<ColorARGB>(image, size);
|
||||
// CreateColorPaletteRefImage<ColorABGR>(image, size);
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
|
||||
//private void CreateColorPaletteRefImage<T>(string image, int size)
|
||||
// where T : unmanaged, IColor
|
||||
//{
|
||||
// RefImage<T> data = ImageHelper.GetImage<T>(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}");
|
||||
//}
|
||||
}
|
||||
@ -17,5 +17,16 @@ public sealed partial class ColorFormatABGR
|
||||
return result;
|
||||
}
|
||||
|
||||
unsafe IColor[] IColorFormat.CreateSimpleColorPalette(ReadOnlySpan<byte> data, int paletteSize)
|
||||
{
|
||||
ColorABGR[] colors = PixelHelper.CreateSimpleColorPalette<ColorABGR>(MemoryMarshal.Cast<byte, ColorABGR>(data), paletteSize);
|
||||
|
||||
IColor[] result = new IColor[colors.Length];
|
||||
for(int i = 0; i < colors.Length; i++)
|
||||
result[i] = colors[i];
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
@ -17,5 +17,16 @@ public sealed partial class ColorFormatARGB
|
||||
return result;
|
||||
}
|
||||
|
||||
unsafe IColor[] IColorFormat.CreateSimpleColorPalette(ReadOnlySpan<byte> data, int paletteSize)
|
||||
{
|
||||
ColorARGB[] colors = PixelHelper.CreateSimpleColorPalette<ColorARGB>(MemoryMarshal.Cast<byte, ColorARGB>(data), paletteSize);
|
||||
|
||||
IColor[] result = new IColor[colors.Length];
|
||||
for(int i = 0; i < colors.Length; i++)
|
||||
result[i] = colors[i];
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
@ -17,5 +17,16 @@ public sealed partial class ColorFormatBGR
|
||||
return result;
|
||||
}
|
||||
|
||||
unsafe IColor[] IColorFormat.CreateSimpleColorPalette(ReadOnlySpan<byte> data, int paletteSize)
|
||||
{
|
||||
ColorBGR[] colors = PixelHelper.CreateSimpleColorPalette<ColorBGR>(MemoryMarshal.Cast<byte, ColorBGR>(data), paletteSize);
|
||||
|
||||
IColor[] result = new IColor[colors.Length];
|
||||
for(int i = 0; i < colors.Length; i++)
|
||||
result[i] = colors[i];
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
@ -17,5 +17,16 @@ public sealed partial class ColorFormatBGRA
|
||||
return result;
|
||||
}
|
||||
|
||||
unsafe IColor[] IColorFormat.CreateSimpleColorPalette(ReadOnlySpan<byte> data, int paletteSize)
|
||||
{
|
||||
ColorBGRA[] colors = PixelHelper.CreateSimpleColorPalette<ColorBGRA>(MemoryMarshal.Cast<byte, ColorBGRA>(data), paletteSize);
|
||||
|
||||
IColor[] result = new IColor[colors.Length];
|
||||
for(int i = 0; i < colors.Length; i++)
|
||||
result[i] = colors[i];
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
@ -17,5 +17,16 @@ public sealed partial class ColorFormatRGB
|
||||
return result;
|
||||
}
|
||||
|
||||
unsafe IColor[] IColorFormat.CreateSimpleColorPalette(ReadOnlySpan<byte> data, int paletteSize)
|
||||
{
|
||||
ColorRGB[] colors = PixelHelper.CreateSimpleColorPalette<ColorRGB>(MemoryMarshal.Cast<byte, ColorRGB>(data), paletteSize);
|
||||
|
||||
IColor[] result = new IColor[colors.Length];
|
||||
for(int i = 0; i < colors.Length; i++)
|
||||
result[i] = colors[i];
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
@ -17,5 +17,16 @@ public sealed partial class ColorFormatRGBA
|
||||
return result;
|
||||
}
|
||||
|
||||
unsafe IColor[] IColorFormat.CreateSimpleColorPalette(ReadOnlySpan<byte> data, int paletteSize)
|
||||
{
|
||||
ColorRGBA[] colors = PixelHelper.CreateSimpleColorPalette<ColorRGBA>(MemoryMarshal.Cast<byte, ColorRGBA>(data), paletteSize);
|
||||
|
||||
IColor[] result = new IColor[colors.Length];
|
||||
for(int i = 0; i < colors.Length; i++)
|
||||
result[i] = colors[i];
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
@ -3,4 +3,5 @@
|
||||
public partial interface IColorFormat
|
||||
{
|
||||
internal IColor[] CreateColorPalette(ReadOnlySpan<byte> data, int paletteSize);
|
||||
internal IColor[] CreateSimpleColorPalette(ReadOnlySpan<byte> data, int paletteSize);
|
||||
}
|
||||
@ -125,6 +125,14 @@ public sealed class Image<T> : IImage<T>, IEquatable<Image<T>>
|
||||
return new Image<T>(data, 0, 0, width, height, stride);
|
||||
}
|
||||
|
||||
public static Image<T> Wrap(byte[] buffer, int width, int height, int stride)
|
||||
{
|
||||
if (stride < width) throw new ArgumentException("Stride can't be smaller than width.");
|
||||
if (buffer.Length < (height * stride)) throw new ArgumentException("Not enough data in the buffer.");
|
||||
|
||||
return new Image<T>(buffer, 0, 0, width, height, stride);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public IImage<TColor> ConvertTo<TColor>()
|
||||
where TColor : struct, IColor
|
||||
|
||||
@ -15,6 +15,7 @@ public readonly ref struct ImageRows<T>
|
||||
private readonly int _width;
|
||||
private readonly int _height;
|
||||
private readonly int _stride;
|
||||
private readonly int _bpp;
|
||||
|
||||
public int Count => _height;
|
||||
|
||||
@ -29,7 +30,7 @@ public readonly ref struct ImageRows<T>
|
||||
{
|
||||
if ((row < 0) || (row >= _height)) throw new IndexOutOfRangeException();
|
||||
|
||||
return new ImageRow<T>(_data, ((row + _y) * _stride) + _x, _width);
|
||||
return new ImageRow<T>(_data, ((row + _y) * _stride) + (_x * _bpp), _width);
|
||||
}
|
||||
}
|
||||
|
||||
@ -46,6 +47,8 @@ public readonly ref struct ImageRows<T>
|
||||
this._width = width;
|
||||
this._height = height;
|
||||
this._stride = stride;
|
||||
|
||||
_bpp = T.ColorFormat.BytesPerPixel;
|
||||
}
|
||||
|
||||
#endregion
|
||||
@ -110,6 +113,7 @@ internal class IColorImageRows<T> : IImageRows
|
||||
private readonly int _width;
|
||||
private readonly int _height;
|
||||
private readonly int _stride;
|
||||
private readonly int _bpp;
|
||||
|
||||
/// <inheritdoc />
|
||||
public int Count => _height;
|
||||
@ -126,7 +130,7 @@ internal class IColorImageRows<T> : IImageRows
|
||||
{
|
||||
if ((row < 0) || (row >= _height)) throw new IndexOutOfRangeException();
|
||||
|
||||
return new IColorImageRow<T>(_data, ((row + _y) * _stride) + _x, _width);
|
||||
return new IColorImageRow<T>(_data, ((row + _y) * _stride) + (_x * _bpp), _width);
|
||||
}
|
||||
}
|
||||
|
||||
@ -143,6 +147,8 @@ internal class IColorImageRows<T> : IImageRows
|
||||
this._width = width;
|
||||
this._height = height;
|
||||
this._stride = stride;
|
||||
|
||||
_bpp = T.ColorFormat.BytesPerPixel;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
@ -86,6 +86,17 @@ public readonly ref struct RefImage<T>
|
||||
|
||||
#region Methods
|
||||
|
||||
public static RefImage<T> Wrap(ReadOnlySpan<T> buffer, int width, int height)
|
||||
=> Wrap(MemoryMarshal.AsBytes(buffer), width, height, width * T.ColorFormat.BytesPerPixel);
|
||||
|
||||
public static RefImage<T> Wrap(ReadOnlySpan<byte> buffer, int width, int height, int stride)
|
||||
{
|
||||
if (stride < width) throw new ArgumentException("Stride can't be smaller than width.");
|
||||
if (buffer.Length < (height * stride)) throw new ArgumentException("Not enough data in the buffer.");
|
||||
|
||||
return new RefImage<T>(buffer, 0, 0, width, height, stride);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Copies the contents of this <see cref="RefImage{T}"/> into a destination <see cref="Span{T}"/> instance.
|
||||
/// </summary>
|
||||
|
||||
@ -13,16 +13,27 @@ public static partial class PixelHelper
|
||||
ArgumentNullException.ThrowIfNull(image);
|
||||
|
||||
int dataLength = image.SizeInBytes;
|
||||
byte[] array = ArrayPool<byte>.Shared.Rent(dataLength);
|
||||
Span<byte> buffer = array.AsSpan()[..dataLength];
|
||||
try
|
||||
|
||||
if (dataLength <= 1024)
|
||||
{
|
||||
Span<byte> buffer = stackalloc byte[dataLength];
|
||||
|
||||
image.CopyTo(buffer);
|
||||
return image.ColorFormat.Average(buffer);
|
||||
}
|
||||
finally
|
||||
else
|
||||
{
|
||||
ArrayPool<byte>.Shared.Return(array);
|
||||
byte[] array = ArrayPool<byte>.Shared.Rent(dataLength);
|
||||
Span<byte> buffer = array.AsSpan()[..dataLength];
|
||||
try
|
||||
{
|
||||
image.CopyTo(buffer);
|
||||
return image.ColorFormat.Average(buffer);
|
||||
}
|
||||
finally
|
||||
{
|
||||
ArrayPool<byte>.Shared.Return(array);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -34,16 +45,28 @@ public static partial class PixelHelper
|
||||
where T : struct, IColor
|
||||
{
|
||||
int dataLength = image.Width * image.Height;
|
||||
T[] array = ArrayPool<T>.Shared.Rent(dataLength);
|
||||
Span<T> buffer = array.AsSpan()[..(dataLength)];
|
||||
try
|
||||
int sizeInBytes = dataLength * T.ColorFormat.BytesPerPixel;
|
||||
|
||||
if (sizeInBytes <= 1024)
|
||||
{
|
||||
Span<T> buffer = MemoryMarshal.Cast<byte, T>(stackalloc byte[sizeInBytes]);
|
||||
|
||||
image.CopyTo(buffer);
|
||||
return Average(buffer);
|
||||
}
|
||||
finally
|
||||
else
|
||||
{
|
||||
ArrayPool<T>.Shared.Return(array);
|
||||
T[] array = ArrayPool<T>.Shared.Rent(dataLength);
|
||||
Span<T> buffer = array.AsSpan()[..dataLength];
|
||||
try
|
||||
{
|
||||
image.CopyTo(buffer);
|
||||
return Average(buffer);
|
||||
}
|
||||
finally
|
||||
{
|
||||
ArrayPool<T>.Shared.Return(array);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -82,9 +105,9 @@ public static partial class PixelHelper
|
||||
Generic4LongData sum = Sum(data);
|
||||
|
||||
float count = data.Length;
|
||||
return new Generic3ByteData((byte)(sum.L1 / count),
|
||||
(byte)(sum.L2 / count),
|
||||
(byte)(sum.L3 / count));
|
||||
return new Generic3ByteData((byte)MathF.Round(sum.L1 / count),
|
||||
(byte)MathF.Round(sum.L2 / count),
|
||||
(byte)MathF.Round(sum.L3 / count));
|
||||
}
|
||||
|
||||
private static Generic4ByteData Average(ReadOnlySpan<Generic4ByteData> data)
|
||||
@ -96,10 +119,10 @@ public static partial class PixelHelper
|
||||
Generic4LongData sum = Sum(data);
|
||||
|
||||
float count = data.Length;
|
||||
return new Generic4ByteData((byte)(sum.L1 / count),
|
||||
(byte)(sum.L2 / count),
|
||||
(byte)(sum.L3 / count),
|
||||
(byte)(sum.L4 / count));
|
||||
return new Generic4ByteData((byte)MathF.Round(sum.L1 / count),
|
||||
(byte)MathF.Round(sum.L2 / count),
|
||||
(byte)MathF.Round(sum.L3 / count),
|
||||
(byte)MathF.Round(sum.L4 / count));
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
@ -14,16 +14,27 @@ public static unsafe partial class PixelHelper
|
||||
ArgumentNullException.ThrowIfNull(image);
|
||||
|
||||
int dataLength = image.SizeInBytes;
|
||||
byte[] array = ArrayPool<byte>.Shared.Rent(dataLength);
|
||||
Span<byte> buffer = array.AsSpan()[..dataLength];
|
||||
try
|
||||
|
||||
if (dataLength <= 1024)
|
||||
{
|
||||
Span<byte> buffer = stackalloc byte[dataLength];
|
||||
|
||||
image.CopyTo(buffer);
|
||||
return image.ColorFormat.MinMax(buffer);
|
||||
}
|
||||
finally
|
||||
else
|
||||
{
|
||||
ArrayPool<byte>.Shared.Return(array);
|
||||
byte[] array = ArrayPool<byte>.Shared.Rent(dataLength);
|
||||
Span<byte> buffer = array.AsSpan()[..dataLength];
|
||||
try
|
||||
{
|
||||
image.CopyTo(buffer);
|
||||
return image.ColorFormat.MinMax(buffer);
|
||||
}
|
||||
finally
|
||||
{
|
||||
ArrayPool<byte>.Shared.Return(array);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -35,16 +46,28 @@ public static unsafe partial class PixelHelper
|
||||
where T : struct, IColor
|
||||
{
|
||||
int dataLength = image.Width * image.Height;
|
||||
T[] array = ArrayPool<T>.Shared.Rent(dataLength);
|
||||
Span<T> buffer = array.AsSpan()[..(dataLength)];
|
||||
try
|
||||
int sizeInBytes = dataLength * T.ColorFormat.BytesPerPixel;
|
||||
|
||||
if (sizeInBytes <= 1024)
|
||||
{
|
||||
Span<T> buffer = MemoryMarshal.Cast<byte, T>(stackalloc byte[sizeInBytes]);
|
||||
|
||||
image.CopyTo(buffer);
|
||||
return MinMax(buffer);
|
||||
}
|
||||
finally
|
||||
else
|
||||
{
|
||||
ArrayPool<T>.Shared.Return(array);
|
||||
T[] array = ArrayPool<T>.Shared.Rent(dataLength);
|
||||
Span<T> buffer = array.AsSpan()[..(dataLength)];
|
||||
try
|
||||
{
|
||||
image.CopyTo(buffer);
|
||||
return MinMax(buffer);
|
||||
}
|
||||
finally
|
||||
{
|
||||
ArrayPool<T>.Shared.Return(array);
|
||||
}
|
||||
}
|
||||
}
|
||||
public static IMinMax MinMax<T>(this Span<T> colors)
|
||||
|
||||
@ -1,5 +1,7 @@
|
||||
using System.Buffers;
|
||||
using System.Numerics;
|
||||
using System.Runtime.InteropServices;
|
||||
using static System.Net.Mime.MediaTypeNames;
|
||||
|
||||
namespace HPPH;
|
||||
|
||||
@ -12,16 +14,27 @@ public static partial class PixelHelper
|
||||
ArgumentNullException.ThrowIfNull(image);
|
||||
|
||||
int dataLength = image.SizeInBytes;
|
||||
byte[] array = ArrayPool<byte>.Shared.Rent(dataLength);
|
||||
Span<byte> buffer = array.AsSpan()[..dataLength];
|
||||
try
|
||||
|
||||
if (dataLength <= 1024)
|
||||
{
|
||||
Span<byte> buffer = stackalloc byte[dataLength];
|
||||
|
||||
image.CopyTo(buffer);
|
||||
return image.ColorFormat.CreateColorPalette(buffer, paletteSize);
|
||||
}
|
||||
finally
|
||||
else
|
||||
{
|
||||
ArrayPool<byte>.Shared.Return(array);
|
||||
byte[] array = ArrayPool<byte>.Shared.Rent(dataLength);
|
||||
Span<byte> buffer = array.AsSpan()[..dataLength];
|
||||
try
|
||||
{
|
||||
image.CopyTo(buffer);
|
||||
return image.ColorFormat.CreateColorPalette(buffer, paletteSize);
|
||||
}
|
||||
finally
|
||||
{
|
||||
ArrayPool<byte>.Shared.Return(array);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -33,57 +46,218 @@ public static partial class PixelHelper
|
||||
where T : unmanaged, IColor
|
||||
{
|
||||
int dataLength = image.Width * image.Height;
|
||||
T[] array = ArrayPool<T>.Shared.Rent(dataLength);
|
||||
Span<T> buffer = array.AsSpan()[..(dataLength)];
|
||||
try
|
||||
int sizeInBytes = dataLength * T.ColorFormat.BytesPerPixel;
|
||||
|
||||
if (sizeInBytes <= 1024)
|
||||
{
|
||||
Span<T> buffer = MemoryMarshal.Cast<byte, T>(stackalloc byte[sizeInBytes]);
|
||||
|
||||
image.CopyTo(buffer);
|
||||
return CreateColorPalette(buffer, paletteSize);
|
||||
}
|
||||
finally
|
||||
else
|
||||
{
|
||||
ArrayPool<T>.Shared.Return(array);
|
||||
T[] array = ArrayPool<T>.Shared.Rent(dataLength);
|
||||
Span<T> buffer = array.AsSpan()[..(dataLength)];
|
||||
try
|
||||
{
|
||||
image.CopyTo(buffer);
|
||||
return CreateColorPalette(buffer, paletteSize);
|
||||
}
|
||||
finally
|
||||
{
|
||||
ArrayPool<T>.Shared.Return(array);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static T[] CreateColorPalette<T>(this ReadOnlySpan<T> colors, int paletteSize)
|
||||
where T : unmanaged, IColor
|
||||
{
|
||||
T[] buffer = ArrayPool<T>.Shared.Rent(colors.Length);
|
||||
try
|
||||
{
|
||||
Span<T> colorBuffer = buffer.AsSpan()[..colors.Length];
|
||||
colors.CopyTo(colorBuffer);
|
||||
int sizeInBytes = colors.Length * T.ColorFormat.BytesPerPixel;
|
||||
|
||||
return CreateColorPalette(colorBuffer, paletteSize);
|
||||
}
|
||||
finally
|
||||
if (sizeInBytes <= 1024)
|
||||
{
|
||||
ArrayPool<T>.Shared.Return(buffer);
|
||||
Span<T> buffer = MemoryMarshal.Cast<byte, T>(stackalloc byte[sizeInBytes]);
|
||||
|
||||
colors.CopyTo(buffer);
|
||||
return CreateColorPalette(buffer, paletteSize);
|
||||
}
|
||||
else
|
||||
{
|
||||
T[] buffer = ArrayPool<T>.Shared.Rent(colors.Length);
|
||||
try
|
||||
{
|
||||
Span<T> colorBuffer = buffer.AsSpan()[..colors.Length];
|
||||
colors.CopyTo(colorBuffer);
|
||||
|
||||
return CreateColorPalette(colorBuffer, paletteSize);
|
||||
}
|
||||
finally
|
||||
{
|
||||
ArrayPool<T>.Shared.Return(buffer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static T[] CreateColorPalette<T>(this Span<T> 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<ColorCube<T>> cubes = new ColorCube<T>[paletteSize];
|
||||
//cubes[0] = new ColorCube<T>(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<T> a, out ColorCube<T> 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;
|
||||
|
||||
if (dataLength <= 1024)
|
||||
{
|
||||
Span<byte> buffer = stackalloc byte[dataLength];
|
||||
|
||||
image.CopyTo(buffer);
|
||||
return image.ColorFormat.CreateSimpleColorPalette(buffer, paletteSize);
|
||||
}
|
||||
else
|
||||
{
|
||||
byte[] array = ArrayPool<byte>.Shared.Rent(dataLength);
|
||||
Span<byte> buffer = array.AsSpan()[..dataLength];
|
||||
try
|
||||
{
|
||||
image.CopyTo(buffer);
|
||||
return image.ColorFormat.CreateSimpleColorPalette(buffer, paletteSize);
|
||||
}
|
||||
finally
|
||||
{
|
||||
ArrayPool<byte>.Shared.Return(array);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static T[] CreateSimpleColorPalette<T>(this IImage<T> image, int paletteSize)
|
||||
where T : unmanaged, IColor
|
||||
=> image.AsRefImage().CreateSimpleColorPalette(paletteSize);
|
||||
|
||||
public static T[] CreateSimpleColorPalette<T>(this RefImage<T> image, int paletteSize)
|
||||
where T : unmanaged, IColor
|
||||
{
|
||||
int dataLength = image.Width * image.Height;
|
||||
int sizeInBytes = dataLength * T.ColorFormat.BytesPerPixel;
|
||||
|
||||
if (sizeInBytes <= 1024)
|
||||
{
|
||||
Span<T> buffer = MemoryMarshal.Cast<byte, T>(stackalloc byte[sizeInBytes]);
|
||||
|
||||
image.CopyTo(buffer);
|
||||
return CreateSimpleColorPalette(buffer, paletteSize);
|
||||
}
|
||||
else
|
||||
{
|
||||
T[] array = ArrayPool<T>.Shared.Rent(dataLength);
|
||||
Span<T> buffer = array.AsSpan()[..(dataLength)];
|
||||
try
|
||||
{
|
||||
image.CopyTo(buffer);
|
||||
return CreateSimpleColorPalette(buffer, paletteSize);
|
||||
}
|
||||
finally
|
||||
{
|
||||
ArrayPool<T>.Shared.Return(array);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static T[] CreateSimpleColorPalette<T>(this ReadOnlySpan<T> colors, int paletteSize)
|
||||
where T : unmanaged, IColor
|
||||
{
|
||||
int sizeInBytes = colors.Length * T.ColorFormat.BytesPerPixel;
|
||||
|
||||
if (sizeInBytes <= 1024)
|
||||
{
|
||||
Span<T> buffer = MemoryMarshal.Cast<byte, T>(stackalloc byte[sizeInBytes]);
|
||||
|
||||
colors.CopyTo(buffer);
|
||||
return CreateSimpleColorPalette(buffer, paletteSize);
|
||||
}
|
||||
else
|
||||
{
|
||||
T[] buffer = ArrayPool<T>.Shared.Rent(colors.Length);
|
||||
try
|
||||
{
|
||||
Span<T> colorBuffer = buffer.AsSpan()[..colors.Length];
|
||||
colors.CopyTo(colorBuffer);
|
||||
|
||||
return CreateSimpleColorPalette(colorBuffer, paletteSize);
|
||||
}
|
||||
finally
|
||||
{
|
||||
ArrayPool<T>.Shared.Return(buffer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static unsafe T[] CreateSimpleColorPalette<T>(this Span<T> 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<ColorCube<T>> cubes = new ColorCube<T>[1 << splits];
|
||||
ColorCube<T>[] cubes = new ColorCube<T>[1 << splits];
|
||||
cubes[0] = new ColorCube<T>(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<ColorCube<T>> 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<T> a, out ColorCube<T> 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<T>(unsafeColorsPtr, colorsLength), out ColorCube<T> a, out ColorCube<T> b);
|
||||
cubes[index] = a;
|
||||
cubes[currentCubeCount + index] = b;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -21,5 +21,7 @@ public static unsafe partial class PixelHelper
|
||||
public static partial void SortByAlpha<T>(this Span<T> colors)
|
||||
where T : unmanaged, IColor;
|
||||
|
||||
//TODO DarthAffe 21.07.2024: Add CIE-Sorting
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
@ -15,16 +15,27 @@ public static unsafe partial class PixelHelper
|
||||
ArgumentNullException.ThrowIfNull(image);
|
||||
|
||||
int dataLength = image.SizeInBytes;
|
||||
byte[] array = ArrayPool<byte>.Shared.Rent(dataLength);
|
||||
Span<byte> buffer = array.AsSpan()[..dataLength];
|
||||
try
|
||||
|
||||
if (dataLength <= 1024)
|
||||
{
|
||||
Span<byte> buffer = stackalloc byte[dataLength];
|
||||
|
||||
image.CopyTo(buffer);
|
||||
return image.ColorFormat.Sum(buffer);
|
||||
}
|
||||
finally
|
||||
else
|
||||
{
|
||||
ArrayPool<byte>.Shared.Return(array);
|
||||
byte[] array = ArrayPool<byte>.Shared.Rent(dataLength);
|
||||
Span<byte> buffer = array.AsSpan()[..dataLength];
|
||||
try
|
||||
{
|
||||
image.CopyTo(buffer);
|
||||
return image.ColorFormat.Sum(buffer);
|
||||
}
|
||||
finally
|
||||
{
|
||||
ArrayPool<byte>.Shared.Return(array);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -36,16 +47,28 @@ public static unsafe partial class PixelHelper
|
||||
where T : struct, IColor
|
||||
{
|
||||
int dataLength = image.Width * image.Height;
|
||||
T[] array = ArrayPool<T>.Shared.Rent(dataLength);
|
||||
Span<T> buffer = array.AsSpan()[..(dataLength)];
|
||||
try
|
||||
int sizeInBytes = dataLength * T.ColorFormat.BytesPerPixel;
|
||||
|
||||
if (sizeInBytes <= 1024)
|
||||
{
|
||||
Span<T> buffer = MemoryMarshal.Cast<byte, T>(stackalloc byte[sizeInBytes]);
|
||||
|
||||
image.CopyTo(buffer);
|
||||
return Sum(buffer);
|
||||
}
|
||||
finally
|
||||
else
|
||||
{
|
||||
ArrayPool<T>.Shared.Return(array);
|
||||
T[] array = ArrayPool<T>.Shared.Rent(dataLength);
|
||||
Span<T> buffer = array.AsSpan()[..(dataLength)];
|
||||
try
|
||||
{
|
||||
image.CopyTo(buffer);
|
||||
return Sum(buffer);
|
||||
}
|
||||
finally
|
||||
{
|
||||
ArrayPool<T>.Shared.Return(array);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -65,7 +88,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<Generic4LongData, TSum>(Sum(MemoryMarshal.Cast<T, Generic3ByteData>(colors))),
|
||||
4 => Unsafe.BitCast<Generic4LongData, TSum>(Sum(MemoryMarshal.Cast<T, Generic4ByteData>(colors))),
|
||||
_ => throw new NotSupportedException("Data is not of a supported valid color-type.")
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user