From d983797baafe23b21aa2f6d80f8dc8d3474113d6 Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Sat, 18 Mar 2017 18:17:54 +0100 Subject: [PATCH] refactor ResamplingWeightedProcessor.Weights --- src/ImageSharp/Image/PixelAccessor{TColor}.cs | 11 -- .../Transforms/CompandingResizeProcessor.cs | 24 ++-- .../Transforms/ResamplingWeightedProcessor.cs | 117 +++++++++++------- .../Processors/Transforms/ResizeProcessor.cs | 63 ++++++---- 4 files changed, 127 insertions(+), 88 deletions(-) diff --git a/src/ImageSharp/Image/PixelAccessor{TColor}.cs b/src/ImageSharp/Image/PixelAccessor{TColor}.cs index cea058951e..f5393cfb38 100644 --- a/src/ImageSharp/Image/PixelAccessor{TColor}.cs +++ b/src/ImageSharp/Image/PixelAccessor{TColor}.cs @@ -241,17 +241,6 @@ namespace ImageSharp this.CopyTo(area, sourceX, sourceY, width, height); } - ///// - ///// Gets a to the row 'y' beginning from the pixel at 'x'. - ///// - ///// The x coordinate - ///// The y coordinate - ///// The - //internal BufferSpan GetRowSpan(int x, int y) - //{ - // return this.pixelBuffer.Slice((y * this.Width) + x, this.Width - x); - //} - /// /// Sets the pixel buffer in an unsafe manner. This should not be used unless you know what its doing!!! /// diff --git a/src/ImageSharp/Processing/Processors/Transforms/CompandingResizeProcessor.cs b/src/ImageSharp/Processing/Processors/Transforms/CompandingResizeProcessor.cs index f5314d448d..14cea480d8 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/CompandingResizeProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/CompandingResizeProcessor.cs @@ -46,7 +46,7 @@ namespace ImageSharp.Processing.Processors public override bool Compand { get; set; } = true; /// - protected override void OnApply(ImageBase source, Rectangle sourceRectangle) + protected override unsafe void OnApply(ImageBase source, Rectangle sourceRectangle) { // Jump out, we'll deal with that later. if (source.Width == this.Width && source.Height == this.Height && sourceRectangle == this.ResizeRectangle) @@ -119,15 +119,18 @@ namespace ImageSharp.Processing.Processors for (int x = minX; x < maxX; x++) { // Ensure offsets are normalised for cropping and padding. - Weight[] horizontalValues = this.HorizontalWeights[x - startX].Values; + 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 < horizontalValues.Length; i++) + for (int i = 0; i < ws.Length; i++) { - Weight xw = horizontalValues[i]; - destination += sourcePixels[xw.Index + sourceX, y].ToVector4().Expand() * xw.Value; + float xw = horizontalValues[i]; + int index = left + i; + destination += sourcePixels[index, y].ToVector4().Expand() * xw; } TColor d = default(TColor); @@ -144,17 +147,20 @@ namespace ImageSharp.Processing.Processors y => { // Ensure offsets are normalised for cropping and padding. - Weight[] verticalValues = this.VerticalWeights[y - startY].Values; + Weights ws = this.VerticalWeights.Weights[y - startY]; + float* verticalValues = ws.Ptr; + int left = ws.Left; for (int x = 0; x < width; x++) { // Destination color components Vector4 destination = Vector4.Zero; - for (int i = 0; i < verticalValues.Length; i++) + for (int i = 0; i < ws.Length; i++) { - Weight yw = verticalValues[i]; - destination += firstPassPixels[x, yw.Index + sourceY].ToVector4().Expand() * yw.Value; + float yw = verticalValues[i]; + int index = left + i; + destination += firstPassPixels[x, index].ToVector4().Expand() * yw; } TColor d = default(TColor); diff --git a/src/ImageSharp/Processing/Processors/Transforms/ResamplingWeightedProcessor.cs b/src/ImageSharp/Processing/Processors/Transforms/ResamplingWeightedProcessor.cs index 2d6de41545..51f382df2a 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/ResamplingWeightedProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/ResamplingWeightedProcessor.cs @@ -6,6 +6,9 @@ namespace ImageSharp.Processing.Processors { using System; + using System.Buffers; + using System.Runtime.CompilerServices; + using System.Runtime.InteropServices; /// /// Provides methods that allow the resizing of images using various algorithms. @@ -59,32 +62,41 @@ namespace ImageSharp.Processing.Processors /// /// Gets or sets the horizontal weights. /// - protected Weights[] HorizontalWeights { get; set; } + protected Weights.Buffer HorizontalWeights { get; set; } /// /// Gets or sets the vertical weights. /// - protected Weights[] VerticalWeights { get; set; } + protected Weights.Buffer VerticalWeights { get; set; } /// protected override void BeforeApply(ImageBase source, Rectangle sourceRectangle) { if (!(this.Sampler is NearestNeighborResampler)) { - this.HorizontalWeights = this.PrecomputeWeights(this.ResizeRectangle.Width, sourceRectangle.Width); - this.VerticalWeights = this.PrecomputeWeights(this.ResizeRectangle.Height, sourceRectangle.Height); + this.HorizontalWeights = this.PrecomputeWeights( + this.ResizeRectangle.Width, + sourceRectangle.Width); + + this.VerticalWeights = this.PrecomputeWeights( + this.ResizeRectangle.Height, + sourceRectangle.Height); } } + protected override void AfterApply(ImageBase source, Rectangle sourceRectangle) + { + base.AfterApply(source, sourceRectangle); + this.HorizontalWeights?.Dispose(); + this.HorizontalWeights = null; + this.VerticalWeights?.Dispose(); + this.VerticalWeights = null; + } + /// /// Computes the weights to apply at each pixel when resizing. /// - /// The destination section size. - /// The source section size. - /// - /// The . - /// - protected Weights[] PrecomputeWeights(int destinationSize, int sourceSize) + protected unsafe Weights.Buffer PrecomputeWeights(int destinationSize, int sourceSize) { float ratio = (float)sourceSize / destinationSize; float scale = ratio; @@ -96,8 +108,8 @@ namespace ImageSharp.Processing.Processors IResampler sampler = this.Sampler; float radius = (float)Math.Ceiling(scale * sampler.Radius); - Weights[] result = new Weights[destinationSize]; - + Weights.Buffer result = new Weights.Buffer(sourceSize, destinationSize); + for (int i = 0; i < destinationSize; i++) { float center = ((i + .5F) * ratio) - .5F; @@ -116,67 +128,82 @@ namespace ImageSharp.Processing.Processors } float sum = 0; - result[i] = new Weights(); - Weight[] weights = new Weight[right - left + 1]; + + result.Weights[i] = result.Slice(i, left, right); + Weights ws = result.Weights[i]; + + float* weights = ws.Ptr; for (int j = left; j <= right; j++) { float weight = sampler.GetValue((j - center) / scale); sum += weight; - weights[j - left] = new Weight(j, weight); + weights[j - left] = weight; } // Normalise, best to do it here rather than in the pixel loop later on. if (sum > 0) { - for (int w = 0; w < weights.Length; w++) + for (int w = 0; w < ws.Length; w++) { - weights[w].Value = weights[w].Value / sum; + weights[w] = weights[w] / sum; } } - - result[i].Values = weights; } return result; } /// - /// Represents the weight to be added to a scaled pixel. + /// Represents a collection of weights and their sum. /// - protected struct Weight + protected unsafe struct Weights { /// - /// Initializes a new instance of the struct. + /// The local left index position /// - /// The index. - /// The value. - public Weight(int index, float value) + 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.Index = index; - this.Value = value; + this.Left = left; + this.Span = span; } - /// - /// Gets the pixel index. - /// - public int Index { get; } + internal unsafe class Buffer : IDisposable + { + private PinnedImageBuffer dataBuffer; - /// - /// Gets or sets the result of the interpolation algorithm. - /// - public float Value { get; set; } - } + public Weights[] Weights { get; } - /// - /// Represents a collection of weights and their sum. - /// - protected class Weights - { - /// - /// Gets or sets the values. - /// - public Weight[] Values { get; set; } + 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 a43745a057..39b739134d 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/ResizeProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/ResizeProcessor.cs @@ -45,7 +45,7 @@ namespace ImageSharp.Processing.Processors } /// - protected override void OnApply(ImageBase source, Rectangle sourceRectangle) + protected override unsafe void OnApply(ImageBase source, Rectangle sourceRectangle) { // Jump out, we'll deal with that later. if (source.Width == this.Width && source.Height == this.Height && sourceRectangle == this.ResizeRectangle) @@ -107,33 +107,47 @@ namespace ImageSharp.Processing.Processors using (PixelAccessor targetPixels = new PixelAccessor(width, height)) { using (PixelAccessor sourcePixels = source.Lock()) - using (PixelAccessor firstPassPixels = new PixelAccessor(width, source.Height)) + using (PinnedImageBuffer firstPassPixels = new PinnedImageBuffer(width, source.Height)) { + firstPassPixels.Clear(); + Parallel.For( 0, sourceRectangle.Bottom, this.ParallelOptions, y => - { - for (int x = minX; x < maxX; x++) { - // Ensure offsets are normalised for cropping and padding. - Weight[] horizontalValues = this.HorizontalWeights[x - startX].Values; - - // Destination color components - Vector4 destination = Vector4.Zero; - - for (int i = 0; i < horizontalValues.Length; i++) + // TODO: Without Parallel.For() this buffer object could be reused: + using (PinnedBuffer tempRowBuffer = new PinnedBuffer(sourcePixels.Width)) { - Weight xw = horizontalValues[i]; - destination += sourcePixels[xw.Index + sourceX, y].ToVector4() * xw.Value; + BufferSpan sourceRow = sourcePixels.GetRowSpan(y); + BulkPixelOperations.Instance.ToVector4( + sourceRow, + tempRowBuffer, + sourceRow.Length); + + 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; + } } - - TColor d = default(TColor); - d.PackFromVector4(destination); - firstPassPixels[x, y] = d; - } - }); + }); // Now process the rows. Parallel.For( @@ -143,17 +157,20 @@ namespace ImageSharp.Processing.Processors y => { // Ensure offsets are normalised for cropping and padding. - Weight[] verticalValues = this.VerticalWeights[y - startY].Values; + Weights ws = this.VerticalWeights.Weights[y - startY]; + float* verticalValues = ws.Ptr; + int left = ws.Left; for (int x = 0; x < width; x++) { // Destination color components Vector4 destination = Vector4.Zero; - for (int i = 0; i < verticalValues.Length; i++) + for (int i = 0; i < ws.Length; i++) { - Weight yw = verticalValues[i]; - destination += firstPassPixels[x, yw.Index + sourceY].ToVector4() * yw.Value; + float yw = verticalValues[i]; + int index = left + i; + destination += firstPassPixels[x, index] * yw; } TColor d = default(TColor);