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);