From c21cb57bd7fd9daa8d469cf693e3bfacb3cbf922 Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Mon, 6 Jan 2020 03:09:45 +0100 Subject: [PATCH 01/12] Added memory pooling in the OilPaintingProcessor --- .../Effects/OilPaintingProcessor{TPixel}.cs | 79 ++++++++++--------- 1 file changed, 43 insertions(+), 36 deletions(-) diff --git a/src/ImageSharp/Processing/Processors/Effects/OilPaintingProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Effects/OilPaintingProcessor{TPixel}.cs index 4cac6b0f66..08bd3c19a6 100644 --- a/src/ImageSharp/Processing/Processors/Effects/OilPaintingProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Effects/OilPaintingProcessor{TPixel}.cs @@ -2,12 +2,15 @@ // Licensed under the Apache License, Version 2.0. using System; +using System.Buffers; using System.Numerics; +using System.Runtime.CompilerServices; using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.Advanced.ParallelUtils; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; +using SixLabors.Memory; using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing.Processors.Effects @@ -53,6 +56,8 @@ namespace SixLabors.ImageSharp.Processing.Processors.Effects int radius = brushSize >> 1; int levels = this.definition.Levels; + Configuration configuration = this.Configuration; + using (Buffer2D targetPixels = this.Configuration.MemoryAllocator.Allocate2D(source.Size())) { source.CopyTo(targetPixels); @@ -73,54 +78,56 @@ namespace SixLabors.ImageSharp.Processing.Processors.Effects int maxIntensity = 0; int maxIndex = 0; - var intensityBin = new int[levels]; - var redBin = new float[levels]; - var blueBin = new float[levels]; - var greenBin = new float[levels]; - - for (int fy = 0; fy <= radius; fy++) + using (IMemoryOwner bins = configuration.MemoryAllocator.Allocate(levels * 4, AllocationOptions.Clean)) { - int fyr = fy - radius; - int offsetY = y + fyr; + ref float binsRef = ref bins.GetReference(); + ref int intensityBinRef = ref Unsafe.As(ref binsRef); + ref float redBinRef = ref Unsafe.Add(ref binsRef, levels); + ref float blueBinRef = ref Unsafe.Add(ref redBinRef, levels); + ref float greenBinRef = ref Unsafe.Add(ref blueBinRef, levels); - offsetY = offsetY.Clamp(0, maxY); + for (int fy = 0; fy <= radius; fy++) + { + int fyr = fy - radius; + int offsetY = y + fyr; - Span sourceOffsetRow = source.GetPixelRowSpan(offsetY); + offsetY = offsetY.Clamp(0, maxY); - for (int fx = 0; fx <= radius; fx++) - { - int fxr = fx - radius; - int offsetX = x + fxr; - offsetX = offsetX.Clamp(0, maxX); + Span sourceOffsetRow = source.GetPixelRowSpan(offsetY); - var vector = sourceOffsetRow[offsetX].ToVector4(); + for (int fx = 0; fx <= radius; fx++) + { + int fxr = fx - radius; + int offsetX = x + fxr; + offsetX = offsetX.Clamp(0, maxX); - float sourceRed = vector.X; - float sourceBlue = vector.Z; - float sourceGreen = vector.Y; + var vector = sourceOffsetRow[offsetX].ToVector4(); - int currentIntensity = (int)MathF.Round( - (sourceBlue + sourceGreen + sourceRed) / 3F * (levels - 1)); + float sourceRed = vector.X; + float sourceBlue = vector.Z; + float sourceGreen = vector.Y; - intensityBin[currentIntensity]++; - blueBin[currentIntensity] += sourceBlue; - greenBin[currentIntensity] += sourceGreen; - redBin[currentIntensity] += sourceRed; + int currentIntensity = (int)MathF.Round((sourceBlue + sourceGreen + sourceRed) / 3F * (levels - 1)); - if (intensityBin[currentIntensity] > maxIntensity) - { - maxIntensity = intensityBin[currentIntensity]; - maxIndex = currentIntensity; + Unsafe.Add(ref intensityBinRef, currentIntensity)++; + Unsafe.Add(ref redBinRef, currentIntensity) += sourceRed; + Unsafe.Add(ref blueBinRef, currentIntensity) += sourceBlue; + Unsafe.Add(ref greenBinRef, currentIntensity) += sourceGreen; + + if (Unsafe.Add(ref intensityBinRef, currentIntensity) > maxIntensity) + { + maxIntensity = Unsafe.Add(ref intensityBinRef, currentIntensity); + maxIndex = currentIntensity; + } } - } - float red = MathF.Abs(redBin[maxIndex] / maxIntensity); - float green = MathF.Abs(greenBin[maxIndex] / maxIntensity); - float blue = MathF.Abs(blueBin[maxIndex] / maxIntensity); + float red = MathF.Abs(Unsafe.Add(ref redBinRef, maxIndex) / maxIntensity); + float blue = MathF.Abs(Unsafe.Add(ref blueBinRef, maxIndex) / maxIntensity); + float green = MathF.Abs(Unsafe.Add(ref greenBinRef, maxIndex) / maxIntensity); - ref TPixel pixel = ref targetRow[x]; - pixel.FromVector4( - new Vector4(red, green, blue, sourceRow[x].ToVector4().W)); + ref TPixel pixel = ref targetRow[x]; + pixel.FromVector4(new Vector4(red, green, blue, sourceRow[x].ToVector4().W)); + } } } } From 692c35b166ca7a18680b5efa6cd863ee48e01e11 Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Mon, 6 Jan 2020 03:47:26 +0100 Subject: [PATCH 02/12] Switched to vectorized pixel conversions --- .../Effects/OilPaintingProcessor{TPixel}.cs | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/src/ImageSharp/Processing/Processors/Effects/OilPaintingProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Effects/OilPaintingProcessor{TPixel}.cs index 08bd3c19a6..943d7f2346 100644 --- a/src/ImageSharp/Processing/Processors/Effects/OilPaintingProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Effects/OilPaintingProcessor{TPixel}.cs @@ -5,6 +5,7 @@ using System; using System.Buffers; using System.Numerics; using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.Advanced.ParallelUtils; @@ -63,15 +64,20 @@ namespace SixLabors.ImageSharp.Processing.Processors.Effects source.CopyTo(targetPixels); var workingRect = Rectangle.FromLTRB(startX, startY, endX, endY); - ParallelHelper.IterateRows( + ParallelHelper.IterateRowsWithTempBuffer( workingRect, this.Configuration, - rows => + (rows, vectorBuffer) => { + Span vectorSpan = vectorBuffer.Span; + int length = vectorSpan.Length; + ref Vector4 vectorSpanRef = ref MemoryMarshal.GetReference(vectorSpan); + for (int y = rows.Min; y < rows.Max; y++) { Span sourceRow = source.GetPixelRowSpan(y); - Span targetRow = targetPixels.GetRowSpan(y); + Span targetRowSpan = targetPixels.GetRowSpan(y).Slice(startX, length); + PixelOperations.Instance.ToVector4(configuration, targetRowSpan, vectorSpan); for (int x = startX; x < endX; x++) { @@ -124,12 +130,14 @@ namespace SixLabors.ImageSharp.Processing.Processors.Effects float red = MathF.Abs(Unsafe.Add(ref redBinRef, maxIndex) / maxIntensity); float blue = MathF.Abs(Unsafe.Add(ref blueBinRef, maxIndex) / maxIntensity); float green = MathF.Abs(Unsafe.Add(ref greenBinRef, maxIndex) / maxIntensity); + float alpha = sourceRow[x].ToVector4().W; - ref TPixel pixel = ref targetRow[x]; - pixel.FromVector4(new Vector4(red, green, blue, sourceRow[x].ToVector4().W)); + Unsafe.Add(ref vectorSpanRef, x) = new Vector4(red, green, blue, alpha); } } } + + PixelOperations.Instance.FromVector4Destructive(configuration, vectorSpan, targetRowSpan); } }); From 0e61f0ea85b6107f2557c7f881eac667a87d9e79 Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Sun, 26 Jan 2020 13:04:58 +0100 Subject: [PATCH 03/12] Removed incorrect using directive --- .../Processors/Effects/OilPaintingProcessor{TPixel}.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/ImageSharp/Processing/Processors/Effects/OilPaintingProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Effects/OilPaintingProcessor{TPixel}.cs index decc0d0aaa..477991a16a 100644 --- a/src/ImageSharp/Processing/Processors/Effects/OilPaintingProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Effects/OilPaintingProcessor{TPixel}.cs @@ -11,7 +11,6 @@ using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.Advanced.ParallelUtils; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; -using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing.Processors.Effects { From 71cf6e84a730a855fc7802e59aada833fae04d3b Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Sun, 26 Jan 2020 13:10:51 +0100 Subject: [PATCH 04/12] Reduced number of memory allocations --- .../Effects/OilPaintingProcessor{TPixel}.cs | 79 ++++++++++--------- 1 file changed, 41 insertions(+), 38 deletions(-) diff --git a/src/ImageSharp/Processing/Processors/Effects/OilPaintingProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Effects/OilPaintingProcessor{TPixel}.cs index 477991a16a..aefbeaff88 100644 --- a/src/ImageSharp/Processing/Processors/Effects/OilPaintingProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Effects/OilPaintingProcessor{TPixel}.cs @@ -72,6 +72,15 @@ namespace SixLabors.ImageSharp.Processing.Processors.Effects int length = vectorSpan.Length; ref Vector4 vectorSpanRef = ref MemoryMarshal.GetReference(vectorSpan); + // Rent the shared buffer only once per parallel item + IMemoryOwner bins = configuration.MemoryAllocator.Allocate(levels * 4); + + ref float binsRef = ref bins.GetReference(); + ref int intensityBinRef = ref Unsafe.As(ref binsRef); + ref float redBinRef = ref Unsafe.Add(ref binsRef, levels); + ref float blueBinRef = ref Unsafe.Add(ref redBinRef, levels); + ref float greenBinRef = ref Unsafe.Add(ref blueBinRef, levels); + for (int y = rows.Min; y < rows.Max; y++) { Span sourceRow = source.GetPixelRowSpan(y); @@ -83,56 +92,50 @@ namespace SixLabors.ImageSharp.Processing.Processors.Effects int maxIntensity = 0; int maxIndex = 0; - using (IMemoryOwner bins = configuration.MemoryAllocator.Allocate(levels * 4, AllocationOptions.Clean)) - { - ref float binsRef = ref bins.GetReference(); - ref int intensityBinRef = ref Unsafe.As(ref binsRef); - ref float redBinRef = ref Unsafe.Add(ref binsRef, levels); - ref float blueBinRef = ref Unsafe.Add(ref redBinRef, levels); - ref float greenBinRef = ref Unsafe.Add(ref blueBinRef, levels); + // Clear the current shared buffer before processing each target pixel + bins.Memory.Span.Clear(); - for (int fy = 0; fy <= radius; fy++) - { - int fyr = fy - radius; - int offsetY = y + fyr; + for (int fy = 0; fy <= radius; fy++) + { + int fyr = fy - radius; + int offsetY = y + fyr; - offsetY = offsetY.Clamp(0, maxY); + offsetY = offsetY.Clamp(0, maxY); - Span sourceOffsetRow = source.GetPixelRowSpan(offsetY); + Span sourceOffsetRow = source.GetPixelRowSpan(offsetY); - for (int fx = 0; fx <= radius; fx++) - { - int fxr = fx - radius; - int offsetX = x + fxr; - offsetX = offsetX.Clamp(0, maxX); + for (int fx = 0; fx <= radius; fx++) + { + int fxr = fx - radius; + int offsetX = x + fxr; + offsetX = offsetX.Clamp(0, maxX); - var vector = sourceOffsetRow[offsetX].ToVector4(); + var vector = sourceOffsetRow[offsetX].ToVector4(); - float sourceRed = vector.X; - float sourceBlue = vector.Z; - float sourceGreen = vector.Y; + float sourceRed = vector.X; + float sourceBlue = vector.Z; + float sourceGreen = vector.Y; - int currentIntensity = (int)MathF.Round((sourceBlue + sourceGreen + sourceRed) / 3F * (levels - 1)); + int currentIntensity = (int)MathF.Round((sourceBlue + sourceGreen + sourceRed) / 3F * (levels - 1)); - Unsafe.Add(ref intensityBinRef, currentIntensity)++; - Unsafe.Add(ref redBinRef, currentIntensity) += sourceRed; - Unsafe.Add(ref blueBinRef, currentIntensity) += sourceBlue; - Unsafe.Add(ref greenBinRef, currentIntensity) += sourceGreen; + Unsafe.Add(ref intensityBinRef, currentIntensity)++; + Unsafe.Add(ref redBinRef, currentIntensity) += sourceRed; + Unsafe.Add(ref blueBinRef, currentIntensity) += sourceBlue; + Unsafe.Add(ref greenBinRef, currentIntensity) += sourceGreen; - if (Unsafe.Add(ref intensityBinRef, currentIntensity) > maxIntensity) - { - maxIntensity = Unsafe.Add(ref intensityBinRef, currentIntensity); - maxIndex = currentIntensity; - } + if (Unsafe.Add(ref intensityBinRef, currentIntensity) > maxIntensity) + { + maxIntensity = Unsafe.Add(ref intensityBinRef, currentIntensity); + maxIndex = currentIntensity; } + } - float red = MathF.Abs(Unsafe.Add(ref redBinRef, maxIndex) / maxIntensity); - float blue = MathF.Abs(Unsafe.Add(ref blueBinRef, maxIndex) / maxIntensity); - float green = MathF.Abs(Unsafe.Add(ref greenBinRef, maxIndex) / maxIntensity); - float alpha = sourceRow[x].ToVector4().W; + float red = MathF.Abs(Unsafe.Add(ref redBinRef, maxIndex) / maxIntensity); + float blue = MathF.Abs(Unsafe.Add(ref blueBinRef, maxIndex) / maxIntensity); + float green = MathF.Abs(Unsafe.Add(ref greenBinRef, maxIndex) / maxIntensity); + float alpha = sourceRow[x].ToVector4().W; - Unsafe.Add(ref vectorSpanRef, x) = new Vector4(red, green, blue, alpha); - } + Unsafe.Add(ref vectorSpanRef, x) = new Vector4(red, green, blue, alpha); } } From 17ff3e1c72389f7af9b84207d50c6ecc68f4fc4d Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Sun, 26 Jan 2020 13:34:08 +0100 Subject: [PATCH 05/12] Added missing using statement --- .../Effects/OilPaintingProcessor{TPixel}.cs | 99 ++++++++++--------- 1 file changed, 50 insertions(+), 49 deletions(-) diff --git a/src/ImageSharp/Processing/Processors/Effects/OilPaintingProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Effects/OilPaintingProcessor{TPixel}.cs index aefbeaff88..cbdd0c6f83 100644 --- a/src/ImageSharp/Processing/Processors/Effects/OilPaintingProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Effects/OilPaintingProcessor{TPixel}.cs @@ -73,73 +73,74 @@ namespace SixLabors.ImageSharp.Processing.Processors.Effects ref Vector4 vectorSpanRef = ref MemoryMarshal.GetReference(vectorSpan); // Rent the shared buffer only once per parallel item - IMemoryOwner bins = configuration.MemoryAllocator.Allocate(levels * 4); - - ref float binsRef = ref bins.GetReference(); - ref int intensityBinRef = ref Unsafe.As(ref binsRef); - ref float redBinRef = ref Unsafe.Add(ref binsRef, levels); - ref float blueBinRef = ref Unsafe.Add(ref redBinRef, levels); - ref float greenBinRef = ref Unsafe.Add(ref blueBinRef, levels); - - for (int y = rows.Min; y < rows.Max; y++) + using (IMemoryOwner bins = configuration.MemoryAllocator.Allocate(levels * 4)) { - Span sourceRow = source.GetPixelRowSpan(y); - Span targetRowSpan = targetPixels.GetRowSpan(y).Slice(startX, length); - PixelOperations.Instance.ToVector4(configuration, targetRowSpan, vectorSpan); + ref float binsRef = ref bins.GetReference(); + ref int intensityBinRef = ref Unsafe.As(ref binsRef); + ref float redBinRef = ref Unsafe.Add(ref binsRef, levels); + ref float blueBinRef = ref Unsafe.Add(ref redBinRef, levels); + ref float greenBinRef = ref Unsafe.Add(ref blueBinRef, levels); - for (int x = startX; x < endX; x++) + for (int y = rows.Min; y < rows.Max; y++) { - int maxIntensity = 0; - int maxIndex = 0; + Span sourceRow = source.GetPixelRowSpan(y); + Span targetRowSpan = targetPixels.GetRowSpan(y).Slice(startX, length); + PixelOperations.Instance.ToVector4(configuration, targetRowSpan, vectorSpan); - // Clear the current shared buffer before processing each target pixel - bins.Memory.Span.Clear(); - - for (int fy = 0; fy <= radius; fy++) + for (int x = startX; x < endX; x++) { - int fyr = fy - radius; - int offsetY = y + fyr; - - offsetY = offsetY.Clamp(0, maxY); + int maxIntensity = 0; + int maxIndex = 0; - Span sourceOffsetRow = source.GetPixelRowSpan(offsetY); + // Clear the current shared buffer before processing each target pixel + bins.Memory.Span.Clear(); - for (int fx = 0; fx <= radius; fx++) + for (int fy = 0; fy <= radius; fy++) { - int fxr = fx - radius; - int offsetX = x + fxr; - offsetX = offsetX.Clamp(0, maxX); + int fyr = fy - radius; + int offsetY = y + fyr; - var vector = sourceOffsetRow[offsetX].ToVector4(); + offsetY = offsetY.Clamp(0, maxY); - float sourceRed = vector.X; - float sourceBlue = vector.Z; - float sourceGreen = vector.Y; + Span sourceOffsetRow = source.GetPixelRowSpan(offsetY); - int currentIntensity = (int)MathF.Round((sourceBlue + sourceGreen + sourceRed) / 3F * (levels - 1)); + for (int fx = 0; fx <= radius; fx++) + { + int fxr = fx - radius; + int offsetX = x + fxr; + offsetX = offsetX.Clamp(0, maxX); - Unsafe.Add(ref intensityBinRef, currentIntensity)++; - Unsafe.Add(ref redBinRef, currentIntensity) += sourceRed; - Unsafe.Add(ref blueBinRef, currentIntensity) += sourceBlue; - Unsafe.Add(ref greenBinRef, currentIntensity) += sourceGreen; + var vector = sourceOffsetRow[offsetX].ToVector4(); - if (Unsafe.Add(ref intensityBinRef, currentIntensity) > maxIntensity) - { - maxIntensity = Unsafe.Add(ref intensityBinRef, currentIntensity); - maxIndex = currentIntensity; + float sourceRed = vector.X; + float sourceBlue = vector.Z; + float sourceGreen = vector.Y; + + int currentIntensity = (int)MathF.Round((sourceBlue + sourceGreen + sourceRed) / 3F * (levels - 1)); + + Unsafe.Add(ref intensityBinRef, currentIntensity)++; + Unsafe.Add(ref redBinRef, currentIntensity) += sourceRed; + Unsafe.Add(ref blueBinRef, currentIntensity) += sourceBlue; + Unsafe.Add(ref greenBinRef, currentIntensity) += sourceGreen; + + if (Unsafe.Add(ref intensityBinRef, currentIntensity) > maxIntensity) + { + maxIntensity = Unsafe.Add(ref intensityBinRef, currentIntensity); + maxIndex = currentIntensity; + } } - } - float red = MathF.Abs(Unsafe.Add(ref redBinRef, maxIndex) / maxIntensity); - float blue = MathF.Abs(Unsafe.Add(ref blueBinRef, maxIndex) / maxIntensity); - float green = MathF.Abs(Unsafe.Add(ref greenBinRef, maxIndex) / maxIntensity); - float alpha = sourceRow[x].ToVector4().W; + float red = MathF.Abs(Unsafe.Add(ref redBinRef, maxIndex) / maxIntensity); + float blue = MathF.Abs(Unsafe.Add(ref blueBinRef, maxIndex) / maxIntensity); + float green = MathF.Abs(Unsafe.Add(ref greenBinRef, maxIndex) / maxIntensity); + float alpha = sourceRow[x].ToVector4().W; - Unsafe.Add(ref vectorSpanRef, x) = new Vector4(red, green, blue, alpha); + Unsafe.Add(ref vectorSpanRef, x) = new Vector4(red, green, blue, alpha); + } } - } - PixelOperations.Instance.FromVector4Destructive(configuration, vectorSpan, targetRowSpan); + PixelOperations.Instance.FromVector4Destructive(configuration, vectorSpan, targetRowSpan); + } } }); From 6d7c83a703ef18f63f1d5a37418335edad1bdb21 Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Sun, 26 Jan 2020 17:19:50 +0100 Subject: [PATCH 06/12] Revert changes from 692c35b166ca7a18680b5efa6cd863ee48e01e11: "Switched to vectorized pixel conversions" --- .../Effects/OilPaintingProcessor{TPixel}.cs | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/src/ImageSharp/Processing/Processors/Effects/OilPaintingProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Effects/OilPaintingProcessor{TPixel}.cs index cbdd0c6f83..3a97fa7275 100644 --- a/src/ImageSharp/Processing/Processors/Effects/OilPaintingProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Effects/OilPaintingProcessor{TPixel}.cs @@ -63,16 +63,12 @@ namespace SixLabors.ImageSharp.Processing.Processors.Effects source.CopyTo(targetPixels); var workingRect = Rectangle.FromLTRB(startX, startY, endX, endY); - ParallelHelper.IterateRowsWithTempBuffer( + ParallelHelper.IterateRows( workingRect, this.Configuration, - (rows, vectorBuffer) => + (rows) => { - Span vectorSpan = vectorBuffer.Span; - int length = vectorSpan.Length; - ref Vector4 vectorSpanRef = ref MemoryMarshal.GetReference(vectorSpan); - - // Rent the shared buffer only once per parallel item + // Rent the shared buffer only once per parallel item. using (IMemoryOwner bins = configuration.MemoryAllocator.Allocate(levels * 4)) { ref float binsRef = ref bins.GetReference(); @@ -84,8 +80,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Effects for (int y = rows.Min; y < rows.Max; y++) { Span sourceRow = source.GetPixelRowSpan(y); - Span targetRowSpan = targetPixels.GetRowSpan(y).Slice(startX, length); - PixelOperations.Instance.ToVector4(configuration, targetRowSpan, vectorSpan); + Span targetRow = targetPixels.GetRowSpan(y); for (int x = startX; x < endX; x++) { @@ -135,11 +130,10 @@ namespace SixLabors.ImageSharp.Processing.Processors.Effects float green = MathF.Abs(Unsafe.Add(ref greenBinRef, maxIndex) / maxIntensity); float alpha = sourceRow[x].ToVector4().W; - Unsafe.Add(ref vectorSpanRef, x) = new Vector4(red, green, blue, alpha); + ref TPixel pixel = ref targetRow[x]; + pixel.FromVector4(new Vector4(red, green, blue, sourceRow[x].ToVector4().W)); } } - - PixelOperations.Instance.FromVector4Destructive(configuration, vectorSpan, targetRowSpan); } } }); From 5e3a1903af7176201ec801029687f4ad66a1e2d6 Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Sun, 26 Jan 2020 17:25:48 +0100 Subject: [PATCH 07/12] Use using declarations to reduce nesting --- .../Effects/OilPaintingProcessor{TPixel}.cs | 131 +++++++++--------- 1 file changed, 63 insertions(+), 68 deletions(-) diff --git a/src/ImageSharp/Processing/Processors/Effects/OilPaintingProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Effects/OilPaintingProcessor{TPixel}.cs index 3a97fa7275..1b350ea8b0 100644 --- a/src/ImageSharp/Processing/Processors/Effects/OilPaintingProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Effects/OilPaintingProcessor{TPixel}.cs @@ -5,7 +5,6 @@ using System; using System.Buffers; using System.Numerics; using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.Advanced.ParallelUtils; @@ -58,88 +57,84 @@ namespace SixLabors.ImageSharp.Processing.Processors.Effects Configuration configuration = this.Configuration; - using (Buffer2D targetPixels = this.Configuration.MemoryAllocator.Allocate2D(source.Size())) - { - source.CopyTo(targetPixels); - - var workingRect = Rectangle.FromLTRB(startX, startY, endX, endY); - ParallelHelper.IterateRows( - workingRect, - this.Configuration, - (rows) => + using Buffer2D targetPixels = this.Configuration.MemoryAllocator.Allocate2D(source.Size()); + source.CopyTo(targetPixels); + + var workingRect = Rectangle.FromLTRB(startX, startY, endX, endY); + ParallelHelper.IterateRows( + workingRect, + this.Configuration, + (rows) => + { + // Rent the shared buffer only once per parallel item. + using IMemoryOwner bins = configuration.MemoryAllocator.Allocate(levels * 4); + ref float binsRef = ref bins.GetReference(); + ref int intensityBinRef = ref Unsafe.As(ref binsRef); + ref float redBinRef = ref Unsafe.Add(ref binsRef, levels); + ref float blueBinRef = ref Unsafe.Add(ref redBinRef, levels); + ref float greenBinRef = ref Unsafe.Add(ref blueBinRef, levels); + + for (int y = rows.Min; y < rows.Max; y++) + { + Span sourceRow = source.GetPixelRowSpan(y); + Span targetRow = targetPixels.GetRowSpan(y); + + for (int x = startX; x < endX; x++) { - // Rent the shared buffer only once per parallel item. - using (IMemoryOwner bins = configuration.MemoryAllocator.Allocate(levels * 4)) - { - ref float binsRef = ref bins.GetReference(); - ref int intensityBinRef = ref Unsafe.As(ref binsRef); - ref float redBinRef = ref Unsafe.Add(ref binsRef, levels); - ref float blueBinRef = ref Unsafe.Add(ref redBinRef, levels); - ref float greenBinRef = ref Unsafe.Add(ref blueBinRef, levels); - - for (int y = rows.Min; y < rows.Max; y++) - { - Span sourceRow = source.GetPixelRowSpan(y); - Span targetRow = targetPixels.GetRowSpan(y); - - for (int x = startX; x < endX; x++) - { - int maxIntensity = 0; - int maxIndex = 0; - - // Clear the current shared buffer before processing each target pixel - bins.Memory.Span.Clear(); + int maxIntensity = 0; + int maxIndex = 0; - for (int fy = 0; fy <= radius; fy++) - { - int fyr = fy - radius; - int offsetY = y + fyr; + // Clear the current shared buffer before processing each target pixel + bins.Memory.Span.Clear(); - offsetY = offsetY.Clamp(0, maxY); - - Span sourceOffsetRow = source.GetPixelRowSpan(offsetY); + for (int fy = 0; fy <= radius; fy++) + { + int fyr = fy - radius; + int offsetY = y + fyr; - for (int fx = 0; fx <= radius; fx++) - { - int fxr = fx - radius; - int offsetX = x + fxr; - offsetX = offsetX.Clamp(0, maxX); + offsetY = offsetY.Clamp(0, maxY); - var vector = sourceOffsetRow[offsetX].ToVector4(); + Span sourceOffsetRow = source.GetPixelRowSpan(offsetY); - float sourceRed = vector.X; - float sourceBlue = vector.Z; - float sourceGreen = vector.Y; + for (int fx = 0; fx <= radius; fx++) + { + int fxr = fx - radius; + int offsetX = x + fxr; + offsetX = offsetX.Clamp(0, maxX); - int currentIntensity = (int)MathF.Round((sourceBlue + sourceGreen + sourceRed) / 3F * (levels - 1)); + var vector = sourceOffsetRow[offsetX].ToVector4(); - Unsafe.Add(ref intensityBinRef, currentIntensity)++; - Unsafe.Add(ref redBinRef, currentIntensity) += sourceRed; - Unsafe.Add(ref blueBinRef, currentIntensity) += sourceBlue; - Unsafe.Add(ref greenBinRef, currentIntensity) += sourceGreen; + float sourceRed = vector.X; + float sourceBlue = vector.Z; + float sourceGreen = vector.Y; - if (Unsafe.Add(ref intensityBinRef, currentIntensity) > maxIntensity) - { - maxIntensity = Unsafe.Add(ref intensityBinRef, currentIntensity); - maxIndex = currentIntensity; - } - } + int currentIntensity = (int)MathF.Round((sourceBlue + sourceGreen + sourceRed) / 3F * (levels - 1)); - float red = MathF.Abs(Unsafe.Add(ref redBinRef, maxIndex) / maxIntensity); - float blue = MathF.Abs(Unsafe.Add(ref blueBinRef, maxIndex) / maxIntensity); - float green = MathF.Abs(Unsafe.Add(ref greenBinRef, maxIndex) / maxIntensity); - float alpha = sourceRow[x].ToVector4().W; + Unsafe.Add(ref intensityBinRef, currentIntensity)++; + Unsafe.Add(ref redBinRef, currentIntensity) += sourceRed; + Unsafe.Add(ref blueBinRef, currentIntensity) += sourceBlue; + Unsafe.Add(ref greenBinRef, currentIntensity) += sourceGreen; - ref TPixel pixel = ref targetRow[x]; - pixel.FromVector4(new Vector4(red, green, blue, sourceRow[x].ToVector4().W)); - } + if (Unsafe.Add(ref intensityBinRef, currentIntensity) > maxIntensity) + { + maxIntensity = Unsafe.Add(ref intensityBinRef, currentIntensity); + maxIndex = currentIntensity; } } + + float red = MathF.Abs(Unsafe.Add(ref redBinRef, maxIndex) / maxIntensity); + float blue = MathF.Abs(Unsafe.Add(ref blueBinRef, maxIndex) / maxIntensity); + float green = MathF.Abs(Unsafe.Add(ref greenBinRef, maxIndex) / maxIntensity); + float alpha = sourceRow[x].ToVector4().W; + + ref TPixel pixel = ref targetRow[x]; + pixel.FromVector4(new Vector4(red, green, blue, sourceRow[x].ToVector4().W)); } - }); + } + } + }); - Buffer2D.SwapOrCopyContent(source.PixelBuffer, targetPixels); - } + Buffer2D.SwapOrCopyContent(source.PixelBuffer, targetPixels); } } } From c2fe3dcf76283a31b61221edee1d9bbe70b3d741 Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Sun, 26 Jan 2020 21:38:59 +0100 Subject: [PATCH 08/12] Fixed a rollback error --- .../Processors/Effects/OilPaintingProcessor{TPixel}.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/ImageSharp/Processing/Processors/Effects/OilPaintingProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Effects/OilPaintingProcessor{TPixel}.cs index 1b350ea8b0..d8e2eec166 100644 --- a/src/ImageSharp/Processing/Processors/Effects/OilPaintingProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Effects/OilPaintingProcessor{TPixel}.cs @@ -68,6 +68,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Effects { // Rent the shared buffer only once per parallel item. using IMemoryOwner bins = configuration.MemoryAllocator.Allocate(levels * 4); + ref float binsRef = ref bins.GetReference(); ref int intensityBinRef = ref Unsafe.As(ref binsRef); ref float redBinRef = ref Unsafe.Add(ref binsRef, levels); @@ -128,7 +129,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Effects float alpha = sourceRow[x].ToVector4().W; ref TPixel pixel = ref targetRow[x]; - pixel.FromVector4(new Vector4(red, green, blue, sourceRow[x].ToVector4().W)); + pixel.FromVector4(new Vector4(red, green, blue, alpha)); } } } From 054750a883d8110204af6018fc66361e3491a3ab Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Sun, 26 Jan 2020 21:47:41 +0100 Subject: [PATCH 09/12] Reintroduced bulk ToVector4 source conversion --- .../Effects/OilPaintingProcessor{TPixel}.cs | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/ImageSharp/Processing/Processors/Effects/OilPaintingProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Effects/OilPaintingProcessor{TPixel}.cs index d8e2eec166..887ae6c8c7 100644 --- a/src/ImageSharp/Processing/Processors/Effects/OilPaintingProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Effects/OilPaintingProcessor{TPixel}.cs @@ -54,6 +54,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Effects int radius = brushSize >> 1; int levels = this.definition.Levels; + int width = this.SourceRectangle.Width; Configuration configuration = this.Configuration; @@ -66,6 +67,11 @@ namespace SixLabors.ImageSharp.Processing.Processors.Effects this.Configuration, (rows) => { + // Allocate the reusable source buffer, to enable vectorized bulk conversion + using IMemoryOwner sourceRowBuffer = configuration.MemoryAllocator.Allocate(width); + + Span sourceRow = sourceRowBuffer.Memory.Span; + // Rent the shared buffer only once per parallel item. using IMemoryOwner bins = configuration.MemoryAllocator.Allocate(levels * 4); @@ -77,7 +83,8 @@ namespace SixLabors.ImageSharp.Processing.Processors.Effects for (int y = rows.Min; y < rows.Max; y++) { - Span sourceRow = source.GetPixelRowSpan(y); + PixelOperations.Instance.ToVector4(configuration, source.GetPixelRowSpan(y), sourceRow); + Span targetRow = targetPixels.GetRowSpan(y); for (int x = startX; x < endX; x++) @@ -126,7 +133,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Effects float red = MathF.Abs(Unsafe.Add(ref redBinRef, maxIndex) / maxIntensity); float blue = MathF.Abs(Unsafe.Add(ref blueBinRef, maxIndex) / maxIntensity); float green = MathF.Abs(Unsafe.Add(ref greenBinRef, maxIndex) / maxIntensity); - float alpha = sourceRow[x].ToVector4().W; + float alpha = sourceRow[x].W; ref TPixel pixel = ref targetRow[x]; pixel.FromVector4(new Vector4(red, green, blue, alpha)); From c5de8ce48acb43a5c108d02eb4b3fbce63dde0f2 Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Sun, 26 Jan 2020 22:04:52 +0100 Subject: [PATCH 10/12] Fixed indexing of source row span --- .../Effects/OilPaintingProcessor{TPixel}.cs | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/src/ImageSharp/Processing/Processors/Effects/OilPaintingProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Effects/OilPaintingProcessor{TPixel}.cs index 887ae6c8c7..5d90101988 100644 --- a/src/ImageSharp/Processing/Processors/Effects/OilPaintingProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Effects/OilPaintingProcessor{TPixel}.cs @@ -54,7 +54,8 @@ namespace SixLabors.ImageSharp.Processing.Processors.Effects int radius = brushSize >> 1; int levels = this.definition.Levels; - int width = this.SourceRectangle.Width; + int rowWidth = source.Width; + int rectangleWidth = this.SourceRectangle.Width; Configuration configuration = this.Configuration; @@ -68,9 +69,10 @@ namespace SixLabors.ImageSharp.Processing.Processors.Effects (rows) => { // Allocate the reusable source buffer, to enable vectorized bulk conversion - using IMemoryOwner sourceRowBuffer = configuration.MemoryAllocator.Allocate(width); + using IMemoryOwner sourceRowBuffer = configuration.MemoryAllocator.Allocate(rowWidth); - Span sourceRow = sourceRowBuffer.Memory.Span; + Span sourceRowVector4Span = sourceRowBuffer.Memory.Span; + Span sourceRowAreaVector4Span = sourceRowVector4Span.Slice(startX, rectangleWidth); // Rent the shared buffer only once per parallel item. using IMemoryOwner bins = configuration.MemoryAllocator.Allocate(levels * 4); @@ -83,7 +85,10 @@ namespace SixLabors.ImageSharp.Processing.Processors.Effects for (int y = rows.Min; y < rows.Max; y++) { - PixelOperations.Instance.ToVector4(configuration, source.GetPixelRowSpan(y), sourceRow); + Span sourceRowPixelSpan = source.GetPixelRowSpan(y); + Span sourceRowAreaPixelSpan = sourceRowPixelSpan.Slice(startX, rectangleWidth); + + PixelOperations.Instance.ToVector4(configuration, sourceRowAreaPixelSpan, sourceRowAreaVector4Span); Span targetRow = targetPixels.GetRowSpan(y); @@ -133,7 +138,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Effects float red = MathF.Abs(Unsafe.Add(ref redBinRef, maxIndex) / maxIntensity); float blue = MathF.Abs(Unsafe.Add(ref blueBinRef, maxIndex) / maxIntensity); float green = MathF.Abs(Unsafe.Add(ref greenBinRef, maxIndex) / maxIntensity); - float alpha = sourceRow[x].W; + float alpha = sourceRowVector4Span[x].W; ref TPixel pixel = ref targetRow[x]; pixel.FromVector4(new Vector4(red, green, blue, alpha)); From e9ed9e83aea8c131ee4b796f075c463147308bd5 Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Sun, 26 Jan 2020 22:12:09 +0100 Subject: [PATCH 11/12] Reimplemented bulk conversion for target row --- .../Effects/OilPaintingProcessor{TPixel}.cs | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/src/ImageSharp/Processing/Processors/Effects/OilPaintingProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Effects/OilPaintingProcessor{TPixel}.cs index 5d90101988..35c14449f9 100644 --- a/src/ImageSharp/Processing/Processors/Effects/OilPaintingProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Effects/OilPaintingProcessor{TPixel}.cs @@ -68,12 +68,18 @@ namespace SixLabors.ImageSharp.Processing.Processors.Effects this.Configuration, (rows) => { - // Allocate the reusable source buffer, to enable vectorized bulk conversion + // Allocate the reusable source row buffer, to enable vectorized bulk conversions using IMemoryOwner sourceRowBuffer = configuration.MemoryAllocator.Allocate(rowWidth); Span sourceRowVector4Span = sourceRowBuffer.Memory.Span; Span sourceRowAreaVector4Span = sourceRowVector4Span.Slice(startX, rectangleWidth); + // Allocate the reusable target row buffer + using IMemoryOwner targetRowBuffer = configuration.MemoryAllocator.Allocate(rowWidth); + + Span targetRowVector4Span = targetRowBuffer.Memory.Span; + Span targetRowAreaVector4Span = targetRowVector4Span.Slice(startX, rectangleWidth); + // Rent the shared buffer only once per parallel item. using IMemoryOwner bins = configuration.MemoryAllocator.Allocate(levels * 4); @@ -90,8 +96,6 @@ namespace SixLabors.ImageSharp.Processing.Processors.Effects PixelOperations.Instance.ToVector4(configuration, sourceRowAreaPixelSpan, sourceRowAreaVector4Span); - Span targetRow = targetPixels.GetRowSpan(y); - for (int x = startX; x < endX; x++) { int maxIntensity = 0; @@ -140,10 +144,13 @@ namespace SixLabors.ImageSharp.Processing.Processors.Effects float green = MathF.Abs(Unsafe.Add(ref greenBinRef, maxIndex) / maxIntensity); float alpha = sourceRowVector4Span[x].W; - ref TPixel pixel = ref targetRow[x]; - pixel.FromVector4(new Vector4(red, green, blue, alpha)); + targetRowVector4Span[x] = new Vector4(red, green, blue, alpha); } } + + Span targetRowAreaPixelSpan = targetPixels.GetRowSpan(y).Slice(startX, rectangleWidth); + + PixelOperations.Instance.FromVector4Destructive(configuration, targetRowAreaVector4Span, targetRowAreaPixelSpan); } }); From c89e0b1e5a1ff9e266cb3a8ef5febaabbde13059 Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Mon, 27 Jan 2020 01:48:21 +0100 Subject: [PATCH 12/12] Removed using statements, added detailed comment --- .../Effects/OilPaintingProcessor{TPixel}.cs | 130 +++++++++--------- 1 file changed, 68 insertions(+), 62 deletions(-) diff --git a/src/ImageSharp/Processing/Processors/Effects/OilPaintingProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Effects/OilPaintingProcessor{TPixel}.cs index 35c14449f9..472c07aa7f 100644 --- a/src/ImageSharp/Processing/Processors/Effects/OilPaintingProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Effects/OilPaintingProcessor{TPixel}.cs @@ -68,89 +68,95 @@ namespace SixLabors.ImageSharp.Processing.Processors.Effects this.Configuration, (rows) => { - // Allocate the reusable source row buffer, to enable vectorized bulk conversions - using IMemoryOwner sourceRowBuffer = configuration.MemoryAllocator.Allocate(rowWidth); - - Span sourceRowVector4Span = sourceRowBuffer.Memory.Span; - Span sourceRowAreaVector4Span = sourceRowVector4Span.Slice(startX, rectangleWidth); - - // Allocate the reusable target row buffer - using IMemoryOwner targetRowBuffer = configuration.MemoryAllocator.Allocate(rowWidth); - - Span targetRowVector4Span = targetRowBuffer.Memory.Span; - Span targetRowAreaVector4Span = targetRowVector4Span.Slice(startX, rectangleWidth); - - // Rent the shared buffer only once per parallel item. - using IMemoryOwner bins = configuration.MemoryAllocator.Allocate(levels * 4); - - ref float binsRef = ref bins.GetReference(); - ref int intensityBinRef = ref Unsafe.As(ref binsRef); - ref float redBinRef = ref Unsafe.Add(ref binsRef, levels); - ref float blueBinRef = ref Unsafe.Add(ref redBinRef, levels); - ref float greenBinRef = ref Unsafe.Add(ref blueBinRef, levels); - - for (int y = rows.Min; y < rows.Max; y++) + /* Allocate the two temporary Vector4 buffers, one for the source row and one for the target row. + * The ParallelHelper.IterateRowsWithTempBuffers overload is not used in this case because + * the two allocated buffers have a length equal to the width of the source image, + * and not just equal to the width of the target rectangle to process. + * Furthermore, there are two buffers being allocated in this case, so using that overload would + * have still required the explicit allocation of the secondary buffer. + * Similarly, one temporary float buffer is also allocated from the pool, and that is used + * to create the target bins for all the color channels being processed. + * This buffer is only rented once outside of the main processing loop, and its contents + * are cleared for each loop iteration, to avoid the repeated allocation for each processed pixel. */ + using (IMemoryOwner sourceRowBuffer = configuration.MemoryAllocator.Allocate(rowWidth)) + using (IMemoryOwner targetRowBuffer = configuration.MemoryAllocator.Allocate(rowWidth)) + using (IMemoryOwner bins = configuration.MemoryAllocator.Allocate(levels * 4)) { - Span sourceRowPixelSpan = source.GetPixelRowSpan(y); - Span sourceRowAreaPixelSpan = sourceRowPixelSpan.Slice(startX, rectangleWidth); + Span sourceRowVector4Span = sourceRowBuffer.Memory.Span; + Span sourceRowAreaVector4Span = sourceRowVector4Span.Slice(startX, rectangleWidth); + + Span targetRowVector4Span = targetRowBuffer.Memory.Span; + Span targetRowAreaVector4Span = targetRowVector4Span.Slice(startX, rectangleWidth); - PixelOperations.Instance.ToVector4(configuration, sourceRowAreaPixelSpan, sourceRowAreaVector4Span); + ref float binsRef = ref bins.GetReference(); + ref int intensityBinRef = ref Unsafe.As(ref binsRef); + ref float redBinRef = ref Unsafe.Add(ref binsRef, levels); + ref float blueBinRef = ref Unsafe.Add(ref redBinRef, levels); + ref float greenBinRef = ref Unsafe.Add(ref blueBinRef, levels); - for (int x = startX; x < endX; x++) + for (int y = rows.Min; y < rows.Max; y++) { - int maxIntensity = 0; - int maxIndex = 0; + Span sourceRowPixelSpan = source.GetPixelRowSpan(y); + Span sourceRowAreaPixelSpan = sourceRowPixelSpan.Slice(startX, rectangleWidth); - // Clear the current shared buffer before processing each target pixel - bins.Memory.Span.Clear(); + PixelOperations.Instance.ToVector4(configuration, sourceRowAreaPixelSpan, sourceRowAreaVector4Span); - for (int fy = 0; fy <= radius; fy++) + for (int x = startX; x < endX; x++) { - int fyr = fy - radius; - int offsetY = y + fyr; + int maxIntensity = 0; + int maxIndex = 0; - offsetY = offsetY.Clamp(0, maxY); + // Clear the current shared buffer before processing each target pixel + bins.Memory.Span.Clear(); - Span sourceOffsetRow = source.GetPixelRowSpan(offsetY); - - for (int fx = 0; fx <= radius; fx++) + for (int fy = 0; fy <= radius; fy++) { - int fxr = fx - radius; - int offsetX = x + fxr; - offsetX = offsetX.Clamp(0, maxX); + int fyr = fy - radius; + int offsetY = y + fyr; - var vector = sourceOffsetRow[offsetX].ToVector4(); + offsetY = offsetY.Clamp(0, maxY); - float sourceRed = vector.X; - float sourceBlue = vector.Z; - float sourceGreen = vector.Y; + Span sourceOffsetRow = source.GetPixelRowSpan(offsetY); - int currentIntensity = (int)MathF.Round((sourceBlue + sourceGreen + sourceRed) / 3F * (levels - 1)); + for (int fx = 0; fx <= radius; fx++) + { + int fxr = fx - radius; + int offsetX = x + fxr; + offsetX = offsetX.Clamp(0, maxX); - Unsafe.Add(ref intensityBinRef, currentIntensity)++; - Unsafe.Add(ref redBinRef, currentIntensity) += sourceRed; - Unsafe.Add(ref blueBinRef, currentIntensity) += sourceBlue; - Unsafe.Add(ref greenBinRef, currentIntensity) += sourceGreen; + var vector = sourceOffsetRow[offsetX].ToVector4(); - if (Unsafe.Add(ref intensityBinRef, currentIntensity) > maxIntensity) - { - maxIntensity = Unsafe.Add(ref intensityBinRef, currentIntensity); - maxIndex = currentIntensity; + float sourceRed = vector.X; + float sourceBlue = vector.Z; + float sourceGreen = vector.Y; + + int currentIntensity = (int)MathF.Round((sourceBlue + sourceGreen + sourceRed) / 3F * (levels - 1)); + + Unsafe.Add(ref intensityBinRef, currentIntensity)++; + Unsafe.Add(ref redBinRef, currentIntensity) += sourceRed; + Unsafe.Add(ref blueBinRef, currentIntensity) += sourceBlue; + Unsafe.Add(ref greenBinRef, currentIntensity) += sourceGreen; + + if (Unsafe.Add(ref intensityBinRef, currentIntensity) > maxIntensity) + { + maxIntensity = Unsafe.Add(ref intensityBinRef, currentIntensity); + maxIndex = currentIntensity; + } } - } - float red = MathF.Abs(Unsafe.Add(ref redBinRef, maxIndex) / maxIntensity); - float blue = MathF.Abs(Unsafe.Add(ref blueBinRef, maxIndex) / maxIntensity); - float green = MathF.Abs(Unsafe.Add(ref greenBinRef, maxIndex) / maxIntensity); - float alpha = sourceRowVector4Span[x].W; + float red = MathF.Abs(Unsafe.Add(ref redBinRef, maxIndex) / maxIntensity); + float blue = MathF.Abs(Unsafe.Add(ref blueBinRef, maxIndex) / maxIntensity); + float green = MathF.Abs(Unsafe.Add(ref greenBinRef, maxIndex) / maxIntensity); + float alpha = sourceRowVector4Span[x].W; - targetRowVector4Span[x] = new Vector4(red, green, blue, alpha); + targetRowVector4Span[x] = new Vector4(red, green, blue, alpha); + } } - } - Span targetRowAreaPixelSpan = targetPixels.GetRowSpan(y).Slice(startX, rectangleWidth); + Span targetRowAreaPixelSpan = targetPixels.GetRowSpan(y).Slice(startX, rectangleWidth); - PixelOperations.Instance.FromVector4Destructive(configuration, targetRowAreaVector4Span, targetRowAreaPixelSpan); + PixelOperations.Instance.FromVector4Destructive(configuration, targetRowAreaVector4Span, targetRowAreaPixelSpan); + } } });