From b9fc38940243d28fe926a69de3f194947d6d1c26 Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Sat, 18 Mar 2017 19:08:00 +0100 Subject: [PATCH] more refactor on Weights stuff --- .../Transforms/CompandingResizeProcessor.cs | 4 +- .../ResamplingWeightedProcessor.Weights.cs | 144 ++++++++++++++++++ .../Transforms/ResamplingWeightedProcessor.cs | 72 ++------- .../Processors/Transforms/ResizeProcessor.cs | 34 +---- 4 files changed, 164 insertions(+), 90 deletions(-) create mode 100644 src/ImageSharp/Processing/Processors/Transforms/ResamplingWeightedProcessor.Weights.cs diff --git a/src/ImageSharp/Processing/Processors/Transforms/CompandingResizeProcessor.cs b/src/ImageSharp/Processing/Processors/Transforms/CompandingResizeProcessor.cs index 14cea480d8..c8a43b3d98 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/CompandingResizeProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/CompandingResizeProcessor.cs @@ -119,7 +119,7 @@ namespace ImageSharp.Processing.Processors for (int x = minX; x < maxX; x++) { // Ensure offsets are normalised for cropping and padding. - Weights ws = this.HorizontalWeights.Weights[x - startX]; + WeightsWindow ws = this.HorizontalWeights.Weights[x - startX]; float* horizontalValues = ws.Ptr; int left = ws.Left; @@ -147,7 +147,7 @@ namespace ImageSharp.Processing.Processors y => { // Ensure offsets are normalised for cropping and padding. - Weights ws = this.VerticalWeights.Weights[y - startY]; + WeightsWindow ws = this.VerticalWeights.Weights[y - startY]; float* verticalValues = ws.Ptr; int left = ws.Left; diff --git a/src/ImageSharp/Processing/Processors/Transforms/ResamplingWeightedProcessor.Weights.cs b/src/ImageSharp/Processing/Processors/Transforms/ResamplingWeightedProcessor.Weights.cs new file mode 100644 index 0000000000..00a81100d2 --- /dev/null +++ b/src/ImageSharp/Processing/Processors/Transforms/ResamplingWeightedProcessor.Weights.cs @@ -0,0 +1,144 @@ +namespace ImageSharp.Processing.Processors +{ + using System; + using System.Numerics; + using System.Runtime.CompilerServices; + + /// + /// Conains the definition of and . + /// + internal abstract partial class ResamplingWeightedProcessor + { + /// + /// Points to a collection of of weights allocated in . + /// + protected unsafe struct WeightsWindow + { + /// + /// The local left index position + /// + public int Left; + + /// + /// The span of weights pointing to . + /// + public BufferSpan Span; + + /// + /// Initializes a new instance of the struct. + /// + /// The local left index + /// The span + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal WeightsWindow(int left, BufferSpan span) + { + this.Left = left; + this.Span = span; + } + + /// + /// Gets an unsafe float* pointer to the beginning of . + /// + public float* Ptr => (float*)this.Span.PointerAtOffset; + + /// + /// Gets the lenghth of the weights window + /// + public int Length => this.Span.Length; + + /// + /// Computes the sum of vectors in 'rowSpan' weighted by weight values, pointed by this instance. + /// + /// The input span of vectors + /// The weighted sum + public Vector4 ComputeWeightedRowSum(BufferSpan rowSpan) + { + float* horizontalValues = this.Ptr; + int left = this.Left; + + // Destination color components + Vector4 result = Vector4.Zero; + + for (int i = 0; i < this.Length; i++) + { + float xw = horizontalValues[i]; + int index = left + i; + result += rowSpan[index] * xw; + } + + return result; + } + + /// + /// Computes the sum of vectors in 'firstPassPixels' at a column pointed by 'x', + /// weighted by weight values, pointed by this instance. + /// + /// The buffer of input vectors in row first order + /// The column position + /// The weighted sum + public Vector4 ComputeWeightedColumnSum(PinnedImageBuffer firstPassPixels, int x) + { + float* verticalValues = this.Ptr; + int left = this.Left; + + // Destination color components + Vector4 result = Vector4.Zero; + + for (int i = 0; i < this.Length; i++) + { + float yw = verticalValues[i]; + int index = left + i; + result += firstPassPixels[x, index] * yw; + } + + return result; + } + } + + /// + /// Holds the values in an optimized contigous memory region. + /// + protected class WeightsBuffer : IDisposable + { + private PinnedImageBuffer dataBuffer; + + /// + /// Initializes a new instance of the class. + /// + /// The size of the source window + /// The size of the destination window + public WeightsBuffer(int sourceSize, int destinationSize) + { + this.dataBuffer = new PinnedImageBuffer(sourceSize, destinationSize); + this.dataBuffer.Clear(); + this.Weights = new WeightsWindow[destinationSize]; + } + + /// + /// Gets the calculated values. + /// + public WeightsWindow[] Weights { get; } + + /// + /// Disposes instance releasing it's backing buffer. + /// + public void Dispose() + { + this.dataBuffer.Dispose(); + } + + /// + /// Slices a weights value at the given positions. + /// + /// The index in destination buffer + /// The local left index value + /// The local right index value + /// The weights + public WeightsWindow GetWeightsWindow(int destIdx, int leftIdx, int rightIdx) + { + BufferSpan span = this.dataBuffer.GetRowSpan(destIdx).Slice(leftIdx, rightIdx - leftIdx); + return new WeightsWindow(leftIdx, span); + } + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/Transforms/ResamplingWeightedProcessor.cs b/src/ImageSharp/Processing/Processors/Transforms/ResamplingWeightedProcessor.cs index 51f382df2a..f74bf7edd2 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/ResamplingWeightedProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/ResamplingWeightedProcessor.cs @@ -7,7 +7,6 @@ namespace ImageSharp.Processing.Processors { using System; using System.Buffers; - using System.Runtime.CompilerServices; using System.Runtime.InteropServices; /// @@ -15,7 +14,7 @@ namespace ImageSharp.Processing.Processors /// Adapted from /// /// The pixel format. - internal abstract class ResamplingWeightedProcessor : ImageProcessor + internal abstract partial class ResamplingWeightedProcessor : ImageProcessor where TColor : struct, IPixel { /// @@ -62,12 +61,12 @@ namespace ImageSharp.Processing.Processors /// /// Gets or sets the horizontal weights. /// - protected Weights.Buffer HorizontalWeights { get; set; } + protected WeightsBuffer HorizontalWeights { get; set; } /// /// Gets or sets the vertical weights. /// - protected Weights.Buffer VerticalWeights { get; set; } + protected WeightsBuffer VerticalWeights { get; set; } /// protected override void BeforeApply(ImageBase source, Rectangle sourceRectangle) @@ -84,6 +83,7 @@ namespace ImageSharp.Processing.Processors } } + /// protected override void AfterApply(ImageBase source, Rectangle sourceRectangle) { base.AfterApply(source, sourceRectangle); @@ -96,7 +96,10 @@ namespace ImageSharp.Processing.Processors /// /// Computes the weights to apply at each pixel when resizing. /// - protected unsafe Weights.Buffer PrecomputeWeights(int destinationSize, int sourceSize) + /// The destination size + /// The source size + /// The + protected unsafe WeightsBuffer PrecomputeWeights(int destinationSize, int sourceSize) { float ratio = (float)sourceSize / destinationSize; float scale = ratio; @@ -108,8 +111,8 @@ namespace ImageSharp.Processing.Processors IResampler sampler = this.Sampler; float radius = (float)Math.Ceiling(scale * sampler.Radius); - Weights.Buffer result = new Weights.Buffer(sourceSize, destinationSize); - + WeightsBuffer result = new WeightsBuffer(sourceSize, destinationSize); + for (int i = 0; i < destinationSize; i++) { float center = ((i + .5F) * ratio) - .5F; @@ -129,8 +132,8 @@ namespace ImageSharp.Processing.Processors float sum = 0; - result.Weights[i] = result.Slice(i, left, right); - Weights ws = result.Weights[i]; + WeightsWindow ws = result.GetWeightsWindow(i, left, right); + result.Weights[i] = ws; float* weights = ws.Ptr; @@ -154,56 +157,5 @@ namespace ImageSharp.Processing.Processors return result; } - /// - /// Represents a collection of weights and their sum. - /// - protected unsafe struct Weights - { - /// - /// The local left index position - /// - public int Left; - - public BufferSpan Span; - - public float* Ptr => (float*)Span.PointerAtOffset; - - public int Length => Span.Length; - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private Weights(int left, BufferSpan span) - { - this.Left = left; - this.Span = span; - } - - internal unsafe class Buffer : IDisposable - { - private PinnedImageBuffer dataBuffer; - - public Weights[] Weights { get; } - - public float* DataPtr { get; } - - internal Weights Slice(int i, int leftIdx, int rightIdx) - { - var span = dataBuffer.GetRowSpan(i).Slice(leftIdx, rightIdx - leftIdx); - return new Weights(leftIdx, span); - } - - public Buffer(int sourceSize, int destinationSize) - { - this.dataBuffer = new PinnedImageBuffer(sourceSize, destinationSize); - this.dataBuffer.Clear(); - this.DataPtr = (float*)this.dataBuffer.Pointer; - this.Weights = new Weights[destinationSize]; - } - - public void Dispose() - { - this.dataBuffer.Dispose(); - } - } - } } } \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/Transforms/ResizeProcessor.cs b/src/ImageSharp/Processing/Processors/Transforms/ResizeProcessor.cs index 39b739134d..77b3f3b6fc 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/ResizeProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/ResizeProcessor.cs @@ -104,6 +104,8 @@ namespace ImageSharp.Processing.Processors // A 2-pass 1D algorithm appears to be faster than splitting a 1-pass 2D algorithm // First process the columns. Since we are not using multiple threads startY and endY // are the upper and lower bounds of the source rectangle. + + // TODO: Using a transposed variant of 'firstPassPixels' could eliminate the need for the WeightsWindow.ComputeWeightedColumnSum() method, and improve speed! using (PixelAccessor targetPixels = new PixelAccessor(width, height)) { using (PixelAccessor sourcePixels = source.Lock()) @@ -128,23 +130,8 @@ namespace ImageSharp.Processing.Processors for (int x = minX; x < maxX; x++) { - // Ensure offsets are normalised for cropping and padding. - - Weights ws = this.HorizontalWeights.Weights[x - startX]; - float* horizontalValues = ws.Ptr; - int left = ws.Left; - - // Destination color components - Vector4 destination = Vector4.Zero; - - for (int i = 0; i < ws.Length; i++) - { - float xw = horizontalValues[i]; - int index = left + i; - destination += tempRowBuffer[index] * xw; - } - - firstPassPixels[x, y] = destination; + WeightsWindow window = this.HorizontalWeights.Weights[x - startX]; + firstPassPixels[x, y] = window.ComputeWeightedRowSum(tempRowBuffer); } } }); @@ -157,21 +144,12 @@ namespace ImageSharp.Processing.Processors y => { // Ensure offsets are normalised for cropping and padding. - Weights ws = this.VerticalWeights.Weights[y - startY]; - float* verticalValues = ws.Ptr; - int left = ws.Left; + WeightsWindow window = this.VerticalWeights.Weights[y - startY]; for (int x = 0; x < width; x++) { // Destination color components - Vector4 destination = Vector4.Zero; - - for (int i = 0; i < ws.Length; i++) - { - float yw = verticalValues[i]; - int index = left + i; - destination += firstPassPixels[x, index] * yw; - } + Vector4 destination = window.ComputeWeightedColumnSum(firstPassPixels, x); TColor d = default(TColor); d.PackFromVector4(destination);