diff --git a/src/Artemis.Core/Artemis.Core.csproj b/src/Artemis.Core/Artemis.Core.csproj index 985ec0b61..c1040eec9 100644 --- a/src/Artemis.Core/Artemis.Core.csproj +++ b/src/Artemis.Core/Artemis.Core.csproj @@ -27,6 +27,7 @@ true enable latest + true diff --git a/src/Artemis.Core/Services/ColorQuantizer/ColorCube.cs b/src/Artemis.Core/Services/ColorQuantizer/ColorCube.cs index d7a4c5573..aa1acd43f 100644 --- a/src/Artemis.Core/Services/ColorQuantizer/ColorCube.cs +++ b/src/Artemis.Core/Services/ColorQuantizer/ColorCube.cs @@ -2,11 +2,20 @@ using SkiaSharp; using System.Diagnostics.CodeAnalysis; using System.Numerics; +using System.Runtime.InteropServices; namespace Artemis.Core.Services; internal class ColorCube { + #region Constants + + private const int BYTES_PER_COLOR = 4; // DarthAffe 01.09.2022: Skia stores colors as uint (BGRA) + private static readonly int ELEMENTS_PER_VECTOR = Vector.Count / BYTES_PER_COLOR; + private static readonly int BYTES_PER_VECTOR = ELEMENTS_PER_VECTOR * BYTES_PER_COLOR; + + #endregion + #region Properties & Fields private readonly int _from; @@ -57,9 +66,63 @@ internal class ColorCube } } - private ColorRanges GetColorRanges(in Span colors) + private unsafe ColorRanges GetColorRanges(in ReadOnlySpan colors) { - if (colors.Length < 70) + if (Vector.IsHardwareAccelerated && (colors.Length >= Vector.Count)) + { + int chunks = colors.Length / ELEMENTS_PER_VECTOR; + int missingElements = colors.Length - (chunks * ELEMENTS_PER_VECTOR); + + Vector max = Vector.Zero; + Vector min = new(byte.MaxValue); + + ReadOnlySpan colorBytes = MemoryMarshal.AsBytes(colors); + fixed (byte* colorPtr = &MemoryMarshal.GetReference(colorBytes)) + { + 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; + } + } + + byte redMin = byte.MaxValue; + byte redMax = byte.MinValue; + byte greenMin = byte.MaxValue; + byte greenMax = byte.MinValue; + byte blueMin = byte.MaxValue; + byte blueMax = byte.MinValue; + + for (int i = 0; i < BYTES_PER_VECTOR; i += BYTES_PER_COLOR) + { + if (min[i + 2] < redMin) redMin = min[i + 2]; + if (max[i + 2] > redMax) redMax = max[i + 2]; + if (min[i + 1] < greenMin) greenMin = min[i + 1]; + if (max[i + 1] > greenMax) greenMax = max[i + 1]; + if (min[i] < blueMin) blueMin = min[i]; + if (max[i] > blueMax) blueMax = max[i]; + } + + for (int i = 0; i < missingElements; i++) + { + SKColor color = colors[^(i + 1)]; + + if (color.Red < redMin) redMin = color.Red; + if (color.Red > redMax) redMax = color.Red; + if (color.Green < greenMin) greenMin = color.Green; + if (color.Green > greenMax) greenMax = color.Green; + if (color.Blue < blueMin) blueMin = color.Blue; + if (color.Blue > blueMax) blueMax = color.Blue; + } + + return new ColorRanges((byte)(redMax - redMin), (byte)(greenMax - greenMin), (byte)(blueMax - blueMin)); + } + else { byte redMin = byte.MaxValue; byte redMax = byte.MinValue; @@ -78,70 +141,6 @@ internal class ColorCube if (color.Blue > blueMax) blueMax = color.Blue; } - return new ColorRanges((byte)(redMax - redMin), (byte)(greenMax - greenMin), (byte)(blueMax - blueMin)); - } - else - { - int elementsPerVector = Vector.Count / 3; - int chunks = colors.Length / elementsPerVector; - int missingElements = colors.Length - (chunks * elementsPerVector); - - Vector max = Vector.Zero; - Vector min = new(byte.MaxValue); - - Span chunkData = stackalloc byte[Vector.Count]; - int dataIndex = 0; - for (int i = 0; i < chunks; i++) - { - int chunkDataIndex = 0; - for (int j = 0; j < elementsPerVector; j++) - { - SKColor color = colors[dataIndex]; - chunkData[chunkDataIndex] = color.Red; - ++chunkDataIndex; - chunkData[chunkDataIndex] = color.Green; - ++chunkDataIndex; - chunkData[chunkDataIndex] = color.Blue; - ++chunkDataIndex; - ++dataIndex; - } - - Vector chunkVector = new(chunkData); - max = Vector.Max(max, chunkVector); - min = Vector.Min(min, chunkVector); - } - - byte redMin = byte.MaxValue; - byte redMax = byte.MinValue; - byte greenMin = byte.MaxValue; - byte greenMax = byte.MinValue; - byte blueMin = byte.MaxValue; - byte blueMax = byte.MinValue; - - int vectorEntries = elementsPerVector * 3; - for (int i = 0; i < vectorEntries; i += 3) - { - if (min[i] < redMin) redMin = min[i]; - if (max[i] > redMax) redMax = max[i]; - if (min[i + 1] < greenMin) greenMin = min[i + 1]; - if (max[i + 1] > greenMax) greenMax = max[i + 1]; - if (min[i + 2] < blueMin) blueMin = min[i + 2]; - if (max[i + 2] > blueMax) blueMax = max[i + 2]; - } - - for (int i = 0; i < missingElements; i++) - { - SKColor color = colors[dataIndex]; - if (color.Red < redMin) redMin = color.Red; - if (color.Red > redMax) redMax = color.Red; - if (color.Green < greenMin) greenMin = color.Green; - if (color.Green > greenMax) greenMax = color.Green; - if (color.Blue < blueMin) blueMin = color.Blue; - if (color.Blue > blueMax) blueMax = color.Blue; - - ++dataIndex; - } - return new ColorRanges((byte)(redMax - redMin), (byte)(greenMax - greenMin), (byte)(blueMax - blueMin)); } }