mirror of
https://github.com/DarthAffe/HPPH.git
synced 2026-01-02 10:43:36 +00:00
Compare commits
6 Commits
7a67f77168
...
d0f2e2d983
| Author | SHA1 | Date | |
|---|---|---|---|
| d0f2e2d983 | |||
| 132bf7f54a | |||
| fb56257e87 | |||
| 597685f920 | |||
| 3f9dd933d3 | |||
| e34af1fbd3 |
@ -1,5 +1,4 @@
|
|||||||
using System;
|
using System.Text;
|
||||||
using System.Text;
|
|
||||||
|
|
||||||
namespace HPPH.Generators;
|
namespace HPPH.Generators;
|
||||||
|
|
||||||
@ -52,19 +51,19 @@ internal readonly struct ColorFormatData(string typeName, int bpp, char firstEnt
|
|||||||
string[] mapping = new string[Bpp];
|
string[] mapping = new string[Bpp];
|
||||||
if (Bpp > 0)
|
if (Bpp > 0)
|
||||||
{
|
{
|
||||||
mapping[0] = GetByteMappingIndex(FirstEntry).ToString();
|
mapping[0] = "Color." + FirstEntry.ToUpper();
|
||||||
|
|
||||||
if (Bpp > 1)
|
if (Bpp > 1)
|
||||||
{
|
{
|
||||||
mapping[1] = GetByteMappingIndex(SecondEntry).ToString();
|
mapping[1] = "Color." + SecondEntry.ToUpper();
|
||||||
|
|
||||||
if (Bpp > 2)
|
if (Bpp > 2)
|
||||||
{
|
{
|
||||||
mapping[2] = GetByteMappingIndex(ThirdEntry).ToString();
|
mapping[2] = "Color." + ThirdEntry.ToUpper();
|
||||||
|
|
||||||
if (Bpp > 3)
|
if (Bpp > 3)
|
||||||
{
|
{
|
||||||
mapping[3] = GetByteMappingIndex(FourthEntry).ToString();
|
mapping[3] = "Color." + FourthEntry.ToUpper();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -83,15 +82,5 @@ internal readonly struct ColorFormatData(string typeName, int bpp, char firstEnt
|
|||||||
_ => string.Empty
|
_ => string.Empty
|
||||||
};
|
};
|
||||||
|
|
||||||
private static int GetByteMappingIndex(string entry)
|
|
||||||
=> entry switch
|
|
||||||
{
|
|
||||||
"r" => 0,
|
|
||||||
"g" => 1,
|
|
||||||
"b" => 2,
|
|
||||||
"a" => 3,
|
|
||||||
_ => throw new IndexOutOfRangeException()
|
|
||||||
};
|
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
}
|
}
|
||||||
@ -26,7 +26,7 @@ public class AverageTests
|
|||||||
ReadOnlySpan<ColorRGB> span = data;
|
ReadOnlySpan<ColorRGB> span = data;
|
||||||
|
|
||||||
ColorRGB reference = ReferencePixelHelper.Average(span);
|
ColorRGB reference = ReferencePixelHelper.Average(span);
|
||||||
ColorRGB test = PixelHelper.Average(span);
|
ColorRGB test = span.Average();
|
||||||
|
|
||||||
Assert.AreEqual(reference.R, test.R, "R differs");
|
Assert.AreEqual(reference.R, test.R, "R differs");
|
||||||
Assert.AreEqual(reference.G, test.G, "G differs");
|
Assert.AreEqual(reference.G, test.G, "G differs");
|
||||||
@ -54,7 +54,7 @@ public class AverageTests
|
|||||||
ReadOnlySpan<ColorRGBA> span = data;
|
ReadOnlySpan<ColorRGBA> span = data;
|
||||||
|
|
||||||
ColorRGBA reference = ReferencePixelHelper.Average(span);
|
ColorRGBA reference = ReferencePixelHelper.Average(span);
|
||||||
ColorRGBA test = PixelHelper.Average(span);
|
ColorRGBA test = span.Average();
|
||||||
|
|
||||||
Assert.AreEqual(reference.R, test.R, "R differs");
|
Assert.AreEqual(reference.R, test.R, "R differs");
|
||||||
Assert.AreEqual(reference.G, test.G, "G differs");
|
Assert.AreEqual(reference.G, test.G, "G differs");
|
||||||
|
|||||||
@ -10,13 +10,15 @@ public class ConvertTests
|
|||||||
{
|
{
|
||||||
foreach (string image in GetTestImages())
|
foreach (string image in GetTestImages())
|
||||||
{
|
{
|
||||||
ColorRGB[] data = ImageHelper.Get3ByteColorsFromImage(image);
|
for (int skip = 0; skip < 4; skip++)
|
||||||
|
{
|
||||||
|
ColorRGB[] data = ImageHelper.Get3ByteColorsFromImage(image).SkipLast(skip).ToArray();
|
||||||
ReadOnlySpan<ColorRGB> referenceData = data;
|
ReadOnlySpan<ColorRGB> referenceData = data;
|
||||||
|
|
||||||
Span<ColorRGB> sourceData = new ColorRGB[referenceData.Length];
|
Span<ColorRGB> sourceData = new ColorRGB[referenceData.Length];
|
||||||
referenceData.CopyTo(sourceData);
|
referenceData.CopyTo(sourceData);
|
||||||
|
|
||||||
Span<ColorBGR> result = PixelHelper.Convert<ColorRGB, ColorBGR>(sourceData);
|
Span<ColorBGR> result = sourceData.Convert<ColorRGB, ColorBGR>();
|
||||||
|
|
||||||
Assert.AreEqual(referenceData.Length, result.Length);
|
Assert.AreEqual(referenceData.Length, result.Length);
|
||||||
for (int i = 0; i < referenceData.Length; i++)
|
for (int i = 0; i < referenceData.Length; i++)
|
||||||
@ -24,10 +26,11 @@ public class ConvertTests
|
|||||||
ColorRGB reference = referenceData[i];
|
ColorRGB reference = referenceData[i];
|
||||||
ColorBGR test = result[i];
|
ColorBGR test = result[i];
|
||||||
|
|
||||||
Assert.AreEqual(reference.R, test.R, "R differs");
|
Assert.AreEqual(reference.R, test.R, $"R differs at index {i}");
|
||||||
Assert.AreEqual(reference.G, test.G, "G differs");
|
Assert.AreEqual(reference.G, test.G, $"G differs at index {i}");
|
||||||
Assert.AreEqual(reference.B, test.B, "B differs");
|
Assert.AreEqual(reference.B, test.B, $"B differs at index {i}");
|
||||||
Assert.AreEqual(reference.A, test.A, "A differs");
|
Assert.AreEqual(reference.A, test.A, $"A differs at index {i}");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -37,13 +40,15 @@ public class ConvertTests
|
|||||||
{
|
{
|
||||||
foreach (string image in GetTestImages())
|
foreach (string image in GetTestImages())
|
||||||
{
|
{
|
||||||
ColorRGBA[] data = ImageHelper.Get4ByteColorsFromImage(image);
|
for (int skip = 0; skip < 4; skip++)
|
||||||
|
{
|
||||||
|
ColorRGBA[] data = ImageHelper.Get4ByteColorsFromImage(image).SkipLast(skip).ToArray();
|
||||||
ReadOnlySpan<ColorRGBA> referenceData = data;
|
ReadOnlySpan<ColorRGBA> referenceData = data;
|
||||||
|
|
||||||
Span<ColorRGBA> sourceData = new ColorRGBA[referenceData.Length];
|
Span<ColorRGBA> sourceData = new ColorRGBA[referenceData.Length];
|
||||||
referenceData.CopyTo(sourceData);
|
referenceData.CopyTo(sourceData);
|
||||||
|
|
||||||
Span<ColorARGB> result = PixelHelper.Convert<ColorRGBA, ColorARGB>(sourceData);
|
Span<ColorARGB> result = sourceData.Convert<ColorRGBA, ColorARGB>();
|
||||||
|
|
||||||
Assert.AreEqual(referenceData.Length, result.Length);
|
Assert.AreEqual(referenceData.Length, result.Length);
|
||||||
for (int i = 0; i < referenceData.Length; i++)
|
for (int i = 0; i < referenceData.Length; i++)
|
||||||
@ -51,10 +56,11 @@ public class ConvertTests
|
|||||||
ColorRGBA reference = referenceData[i];
|
ColorRGBA reference = referenceData[i];
|
||||||
ColorARGB test = result[i];
|
ColorARGB test = result[i];
|
||||||
|
|
||||||
Assert.AreEqual(reference.R, test.R, "R differs");
|
Assert.AreEqual(reference.R, test.R, $"R differs at index {i}");
|
||||||
Assert.AreEqual(reference.G, test.G, "G differs");
|
Assert.AreEqual(reference.G, test.G, $"G differs at index {i}");
|
||||||
Assert.AreEqual(reference.B, test.B, "B differs");
|
Assert.AreEqual(reference.B, test.B, $"B differs at index {i}");
|
||||||
Assert.AreEqual(reference.A, test.A, "A differs");
|
Assert.AreEqual(reference.A, test.A, $"A differs at index {i}");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -64,13 +70,15 @@ public class ConvertTests
|
|||||||
{
|
{
|
||||||
foreach (string image in GetTestImages())
|
foreach (string image in GetTestImages())
|
||||||
{
|
{
|
||||||
ColorRGBA[] data = ImageHelper.Get4ByteColorsFromImage(image);
|
for (int skip = 0; skip < 4; skip++)
|
||||||
|
{
|
||||||
|
ColorRGBA[] data = ImageHelper.Get4ByteColorsFromImage(image).SkipLast(skip).ToArray();
|
||||||
ReadOnlySpan<ColorRGBA> referenceData = data;
|
ReadOnlySpan<ColorRGBA> referenceData = data;
|
||||||
|
|
||||||
Span<ColorRGBA> sourceData = new ColorRGBA[referenceData.Length];
|
Span<ColorRGBA> sourceData = new ColorRGBA[referenceData.Length];
|
||||||
referenceData.CopyTo(sourceData);
|
referenceData.CopyTo(sourceData);
|
||||||
|
|
||||||
Span<ColorBGRA> result = PixelHelper.Convert<ColorRGBA, ColorBGRA>(sourceData);
|
Span<ColorBGRA> result = sourceData.Convert<ColorRGBA, ColorBGRA>();
|
||||||
|
|
||||||
Assert.AreEqual(referenceData.Length, result.Length);
|
Assert.AreEqual(referenceData.Length, result.Length);
|
||||||
for (int i = 0; i < referenceData.Length; i++)
|
for (int i = 0; i < referenceData.Length; i++)
|
||||||
@ -78,10 +86,191 @@ public class ConvertTests
|
|||||||
ColorRGBA reference = referenceData[i];
|
ColorRGBA reference = referenceData[i];
|
||||||
ColorBGRA test = result[i];
|
ColorBGRA test = result[i];
|
||||||
|
|
||||||
Assert.AreEqual(reference.R, test.R, "R differs");
|
Assert.AreEqual(reference.R, test.R, $"R differs at index {i}");
|
||||||
Assert.AreEqual(reference.G, test.G, "G differs");
|
Assert.AreEqual(reference.G, test.G, $"G differs at index {i}");
|
||||||
Assert.AreEqual(reference.B, test.B, "B differs");
|
Assert.AreEqual(reference.B, test.B, $"B differs at index {i}");
|
||||||
Assert.AreEqual(reference.A, test.A, "A differs");
|
Assert.AreEqual(reference.A, test.A, $"A differs at index {i}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public void ConvertNarrow4ByteRGBAToRGB()
|
||||||
|
{
|
||||||
|
foreach (string image in GetTestImages())
|
||||||
|
{
|
||||||
|
for (int skip = 0; skip < 4; skip++)
|
||||||
|
{
|
||||||
|
ColorRGBA[] data = ImageHelper.Get4ByteColorsFromImage(image).SkipLast(skip).ToArray();
|
||||||
|
ReadOnlySpan<ColorRGBA> referenceData = data;
|
||||||
|
|
||||||
|
Span<ColorRGBA> sourceData = new ColorRGBA[referenceData.Length];
|
||||||
|
referenceData.CopyTo(sourceData);
|
||||||
|
|
||||||
|
Span<ColorRGB> result = sourceData.Convert<ColorRGBA, ColorRGB>();
|
||||||
|
|
||||||
|
Assert.AreEqual(referenceData.Length, result.Length);
|
||||||
|
for (int i = 0; i < referenceData.Length; i++)
|
||||||
|
{
|
||||||
|
ColorRGBA reference = referenceData[i];
|
||||||
|
ColorRGB test = result[i];
|
||||||
|
|
||||||
|
Assert.AreEqual(reference.R, test.R, $"R differs at index {i}");
|
||||||
|
Assert.AreEqual(reference.G, test.G, $"G differs at index {i}");
|
||||||
|
Assert.AreEqual(reference.B, test.B, $"B differs at index {i}");
|
||||||
|
Assert.AreEqual(reference.A, test.A, $"A differs at index {i}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public void ConvertNarrow4ByteRGBAToBGR()
|
||||||
|
{
|
||||||
|
foreach (string image in GetTestImages())
|
||||||
|
{
|
||||||
|
for (int skip = 0; skip < 4; skip++)
|
||||||
|
{
|
||||||
|
ColorRGBA[] data = ImageHelper.Get4ByteColorsFromImage(image).SkipLast(skip).ToArray();
|
||||||
|
ReadOnlySpan<ColorRGBA> referenceData = data;
|
||||||
|
|
||||||
|
Span<ColorRGBA> sourceData = new ColorRGBA[referenceData.Length];
|
||||||
|
referenceData.CopyTo(sourceData);
|
||||||
|
|
||||||
|
Span<ColorBGR> result = sourceData.Convert<ColorRGBA, ColorBGR>();
|
||||||
|
|
||||||
|
Assert.AreEqual(referenceData.Length, result.Length);
|
||||||
|
for (int i = 0; i < referenceData.Length; i++)
|
||||||
|
{
|
||||||
|
ColorRGBA reference = referenceData[i];
|
||||||
|
ColorBGR test = result[i];
|
||||||
|
|
||||||
|
Assert.AreEqual(reference.R, test.R, $"R differs at index {i}");
|
||||||
|
Assert.AreEqual(reference.G, test.G, $"G differs at index {i}");
|
||||||
|
Assert.AreEqual(reference.B, test.B, $"B differs at index {i}");
|
||||||
|
Assert.AreEqual(reference.A, test.A, $"A differs at index {i}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public void ConvertWiden3ByteRGBToRGBA()
|
||||||
|
{
|
||||||
|
foreach (string image in GetTestImages())
|
||||||
|
{
|
||||||
|
for (int skip = 0; skip < 4; skip++)
|
||||||
|
{
|
||||||
|
ColorRGB[] data = ImageHelper.Get3ByteColorsFromImage(image).SkipLast(skip).ToArray();
|
||||||
|
ReadOnlySpan<ColorRGB> referenceData = data;
|
||||||
|
|
||||||
|
Span<ColorRGB> sourceData = new ColorRGB[referenceData.Length];
|
||||||
|
referenceData.CopyTo(sourceData);
|
||||||
|
|
||||||
|
Span<ColorRGBA> result = sourceData.Convert<ColorRGB, ColorRGBA>();
|
||||||
|
|
||||||
|
Assert.AreEqual(referenceData.Length, result.Length);
|
||||||
|
for (int i = 0; i < referenceData.Length; i++)
|
||||||
|
{
|
||||||
|
ColorRGB reference = referenceData[i];
|
||||||
|
ColorRGBA test = result[i];
|
||||||
|
|
||||||
|
Assert.AreEqual(reference.R, test.R, $"R differs at index {i}");
|
||||||
|
Assert.AreEqual(reference.G, test.G, $"G differs at index {i}");
|
||||||
|
Assert.AreEqual(reference.B, test.B, $"B differs at index {i}");
|
||||||
|
Assert.AreEqual(reference.A, test.A, $"A differs at index {i}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public void ConvertWiden3ByteRGBToARGB()
|
||||||
|
{
|
||||||
|
foreach (string image in GetTestImages())
|
||||||
|
{
|
||||||
|
for (int skip = 0; skip < 4; skip++)
|
||||||
|
{
|
||||||
|
ColorRGB[] data = ImageHelper.Get3ByteColorsFromImage(image).SkipLast(skip).ToArray();
|
||||||
|
ReadOnlySpan<ColorRGB> referenceData = data;
|
||||||
|
|
||||||
|
Span<ColorRGB> sourceData = new ColorRGB[referenceData.Length];
|
||||||
|
referenceData.CopyTo(sourceData);
|
||||||
|
|
||||||
|
Span<ColorARGB> result = sourceData.Convert<ColorRGB, ColorARGB>();
|
||||||
|
|
||||||
|
Assert.AreEqual(referenceData.Length, result.Length);
|
||||||
|
for (int i = 0; i < referenceData.Length; i++)
|
||||||
|
{
|
||||||
|
ColorRGB reference = referenceData[i];
|
||||||
|
ColorARGB test = result[i];
|
||||||
|
|
||||||
|
Assert.AreEqual(reference.R, test.R, $"R differs at index {i}");
|
||||||
|
Assert.AreEqual(reference.G, test.G, $"G differs at index {i}");
|
||||||
|
Assert.AreEqual(reference.B, test.B, $"B differs at index {i}");
|
||||||
|
Assert.AreEqual(reference.A, test.A, $"A differs at index {i}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public void ConvertWiden3ByteRGBToBGRA()
|
||||||
|
{
|
||||||
|
foreach (string image in GetTestImages())
|
||||||
|
{
|
||||||
|
for (int skip = 0; skip < 4; skip++)
|
||||||
|
{
|
||||||
|
ColorRGB[] data = ImageHelper.Get3ByteColorsFromImage(image).SkipLast(skip).ToArray();
|
||||||
|
ReadOnlySpan<ColorRGB> referenceData = data;
|
||||||
|
|
||||||
|
Span<ColorRGB> sourceData = new ColorRGB[referenceData.Length];
|
||||||
|
referenceData.CopyTo(sourceData);
|
||||||
|
|
||||||
|
Span<ColorBGRA> result = sourceData.Convert<ColorRGB, ColorBGRA>();
|
||||||
|
|
||||||
|
Assert.AreEqual(referenceData.Length, result.Length);
|
||||||
|
for (int i = 0; i < referenceData.Length; i++)
|
||||||
|
{
|
||||||
|
ColorRGB reference = referenceData[i];
|
||||||
|
ColorBGRA test = result[i];
|
||||||
|
|
||||||
|
Assert.AreEqual(reference.R, test.R, $"R differs at index {i}");
|
||||||
|
Assert.AreEqual(reference.G, test.G, $"G differs at index {i}");
|
||||||
|
Assert.AreEqual(reference.B, test.B, $"B differs at index {i}");
|
||||||
|
Assert.AreEqual(reference.A, test.A, $"A differs at index {i}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public void ConvertWiden3ByteRGBToABGR()
|
||||||
|
{
|
||||||
|
foreach (string image in GetTestImages())
|
||||||
|
{
|
||||||
|
for (int skip = 0; skip < 4; skip++)
|
||||||
|
{
|
||||||
|
ColorRGB[] data = ImageHelper.Get3ByteColorsFromImage(image).SkipLast(skip).ToArray();
|
||||||
|
ReadOnlySpan<ColorRGB> referenceData = data;
|
||||||
|
|
||||||
|
Span<ColorRGB> sourceData = new ColorRGB[referenceData.Length];
|
||||||
|
referenceData.CopyTo(sourceData);
|
||||||
|
|
||||||
|
Span<ColorABGR> result = sourceData.Convert<ColorRGB, ColorABGR>();
|
||||||
|
|
||||||
|
Assert.AreEqual(referenceData.Length, result.Length);
|
||||||
|
for (int i = 0; i < referenceData.Length; i++)
|
||||||
|
{
|
||||||
|
ColorRGB reference = referenceData[i];
|
||||||
|
ColorABGR test = result[i];
|
||||||
|
|
||||||
|
Assert.AreEqual(reference.R, test.R, $"R differs at index {i}");
|
||||||
|
Assert.AreEqual(reference.G, test.G, $"G differs at index {i}");
|
||||||
|
Assert.AreEqual(reference.B, test.B, $"B differs at index {i}");
|
||||||
|
Assert.AreEqual(reference.A, test.A, $"A differs at index {i}");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -26,7 +26,7 @@ public class MinMaxTests
|
|||||||
ReadOnlySpan<ColorRGB> span = data;
|
ReadOnlySpan<ColorRGB> span = data;
|
||||||
|
|
||||||
IMinMax reference = ReferencePixelHelper.MinMax(span);
|
IMinMax reference = ReferencePixelHelper.MinMax(span);
|
||||||
IMinMax test = PixelHelper.MinMax(span);
|
IMinMax test = span.MinMax();
|
||||||
|
|
||||||
Assert.AreEqual(reference.RedMin, test.RedMin, "RedMin differs");
|
Assert.AreEqual(reference.RedMin, test.RedMin, "RedMin differs");
|
||||||
Assert.AreEqual(reference.GreenMin, test.GreenMin, "GreenMin differs");
|
Assert.AreEqual(reference.GreenMin, test.GreenMin, "GreenMin differs");
|
||||||
@ -64,7 +64,7 @@ public class MinMaxTests
|
|||||||
ReadOnlySpan<ColorRGBA> span = data;
|
ReadOnlySpan<ColorRGBA> span = data;
|
||||||
|
|
||||||
IMinMax reference = ReferencePixelHelper.MinMax(span);
|
IMinMax reference = ReferencePixelHelper.MinMax(span);
|
||||||
IMinMax test = PixelHelper.MinMax(span);
|
IMinMax test = span.MinMax();
|
||||||
|
|
||||||
Assert.AreEqual(reference.RedMin, test.RedMin, "RedMin differs");
|
Assert.AreEqual(reference.RedMin, test.RedMin, "RedMin differs");
|
||||||
Assert.AreEqual(reference.GreenMin, test.GreenMin, "GreenMin differs");
|
Assert.AreEqual(reference.GreenMin, test.GreenMin, "GreenMin differs");
|
||||||
|
|||||||
@ -16,7 +16,7 @@ public class CreateColorPaletteTests
|
|||||||
Span<ColorRGB> span = data;
|
Span<ColorRGB> span = data;
|
||||||
|
|
||||||
ColorRGB[] reference = [.. ReferencePixelHelper.CreateColorPalette(span, 1).OrderBy(x => x.R).ThenBy(x => x.G).ThenBy(x => x.B).ThenBy(x => x.A)];
|
ColorRGB[] reference = [.. ReferencePixelHelper.CreateColorPalette(span, 1).OrderBy(x => x.R).ThenBy(x => x.G).ThenBy(x => x.B).ThenBy(x => x.A)];
|
||||||
ColorRGB[] test = [.. PixelHelper.CreateColorPalette(span, 1).OrderBy(x => x.R).ThenBy(x => x.G).ThenBy(x => x.B).ThenBy(x => x.A)];
|
ColorRGB[] test = [.. span.CreateColorPalette(1).OrderBy(x => x.R).ThenBy(x => x.G).ThenBy(x => x.B).ThenBy(x => x.A)];
|
||||||
|
|
||||||
Assert.AreEqual(reference.Length, test.Length, "Palette Size differs");
|
Assert.AreEqual(reference.Length, test.Length, "Palette Size differs");
|
||||||
|
|
||||||
@ -34,7 +34,7 @@ public class CreateColorPaletteTests
|
|||||||
Span<ColorRGB> span = data;
|
Span<ColorRGB> span = data;
|
||||||
|
|
||||||
ColorRGB[] reference = [.. ReferencePixelHelper.CreateColorPalette(span, 2).OrderBy(x => x.R).ThenBy(x => x.G).ThenBy(x => x.B).ThenBy(x => x.A)];
|
ColorRGB[] reference = [.. ReferencePixelHelper.CreateColorPalette(span, 2).OrderBy(x => x.R).ThenBy(x => x.G).ThenBy(x => x.B).ThenBy(x => x.A)];
|
||||||
ColorRGB[] test = [.. PixelHelper.CreateColorPalette(span, 2).OrderBy(x => x.R).ThenBy(x => x.G).ThenBy(x => x.B).ThenBy(x => x.A)];
|
ColorRGB[] test = [.. span.CreateColorPalette(2).OrderBy(x => x.R).ThenBy(x => x.G).ThenBy(x => x.B).ThenBy(x => x.A)];
|
||||||
|
|
||||||
Assert.AreEqual(reference.Length, test.Length, "Palette Size differs");
|
Assert.AreEqual(reference.Length, test.Length, "Palette Size differs");
|
||||||
|
|
||||||
@ -52,7 +52,7 @@ public class CreateColorPaletteTests
|
|||||||
Span<ColorRGB> span = data;
|
Span<ColorRGB> span = data;
|
||||||
|
|
||||||
ColorRGB[] reference = [.. ReferencePixelHelper.CreateColorPalette(span, 16).OrderBy(x => x.R).ThenBy(x => x.G).ThenBy(x => x.B).ThenBy(x => x.A)];
|
ColorRGB[] reference = [.. ReferencePixelHelper.CreateColorPalette(span, 16).OrderBy(x => x.R).ThenBy(x => x.G).ThenBy(x => x.B).ThenBy(x => x.A)];
|
||||||
ColorRGB[] test = [.. PixelHelper.CreateColorPalette(span, 16).OrderBy(x => x.R).ThenBy(x => x.G).ThenBy(x => x.B).ThenBy(x => x.A)];
|
ColorRGB[] test = [.. span.CreateColorPalette(16).OrderBy(x => x.R).ThenBy(x => x.G).ThenBy(x => x.B).ThenBy(x => x.A)];
|
||||||
|
|
||||||
Assert.AreEqual(reference.Length, test.Length, "Palette Size differs");
|
Assert.AreEqual(reference.Length, test.Length, "Palette Size differs");
|
||||||
|
|
||||||
@ -70,7 +70,7 @@ public class CreateColorPaletteTests
|
|||||||
Span<ColorRGBA> span = data;
|
Span<ColorRGBA> span = data;
|
||||||
|
|
||||||
ColorRGBA[] reference = [.. ReferencePixelHelper.CreateColorPalette(span, 1).OrderBy(x => x.R).ThenBy(x => x.G).ThenBy(x => x.B).ThenBy(x => x.A)];
|
ColorRGBA[] reference = [.. ReferencePixelHelper.CreateColorPalette(span, 1).OrderBy(x => x.R).ThenBy(x => x.G).ThenBy(x => x.B).ThenBy(x => x.A)];
|
||||||
ColorRGBA[] test = [.. PixelHelper.CreateColorPalette(span, 1).OrderBy(x => x.R).ThenBy(x => x.G).ThenBy(x => x.B).ThenBy(x => x.A)];
|
ColorRGBA[] test = [.. span.CreateColorPalette(1).OrderBy(x => x.R).ThenBy(x => x.G).ThenBy(x => x.B).ThenBy(x => x.A)];
|
||||||
|
|
||||||
Assert.AreEqual(reference.Length, test.Length, "Palette Size differs");
|
Assert.AreEqual(reference.Length, test.Length, "Palette Size differs");
|
||||||
|
|
||||||
@ -88,7 +88,7 @@ public class CreateColorPaletteTests
|
|||||||
Span<ColorRGBA> span = data;
|
Span<ColorRGBA> span = data;
|
||||||
|
|
||||||
ColorRGBA[] reference = [.. ReferencePixelHelper.CreateColorPalette(span, 2).OrderBy(x => x.R).ThenBy(x => x.G).ThenBy(x => x.B).ThenBy(x => x.A)];
|
ColorRGBA[] reference = [.. ReferencePixelHelper.CreateColorPalette(span, 2).OrderBy(x => x.R).ThenBy(x => x.G).ThenBy(x => x.B).ThenBy(x => x.A)];
|
||||||
ColorRGBA[] test = [.. PixelHelper.CreateColorPalette(span, 2).OrderBy(x => x.R).ThenBy(x => x.G).ThenBy(x => x.B).ThenBy(x => x.A)];
|
ColorRGBA[] test = [.. span.CreateColorPalette(2).OrderBy(x => x.R).ThenBy(x => x.G).ThenBy(x => x.B).ThenBy(x => x.A)];
|
||||||
|
|
||||||
Assert.AreEqual(reference.Length, test.Length, "Palette Size differs");
|
Assert.AreEqual(reference.Length, test.Length, "Palette Size differs");
|
||||||
|
|
||||||
@ -106,7 +106,7 @@ public class CreateColorPaletteTests
|
|||||||
Span<ColorRGBA> span = data;
|
Span<ColorRGBA> span = data;
|
||||||
|
|
||||||
ColorRGBA[] reference = [.. ReferencePixelHelper.CreateColorPalette(span, 16).OrderBy(x => x.R).ThenBy(x => x.G).ThenBy(x => x.B).ThenBy(x => x.A)];
|
ColorRGBA[] reference = [.. ReferencePixelHelper.CreateColorPalette(span, 16).OrderBy(x => x.R).ThenBy(x => x.G).ThenBy(x => x.B).ThenBy(x => x.A)];
|
||||||
ColorRGBA[] test = [.. PixelHelper.CreateColorPalette(span, 16).OrderBy(x => x.R).ThenBy(x => x.G).ThenBy(x => x.B).ThenBy(x => x.A)];
|
ColorRGBA[] test = [.. span.CreateColorPalette(16).OrderBy(x => x.R).ThenBy(x => x.G).ThenBy(x => x.B).ThenBy(x => x.A)];
|
||||||
|
|
||||||
Assert.AreEqual(reference.Length, test.Length, "Palette Size differs");
|
Assert.AreEqual(reference.Length, test.Length, "Palette Size differs");
|
||||||
|
|
||||||
|
|||||||
@ -20,7 +20,7 @@ public class SortTests
|
|||||||
referenceSpan.CopyTo(testSpan);
|
referenceSpan.CopyTo(testSpan);
|
||||||
|
|
||||||
ReferencePixelHelper.SortByRed(referenceSpan);
|
ReferencePixelHelper.SortByRed(referenceSpan);
|
||||||
PixelHelper.SortByRed(testSpan);
|
testSpan.SortByRed();
|
||||||
|
|
||||||
for (int i = 0; i < referenceData.Length; i++)
|
for (int i = 0; i < referenceData.Length; i++)
|
||||||
Assert.AreEqual(referenceSpan[i], testSpan[i], $"Index {i} differs");
|
Assert.AreEqual(referenceSpan[i], testSpan[i], $"Index {i} differs");
|
||||||
@ -39,7 +39,7 @@ public class SortTests
|
|||||||
referenceSpan.CopyTo(testSpan);
|
referenceSpan.CopyTo(testSpan);
|
||||||
|
|
||||||
ReferencePixelHelper.SortByGreen(referenceSpan);
|
ReferencePixelHelper.SortByGreen(referenceSpan);
|
||||||
PixelHelper.SortByGreen(testSpan);
|
testSpan.SortByGreen();
|
||||||
|
|
||||||
for (int i = 0; i < referenceData.Length; i++)
|
for (int i = 0; i < referenceData.Length; i++)
|
||||||
Assert.AreEqual(referenceSpan[i], testSpan[i], $"Index {i} differs");
|
Assert.AreEqual(referenceSpan[i], testSpan[i], $"Index {i} differs");
|
||||||
@ -58,7 +58,7 @@ public class SortTests
|
|||||||
referenceSpan.CopyTo(testSpan);
|
referenceSpan.CopyTo(testSpan);
|
||||||
|
|
||||||
ReferencePixelHelper.SortByBlue(referenceSpan);
|
ReferencePixelHelper.SortByBlue(referenceSpan);
|
||||||
PixelHelper.SortByBlue(testSpan);
|
testSpan.SortByBlue();
|
||||||
|
|
||||||
for (int i = 0; i < referenceData.Length; i++)
|
for (int i = 0; i < referenceData.Length; i++)
|
||||||
Assert.AreEqual(referenceSpan[i], testSpan[i], $"Index {i} differs");
|
Assert.AreEqual(referenceSpan[i], testSpan[i], $"Index {i} differs");
|
||||||
@ -77,7 +77,7 @@ public class SortTests
|
|||||||
referenceSpan.CopyTo(testSpan);
|
referenceSpan.CopyTo(testSpan);
|
||||||
|
|
||||||
ReferencePixelHelper.SortByRed(referenceSpan);
|
ReferencePixelHelper.SortByRed(referenceSpan);
|
||||||
PixelHelper.SortByRed(testSpan);
|
testSpan.SortByRed();
|
||||||
|
|
||||||
for (int i = 0; i < referenceData.Length; i++)
|
for (int i = 0; i < referenceData.Length; i++)
|
||||||
Assert.AreEqual(referenceSpan[i], testSpan[i], $"Index {i} differs");
|
Assert.AreEqual(referenceSpan[i], testSpan[i], $"Index {i} differs");
|
||||||
@ -96,7 +96,7 @@ public class SortTests
|
|||||||
referenceSpan.CopyTo(testSpan);
|
referenceSpan.CopyTo(testSpan);
|
||||||
|
|
||||||
ReferencePixelHelper.SortByGreen(referenceSpan);
|
ReferencePixelHelper.SortByGreen(referenceSpan);
|
||||||
PixelHelper.SortByGreen(testSpan);
|
testSpan.SortByGreen();
|
||||||
|
|
||||||
for (int i = 0; i < referenceData.Length; i++)
|
for (int i = 0; i < referenceData.Length; i++)
|
||||||
Assert.AreEqual(referenceSpan[i], testSpan[i], $"Index {i} differs");
|
Assert.AreEqual(referenceSpan[i], testSpan[i], $"Index {i} differs");
|
||||||
@ -115,7 +115,7 @@ public class SortTests
|
|||||||
referenceSpan.CopyTo(testSpan);
|
referenceSpan.CopyTo(testSpan);
|
||||||
|
|
||||||
ReferencePixelHelper.SortByBlue(referenceSpan);
|
ReferencePixelHelper.SortByBlue(referenceSpan);
|
||||||
PixelHelper.SortByBlue(testSpan);
|
testSpan.SortByBlue();
|
||||||
|
|
||||||
for (int i = 0; i < referenceData.Length; i++)
|
for (int i = 0; i < referenceData.Length; i++)
|
||||||
Assert.AreEqual(referenceSpan[i], testSpan[i], $"Index {i} differs");
|
Assert.AreEqual(referenceSpan[i], testSpan[i], $"Index {i} differs");
|
||||||
@ -134,7 +134,7 @@ public class SortTests
|
|||||||
referenceSpan.CopyTo(testSpan);
|
referenceSpan.CopyTo(testSpan);
|
||||||
|
|
||||||
ReferencePixelHelper.SortByAlpha(referenceSpan);
|
ReferencePixelHelper.SortByAlpha(referenceSpan);
|
||||||
PixelHelper.SortByAlpha(testSpan);
|
testSpan.SortByAlpha();
|
||||||
|
|
||||||
for (int i = 0; i < referenceData.Length; i++)
|
for (int i = 0; i < referenceData.Length; i++)
|
||||||
Assert.AreEqual(referenceSpan[i], testSpan[i], $"Index {i} differs");
|
Assert.AreEqual(referenceSpan[i], testSpan[i], $"Index {i} differs");
|
||||||
|
|||||||
@ -26,7 +26,7 @@ public class SumTests
|
|||||||
ReadOnlySpan<ColorRGB> span = data;
|
ReadOnlySpan<ColorRGB> span = data;
|
||||||
|
|
||||||
ISum reference = ReferencePixelHelper.Sum(span);
|
ISum reference = ReferencePixelHelper.Sum(span);
|
||||||
ISum test = PixelHelper.Sum(span);
|
ISum test = span.Sum();
|
||||||
|
|
||||||
Assert.AreEqual(reference.R, test.R, "R differs");
|
Assert.AreEqual(reference.R, test.R, "R differs");
|
||||||
Assert.AreEqual(reference.G, test.G, "G differs");
|
Assert.AreEqual(reference.G, test.G, "G differs");
|
||||||
@ -54,7 +54,7 @@ public class SumTests
|
|||||||
ReadOnlySpan<ColorRGBA> span = data;
|
ReadOnlySpan<ColorRGBA> span = data;
|
||||||
|
|
||||||
ISum reference = ReferencePixelHelper.Sum(span);
|
ISum reference = ReferencePixelHelper.Sum(span);
|
||||||
ISum test = PixelHelper.Sum(span);
|
ISum test = span.Sum();
|
||||||
|
|
||||||
Assert.AreEqual(reference.R, test.R, "R differs");
|
Assert.AreEqual(reference.R, test.R, "R differs");
|
||||||
Assert.AreEqual(reference.G, test.G, "G differs");
|
Assert.AreEqual(reference.G, test.G, "G differs");
|
||||||
|
|||||||
@ -3,6 +3,14 @@
|
|||||||
|
|
||||||
namespace HPPH;
|
namespace HPPH;
|
||||||
|
|
||||||
|
internal static class Color
|
||||||
|
{
|
||||||
|
internal const int R = 0;
|
||||||
|
internal const int G = 1;
|
||||||
|
internal const int B = 2;
|
||||||
|
internal const int A = 3;
|
||||||
|
}
|
||||||
|
|
||||||
[ColorGenerator]
|
[ColorGenerator]
|
||||||
public readonly partial struct ColorRGB;
|
public readonly partial struct ColorRGB;
|
||||||
|
|
||||||
|
|||||||
@ -4,7 +4,7 @@ namespace HPPH;
|
|||||||
|
|
||||||
public static unsafe partial class PixelHelper
|
public static unsafe partial class PixelHelper
|
||||||
{
|
{
|
||||||
public static partial void SortByRed<T>(Span<T> colors) where T : unmanaged, IColor
|
public static partial void SortByRed<T>(this Span<T> colors) where T : unmanaged, IColor
|
||||||
{
|
{
|
||||||
fixed (T* ptr = colors)
|
fixed (T* ptr = colors)
|
||||||
{
|
{
|
||||||
@ -39,7 +39,7 @@ public static unsafe partial class PixelHelper
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
public static partial void SortByGreen<T>(Span<T> colors) where T : unmanaged, IColor
|
public static partial void SortByGreen<T>(this Span<T> colors) where T : unmanaged, IColor
|
||||||
{
|
{
|
||||||
fixed (T* ptr = colors)
|
fixed (T* ptr = colors)
|
||||||
{
|
{
|
||||||
@ -74,7 +74,7 @@ public static unsafe partial class PixelHelper
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
public static partial void SortByBlue<T>(Span<T> colors) where T : unmanaged, IColor
|
public static partial void SortByBlue<T>(this Span<T> colors) where T : unmanaged, IColor
|
||||||
{
|
{
|
||||||
fixed (T* ptr = colors)
|
fixed (T* ptr = colors)
|
||||||
{
|
{
|
||||||
@ -109,7 +109,7 @@ public static unsafe partial class PixelHelper
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
public static partial void SortByAlpha<T>(Span<T> colors) where T : unmanaged, IColor
|
public static partial void SortByAlpha<T>(this Span<T> colors) where T : unmanaged, IColor
|
||||||
{
|
{
|
||||||
fixed (T* ptr = colors)
|
fixed (T* ptr = colors)
|
||||||
{
|
{
|
||||||
@ -144,250 +144,5 @@ public static unsafe partial class PixelHelper
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
internal static partial void SortB1(Span<Generic3ByteData> colors)
|
|
||||||
{
|
|
||||||
fixed (Generic3ByteData* ptr = colors)
|
|
||||||
{
|
|
||||||
Generic3ByteData* end = ptr + colors.Length;
|
|
||||||
|
|
||||||
Span<int> histogram = stackalloc int[256];
|
|
||||||
histogram.Clear();
|
|
||||||
for (Generic3ByteData* color = ptr; color < end; color++)
|
|
||||||
histogram[(*color).B1]++;
|
|
||||||
|
|
||||||
Generic3ByteData[] bucketsArray = ArrayPool<Generic3ByteData>.Shared.Rent(colors.Length);
|
|
||||||
try
|
|
||||||
{
|
|
||||||
Span<Generic3ByteData> buckets = bucketsArray.AsSpan()[..colors.Length];
|
|
||||||
Span<int> currentBucketIndex = stackalloc int[256];
|
|
||||||
|
|
||||||
int offset = 0;
|
|
||||||
for (int i = 0; i < histogram.Length; i++)
|
|
||||||
{
|
|
||||||
currentBucketIndex[i] = offset;
|
|
||||||
offset += histogram[i];
|
|
||||||
}
|
|
||||||
|
|
||||||
for (Generic3ByteData* color = ptr; color < end; color++)
|
|
||||||
buckets[currentBucketIndex[(*color).B1]++] = (*color);
|
|
||||||
|
|
||||||
buckets.CopyTo(colors);
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
ArrayPool<Generic3ByteData>.Shared.Return(bucketsArray);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
internal static partial void SortB2(Span<Generic3ByteData> colors)
|
|
||||||
{
|
|
||||||
fixed (Generic3ByteData* ptr = colors)
|
|
||||||
{
|
|
||||||
Generic3ByteData* end = ptr + colors.Length;
|
|
||||||
|
|
||||||
Span<int> histogram = stackalloc int[256];
|
|
||||||
histogram.Clear();
|
|
||||||
for (Generic3ByteData* color = ptr; color < end; color++)
|
|
||||||
histogram[(*color).B2]++;
|
|
||||||
|
|
||||||
Generic3ByteData[] bucketsArray = ArrayPool<Generic3ByteData>.Shared.Rent(colors.Length);
|
|
||||||
try
|
|
||||||
{
|
|
||||||
Span<Generic3ByteData> buckets = bucketsArray.AsSpan()[..colors.Length];
|
|
||||||
Span<int> currentBucketIndex = stackalloc int[256];
|
|
||||||
|
|
||||||
int offset = 0;
|
|
||||||
for (int i = 0; i < histogram.Length; i++)
|
|
||||||
{
|
|
||||||
currentBucketIndex[i] = offset;
|
|
||||||
offset += histogram[i];
|
|
||||||
}
|
|
||||||
|
|
||||||
for (Generic3ByteData* color = ptr; color < end; color++)
|
|
||||||
buckets[currentBucketIndex[(*color).B2]++] = (*color);
|
|
||||||
|
|
||||||
buckets.CopyTo(colors);
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
ArrayPool<Generic3ByteData>.Shared.Return(bucketsArray);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
internal static partial void SortB3(Span<Generic3ByteData> colors)
|
|
||||||
{
|
|
||||||
fixed (Generic3ByteData* ptr = colors)
|
|
||||||
{
|
|
||||||
Generic3ByteData* end = ptr + colors.Length;
|
|
||||||
|
|
||||||
Span<int> histogram = stackalloc int[256];
|
|
||||||
histogram.Clear();
|
|
||||||
for (Generic3ByteData* color = ptr; color < end; color++)
|
|
||||||
histogram[(*color).B3]++;
|
|
||||||
|
|
||||||
Generic3ByteData[] bucketsArray = ArrayPool<Generic3ByteData>.Shared.Rent(colors.Length);
|
|
||||||
try
|
|
||||||
{
|
|
||||||
Span<Generic3ByteData> buckets = bucketsArray.AsSpan()[..colors.Length];
|
|
||||||
Span<int> currentBucketIndex = stackalloc int[256];
|
|
||||||
|
|
||||||
int offset = 0;
|
|
||||||
for (int i = 0; i < histogram.Length; i++)
|
|
||||||
{
|
|
||||||
currentBucketIndex[i] = offset;
|
|
||||||
offset += histogram[i];
|
|
||||||
}
|
|
||||||
|
|
||||||
for (Generic3ByteData* color = ptr; color < end; color++)
|
|
||||||
buckets[currentBucketIndex[(*color).B3]++] = (*color);
|
|
||||||
|
|
||||||
buckets.CopyTo(colors);
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
ArrayPool<Generic3ByteData>.Shared.Return(bucketsArray);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
internal static partial void SortB1(Span<Generic4ByteData> colors)
|
|
||||||
{
|
|
||||||
fixed (Generic4ByteData* ptr = colors)
|
|
||||||
{
|
|
||||||
Generic4ByteData* end = ptr + colors.Length;
|
|
||||||
|
|
||||||
Span<int> histogram = stackalloc int[256];
|
|
||||||
histogram.Clear();
|
|
||||||
for (Generic4ByteData* color = ptr; color < end; color++)
|
|
||||||
histogram[(*color).B1]++;
|
|
||||||
|
|
||||||
Generic4ByteData[] bucketsArray = ArrayPool<Generic4ByteData>.Shared.Rent(colors.Length);
|
|
||||||
try
|
|
||||||
{
|
|
||||||
Span<Generic4ByteData> buckets = bucketsArray.AsSpan()[..colors.Length];
|
|
||||||
Span<int> currentBucketIndex = stackalloc int[256];
|
|
||||||
|
|
||||||
int offset = 0;
|
|
||||||
for (int i = 0; i < histogram.Length; i++)
|
|
||||||
{
|
|
||||||
currentBucketIndex[i] = offset;
|
|
||||||
offset += histogram[i];
|
|
||||||
}
|
|
||||||
|
|
||||||
for (Generic4ByteData* color = ptr; color < end; color++)
|
|
||||||
buckets[currentBucketIndex[(*color).B1]++] = (*color);
|
|
||||||
|
|
||||||
buckets.CopyTo(colors);
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
ArrayPool<Generic4ByteData>.Shared.Return(bucketsArray);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
internal static partial void SortB2(Span<Generic4ByteData> colors)
|
|
||||||
{
|
|
||||||
fixed (Generic4ByteData* ptr = colors)
|
|
||||||
{
|
|
||||||
Generic4ByteData* end = ptr + colors.Length;
|
|
||||||
|
|
||||||
Span<int> histogram = stackalloc int[256];
|
|
||||||
histogram.Clear();
|
|
||||||
for (Generic4ByteData* color = ptr; color < end; color++)
|
|
||||||
histogram[(*color).B2]++;
|
|
||||||
|
|
||||||
Generic4ByteData[] bucketsArray = ArrayPool<Generic4ByteData>.Shared.Rent(colors.Length);
|
|
||||||
try
|
|
||||||
{
|
|
||||||
Span<Generic4ByteData> buckets = bucketsArray.AsSpan()[..colors.Length];
|
|
||||||
Span<int> currentBucketIndex = stackalloc int[256];
|
|
||||||
|
|
||||||
int offset = 0;
|
|
||||||
for (int i = 0; i < histogram.Length; i++)
|
|
||||||
{
|
|
||||||
currentBucketIndex[i] = offset;
|
|
||||||
offset += histogram[i];
|
|
||||||
}
|
|
||||||
|
|
||||||
for (Generic4ByteData* color = ptr; color < end; color++)
|
|
||||||
buckets[currentBucketIndex[(*color).B2]++] = (*color);
|
|
||||||
|
|
||||||
buckets.CopyTo(colors);
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
ArrayPool<Generic4ByteData>.Shared.Return(bucketsArray);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
internal static partial void SortB3(Span<Generic4ByteData> colors)
|
|
||||||
{
|
|
||||||
fixed (Generic4ByteData* ptr = colors)
|
|
||||||
{
|
|
||||||
Generic4ByteData* end = ptr + colors.Length;
|
|
||||||
|
|
||||||
Span<int> histogram = stackalloc int[256];
|
|
||||||
histogram.Clear();
|
|
||||||
for (Generic4ByteData* color = ptr; color < end; color++)
|
|
||||||
histogram[(*color).B3]++;
|
|
||||||
|
|
||||||
Generic4ByteData[] bucketsArray = ArrayPool<Generic4ByteData>.Shared.Rent(colors.Length);
|
|
||||||
try
|
|
||||||
{
|
|
||||||
Span<Generic4ByteData> buckets = bucketsArray.AsSpan()[..colors.Length];
|
|
||||||
Span<int> currentBucketIndex = stackalloc int[256];
|
|
||||||
|
|
||||||
int offset = 0;
|
|
||||||
for (int i = 0; i < histogram.Length; i++)
|
|
||||||
{
|
|
||||||
currentBucketIndex[i] = offset;
|
|
||||||
offset += histogram[i];
|
|
||||||
}
|
|
||||||
|
|
||||||
for (Generic4ByteData* color = ptr; color < end; color++)
|
|
||||||
buckets[currentBucketIndex[(*color).B3]++] = (*color);
|
|
||||||
|
|
||||||
buckets.CopyTo(colors);
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
ArrayPool<Generic4ByteData>.Shared.Return(bucketsArray);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
internal static partial void SortB4(Span<Generic4ByteData> colors)
|
|
||||||
{
|
|
||||||
fixed (Generic4ByteData* ptr = colors)
|
|
||||||
{
|
|
||||||
Generic4ByteData* end = ptr + colors.Length;
|
|
||||||
|
|
||||||
Span<int> histogram = stackalloc int[256];
|
|
||||||
histogram.Clear();
|
|
||||||
for (Generic4ByteData* color = ptr; color < end; color++)
|
|
||||||
histogram[(*color).B4]++;
|
|
||||||
|
|
||||||
Generic4ByteData[] bucketsArray = ArrayPool<Generic4ByteData>.Shared.Rent(colors.Length);
|
|
||||||
try
|
|
||||||
{
|
|
||||||
Span<Generic4ByteData> buckets = bucketsArray.AsSpan()[..colors.Length];
|
|
||||||
Span<int> currentBucketIndex = stackalloc int[256];
|
|
||||||
|
|
||||||
int offset = 0;
|
|
||||||
for (int i = 0; i < histogram.Length; i++)
|
|
||||||
{
|
|
||||||
currentBucketIndex[i] = offset;
|
|
||||||
offset += histogram[i];
|
|
||||||
}
|
|
||||||
|
|
||||||
for (Generic4ByteData* color = ptr; color < end; color++)
|
|
||||||
buckets[currentBucketIndex[(*color).B4]++] = (*color);
|
|
||||||
|
|
||||||
buckets.CopyTo(colors);
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
ArrayPool<Generic4ByteData>.Shared.Return(bucketsArray);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -10,7 +10,7 @@ public sealed partial class ColorFormatABGR : IColorFormat
|
|||||||
|
|
||||||
public string Name => "ABGR";
|
public string Name => "ABGR";
|
||||||
|
|
||||||
ReadOnlySpan<byte> IColorFormat.ByteMapping => [3, 2, 1, 0];
|
ReadOnlySpan<byte> IColorFormat.ByteMapping => [Color.A, Color.B, Color.G, Color.R];
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
|
|||||||
@ -10,7 +10,7 @@ public sealed partial class ColorFormatARGB : IColorFormat
|
|||||||
|
|
||||||
public string Name => "ARGB";
|
public string Name => "ARGB";
|
||||||
|
|
||||||
ReadOnlySpan<byte> IColorFormat.ByteMapping => [3, 0, 1, 2];
|
ReadOnlySpan<byte> IColorFormat.ByteMapping => [Color.A, Color.R, Color.G, Color.B];
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
|
|||||||
@ -10,7 +10,7 @@ public sealed partial class ColorFormatBGR : IColorFormat
|
|||||||
|
|
||||||
public string Name => "BGR";
|
public string Name => "BGR";
|
||||||
|
|
||||||
ReadOnlySpan<byte> IColorFormat.ByteMapping => [2, 1, 0];
|
ReadOnlySpan<byte> IColorFormat.ByteMapping => [Color.B, Color.G, Color.R];
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
|
|||||||
@ -10,7 +10,7 @@ public sealed partial class ColorFormatBGRA : IColorFormat
|
|||||||
|
|
||||||
public string Name => "BGRA";
|
public string Name => "BGRA";
|
||||||
|
|
||||||
ReadOnlySpan<byte> IColorFormat.ByteMapping => [2, 1, 0, 3];
|
ReadOnlySpan<byte> IColorFormat.ByteMapping => [Color.B, Color.G, Color.R, Color.A];
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
|
|||||||
@ -10,7 +10,7 @@ public sealed partial class ColorFormatRGB : IColorFormat
|
|||||||
|
|
||||||
public string Name => "RGB";
|
public string Name => "RGB";
|
||||||
|
|
||||||
ReadOnlySpan<byte> IColorFormat.ByteMapping => [0, 1, 2];
|
ReadOnlySpan<byte> IColorFormat.ByteMapping => [Color.R, Color.G, Color.B];
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
|
|||||||
@ -10,7 +10,7 @@ public sealed partial class ColorFormatRGBA : IColorFormat
|
|||||||
|
|
||||||
public string Name => "RGBA";
|
public string Name => "RGBA";
|
||||||
|
|
||||||
ReadOnlySpan<byte> IColorFormat.ByteMapping => [0, 1, 2, 3];
|
ReadOnlySpan<byte> IColorFormat.ByteMapping => [Color.R, Color.G, Color.B, Color.A];
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
|
|||||||
@ -8,7 +8,7 @@ public static partial class PixelHelper
|
|||||||
{
|
{
|
||||||
#region Methods
|
#region Methods
|
||||||
|
|
||||||
public static IColor Average(IImage image)
|
public static IColor Average(this IImage image)
|
||||||
{
|
{
|
||||||
ArgumentNullException.ThrowIfNull(image);
|
ArgumentNullException.ThrowIfNull(image);
|
||||||
|
|
||||||
@ -26,7 +26,7 @@ public static partial class PixelHelper
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static T Average<T>(RefImage<T> image)
|
public static T Average<T>(this RefImage<T> image)
|
||||||
where T : struct, IColor
|
where T : struct, IColor
|
||||||
{
|
{
|
||||||
int dataLength = image.Width * image.Height;
|
int dataLength = image.Width * image.Height;
|
||||||
@ -43,7 +43,7 @@ public static partial class PixelHelper
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static T Average<T>(ReadOnlySpan<T> colors)
|
public static T Average<T>(this ReadOnlySpan<T> colors)
|
||||||
where T : struct, IColor
|
where T : struct, IColor
|
||||||
{
|
{
|
||||||
if (colors == null) throw new ArgumentNullException(nameof(colors));
|
if (colors == null) throw new ArgumentNullException(nameof(colors));
|
||||||
|
|||||||
@ -7,110 +7,78 @@ public static unsafe partial class PixelHelper
|
|||||||
{
|
{
|
||||||
#region Methods
|
#region Methods
|
||||||
|
|
||||||
public static Span<TTarget> Convert<TSource, TTarget>(ReadOnlySpan<TSource> data)
|
public static Span<TTarget> ConvertInPlace<TSource, TTarget>(this Span<TSource> colors)
|
||||||
where TSource : struct, IColor
|
where TSource : struct, IColor
|
||||||
where TTarget : struct, IColor
|
where TTarget : struct, IColor
|
||||||
{
|
{
|
||||||
if (data == null) throw new ArgumentNullException(nameof(data));
|
if (colors == null) throw new ArgumentNullException(nameof(colors));
|
||||||
|
if (colors.Length == 0) return MemoryMarshal.Cast<TSource, TTarget>(colors);
|
||||||
|
|
||||||
Span<TSource> dataCopy = new TSource[data.Length];
|
IColorFormat sourceFormat = TSource.ColorFormat;
|
||||||
data.CopyTo(dataCopy);
|
IColorFormat targetFormat = TTarget.ColorFormat;
|
||||||
|
|
||||||
return Convert<TSource, TTarget>(dataCopy);
|
if (sourceFormat == targetFormat) return MemoryMarshal.Cast<TSource, TTarget>(colors);
|
||||||
|
if (sourceFormat.BytesPerPixel != targetFormat.BytesPerPixel) throw new NotSupportedException("In-place conversion requires the same BPP for source and target.");
|
||||||
|
|
||||||
|
Span<byte> data = MemoryMarshal.AsBytes(colors);
|
||||||
|
Convert(data, data, sourceFormat, targetFormat);
|
||||||
|
|
||||||
|
return MemoryMarshal.Cast<byte, TTarget>(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Span<TTarget> Convert<TSource, TTarget>(Span<TSource> data)
|
public static TTarget[] Convert<TSource, TTarget>(this Span<TSource> colors)
|
||||||
where TSource : struct, IColor
|
where TSource : struct, IColor
|
||||||
where TTarget : struct, IColor
|
where TTarget : struct, IColor
|
||||||
{
|
{
|
||||||
if (data == null) throw new ArgumentNullException(nameof(data));
|
if (colors == null) throw new ArgumentNullException(nameof(colors));
|
||||||
|
|
||||||
Convert(MemoryMarshal.AsBytes(data), TSource.ColorFormat, TTarget.ColorFormat);
|
TTarget[] buffer = new TTarget[colors.Length];
|
||||||
|
Convert<TSource, TTarget>(colors, buffer.AsSpan());
|
||||||
return MemoryMarshal.Cast<TSource, TTarget>(data);
|
return buffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
internal static void Convert(Span<byte> data, IColorFormat sourceFormat, IColorFormat targetFormat)
|
public static TTarget[] Convert<TSource, TTarget>(this ReadOnlySpan<TSource> colors)
|
||||||
|
where TSource : struct, IColor
|
||||||
|
where TTarget : struct, IColor
|
||||||
{
|
{
|
||||||
if (data == null) throw new ArgumentNullException(nameof(data));
|
if (colors == null) throw new ArgumentNullException(nameof(colors));
|
||||||
ArgumentNullException.ThrowIfNull(sourceFormat);
|
|
||||||
ArgumentNullException.ThrowIfNull(targetFormat);
|
|
||||||
|
|
||||||
if (sourceFormat == targetFormat) return;
|
TTarget[] buffer = new TTarget[colors.Length];
|
||||||
|
Convert(colors, buffer.AsSpan());
|
||||||
if (sourceFormat.BytesPerPixel == targetFormat.BytesPerPixel)
|
return buffer;
|
||||||
ConvertEqualBpp(data, sourceFormat, targetFormat);
|
|
||||||
else if ((sourceFormat.BytesPerPixel == 3) && (targetFormat.BytesPerPixel == 4))
|
|
||||||
ConvertWiden3To4Bytes(data, sourceFormat, targetFormat);
|
|
||||||
else if ((sourceFormat.BytesPerPixel == 4) && (targetFormat.BytesPerPixel == 3))
|
|
||||||
ConvertNarrow4To3Bytes(data, sourceFormat, targetFormat);
|
|
||||||
else
|
|
||||||
throw new NotSupportedException("Data is not of a supported valid color-type.");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void ConvertEqualBpp(Span<byte> data, IColorFormat sourceFormat, IColorFormat targetFormat)
|
public static void Convert<TSource, TTarget>(this ReadOnlySpan<TSource> source, Span<TTarget> target)
|
||||||
|
where TSource : struct, IColor
|
||||||
|
where TTarget : struct, IColor
|
||||||
{
|
{
|
||||||
ReadOnlySpan<byte> sourceMapping = sourceFormat.ByteMapping;
|
if (source == null) throw new ArgumentNullException(nameof(source));
|
||||||
ReadOnlySpan<byte> targetMapping = targetFormat.ByteMapping;
|
if (target == null) throw new ArgumentNullException(nameof(target));
|
||||||
|
if (target.Length < source.Length) throw new ArgumentException($"Target-buffer is not big enough. {target.Length} < {source.Length}", nameof(target));
|
||||||
|
|
||||||
|
Convert(MemoryMarshal.AsBytes(source), MemoryMarshal.AsBytes(target), TSource.ColorFormat, TTarget.ColorFormat);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void Convert(ReadOnlySpan<byte> source, Span<byte> target, IColorFormat sourceFormat, IColorFormat targetFormat)
|
||||||
|
{
|
||||||
|
if (source.Length == 0) return;
|
||||||
|
|
||||||
switch (sourceFormat.BytesPerPixel)
|
switch (sourceFormat.BytesPerPixel)
|
||||||
{
|
{
|
||||||
case 3:
|
case 3 when (targetFormat.BytesPerPixel == 3):
|
||||||
ReadOnlySpan<byte> mapping3 = [targetMapping[sourceMapping[0]], targetMapping[sourceMapping[1]], targetMapping[sourceMapping[2]]];
|
Convert3Bytes(source, target, sourceFormat, targetFormat);
|
||||||
ReadOnlySpan<byte> mask3 =
|
|
||||||
[
|
|
||||||
mapping3[0],
|
|
||||||
mapping3[1],
|
|
||||||
mapping3[2],
|
|
||||||
|
|
||||||
(byte)(mapping3[0] + 3),
|
|
||||||
(byte)(mapping3[1] + 3),
|
|
||||||
(byte)(mapping3[2] + 3),
|
|
||||||
|
|
||||||
(byte)(mapping3[0] + 6),
|
|
||||||
(byte)(mapping3[1] + 6),
|
|
||||||
(byte)(mapping3[2] + 6),
|
|
||||||
|
|
||||||
(byte)(mapping3[0] + 9),
|
|
||||||
(byte)(mapping3[1] + 9),
|
|
||||||
(byte)(mapping3[2] + 9),
|
|
||||||
|
|
||||||
(byte)(mapping3[0] + 12),
|
|
||||||
(byte)(mapping3[1] + 12),
|
|
||||||
(byte)(mapping3[2] + 12),
|
|
||||||
|
|
||||||
15
|
|
||||||
];
|
|
||||||
|
|
||||||
ConvertEqualBpp(data, mask3, 3);
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 4:
|
case 4 when (targetFormat.BytesPerPixel == 4):
|
||||||
ReadOnlySpan<byte> mapping4 = [targetMapping[sourceMapping[0]], targetMapping[sourceMapping[1]], targetMapping[sourceMapping[2]], targetMapping[sourceMapping[3]]];
|
Convert4Bytes(source, target, sourceFormat, targetFormat);
|
||||||
ReadOnlySpan<byte> mask4 =
|
break;
|
||||||
[
|
|
||||||
mapping4[0],
|
|
||||||
mapping4[1],
|
|
||||||
mapping4[2],
|
|
||||||
mapping4[3],
|
|
||||||
|
|
||||||
(byte)(mapping4[0] + 4),
|
case 3 when (targetFormat.BytesPerPixel == 4):
|
||||||
(byte)(mapping4[1] + 4),
|
ConvertWiden3To4Bytes(source, target, sourceFormat, targetFormat);
|
||||||
(byte)(mapping4[2] + 4),
|
break;
|
||||||
(byte)(mapping4[3] + 4),
|
|
||||||
|
|
||||||
(byte)(mapping4[0] + 8),
|
case 4 when (targetFormat.BytesPerPixel == 3):
|
||||||
(byte)(mapping4[1] + 8),
|
ConvertNarrow4To3Bytes(source, target, sourceFormat, targetFormat);
|
||||||
(byte)(mapping4[2] + 8),
|
|
||||||
(byte)(mapping4[3] + 8),
|
|
||||||
|
|
||||||
(byte)(mapping4[0] + 12),
|
|
||||||
(byte)(mapping4[1] + 12),
|
|
||||||
(byte)(mapping4[2] + 12),
|
|
||||||
(byte)(mapping4[3] + 12),
|
|
||||||
];
|
|
||||||
|
|
||||||
ConvertEqualBpp(data, mask4, 4);
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
@ -118,49 +86,277 @@ public static unsafe partial class PixelHelper
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// DarthAffe 07.07.2024: No fallback-implementation here. Shuffle Requires only Ssse3 which should be supported nearly anywhere and if not the fallback of Vector128.Shuffle is perfectly fine.
|
private static void Convert3Bytes(ReadOnlySpan<byte> source, Span<byte> target, IColorFormat sourceFormat, IColorFormat targetFormat)
|
||||||
private static void ConvertEqualBpp(Span<byte> data, ReadOnlySpan<byte> mask, int bpp)
|
{
|
||||||
|
ReadOnlySpan<byte> sourceMapping = sourceFormat.ByteMapping;
|
||||||
|
ReadOnlySpan<byte> targetMapping = targetFormat.ByteMapping;
|
||||||
|
|
||||||
|
ReadOnlySpan<byte> mapping = [sourceMapping[targetMapping[0]], sourceMapping[targetMapping[1]], sourceMapping[targetMapping[2]]];
|
||||||
|
ReadOnlySpan<byte> mask =
|
||||||
|
[
|
||||||
|
mapping[0],
|
||||||
|
mapping[1],
|
||||||
|
mapping[2],
|
||||||
|
|
||||||
|
(byte)(mapping[0] + 3),
|
||||||
|
(byte)(mapping[1] + 3),
|
||||||
|
(byte)(mapping[2] + 3),
|
||||||
|
|
||||||
|
(byte)(mapping[0] + 6),
|
||||||
|
(byte)(mapping[1] + 6),
|
||||||
|
(byte)(mapping[2] + 6),
|
||||||
|
|
||||||
|
(byte)(mapping[0] + 9),
|
||||||
|
(byte)(mapping[1] + 9),
|
||||||
|
(byte)(mapping[2] + 9),
|
||||||
|
|
||||||
|
(byte)(mapping[0] + 12),
|
||||||
|
(byte)(mapping[1] + 12),
|
||||||
|
(byte)(mapping[2] + 12),
|
||||||
|
|
||||||
|
15
|
||||||
|
];
|
||||||
|
|
||||||
|
ConvertSameBpp(source, target, mask, 3);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void Convert4Bytes(ReadOnlySpan<byte> source, Span<byte> target, IColorFormat sourceFormat, IColorFormat targetFormat)
|
||||||
|
{
|
||||||
|
ReadOnlySpan<byte> sourceMapping = sourceFormat.ByteMapping;
|
||||||
|
ReadOnlySpan<byte> targetMapping = targetFormat.ByteMapping;
|
||||||
|
|
||||||
|
ReadOnlySpan<byte> mapping = [sourceMapping[targetMapping[0]], sourceMapping[targetMapping[1]], sourceMapping[targetMapping[2]], sourceMapping[targetMapping[3]]];
|
||||||
|
ReadOnlySpan<byte> mask =
|
||||||
|
[
|
||||||
|
mapping[0],
|
||||||
|
mapping[1],
|
||||||
|
mapping[2],
|
||||||
|
mapping[3],
|
||||||
|
|
||||||
|
(byte)(mapping[0] + 4),
|
||||||
|
(byte)(mapping[1] + 4),
|
||||||
|
(byte)(mapping[2] + 4),
|
||||||
|
(byte)(mapping[3] + 4),
|
||||||
|
|
||||||
|
(byte)(mapping[0] + 8),
|
||||||
|
(byte)(mapping[1] + 8),
|
||||||
|
(byte)(mapping[2] + 8),
|
||||||
|
(byte)(mapping[3] + 8),
|
||||||
|
|
||||||
|
(byte)(mapping[0] + 12),
|
||||||
|
(byte)(mapping[1] + 12),
|
||||||
|
(byte)(mapping[2] + 12),
|
||||||
|
(byte)(mapping[3] + 12),
|
||||||
|
];
|
||||||
|
|
||||||
|
ConvertSameBpp(source, target, mask, 4);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void ConvertSameBpp(ReadOnlySpan<byte> source, Span<byte> target, ReadOnlySpan<byte> mask, int bpp)
|
||||||
{
|
{
|
||||||
int elementsPerVector = Vector128<byte>.Count / bpp;
|
int elementsPerVector = Vector128<byte>.Count / bpp;
|
||||||
int bytesPerVector = elementsPerVector * bpp;
|
int bytesPerVector = elementsPerVector * bpp;
|
||||||
|
|
||||||
int chunks = data.Length / bytesPerVector;
|
int chunks = source.Length / bytesPerVector;
|
||||||
Vector128<byte> maskVector = Vector128.LoadUnsafe(ref MemoryMarshal.GetReference(mask));
|
Vector128<byte> maskVector = Vector128.LoadUnsafe(ref MemoryMarshal.GetReference(mask));
|
||||||
|
|
||||||
int missingElements = (data.Length - (chunks * bytesPerVector)) / bpp;
|
int missingElements = (source.Length - (chunks * bytesPerVector)) / bpp;
|
||||||
|
|
||||||
fixed (byte* dataPtr = data)
|
fixed (byte* sourcePtr = source)
|
||||||
|
fixed (byte* targetPtr = target)
|
||||||
{
|
{
|
||||||
byte* ptr = dataPtr;
|
byte* src = sourcePtr;
|
||||||
|
byte* tar = targetPtr;
|
||||||
|
|
||||||
for (int i = 0; i < chunks; i++)
|
for (int i = 0; i < chunks; i++)
|
||||||
{
|
{
|
||||||
Vector128<byte> vector = Vector128.Load(ptr);
|
Vector128<byte> vector = Vector128.Load(src);
|
||||||
Vector128.Shuffle(vector, maskVector).Store(ptr);
|
Vector128.Shuffle(vector, maskVector).Store(tar);
|
||||||
|
|
||||||
ptr += bytesPerVector;
|
src += bytesPerVector;
|
||||||
|
tar += bytesPerVector;
|
||||||
}
|
}
|
||||||
|
|
||||||
Span<byte> buffer = stackalloc byte[missingElements * bpp]; // DarthAffe 07.07.2024: This is fine as it's always < 16 bytes
|
Span<byte> buffer = stackalloc byte[missingElements * bpp]; // DarthAffe 08.07.2024: This is fine as it's always < 16 bytes
|
||||||
for (int i = 0; i < missingElements; i++)
|
|
||||||
{
|
|
||||||
int elementIndex = i * buffer.Length;
|
|
||||||
for (int j = 0; j < buffer.Length; j++)
|
for (int j = 0; j < buffer.Length; j++)
|
||||||
buffer[elementIndex + j] = ptr[elementIndex + mask[j]];
|
buffer[j] = src[mask[j]];
|
||||||
}
|
|
||||||
|
|
||||||
buffer.CopyTo(new Span<byte>(ptr, buffer.Length));
|
buffer.CopyTo(new Span<byte>(tar, buffer.Length));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void ConvertWiden3To4Bytes(Span<byte> data, IColorFormat sourceFormat, IColorFormat targetFormat)
|
private static void ConvertWiden3To4Bytes(ReadOnlySpan<byte> source, Span<byte> target, IColorFormat sourceFormat, IColorFormat targetFormat)
|
||||||
{
|
{
|
||||||
throw new NotImplementedException();
|
ReadOnlySpan<byte> sourceMapping = sourceFormat.ByteMapping;
|
||||||
|
ReadOnlySpan<byte> targetMapping = targetFormat.ByteMapping;
|
||||||
|
|
||||||
|
// DarthAffe 08.07.2024: For now alpha is the only thing to be added
|
||||||
|
Span<byte> isAlpha =
|
||||||
|
[
|
||||||
|
targetMapping[0] == Color.A ? byte.MaxValue : (byte)0,
|
||||||
|
targetMapping[1] == Color.A ? byte.MaxValue : (byte)0,
|
||||||
|
targetMapping[2] == Color.A ? byte.MaxValue : (byte)0,
|
||||||
|
targetMapping[3] == Color.A ? byte.MaxValue : (byte)0,
|
||||||
|
];
|
||||||
|
|
||||||
|
ReadOnlySpan<byte> mapping =
|
||||||
|
[
|
||||||
|
isAlpha[0] > 0 ? (byte)0 : sourceMapping[targetMapping[0]],
|
||||||
|
isAlpha[1] > 0 ? (byte)0 : sourceMapping[targetMapping[1]],
|
||||||
|
isAlpha[2] > 0 ? (byte)0 : sourceMapping[targetMapping[2]],
|
||||||
|
isAlpha[3] > 0 ? (byte)0 : sourceMapping[targetMapping[3]]
|
||||||
|
];
|
||||||
|
|
||||||
|
ReadOnlySpan<byte> mask =
|
||||||
|
[
|
||||||
|
mapping[0],
|
||||||
|
mapping[1],
|
||||||
|
mapping[2],
|
||||||
|
mapping[3],
|
||||||
|
|
||||||
|
(byte)(mapping[0] + 3),
|
||||||
|
(byte)(mapping[1] + 3),
|
||||||
|
(byte)(mapping[2] + 3),
|
||||||
|
(byte)(mapping[3] + 3),
|
||||||
|
|
||||||
|
(byte)(mapping[0] + 6),
|
||||||
|
(byte)(mapping[1] + 6),
|
||||||
|
(byte)(mapping[2] + 6),
|
||||||
|
(byte)(mapping[3] + 6),
|
||||||
|
|
||||||
|
(byte)(mapping[0] + 9),
|
||||||
|
(byte)(mapping[1] + 9),
|
||||||
|
(byte)(mapping[2] + 9),
|
||||||
|
(byte)(mapping[3] + 9),
|
||||||
|
];
|
||||||
|
|
||||||
|
ReadOnlySpan<byte> alphaMask =
|
||||||
|
[
|
||||||
|
isAlpha[0],
|
||||||
|
isAlpha[1],
|
||||||
|
isAlpha[2],
|
||||||
|
isAlpha[3],
|
||||||
|
|
||||||
|
isAlpha[0],
|
||||||
|
isAlpha[1],
|
||||||
|
isAlpha[2],
|
||||||
|
isAlpha[3],
|
||||||
|
|
||||||
|
isAlpha[0],
|
||||||
|
isAlpha[1],
|
||||||
|
isAlpha[2],
|
||||||
|
isAlpha[3],
|
||||||
|
|
||||||
|
isAlpha[0],
|
||||||
|
isAlpha[1],
|
||||||
|
isAlpha[2],
|
||||||
|
isAlpha[3],
|
||||||
|
];
|
||||||
|
|
||||||
|
int sourceBpp = sourceFormat.BytesPerPixel;
|
||||||
|
int targetBpp = targetFormat.BytesPerPixel;
|
||||||
|
|
||||||
|
int targetElementsPerVector = Vector128<byte>.Count / targetBpp;
|
||||||
|
int targetBytesPerVector = targetElementsPerVector * targetBpp;
|
||||||
|
int sourceBytesPerVector = targetElementsPerVector * sourceBpp;
|
||||||
|
|
||||||
|
int chunks = (source.Length / sourceBytesPerVector);
|
||||||
|
Vector128<byte> maskVector = Vector128.LoadUnsafe(ref MemoryMarshal.GetReference(mask));
|
||||||
|
Vector128<byte> alphaMaskVector = Vector128.LoadUnsafe(ref MemoryMarshal.GetReference(alphaMask));
|
||||||
|
|
||||||
|
int missingElements = (source.Length - (chunks * sourceBytesPerVector)) / sourceBpp;
|
||||||
|
|
||||||
|
fixed (byte* sourcePtr = source)
|
||||||
|
fixed (byte* targetPtr = target)
|
||||||
|
{
|
||||||
|
byte* src = sourcePtr;
|
||||||
|
byte* tar = targetPtr;
|
||||||
|
|
||||||
|
for (int i = 0; i < chunks; i++)
|
||||||
|
{
|
||||||
|
Vector128<byte> vector = Vector128.Load(src);
|
||||||
|
Vector128<byte> shuffled = Vector128.Shuffle(vector, maskVector);
|
||||||
|
Vector128.BitwiseOr(shuffled, alphaMaskVector).Store(tar);
|
||||||
|
|
||||||
|
src += sourceBytesPerVector;
|
||||||
|
tar += targetBytesPerVector;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void ConvertNarrow4To3Bytes(Span<byte> data, IColorFormat sourceFormat, IColorFormat targetFormat)
|
Span<byte> buffer = stackalloc byte[missingElements * targetBpp]; // DarthAffe 08.07.2024: This is fine as it's always < 16 bytes
|
||||||
|
for (int i = 0; i < missingElements; i++)
|
||||||
|
for (int j = 0; j < targetBpp; j++)
|
||||||
|
buffer[(i * targetBpp) + j] = Math.Max(isAlpha[j], src[(i * sourceBpp) + mask[j]]);
|
||||||
|
|
||||||
|
buffer.CopyTo(new Span<byte>(tar, buffer.Length));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void ConvertNarrow4To3Bytes(ReadOnlySpan<byte> source, Span<byte> target, IColorFormat sourceFormat, IColorFormat targetFormat)
|
||||||
{
|
{
|
||||||
throw new NotImplementedException();
|
ReadOnlySpan<byte> sourceMapping = sourceFormat.ByteMapping;
|
||||||
|
ReadOnlySpan<byte> targetMapping = targetFormat.ByteMapping;
|
||||||
|
|
||||||
|
// DarthAffe 08.07.2024: For now alpha is the only thing to be narrowed away
|
||||||
|
ReadOnlySpan<byte> mapping = [sourceMapping[targetMapping[0]], sourceMapping[targetMapping[1]], sourceMapping[targetMapping[2]]];
|
||||||
|
|
||||||
|
ReadOnlySpan<byte> mask =
|
||||||
|
[
|
||||||
|
mapping[0],
|
||||||
|
mapping[1],
|
||||||
|
mapping[2],
|
||||||
|
|
||||||
|
(byte)(mapping[0] + 4),
|
||||||
|
(byte)(mapping[1] + 4),
|
||||||
|
(byte)(mapping[2] + 4),
|
||||||
|
|
||||||
|
(byte)(mapping[0] + 8),
|
||||||
|
(byte)(mapping[1] + 8),
|
||||||
|
(byte)(mapping[2] + 8),
|
||||||
|
|
||||||
|
(byte)(mapping[0] + 12),
|
||||||
|
(byte)(mapping[1] + 12),
|
||||||
|
(byte)(mapping[2] + 12),
|
||||||
|
|
||||||
|
12,
|
||||||
|
13,
|
||||||
|
14,
|
||||||
|
15
|
||||||
|
];
|
||||||
|
|
||||||
|
int sourceBpp = sourceFormat.BytesPerPixel;
|
||||||
|
int targetBpp = targetFormat.BytesPerPixel;
|
||||||
|
|
||||||
|
int sourceElementsPerVector = Vector128<byte>.Count / sourceBpp;
|
||||||
|
int sourceBytesPerVector = sourceElementsPerVector * sourceBpp;
|
||||||
|
int targetBytesPerVector = sourceElementsPerVector * targetBpp;
|
||||||
|
|
||||||
|
int chunks = (source.Length / sourceBytesPerVector) - 1; // DarthAffe 08.07.2024: -1 since we don't have enough space to copy a full target vector for the last set
|
||||||
|
Vector128<byte> maskVector = Vector128.LoadUnsafe(ref MemoryMarshal.GetReference(mask));
|
||||||
|
|
||||||
|
int missingElements = (source.Length - (chunks * sourceBytesPerVector)) / sourceBpp;
|
||||||
|
|
||||||
|
fixed (byte* sourcePtr = source)
|
||||||
|
fixed (byte* targetPtr = target)
|
||||||
|
{
|
||||||
|
byte* src = sourcePtr;
|
||||||
|
byte* tar = targetPtr;
|
||||||
|
|
||||||
|
for (int i = 0; i < chunks; i++)
|
||||||
|
{
|
||||||
|
Vector128<byte> vector = Vector128.Load(src);
|
||||||
|
Vector128.Shuffle(vector, maskVector).Store(tar);
|
||||||
|
|
||||||
|
src += sourceBytesPerVector;
|
||||||
|
tar += targetBytesPerVector;
|
||||||
|
}
|
||||||
|
|
||||||
|
Span<byte> buffer = stackalloc byte[missingElements * targetBpp]; // DarthAffe 08.07.2024: This is fine as it's always < 24 bytes
|
||||||
|
for (int i = 0; i < missingElements; i++)
|
||||||
|
for (int j = 0; j < targetBpp; j++)
|
||||||
|
buffer[(i * targetBpp) + j] = src[(i * sourceBpp) + mask[j]];
|
||||||
|
|
||||||
|
buffer.CopyTo(new Span<byte>(tar, buffer.Length));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|||||||
@ -9,7 +9,7 @@ public static unsafe partial class PixelHelper
|
|||||||
{
|
{
|
||||||
#region Methods
|
#region Methods
|
||||||
|
|
||||||
public static IMinMax MinMax(IImage image)
|
public static IMinMax MinMax(this IImage image)
|
||||||
{
|
{
|
||||||
ArgumentNullException.ThrowIfNull(image);
|
ArgumentNullException.ThrowIfNull(image);
|
||||||
|
|
||||||
@ -27,7 +27,7 @@ public static unsafe partial class PixelHelper
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static IMinMax MinMax<T>(RefImage<T> image)
|
public static IMinMax MinMax<T>(this RefImage<T> image)
|
||||||
where T : struct, IColor
|
where T : struct, IColor
|
||||||
{
|
{
|
||||||
int dataLength = image.Width * image.Height;
|
int dataLength = image.Width * image.Height;
|
||||||
@ -44,7 +44,7 @@ public static unsafe partial class PixelHelper
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static IMinMax MinMax<T>(ReadOnlySpan<T> colors)
|
public static IMinMax MinMax<T>(this ReadOnlySpan<T> colors)
|
||||||
where T : struct, IColor
|
where T : struct, IColor
|
||||||
=> T.ColorFormat.MinMax(MemoryMarshal.AsBytes(colors));
|
=> T.ColorFormat.MinMax(MemoryMarshal.AsBytes(colors));
|
||||||
|
|
||||||
@ -84,7 +84,7 @@ public static unsafe partial class PixelHelper
|
|||||||
{
|
{
|
||||||
for (int i = 0; i < chunks; i++)
|
for (int i = 0; i < chunks; i++)
|
||||||
{
|
{
|
||||||
Vector<byte> vector = *(Vector<byte>*)(colorPtr + (i * bytesPerVector));
|
Vector<byte> vector = Vector.Load(colorPtr + (i * bytesPerVector));
|
||||||
|
|
||||||
max = Vector.Max(max, vector);
|
max = Vector.Max(max, vector);
|
||||||
min = Vector.Min(min, vector);
|
min = Vector.Min(min, vector);
|
||||||
@ -154,7 +154,7 @@ public static unsafe partial class PixelHelper
|
|||||||
{
|
{
|
||||||
for (int i = 0; i < chunks; i++)
|
for (int i = 0; i < chunks; i++)
|
||||||
{
|
{
|
||||||
Vector<byte> vector = *(Vector<byte>*)(colorPtr + (i * bytesPerVector));
|
Vector<byte> vector = Vector.Load(colorPtr + (i * bytesPerVector));
|
||||||
|
|
||||||
max = Vector.Max(max, vector);
|
max = Vector.Max(max, vector);
|
||||||
min = Vector.Min(min, vector);
|
min = Vector.Min(min, vector);
|
||||||
|
|||||||
@ -7,7 +7,7 @@ public static partial class PixelHelper
|
|||||||
{
|
{
|
||||||
#region Methods
|
#region Methods
|
||||||
|
|
||||||
public static IColor[] CreateColorPalette(IImage image, int paletteSize)
|
public static IColor[] CreateColorPalette(this IImage image, int paletteSize)
|
||||||
{
|
{
|
||||||
ArgumentNullException.ThrowIfNull(image);
|
ArgumentNullException.ThrowIfNull(image);
|
||||||
|
|
||||||
@ -25,7 +25,7 @@ public static partial class PixelHelper
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static T[] CreateColorPalette<T>(RefImage<T> image, int paletteSize)
|
public static T[] CreateColorPalette<T>(this RefImage<T> image, int paletteSize)
|
||||||
where T : unmanaged, IColor
|
where T : unmanaged, IColor
|
||||||
{
|
{
|
||||||
int dataLength = image.Width * image.Height;
|
int dataLength = image.Width * image.Height;
|
||||||
@ -42,7 +42,7 @@ public static partial class PixelHelper
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static T[] CreateColorPalette<T>(ReadOnlySpan<T> colors, int paletteSize)
|
public static T[] CreateColorPalette<T>(this ReadOnlySpan<T> colors, int paletteSize)
|
||||||
where T : unmanaged, IColor
|
where T : unmanaged, IColor
|
||||||
{
|
{
|
||||||
T[] buffer = ArrayPool<T>.Shared.Rent(colors.Length);
|
T[] buffer = ArrayPool<T>.Shared.Rent(colors.Length);
|
||||||
@ -59,7 +59,7 @@ public static partial class PixelHelper
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static T[] CreateColorPalette<T>(Span<T> colors, int paletteSize)
|
public static T[] CreateColorPalette<T>(this Span<T> colors, int paletteSize)
|
||||||
where T : unmanaged, IColor
|
where T : unmanaged, IColor
|
||||||
{
|
{
|
||||||
int splits = BitOperations.Log2((uint)paletteSize);
|
int splits = BitOperations.Log2((uint)paletteSize);
|
||||||
|
|||||||
@ -6,41 +6,20 @@ public static unsafe partial class PixelHelper
|
|||||||
#region Methods
|
#region Methods
|
||||||
|
|
||||||
[ColorSortGenerator("T", "R")]
|
[ColorSortGenerator("T", "R")]
|
||||||
public static partial void SortByRed<T>(Span<T> colors)
|
public static partial void SortByRed<T>(this Span<T> colors)
|
||||||
where T : unmanaged, IColor;
|
where T : unmanaged, IColor;
|
||||||
|
|
||||||
[ColorSortGenerator("T", "G")]
|
[ColorSortGenerator("T", "G")]
|
||||||
public static partial void SortByGreen<T>(Span<T> colors)
|
public static partial void SortByGreen<T>(this Span<T> colors)
|
||||||
where T : unmanaged, IColor;
|
where T : unmanaged, IColor;
|
||||||
|
|
||||||
[ColorSortGenerator("T", "B")]
|
[ColorSortGenerator("T", "B")]
|
||||||
public static partial void SortByBlue<T>(Span<T> colors)
|
public static partial void SortByBlue<T>(this Span<T> colors)
|
||||||
where T : unmanaged, IColor;
|
where T : unmanaged, IColor;
|
||||||
|
|
||||||
[ColorSortGenerator("T", "A")]
|
[ColorSortGenerator("T", "A")]
|
||||||
public static partial void SortByAlpha<T>(Span<T> colors)
|
public static partial void SortByAlpha<T>(this Span<T> colors)
|
||||||
where T : unmanaged, IColor;
|
where T : unmanaged, IColor;
|
||||||
|
|
||||||
[ColorSortGenerator("Generic3ByteData", "B1")]
|
|
||||||
internal static partial void SortB1(Span<Generic3ByteData> colors);
|
|
||||||
|
|
||||||
[ColorSortGenerator("Generic3ByteData", "B2")]
|
|
||||||
internal static partial void SortB2(Span<Generic3ByteData> colors);
|
|
||||||
|
|
||||||
[ColorSortGenerator("Generic3ByteData", "B3")]
|
|
||||||
internal static partial void SortB3(Span<Generic3ByteData> colors);
|
|
||||||
|
|
||||||
[ColorSortGenerator("Generic4ByteData", "B1")]
|
|
||||||
internal static partial void SortB1(Span<Generic4ByteData> colors);
|
|
||||||
|
|
||||||
[ColorSortGenerator("Generic4ByteData", "B2")]
|
|
||||||
internal static partial void SortB2(Span<Generic4ByteData> colors);
|
|
||||||
|
|
||||||
[ColorSortGenerator("Generic4ByteData", "B3")]
|
|
||||||
internal static partial void SortB3(Span<Generic4ByteData> colors);
|
|
||||||
|
|
||||||
[ColorSortGenerator("Generic4ByteData", "B4")]
|
|
||||||
internal static partial void SortB4(Span<Generic4ByteData> colors);
|
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
}
|
}
|
||||||
|
|||||||
@ -10,7 +10,7 @@ public static unsafe partial class PixelHelper
|
|||||||
{
|
{
|
||||||
#region Methods
|
#region Methods
|
||||||
|
|
||||||
public static ISum Sum(IImage image)
|
public static ISum Sum(this IImage image)
|
||||||
{
|
{
|
||||||
ArgumentNullException.ThrowIfNull(image);
|
ArgumentNullException.ThrowIfNull(image);
|
||||||
|
|
||||||
@ -28,7 +28,7 @@ public static unsafe partial class PixelHelper
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ISum Sum<T>(RefImage<T> image)
|
public static ISum Sum<T>(this RefImage<T> image)
|
||||||
where T : struct, IColor
|
where T : struct, IColor
|
||||||
{
|
{
|
||||||
int dataLength = image.Width * image.Height;
|
int dataLength = image.Width * image.Height;
|
||||||
@ -45,7 +45,7 @@ public static unsafe partial class PixelHelper
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ISum Sum<T>(ReadOnlySpan<T> colors)
|
public static ISum Sum<T>(this ReadOnlySpan<T> colors)
|
||||||
where T : struct, IColor
|
where T : struct, IColor
|
||||||
=> T.ColorFormat.Sum(MemoryMarshal.AsBytes(colors));
|
=> T.ColorFormat.Sum(MemoryMarshal.AsBytes(colors));
|
||||||
|
|
||||||
@ -133,9 +133,9 @@ public static unsafe partial class PixelHelper
|
|||||||
for (int i = 0; i < chunks; i++)
|
for (int i = 0; i < chunks; i++)
|
||||||
{
|
{
|
||||||
byte* basePtr = bytePtr + (i * 96);
|
byte* basePtr = bytePtr + (i * 96);
|
||||||
Vector256<byte> data1 = *(Vector256<byte>*)(basePtr);
|
Vector256<byte> data1 = Vector256.Load(basePtr);
|
||||||
Vector256<byte> data2 = *(Vector256<byte>*)(basePtr + 32);
|
Vector256<byte> data2 = Vector256.Load(basePtr + 32);
|
||||||
Vector256<byte> data3 = *(Vector256<byte>*)(basePtr + 64);
|
Vector256<byte> data3 = Vector256.Load(basePtr + 64);
|
||||||
|
|
||||||
Vector256<byte> vectorB1Blend1 = Avx2.BlendVariable(data2, data1, blend1MaskVector);
|
Vector256<byte> vectorB1Blend1 = Avx2.BlendVariable(data2, data1, blend1MaskVector);
|
||||||
Vector256<byte> vectorB2Blend1 = Avx2.BlendVariable(data2, data1, blend2MaskVector);
|
Vector256<byte> vectorB2Blend1 = Avx2.BlendVariable(data2, data1, blend2MaskVector);
|
||||||
@ -222,9 +222,9 @@ public static unsafe partial class PixelHelper
|
|||||||
|
|
||||||
for (int j = 0; j < (data.Length / 8); j++, i += 8)
|
for (int j = 0; j < (data.Length / 8); j++, i += 8)
|
||||||
{
|
{
|
||||||
Vector256<byte> chunk = *(Vector256<byte>*)(bytePtr + (i * 4));
|
Vector256<byte> chunk = Vector256.Load(bytePtr + (i * 4));
|
||||||
Vector256<byte> deinterleaved = Avx2.Shuffle(chunk, avx2ShuffleMaskVector);
|
Vector256<byte> deinterleaved = Avx2.Shuffle(chunk, avx2ShuffleMaskVector);
|
||||||
Vector256<int> deinterleaved2 = Avx2.PermuteVar8x32(deinterleaved.AsInt32(), *(Vector256<int>*)controlPtr);
|
Vector256<int> deinterleaved2 = Avx2.PermuteVar8x32(deinterleaved.AsInt32(), Vector256.Load(controlPtr));
|
||||||
Vector256<long> sum = Avx2.SumAbsoluteDifferences(deinterleaved2.AsByte(), Vector256<byte>.Zero).AsInt64();
|
Vector256<long> sum = Avx2.SumAbsoluteDifferences(deinterleaved2.AsByte(), Vector256<byte>.Zero).AsInt64();
|
||||||
rgbaSum64 = Avx2.Add(rgbaSum64, sum);
|
rgbaSum64 = Avx2.Add(rgbaSum64, sum);
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user