mirror of https://github.com/SixLabors/ImageSharp
22 changed files with 348 additions and 511 deletions
@ -1,164 +0,0 @@ |
|||
// Copyright (c) Six Labors and contributors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
|
|||
using System; |
|||
using System.Runtime.CompilerServices; |
|||
using SixLabors.ImageSharp.PixelFormats; |
|||
using SixLabors.Primitives; |
|||
|
|||
namespace SixLabors.ImageSharp.Processing.Processors |
|||
{ |
|||
/// <summary>
|
|||
/// Provides methods that allow the resizing of images using various algorithms.
|
|||
/// Adapted from <see href="http://www.realtimerendering.com/resources/GraphicsGems/gemsiii/filter_rcg.c"/>
|
|||
/// </summary>
|
|||
/// <typeparam name="TPixel">The pixel format.</typeparam>
|
|||
internal abstract class ResamplingWeightedProcessor<TPixel> : TransformProcessorBase<TPixel> |
|||
where TPixel : struct, IPixel<TPixel> |
|||
{ |
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="ResamplingWeightedProcessor{TPixel}"/> class.
|
|||
/// </summary>
|
|||
/// <param name="sampler">The sampler to perform the resize operation.</param>
|
|||
/// <param name="width">The target width.</param>
|
|||
/// <param name="height">The target height.</param>
|
|||
/// <param name="resizeRectangle">
|
|||
/// The <see cref="Rectangle"/> structure that specifies the portion of the target image object to draw to.
|
|||
/// </param>
|
|||
protected ResamplingWeightedProcessor(IResampler sampler, int width, int height, Rectangle resizeRectangle) |
|||
{ |
|||
Guard.NotNull(sampler, nameof(sampler)); |
|||
Guard.MustBeGreaterThan(width, 0, nameof(width)); |
|||
Guard.MustBeGreaterThan(height, 0, nameof(height)); |
|||
|
|||
this.Sampler = sampler; |
|||
this.Width = width; |
|||
this.Height = height; |
|||
this.ResizeRectangle = resizeRectangle; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets the sampler to perform the resize operation.
|
|||
/// </summary>
|
|||
public IResampler Sampler { get; } |
|||
|
|||
/// <summary>
|
|||
/// Gets or sets the width.
|
|||
/// </summary>
|
|||
public int Width { get; protected set; } |
|||
|
|||
/// <summary>
|
|||
/// Gets or sets the height.
|
|||
/// </summary>
|
|||
public int Height { get; protected set; } |
|||
|
|||
/// <summary>
|
|||
/// Gets or sets the resize rectangle.
|
|||
/// </summary>
|
|||
public Rectangle ResizeRectangle { get; protected set; } |
|||
|
|||
/// <summary>
|
|||
/// Gets or sets the horizontal weights.
|
|||
/// </summary>
|
|||
protected WeightsBuffer HorizontalWeights { get; set; } |
|||
|
|||
/// <summary>
|
|||
/// Gets or sets the vertical weights.
|
|||
/// </summary>
|
|||
protected WeightsBuffer VerticalWeights { get; set; } |
|||
|
|||
/// <summary>
|
|||
/// Computes the weights to apply at each pixel when resizing.
|
|||
/// </summary>
|
|||
/// <param name="destinationSize">The destination size</param>
|
|||
/// <param name="sourceSize">The source size</param>
|
|||
/// <returns>The <see cref="WeightsBuffer"/></returns>
|
|||
// TODO: Made internal to simplify experimenting with weights data. Make it protected again when finished figuring out how to optimize all the stuff!
|
|||
internal unsafe WeightsBuffer PrecomputeWeights(int destinationSize, int sourceSize) |
|||
{ |
|||
float ratio = (float)sourceSize / destinationSize; |
|||
float scale = ratio; |
|||
|
|||
if (scale < 1F) |
|||
{ |
|||
scale = 1F; |
|||
} |
|||
|
|||
IResampler sampler = this.Sampler; |
|||
float radius = MathF.Ceiling(scale * sampler.Radius); |
|||
var result = new WeightsBuffer(sourceSize, destinationSize); |
|||
|
|||
for (int i = 0; i < destinationSize; i++) |
|||
{ |
|||
float center = ((i + .5F) * ratio) - .5F; |
|||
|
|||
// Keep inside bounds.
|
|||
int left = (int)Math.Ceiling(center - radius); |
|||
if (left < 0) |
|||
{ |
|||
left = 0; |
|||
} |
|||
|
|||
int right = (int)Math.Floor(center + radius); |
|||
if (right > sourceSize - 1) |
|||
{ |
|||
right = sourceSize - 1; |
|||
} |
|||
|
|||
float sum = 0; |
|||
|
|||
WeightsWindow ws = result.GetWeightsWindow(i, left, right); |
|||
result.Weights[i] = ws; |
|||
|
|||
ref float weightsBaseRef = ref ws.GetStartReference(); |
|||
|
|||
for (int j = left; j <= right; j++) |
|||
{ |
|||
float weight = sampler.GetValue((j - center) / scale); |
|||
sum += weight; |
|||
|
|||
// weights[j - left] = weight:
|
|||
Unsafe.Add(ref weightsBaseRef, 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 < ws.Length; w++) |
|||
{ |
|||
// weights[w] = weights[w] / sum:
|
|||
ref float wRef = ref Unsafe.Add(ref weightsBaseRef, w); |
|||
wRef = wRef / sum; |
|||
} |
|||
} |
|||
} |
|||
|
|||
return result; |
|||
} |
|||
|
|||
/// <inheritdoc/>
|
|||
protected override void BeforeApply(ImageFrame<TPixel> source, ImageFrame<TPixel> destination, Rectangle sourceRectangle, Configuration configuration) |
|||
{ |
|||
if (!(this.Sampler is NearestNeighborResampler)) |
|||
{ |
|||
this.HorizontalWeights = this.PrecomputeWeights( |
|||
this.ResizeRectangle.Width, |
|||
sourceRectangle.Width); |
|||
|
|||
this.VerticalWeights = this.PrecomputeWeights( |
|||
this.ResizeRectangle.Height, |
|||
sourceRectangle.Height); |
|||
} |
|||
} |
|||
|
|||
/// <inheritdoc />
|
|||
protected override void AfterApply(ImageFrame<TPixel> source, ImageFrame<TPixel> destination, Rectangle sourceRectangle, Configuration configuration) |
|||
{ |
|||
base.AfterApply(source, destination, sourceRectangle, configuration); |
|||
this.HorizontalWeights?.Dispose(); |
|||
this.HorizontalWeights = null; |
|||
this.VerticalWeights?.Dispose(); |
|||
this.VerticalWeights = null; |
|||
} |
|||
} |
|||
} |
|||
Loading…
Reference in new issue