Compare commits

...

6 Commits

21 changed files with 757 additions and 165 deletions

View File

@ -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);
}
""";
}

View File

@ -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

View File

@ -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 [];

View File

@ -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()
{

View File

@ -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()
{

View File

@ -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}");
//}
}

View File

@ -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
}

View File

@ -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
}

View File

@ -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
}

View File

@ -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
}

View File

@ -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
}

View File

@ -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
}

View File

@ -3,4 +3,5 @@
public partial interface IColorFormat
{
internal IColor[] CreateColorPalette(ReadOnlySpan<byte> data, int paletteSize);
internal IColor[] CreateSimpleColorPalette(ReadOnlySpan<byte> data, int paletteSize);
}

View File

@ -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

View File

@ -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

View File

@ -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>

View File

@ -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

View File

@ -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)

View File

@ -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;
}
}
}

View File

@ -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
}

View File

@ -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.")