1
0
mirror of https://github.com/Artemis-RGB/Artemis synced 2025-12-13 05:48:35 +00:00

Removed the need for enabled unsafe blocks in Core; Added try/finally blocks for ArrayPool rents

This commit is contained in:
Darth Affe 2022-11-20 22:55:46 +01:00
parent d0f665aa10
commit eb4003cec8
4 changed files with 98 additions and 84 deletions

View File

@ -12,7 +12,6 @@
<PackageId>ArtemisRGB.Core</PackageId> <PackageId>ArtemisRGB.Core</PackageId>
<PluginApiVersion>1</PluginApiVersion> <PluginApiVersion>1</PluginApiVersion>
<Nullable>enable</Nullable> <Nullable>enable</Nullable>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>

View File

@ -1,6 +1,7 @@
using SkiaSharp; using SkiaSharp;
using System; using System;
using System.Numerics; using System.Numerics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
namespace Artemis.Core.ColorScience; namespace Artemis.Core.ColorScience;
@ -19,7 +20,7 @@ internal readonly struct ColorRanges
} }
} }
internal class ColorCube internal readonly struct ColorCube
{ {
private const int BYTES_PER_COLOR = 4; private const int BYTES_PER_COLOR = 4;
private static readonly int ELEMENTS_PER_VECTOR = Vector<byte>.Count / BYTES_PER_COLOR; private static readonly int ELEMENTS_PER_VECTOR = Vector<byte>.Count / BYTES_PER_COLOR;
@ -27,19 +28,16 @@ internal class ColorCube
private readonly int _from; private readonly int _from;
private readonly int _length; private readonly int _length;
private SortTarget _currentOrder = SortTarget.None; private readonly SortTarget _currentOrder = SortTarget.None;
public ColorCube(in Span<SKColor> fullColorList, int from, int length, SortTarget preOrdered) public ColorCube(in Span<SKColor> fullColorList, int from, int length, SortTarget preOrdered)
{ {
this._from = from; this._from = from;
this._length = length; this._length = length;
OrderColors(fullColorList.Slice(from, length), preOrdered); if (length < 2) return;
}
private void OrderColors(in Span<SKColor> colors, SortTarget preOrdered) Span<SKColor> colors = fullColorList.Slice(from, length);
{
if (colors.Length < 2) return;
ColorRanges colorRanges = GetColorRanges(colors); ColorRanges colorRanges = GetColorRanges(colors);
if ((colorRanges.RedRange > colorRanges.GreenRange) && (colorRanges.RedRange > colorRanges.BlueRange)) if ((colorRanges.RedRange > colorRanges.GreenRange) && (colorRanges.RedRange > colorRanges.BlueRange))
@ -65,29 +63,21 @@ internal class ColorCube
} }
} }
private unsafe ColorRanges GetColorRanges(in ReadOnlySpan<SKColor> colors) [MethodImpl(MethodImplOptions.AggressiveInlining)]
private ColorRanges GetColorRanges(in ReadOnlySpan<SKColor> colors)
{ {
if (Vector.IsHardwareAccelerated && (colors.Length >= Vector<byte>.Count)) if (Vector.IsHardwareAccelerated && (colors.Length >= Vector<byte>.Count))
{ {
int chunks = colors.Length / ELEMENTS_PER_VECTOR; int chunks = colors.Length / ELEMENTS_PER_VECTOR;
int missingElements = colors.Length - (chunks * ELEMENTS_PER_VECTOR); int vectorElements = (chunks * ELEMENTS_PER_VECTOR);
int missingElements = colors.Length - vectorElements;
Vector<byte> max = Vector<byte>.Zero; Vector<byte> max = Vector<byte>.Zero;
Vector<byte> min = new(byte.MaxValue); Vector<byte> min = new(byte.MaxValue);
foreach (Vector<byte> currentVector in MemoryMarshal.Cast<SKColor, Vector<byte>>(colors[..vectorElements]))
ReadOnlySpan<byte> colorBytes = MemoryMarshal.AsBytes(colors);
fixed (byte* colorPtr = &MemoryMarshal.GetReference(colorBytes))
{ {
byte* current = colorPtr;
for (int i = 0; i < chunks; i++)
{
Vector<byte> currentVector = *(Vector<byte>*)current;
max = Vector.Max(max, currentVector); max = Vector.Max(max, currentVector);
min = Vector.Min(min, currentVector); min = Vector.Min(min, currentVector);
current += BYTES_PER_VECTOR;
}
} }
byte redMin = byte.MaxValue; byte redMin = byte.MaxValue;
@ -144,7 +134,7 @@ internal class ColorCube
} }
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal void Split(in Span<SKColor> fullColorList, out ColorCube a, out ColorCube b) internal void Split(in Span<SKColor> fullColorList, out ColorCube a, out ColorCube b)
{ {
Span<SKColor> colors = fullColorList.Slice(_from, _length); Span<SKColor> colors = fullColorList.Slice(_from, _length);
@ -155,6 +145,7 @@ internal class ColorCube
b = new ColorCube(fullColorList, _from + median, colors.Length - median, _currentOrder); b = new ColorCube(fullColorList, _from + median, colors.Length - median, _currentOrder);
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal SKColor GetAverageColor(in ReadOnlySpan<SKColor> fullColorList) internal SKColor GetAverageColor(in ReadOnlySpan<SKColor> fullColorList)
{ {
ReadOnlySpan<SKColor> colors = fullColorList.Slice(_from, _length); ReadOnlySpan<SKColor> colors = fullColorList.Slice(_from, _length);

View File

@ -9,14 +9,17 @@ internal static class QuantizerSort
{ {
#region Methods #region Methods
public static void SortRed(in Span<SKColor> span) public static void SortRed(in Span<SKColor> colors)
{ {
Span<int> counts = stackalloc int[256]; Span<int> counts = stackalloc int[256];
foreach (SKColor t in span) foreach (SKColor t in colors)
counts[t.Red]++; counts[t.Red]++;
SKColor[] bucketsArray = ArrayPool<SKColor>.Shared.Rent(span.Length); SKColor[] bucketsArray = ArrayPool<SKColor>.Shared.Rent(colors.Length);
Span<SKColor> buckets = bucketsArray.AsSpan().Slice(0, span.Length);
try
{
Span<SKColor> buckets = bucketsArray.AsSpan().Slice(0, colors.Length);
Span<int> currentBucketIndex = stackalloc int[256]; Span<int> currentBucketIndex = stackalloc int[256];
int offset = 0; int offset = 0;
@ -26,7 +29,7 @@ internal static class QuantizerSort
offset += counts[i]; offset += counts[i];
} }
foreach (SKColor color in span) foreach (SKColor color in colors)
{ {
int index = color.Red; int index = color.Red;
int bucketIndex = currentBucketIndex[index]; int bucketIndex = currentBucketIndex[index];
@ -34,19 +37,25 @@ internal static class QuantizerSort
buckets[bucketIndex] = color; buckets[bucketIndex] = color;
} }
buckets.CopyTo(span); buckets.CopyTo(colors);
}
finally
{
ArrayPool<SKColor>.Shared.Return(bucketsArray); ArrayPool<SKColor>.Shared.Return(bucketsArray);
} }
}
public static void SortGreen(in Span<SKColor> span) public static void SortGreen(in Span<SKColor> colors)
{ {
Span<int> counts = stackalloc int[256]; Span<int> counts = stackalloc int[256];
foreach (SKColor t in span) foreach (SKColor t in colors)
counts[t.Green]++; counts[t.Green]++;
SKColor[] bucketsArray = ArrayPool<SKColor>.Shared.Rent(span.Length); SKColor[] bucketsArray = ArrayPool<SKColor>.Shared.Rent(colors.Length);
Span<SKColor> buckets = bucketsArray.AsSpan().Slice(0, span.Length);
try
{
Span<SKColor> buckets = bucketsArray.AsSpan().Slice(0, colors.Length);
Span<int> currentBucketIndex = stackalloc int[256]; Span<int> currentBucketIndex = stackalloc int[256];
int offset = 0; int offset = 0;
@ -56,7 +65,7 @@ internal static class QuantizerSort
offset += counts[i]; offset += counts[i];
} }
foreach (SKColor color in span) foreach (SKColor color in colors)
{ {
int index = color.Green; int index = color.Green;
int bucketIndex = currentBucketIndex[index]; int bucketIndex = currentBucketIndex[index];
@ -64,19 +73,25 @@ internal static class QuantizerSort
buckets[bucketIndex] = color; buckets[bucketIndex] = color;
} }
buckets.CopyTo(span); buckets.CopyTo(colors);
}
finally
{
ArrayPool<SKColor>.Shared.Return(bucketsArray); ArrayPool<SKColor>.Shared.Return(bucketsArray);
} }
}
public static void SortBlue(in Span<SKColor> span) public static void SortBlue(in Span<SKColor> colors)
{ {
Span<int> counts = stackalloc int[256]; Span<int> counts = stackalloc int[256];
foreach (SKColor t in span) foreach (SKColor t in colors)
counts[t.Blue]++; counts[t.Blue]++;
SKColor[] bucketsArray = ArrayPool<SKColor>.Shared.Rent(span.Length); SKColor[] bucketsArray = ArrayPool<SKColor>.Shared.Rent(colors.Length);
Span<SKColor> buckets = bucketsArray.AsSpan().Slice(0, span.Length);
try
{
Span<SKColor> buckets = bucketsArray.AsSpan().Slice(0, colors.Length);
Span<int> currentBucketIndex = stackalloc int[256]; Span<int> currentBucketIndex = stackalloc int[256];
int offset = 0; int offset = 0;
@ -86,7 +101,7 @@ internal static class QuantizerSort
offset += counts[i]; offset += counts[i];
} }
foreach (SKColor color in span) foreach (SKColor color in colors)
{ {
int index = color.Blue; int index = color.Blue;
int bucketIndex = currentBucketIndex[index]; int bucketIndex = currentBucketIndex[index];
@ -94,10 +109,13 @@ internal static class QuantizerSort
buckets[bucketIndex] = color; buckets[bucketIndex] = color;
} }
buckets.CopyTo(span); buckets.CopyTo(colors);
}
finally
{
ArrayPool<SKColor>.Shared.Return(bucketsArray); ArrayPool<SKColor>.Shared.Return(bucketsArray);
} }
}
#endregion #endregion
} }

View File

@ -66,11 +66,17 @@ namespace Artemis.Core.ColorScience
else else
{ {
SortColor[] sortColorArray = ArrayPool<SortColor>.Shared.Rent(colors.Length); SortColor[] sortColorArray = ArrayPool<SortColor>.Shared.Rent(colors.Length);
try
{
Span<SortColor> sortColors = sortColorArray.AsSpan(0, colors.Length); Span<SortColor> sortColors = sortColorArray.AsSpan(0, colors.Length);
Sort(colors, sortColors, referenceColor); Sort(colors, sortColors, referenceColor);
}
finally
{
ArrayPool<SortColor>.Shared.Return(sortColorArray); ArrayPool<SortColor>.Shared.Return(sortColorArray);
} }
} }
}
/// <summary> /// <summary>
/// Gets the Cie94 difference between two colors. /// Gets the Cie94 difference between two colors.