diff --git a/RGB.NET.Core/Rendering/Textures/PixelTexture.cs b/RGB.NET.Core/Rendering/Textures/PixelTexture.cs index f6525cf..f193ec2 100644 --- a/RGB.NET.Core/Rendering/Textures/PixelTexture.cs +++ b/RGB.NET.Core/Rendering/Textures/PixelTexture.cs @@ -15,13 +15,14 @@ namespace RGB.NET.Core #region Properties & Fields - private readonly int _dataPerColor; - - protected ISampler Sampler { get; set; } - protected T[] Data { get; set; } + private readonly int _dataPerPixel; + private readonly int _stride; + public ISampler Sampler { get; set; } public Size Size { get; } + protected abstract ReadOnlySpan Data { get; } + public virtual Color this[in Point point] { get @@ -30,7 +31,7 @@ namespace RGB.NET.Core int x = (int)Math.Round(Size.Width * point.X.Clamp(0, 1)); int y = (int)Math.Round(Size.Height * point.Y.Clamp(0, 1)); - return GetColor(x, y); + return GetColor(GetPixelData(x, y)); } } @@ -45,22 +46,30 @@ namespace RGB.NET.Core int width = (int)Math.Round(Size.Width * rectangle.Size.Width.Clamp(0, 1)); int height = (int)Math.Round(Size.Height * rectangle.Size.Height.Clamp(0, 1)); - int bufferSize = width * height * _dataPerColor; + int bufferSize = width * height * _dataPerPixel; if (bufferSize <= STACK_ALLOC_LIMIT) { Span buffer = stackalloc T[bufferSize]; GetRegionData(x, y, width, height, buffer); - return Sampler.SampleColor(new SamplerInfo(width, height, buffer)); + + Span pixelData = stackalloc T[_dataPerPixel]; + Sampler.SampleColor(new SamplerInfo(width, height, buffer), pixelData); + + return GetColor(pixelData); } else { T[] rent = ArrayPool.Shared.Rent(bufferSize); + Span buffer = new Span(rent).Slice(0, bufferSize); GetRegionData(x, y, width, height, buffer); - Color color = Sampler.SampleColor(new SamplerInfo(width, height, buffer)); + + Span pixelData = stackalloc T[_dataPerPixel]; + Sampler.SampleColor(new SamplerInfo(width, height, buffer), pixelData); + ArrayPool.Shared.Return(rent); - return color; + return GetColor(pixelData); } } } @@ -69,10 +78,10 @@ namespace RGB.NET.Core #region Constructors - public PixelTexture(int with, int height, T[] data, int dataPerColor, ISampler sampler) + public PixelTexture(int with, int height, int dataPerPixel, ISampler sampler) { - this.Data = data; - this._dataPerColor = dataPerColor; + this._stride = with; + this._dataPerPixel = dataPerPixel; this.Sampler = sampler; Size = new Size(with, height); @@ -82,8 +91,22 @@ namespace RGB.NET.Core #region Methods - protected abstract Color GetColor(int x, int y); - protected abstract void GetRegionData(int x, int y, int width, int height, in Span buffer); + protected abstract Color GetColor(ReadOnlySpan pixel); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + protected virtual ReadOnlySpan GetPixelData(int x, int y) => Data.Slice((y * _stride) + x, _dataPerPixel); + + protected virtual void GetRegionData(int x, int y, int width, int height, Span buffer) + { + int dataWidth = width * _dataPerPixel; + ReadOnlySpan data = Data; + for (int i = 0; i < height; i++) + { + ReadOnlySpan dataSlice = data.Slice((((y + i) * _stride) + x) * _dataPerPixel, dataWidth); + Span destination = buffer.Slice(i * dataWidth, dataWidth); + dataSlice.CopyTo(destination); + } + } #endregion } @@ -92,7 +115,9 @@ namespace RGB.NET.Core { #region Properties & Fields - private readonly int _stride; + private readonly Color[] _data; + + protected override ReadOnlySpan Data => _data; #endregion @@ -103,9 +128,9 @@ namespace RGB.NET.Core { } public PixelTexture(int with, int height, Color[] data, ISampler sampler) - : base(with, height, data, 1, sampler) + : base(with, height, 1, sampler) { - this._stride = with; + this._data = data; if (Data.Length != (with * height)) throw new ArgumentException($"Data-Length {Data.Length} differs from the given size {with}x{height} ({with * height})."); } @@ -114,19 +139,7 @@ namespace RGB.NET.Core #region Methods - [MethodImpl(MethodImplOptions.AggressiveInlining)] - protected override Color GetColor(int x, int y) => Data[(y * _stride) + x]; - - protected override void GetRegionData(int x, int y, int width, int height, in Span buffer) - { - ReadOnlySpan data = Data.AsSpan(); - for (int i = 0; i < height; i++) - { - ReadOnlySpan dataSlice = data.Slice(((y + i) * _stride) + x, width); - Span destination = buffer.Slice(i * width, width); - dataSlice.CopyTo(destination); - } - } + protected override Color GetColor(ReadOnlySpan pixel) => pixel[0]; #endregion } diff --git a/RGB.NET.Core/Rendering/Textures/Sampler/AverageColorSampler.cs b/RGB.NET.Core/Rendering/Textures/Sampler/AverageColorSampler.cs index 5492f90..11c3dd2 100644 --- a/RGB.NET.Core/Rendering/Textures/Sampler/AverageColorSampler.cs +++ b/RGB.NET.Core/Rendering/Textures/Sampler/AverageColorSampler.cs @@ -1,19 +1,15 @@ -namespace RGB.NET.Core +using System; + +namespace RGB.NET.Core { public class AverageColorSampler : ISampler { - #region Properties & Fields - - public bool SampleAlpha { get; set; } - - #endregion - #region Methods - public Color SampleColor(SamplerInfo info) + public void SampleColor(in SamplerInfo info, Span pixelData) { int count = info.Width * info.Height; - if (count == 0) return Color.Transparent; + if (count == 0) return; float a = 0, r = 0, g = 0, b = 0; foreach (Color color in info.Data) @@ -24,9 +20,7 @@ b += color.B; } - return SampleAlpha - ? new Color(a / count, r / count, g / count, b / count) - : new Color(r / count, g / count, b / count); + pixelData[0] = new Color(a / count, r / count, g / count, b / count); } #endregion diff --git a/RGB.NET.Core/Rendering/Textures/Sampler/ISampler.cs b/RGB.NET.Core/Rendering/Textures/Sampler/ISampler.cs index 9bfd812..2b76c5f 100644 --- a/RGB.NET.Core/Rendering/Textures/Sampler/ISampler.cs +++ b/RGB.NET.Core/Rendering/Textures/Sampler/ISampler.cs @@ -1,7 +1,9 @@ -namespace RGB.NET.Core +using System; + +namespace RGB.NET.Core { public interface ISampler { - Color SampleColor(SamplerInfo info); + void SampleColor(in SamplerInfo info, Span pixelData); } } diff --git a/RGB.NET.Presets/Textures/BytePixelTexture.cs b/RGB.NET.Presets/Textures/BytePixelTexture.cs index c1ed31c..1e993b6 100644 --- a/RGB.NET.Presets/Textures/BytePixelTexture.cs +++ b/RGB.NET.Presets/Textures/BytePixelTexture.cs @@ -8,22 +8,11 @@ namespace RGB.NET.Presets.Textures { #region Properties & Fields - private readonly int _stride; + private readonly byte[] _data; + protected override ReadOnlySpan Data => _data; public ColorFormat ColorFormat { get; } - public override Color this[in Rectangle rectangle] - { - get - { - Color color = base[rectangle]; - if (ColorFormat == ColorFormat.BGR) - return new Color(color.A, color.B, color.G, color.R); - - return color; - } - } - #endregion #region Constructors @@ -33,9 +22,9 @@ namespace RGB.NET.Presets.Textures { } public BytePixelTexture(int with, int height, byte[] data, ISampler sampler, ColorFormat colorFormat = ColorFormat.RGB) - : base(with, height, data, 3, sampler) + : base(with, height, 3, sampler) { - this._stride = with; + this._data = data; this.ColorFormat = colorFormat; if (Data.Length != ((with * height) * 3)) throw new ArgumentException($"Data-Length {Data.Length} differs from the given size {with}x{height} * 3 bytes ({with * height * 3})."); @@ -45,26 +34,12 @@ namespace RGB.NET.Presets.Textures #region Methods - protected override Color GetColor(int x, int y) + protected override Color GetColor(ReadOnlySpan pixel) { - int offset = ((y * _stride) + x) * 3; - if (ColorFormat == ColorFormat.BGR) - return new Color(Data[offset + 2], Data[offset + 1], Data[offset + 1]); + return new Color(pixel[2], pixel[1], pixel[0]); - return new Color(Data[offset], Data[offset + 1], Data[offset + 2]); - } - - protected override void GetRegionData(int x, int y, int width, int height, in Span buffer) - { - int width3 = width * 3; - ReadOnlySpan data = Data.AsSpan(); - for (int i = 0; i < height; i++) - { - ReadOnlySpan dataSlice = data.Slice((((y + i) * _stride) + x) * 3, width3); - Span destination = buffer.Slice(i * width3, width3); - dataSlice.CopyTo(destination); - } + return new Color(pixel[0], pixel[1], pixel[2]); } #endregion diff --git a/RGB.NET.Presets/Textures/FloatPixelTexture.cs b/RGB.NET.Presets/Textures/FloatPixelTexture.cs index aa8bd77..936cdd9 100644 --- a/RGB.NET.Presets/Textures/FloatPixelTexture.cs +++ b/RGB.NET.Presets/Textures/FloatPixelTexture.cs @@ -8,22 +8,11 @@ namespace RGB.NET.Presets.Textures { #region Properties & Fields - private readonly int _stride; + private readonly float[] _data; + protected override ReadOnlySpan Data => _data; public ColorFormat ColorFormat { get; } - public override Color this[in Rectangle rectangle] - { - get - { - Color color = base[rectangle]; - if (ColorFormat == ColorFormat.BGR) - return new Color(color.A, color.B, color.G, color.R); - - return color; - } - } - #endregion #region Constructors @@ -33,9 +22,9 @@ namespace RGB.NET.Presets.Textures { } public FloatPixelTexture(int with, int height, float[] data, ISampler sampler, ColorFormat colorFormat = ColorFormat.RGB) - : base(with, height, data, 3, sampler) + : base(with, height, 3, sampler) { - this._stride = with; + this._data = data; this.ColorFormat = colorFormat; if (Data.Length != ((with * height) * 3)) throw new ArgumentException($"Data-Length {Data.Length} differs from the given size {with}x{height} * 3 bytes ({with * height * 3})."); @@ -45,26 +34,12 @@ namespace RGB.NET.Presets.Textures #region Methods - protected override Color GetColor(int x, int y) + protected override Color GetColor(ReadOnlySpan pixel) { - int offset = ((y * _stride) + x) * 3; - if (ColorFormat == ColorFormat.BGR) - return new Color(Data[offset + 2], Data[offset + 1], Data[offset + 1]); + return new Color(pixel[2], pixel[1], pixel[0]); - return new Color(Data[offset], Data[offset + 1], Data[offset + 2]); - } - - protected override void GetRegionData(int x, int y, int width, int height, in Span buffer) - { - int width3 = width * 3; - ReadOnlySpan data = Data.AsSpan(); - for (int i = 0; i < height; i++) - { - ReadOnlySpan dataSlice = data.Slice((((y + i) * _stride) + x) * 3, width3); - Span destination = buffer.Slice(i * width3, width3); - dataSlice.CopyTo(destination); - } + return new Color(pixel[0], pixel[1], pixel[2]); } #endregion diff --git a/RGB.NET.Presets/Textures/Sampler/AverageByteSampler.cs b/RGB.NET.Presets/Textures/Sampler/AverageByteSampler.cs index 694dbe6..8c91242 100644 --- a/RGB.NET.Presets/Textures/Sampler/AverageByteSampler.cs +++ b/RGB.NET.Presets/Textures/Sampler/AverageByteSampler.cs @@ -7,23 +7,21 @@ namespace RGB.NET.Presets.Textures.Sampler { #region Methods - public Color SampleColor(SamplerInfo info) + public void SampleColor(in SamplerInfo info, Span pixelData) { int count = info.Width * info.Height; - if (count == 0) return Color.Transparent; + if (count == 0) return; ReadOnlySpan data = info.Data; - uint r = 0, g = 0, b = 0; - for (int i = 0; i < data.Length; i += 3) - { - r += data[i]; - g += data[i + 1]; - b += data[i + 2]; - } + int dataLength = pixelData.Length; + Span sums = stackalloc uint[dataLength]; + for (int i = 0; i < data.Length; i += dataLength) + for (int j = 0; j < sums.Length; j++) + sums[j] += data[i + j]; - float divisor = count * byte.MaxValue; - return new Color(r / divisor, g / divisor, b / divisor); + for (int i = 0; i < pixelData.Length; i++) + pixelData[i] = (byte)MathF.Round(sums[i] / (float)count); } #endregion diff --git a/RGB.NET.Presets/Textures/Sampler/AverageFloatSampler.cs b/RGB.NET.Presets/Textures/Sampler/AverageFloatSampler.cs index 59e1987..a9584a3 100644 --- a/RGB.NET.Presets/Textures/Sampler/AverageFloatSampler.cs +++ b/RGB.NET.Presets/Textures/Sampler/AverageFloatSampler.cs @@ -7,22 +7,21 @@ namespace RGB.NET.Presets.Textures.Sampler { #region Methods - public Color SampleColor(SamplerInfo info) + public void SampleColor(in SamplerInfo info, Span pixelData) { int count = info.Width * info.Height; - if (count == 0) return Color.Transparent; + if (count == 0) return; ReadOnlySpan data = info.Data; - float r = 0, g = 0, b = 0; - for (int i = 0; i < data.Length; i += 3) - { - r += data[i]; - g += data[i + 1]; - b += data[i + 2]; - } + int dataLength = pixelData.Length; + Span sums = stackalloc float[dataLength]; + for (int i = 0; i < data.Length; i += dataLength) + for (int j = 0; j < sums.Length; j++) + sums[j] += data[i + j]; - return new Color(r / count, g / count, b / count); + for (int i = 0; i < pixelData.Length; i++) + pixelData[i] = sums[i] / count; } #endregion