diff --git a/src/Artemis.Core/Artemis.Core.csproj b/src/Artemis.Core/Artemis.Core.csproj index d4caadce7..9a766c559 100644 --- a/src/Artemis.Core/Artemis.Core.csproj +++ b/src/Artemis.Core/Artemis.Core.csproj @@ -12,7 +12,6 @@ ArtemisRGB.Core 1 enable - true diff --git a/src/Artemis.Core/ColorScience/Quantization/ColorCube.cs b/src/Artemis.Core/ColorScience/Quantization/ColorCube.cs index 0bfc2fe59..5b7b353a0 100644 --- a/src/Artemis.Core/ColorScience/Quantization/ColorCube.cs +++ b/src/Artemis.Core/ColorScience/Quantization/ColorCube.cs @@ -1,6 +1,7 @@ using SkiaSharp; using System; using System.Numerics; +using System.Runtime.CompilerServices; using System.Runtime.InteropServices; 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 static readonly int ELEMENTS_PER_VECTOR = Vector.Count / BYTES_PER_COLOR; @@ -27,19 +28,16 @@ internal class ColorCube private readonly int _from; private readonly int _length; - private SortTarget _currentOrder = SortTarget.None; + private readonly SortTarget _currentOrder = SortTarget.None; public ColorCube(in Span fullColorList, int from, int length, SortTarget preOrdered) { this._from = from; this._length = length; - OrderColors(fullColorList.Slice(from, length), preOrdered); - } + if (length < 2) return; - private void OrderColors(in Span colors, SortTarget preOrdered) - { - if (colors.Length < 2) return; + Span colors = fullColorList.Slice(from, length); ColorRanges colorRanges = GetColorRanges(colors); if ((colorRanges.RedRange > colorRanges.GreenRange) && (colorRanges.RedRange > colorRanges.BlueRange)) @@ -65,29 +63,21 @@ internal class ColorCube } } - private unsafe ColorRanges GetColorRanges(in ReadOnlySpan colors) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private ColorRanges GetColorRanges(in ReadOnlySpan colors) { if (Vector.IsHardwareAccelerated && (colors.Length >= Vector.Count)) { 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 max = Vector.Zero; Vector min = new(byte.MaxValue); - - ReadOnlySpan colorBytes = MemoryMarshal.AsBytes(colors); - fixed (byte* colorPtr = &MemoryMarshal.GetReference(colorBytes)) + foreach (Vector currentVector in MemoryMarshal.Cast>(colors[..vectorElements])) { - byte* current = colorPtr; - for (int i = 0; i < chunks; i++) - { - Vector currentVector = *(Vector*)current; - - max = Vector.Max(max, currentVector); - min = Vector.Min(min, currentVector); - - current += BYTES_PER_VECTOR; - } + max = Vector.Max(max, currentVector); + min = Vector.Min(min, currentVector); } byte redMin = byte.MaxValue; @@ -144,7 +134,7 @@ internal class ColorCube } } - + [MethodImpl(MethodImplOptions.AggressiveInlining)] internal void Split(in Span fullColorList, out ColorCube a, out ColorCube b) { Span colors = fullColorList.Slice(_from, _length); @@ -155,6 +145,7 @@ internal class ColorCube b = new ColorCube(fullColorList, _from + median, colors.Length - median, _currentOrder); } + [MethodImpl(MethodImplOptions.AggressiveInlining)] internal SKColor GetAverageColor(in ReadOnlySpan fullColorList) { ReadOnlySpan colors = fullColorList.Slice(_from, _length); diff --git a/src/Artemis.Core/ColorScience/Quantization/QuantizerSort.cs b/src/Artemis.Core/ColorScience/Quantization/QuantizerSort.cs index 3126a9b7c..46b593ece 100644 --- a/src/Artemis.Core/ColorScience/Quantization/QuantizerSort.cs +++ b/src/Artemis.Core/ColorScience/Quantization/QuantizerSort.cs @@ -9,94 +9,112 @@ internal static class QuantizerSort { #region Methods - public static void SortRed(in Span span) + public static void SortRed(in Span colors) { Span counts = stackalloc int[256]; - foreach (SKColor t in span) + foreach (SKColor t in colors) counts[t.Red]++; - SKColor[] bucketsArray = ArrayPool.Shared.Rent(span.Length); - Span buckets = bucketsArray.AsSpan().Slice(0, span.Length); - Span currentBucketIndex = stackalloc int[256]; + SKColor[] bucketsArray = ArrayPool.Shared.Rent(colors.Length); - int offset = 0; - for (int i = 0; i < counts.Length; i++) + try { - currentBucketIndex[i] = offset; - offset += counts[i]; - } + Span buckets = bucketsArray.AsSpan().Slice(0, colors.Length); + Span currentBucketIndex = stackalloc int[256]; - foreach (SKColor color in span) + int offset = 0; + for (int i = 0; i < counts.Length; i++) + { + currentBucketIndex[i] = offset; + offset += counts[i]; + } + + foreach (SKColor color in colors) + { + int index = color.Red; + int bucketIndex = currentBucketIndex[index]; + currentBucketIndex[index]++; + buckets[bucketIndex] = color; + } + + buckets.CopyTo(colors); + } + finally { - int index = color.Red; - int bucketIndex = currentBucketIndex[index]; - currentBucketIndex[index]++; - buckets[bucketIndex] = color; + ArrayPool.Shared.Return(bucketsArray); } - - buckets.CopyTo(span); - - ArrayPool.Shared.Return(bucketsArray); } - public static void SortGreen(in Span span) + public static void SortGreen(in Span colors) { Span counts = stackalloc int[256]; - foreach (SKColor t in span) + foreach (SKColor t in colors) counts[t.Green]++; - SKColor[] bucketsArray = ArrayPool.Shared.Rent(span.Length); - Span buckets = bucketsArray.AsSpan().Slice(0, span.Length); - Span currentBucketIndex = stackalloc int[256]; + SKColor[] bucketsArray = ArrayPool.Shared.Rent(colors.Length); - int offset = 0; - for (int i = 0; i < counts.Length; i++) + try { - currentBucketIndex[i] = offset; - offset += counts[i]; - } + Span buckets = bucketsArray.AsSpan().Slice(0, colors.Length); + Span currentBucketIndex = stackalloc int[256]; - foreach (SKColor color in span) + int offset = 0; + for (int i = 0; i < counts.Length; i++) + { + currentBucketIndex[i] = offset; + offset += counts[i]; + } + + foreach (SKColor color in colors) + { + int index = color.Green; + int bucketIndex = currentBucketIndex[index]; + currentBucketIndex[index]++; + buckets[bucketIndex] = color; + } + + buckets.CopyTo(colors); + } + finally { - int index = color.Green; - int bucketIndex = currentBucketIndex[index]; - currentBucketIndex[index]++; - buckets[bucketIndex] = color; + ArrayPool.Shared.Return(bucketsArray); } - - buckets.CopyTo(span); - - ArrayPool.Shared.Return(bucketsArray); } - public static void SortBlue(in Span span) + public static void SortBlue(in Span colors) { Span counts = stackalloc int[256]; - foreach (SKColor t in span) + foreach (SKColor t in colors) counts[t.Blue]++; - SKColor[] bucketsArray = ArrayPool.Shared.Rent(span.Length); - Span buckets = bucketsArray.AsSpan().Slice(0, span.Length); - Span currentBucketIndex = stackalloc int[256]; + SKColor[] bucketsArray = ArrayPool.Shared.Rent(colors.Length); - int offset = 0; - for (int i = 0; i < counts.Length; i++) + try { - currentBucketIndex[i] = offset; - offset += counts[i]; - } + Span buckets = bucketsArray.AsSpan().Slice(0, colors.Length); + Span currentBucketIndex = stackalloc int[256]; - foreach (SKColor color in span) + int offset = 0; + for (int i = 0; i < counts.Length; i++) + { + currentBucketIndex[i] = offset; + offset += counts[i]; + } + + foreach (SKColor color in colors) + { + int index = color.Blue; + int bucketIndex = currentBucketIndex[index]; + currentBucketIndex[index]++; + buckets[bucketIndex] = color; + } + + buckets.CopyTo(colors); + } + finally { - int index = color.Blue; - int bucketIndex = currentBucketIndex[index]; - currentBucketIndex[index]++; - buckets[bucketIndex] = color; + ArrayPool.Shared.Return(bucketsArray); } - - buckets.CopyTo(span); - - ArrayPool.Shared.Return(bucketsArray); } #endregion diff --git a/src/Artemis.Core/ColorScience/Sorting/ColorSorter.cs b/src/Artemis.Core/ColorScience/Sorting/ColorSorter.cs index 63f3f04d9..0ff7c5d6f 100644 --- a/src/Artemis.Core/ColorScience/Sorting/ColorSorter.cs +++ b/src/Artemis.Core/ColorScience/Sorting/ColorSorter.cs @@ -66,9 +66,15 @@ namespace Artemis.Core.ColorScience else { SortColor[] sortColorArray = ArrayPool.Shared.Rent(colors.Length); - Span sortColors = sortColorArray.AsSpan(0, colors.Length); - Sort(colors, sortColors, referenceColor); - ArrayPool.Shared.Return(sortColorArray); + try + { + Span sortColors = sortColorArray.AsSpan(0, colors.Length); + Sort(colors, sortColors, referenceColor); + } + finally + { + ArrayPool.Shared.Return(sortColorArray); + } } }