|
|
|
@ -6,6 +6,9 @@ |
|
|
|
namespace ImageSharp.Processing.Processors |
|
|
|
{ |
|
|
|
using System; |
|
|
|
using System.Buffers; |
|
|
|
using System.Runtime.CompilerServices; |
|
|
|
using System.Runtime.InteropServices; |
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// Provides methods that allow the resizing of images using various algorithms.
|
|
|
|
@ -59,32 +62,41 @@ namespace ImageSharp.Processing.Processors |
|
|
|
/// <summary>
|
|
|
|
/// Gets or sets the horizontal weights.
|
|
|
|
/// </summary>
|
|
|
|
protected Weights[] HorizontalWeights { get; set; } |
|
|
|
protected Weights.Buffer HorizontalWeights { get; set; } |
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// Gets or sets the vertical weights.
|
|
|
|
/// </summary>
|
|
|
|
protected Weights[] VerticalWeights { get; set; } |
|
|
|
protected Weights.Buffer VerticalWeights { get; set; } |
|
|
|
|
|
|
|
/// <inheritdoc/>
|
|
|
|
protected override void BeforeApply(ImageBase<TColor> 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<TColor> source, Rectangle sourceRectangle) |
|
|
|
{ |
|
|
|
base.AfterApply(source, sourceRectangle); |
|
|
|
this.HorizontalWeights?.Dispose(); |
|
|
|
this.HorizontalWeights = null; |
|
|
|
this.VerticalWeights?.Dispose(); |
|
|
|
this.VerticalWeights = null; |
|
|
|
} |
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// Computes the weights to apply at each pixel when resizing.
|
|
|
|
/// </summary>
|
|
|
|
/// <param name="destinationSize">The destination section size.</param>
|
|
|
|
/// <param name="sourceSize">The source section size.</param>
|
|
|
|
/// <returns>
|
|
|
|
/// The <see cref="T:Weights[]"/>.
|
|
|
|
/// </returns>
|
|
|
|
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; |
|
|
|
} |
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// Represents the weight to be added to a scaled pixel.
|
|
|
|
/// Represents a collection of weights and their sum.
|
|
|
|
/// </summary>
|
|
|
|
protected struct Weight |
|
|
|
protected unsafe struct Weights |
|
|
|
{ |
|
|
|
/// <summary>
|
|
|
|
/// Initializes a new instance of the <see cref="Weight"/> struct.
|
|
|
|
/// The local left index position
|
|
|
|
/// </summary>
|
|
|
|
/// <param name="index">The index.</param>
|
|
|
|
/// <param name="value">The value.</param>
|
|
|
|
public Weight(int index, float value) |
|
|
|
public int Left; |
|
|
|
|
|
|
|
public BufferSpan<float> Span; |
|
|
|
|
|
|
|
public float* Ptr => (float*)Span.PointerAtOffset; |
|
|
|
|
|
|
|
public int Length => Span.Length; |
|
|
|
|
|
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|
|
|
private Weights(int left, BufferSpan<float> span) |
|
|
|
{ |
|
|
|
this.Index = index; |
|
|
|
this.Value = value; |
|
|
|
this.Left = left; |
|
|
|
this.Span = span; |
|
|
|
} |
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// Gets the pixel index.
|
|
|
|
/// </summary>
|
|
|
|
public int Index { get; } |
|
|
|
internal unsafe class Buffer : IDisposable |
|
|
|
{ |
|
|
|
private PinnedImageBuffer<float> dataBuffer; |
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// Gets or sets the result of the interpolation algorithm.
|
|
|
|
/// </summary>
|
|
|
|
public float Value { get; set; } |
|
|
|
} |
|
|
|
public Weights[] Weights { get; } |
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// Represents a collection of weights and their sum.
|
|
|
|
/// </summary>
|
|
|
|
protected class Weights |
|
|
|
{ |
|
|
|
/// <summary>
|
|
|
|
/// Gets or sets the values.
|
|
|
|
/// </summary>
|
|
|
|
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<float>(sourceSize, destinationSize); |
|
|
|
this.dataBuffer.Clear(); |
|
|
|
this.DataPtr = (float*)this.dataBuffer.Pointer; |
|
|
|
this.Weights = new Weights[destinationSize]; |
|
|
|
} |
|
|
|
|
|
|
|
public void Dispose() |
|
|
|
{ |
|
|
|
this.dataBuffer.Dispose(); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |