Browse Source

Simplify IResampler contract

pull/1118/head
James Jackson-South 6 years ago
parent
commit
585b7be0e7
  1. 29
      src/ImageSharp/Common/Helpers/Guard.cs
  2. 2
      src/ImageSharp/Processing/Processors/Transforms/Automorphic/AffineTransformProcessor.cs
  3. 209
      src/ImageSharp/Processing/Processors/Transforms/Automorphic/AffineTransformProcessor{TPixel}.Transforms.cs
  4. 20
      src/ImageSharp/Processing/Processors/Transforms/Automorphic/AffineTransformProcessor{TPixel}.cs
  5. 0
      src/ImageSharp/Processing/Processors/Transforms/Automorphic/AutoOrientProcessor.cs
  6. 0
      src/ImageSharp/Processing/Processors/Transforms/Automorphic/AutoOrientProcessor{TPixel}.cs
  7. 104
      src/ImageSharp/Processing/Processors/Transforms/Automorphic/AutomorphicTransformUtilities.cs
  8. 0
      src/ImageSharp/Processing/Processors/Transforms/Automorphic/FlipProcessor.cs
  9. 0
      src/ImageSharp/Processing/Processors/Transforms/Automorphic/FlipProcessor{TPixel}.cs
  10. 2
      src/ImageSharp/Processing/Processors/Transforms/Automorphic/ProjectiveTransformProcessor.cs
  11. 209
      src/ImageSharp/Processing/Processors/Transforms/Automorphic/ProjectiveTransformProcessor{TPixel}.Transforms.cs
  12. 20
      src/ImageSharp/Processing/Processors/Transforms/Automorphic/ProjectiveTransformProcessor{TPixel}.cs
  13. 0
      src/ImageSharp/Processing/Processors/Transforms/Automorphic/RotateProcessor.cs
  14. 0
      src/ImageSharp/Processing/Processors/Transforms/Automorphic/RotateProcessor{TPixel}.cs
  15. 0
      src/ImageSharp/Processing/Processors/Transforms/Automorphic/SkewProcessor.cs
  16. 48
      src/ImageSharp/Processing/Processors/Transforms/IResampler.cs
  17. 23
      src/ImageSharp/Processing/Processors/Transforms/IResamplingImageProcessor{TPixel}.cs
  18. 263
      src/ImageSharp/Processing/Processors/Transforms/ResamplerExtensions.Operations.cs
  19. 249
      src/ImageSharp/Processing/Processors/Transforms/ResamplerExtensions.cs
  20. 47
      src/ImageSharp/Processing/Processors/Transforms/Resamplers/BicubicResampler.cs
  21. 47
      src/ImageSharp/Processing/Processors/Transforms/Resamplers/BoxResampler.cs
  22. 47
      src/ImageSharp/Processing/Processors/Transforms/Resamplers/CubicResampler.cs
  23. 47
      src/ImageSharp/Processing/Processors/Transforms/Resamplers/LanczosResampler.cs
  24. 47
      src/ImageSharp/Processing/Processors/Transforms/Resamplers/NearestNeighborResampler.cs
  25. 47
      src/ImageSharp/Processing/Processors/Transforms/Resamplers/TriangleResampler.cs
  26. 47
      src/ImageSharp/Processing/Processors/Transforms/Resamplers/WelchResampler.cs
  27. 6
      src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeKernelMap.cs
  28. 1
      src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor.cs
  29. 22
      src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor{TPixel}.Transforms.cs
  30. 23
      src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor{TPixel}.cs
  31. 2
      tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeKernelMapTests.ReferenceKernelMap.cs
  32. 6
      tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeKernelMapTests.cs

29
src/ImageSharp/Common/Helpers/Guard.cs

@ -0,0 +1,29 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
using System.Reflection;
using System.Runtime.CompilerServices;
using SixLabors.ImageSharp;
namespace SixLabors
{
internal static partial class Guard
{
/// <summary>
/// Ensures that the value is a value type.
/// </summary>
/// <param name="value">The target object, which cannot be null.</param>
/// <param name="parameterName">The name of the parameter that is to be checked.</param>
/// <typeparam name="TValue">The type of the value.</typeparam>
/// <exception cref="ArgumentException"><paramref name="value"/> is not a value type.</exception>
[MethodImpl(InliningOptions.ShortMethod)]
public static void MustBeValueType<TValue>(TValue value, string parameterName)
{
if (!value.GetType().GetTypeInfo().IsValueType)
{
ThrowArgumentException("Type must be a struct.", parameterName);
}
}
}
}

2
src/ImageSharp/Processing/Processors/Transforms/AffineTransformProcessor.cs → src/ImageSharp/Processing/Processors/Transforms/Automorphic/AffineTransformProcessor.cs

@ -19,6 +19,8 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
public AffineTransformProcessor(Matrix3x2 matrix, IResampler sampler, Size targetDimensions) public AffineTransformProcessor(Matrix3x2 matrix, IResampler sampler, Size targetDimensions)
{ {
Guard.NotNull(sampler, nameof(sampler)); Guard.NotNull(sampler, nameof(sampler));
Guard.MustBeValueType(sampler, nameof(sampler));
this.Sampler = sampler; this.Sampler = sampler;
this.TransformMatrix = matrix; this.TransformMatrix = matrix;
this.DestinationSize = targetDimensions; this.DestinationSize = targetDimensions;

209
src/ImageSharp/Processing/Processors/Transforms/Automorphic/AffineTransformProcessor{TPixel}.Transforms.cs

@ -0,0 +1,209 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
using System.Numerics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp.Processing.Processors.Transforms
{
/// <content>
/// Contains the application code for performing an affine transform.
/// </content>
internal partial class AffineTransformProcessor<TPixel>
{
/// <summary>
/// Applies an affine transformation upon an image.
/// </summary>
/// <typeparam name="TResampler">The type of sampler.</typeparam>
/// <param name="configuration">The configuration.</param>
/// <param name="sampler">The pixel sampler.</param>
/// <param name="source">The source image frame.</param>
/// <param name="destination">The destination image frame.</param>
/// <param name="matrix">The transform matrix.</param>
public static void ApplyAffineTransform<TResampler>(
Configuration configuration,
in TResampler sampler,
ImageFrame<TPixel> source,
ImageFrame<TPixel> destination,
Matrix3x2 matrix)
where TResampler : struct, IResampler
{
// Handle transforms that result in output identical to the original.
if (matrix.Equals(default) || matrix.Equals(Matrix3x2.Identity))
{
// The clone will be blank here copy all the pixel data over
source.GetPixelMemoryGroup().CopyTo(destination.GetPixelMemoryGroup());
return;
}
// Convert from screen to world space.
Matrix3x2.Invert(matrix, out matrix);
if (sampler is NearestNeighborResampler)
{
var nnOperation = new NNAffineOperation(source, destination, matrix);
ParallelRowIterator.IterateRows(
configuration,
destination.Bounds(),
in nnOperation);
return;
}
int yRadius = AutomorphicTransformUtilities.GetSamplingRadius(in sampler, source.Height, destination.Height);
int xRadius = AutomorphicTransformUtilities.GetSamplingRadius(in sampler, source.Width, destination.Width);
var radialExtents = new Vector2(xRadius, yRadius);
int yLength = (yRadius * 2) + 1;
int xLength = (xRadius * 2) + 1;
// We use 2D buffers so that we can access the weight spans per row in parallel.
using Buffer2D<float> yKernelBuffer = configuration.MemoryAllocator.Allocate2D<float>(yLength, destination.Height);
using Buffer2D<float> xKernelBuffer = configuration.MemoryAllocator.Allocate2D<float>(xLength, destination.Height);
int maxX = source.Width - 1;
int maxY = source.Height - 1;
var maxSourceExtents = new Vector4(maxX, maxY, maxX, maxY);
var operation = new AffineOperation<TResampler>(
configuration,
source,
destination,
yKernelBuffer,
xKernelBuffer,
in sampler,
matrix,
radialExtents,
maxSourceExtents);
ParallelRowIterator.IterateRows<AffineOperation<TResampler>, Vector4>(
configuration,
destination.Bounds(),
in operation);
}
private readonly struct NNAffineOperation : IRowIntervalOperation
{
private readonly ImageFrame<TPixel> source;
private readonly ImageFrame<TPixel> destination;
private readonly Rectangle bounds;
private readonly Matrix3x2 matrix;
private readonly int maxX;
[MethodImpl(InliningOptions.ShortMethod)]
public NNAffineOperation(
ImageFrame<TPixel> source,
ImageFrame<TPixel> destination,
Matrix3x2 matrix)
{
this.source = source;
this.destination = destination;
this.bounds = source.Bounds();
this.matrix = matrix;
this.maxX = destination.Width;
}
[MethodImpl(InliningOptions.ShortMethod)]
public void Invoke(in RowInterval rows)
{
for (int y = rows.Min; y < rows.Max; y++)
{
Span<TPixel> destRow = this.destination.GetPixelRowSpan(y);
for (int x = 0; x < this.maxX; x++)
{
var point = Vector2.Transform(new Vector2(x, y), this.matrix);
int px = (int)MathF.Round(point.X);
int py = (int)MathF.Round(point.Y);
if (this.bounds.Contains(px, py))
{
destRow[x] = this.source[px, py];
}
}
}
}
}
private readonly struct AffineOperation<TResampler> : IRowIntervalOperation<Vector4>
where TResampler : struct, IResampler
{
private readonly Configuration configuration;
private readonly ImageFrame<TPixel> source;
private readonly ImageFrame<TPixel> destination;
private readonly Buffer2D<float> yKernelBuffer;
private readonly Buffer2D<float> xKernelBuffer;
private readonly TResampler sampler;
private readonly Matrix3x2 matrix;
private readonly Vector2 radialExtents;
private readonly Vector4 maxSourceExtents;
private readonly int maxX;
[MethodImpl(InliningOptions.ShortMethod)]
public AffineOperation(
Configuration configuration,
ImageFrame<TPixel> source,
ImageFrame<TPixel> destination,
Buffer2D<float> yKernelBuffer,
Buffer2D<float> xKernelBuffer,
in TResampler sampler,
Matrix3x2 matrix,
Vector2 radialExtents,
Vector4 maxSourceExtents)
{
this.configuration = configuration;
this.source = source;
this.destination = destination;
this.yKernelBuffer = yKernelBuffer;
this.xKernelBuffer = xKernelBuffer;
this.sampler = sampler;
this.matrix = matrix;
this.radialExtents = radialExtents;
this.maxSourceExtents = maxSourceExtents;
this.maxX = destination.Width;
}
[MethodImpl(InliningOptions.ShortMethod)]
public void Invoke(in RowInterval rows, Span<Vector4> span)
{
Buffer2D<TPixel> sourceBuffer = this.source.PixelBuffer;
for (int y = rows.Min; y < rows.Max; y++)
{
PixelOperations<TPixel>.Instance.ToVector4(
this.configuration,
this.destination.GetPixelRowSpan(y),
span);
ref float yKernelSpanRef = ref MemoryMarshal.GetReference(this.yKernelBuffer.GetRowSpan(y));
ref float xKernelSpanRef = ref MemoryMarshal.GetReference(this.xKernelBuffer.GetRowSpan(y));
for (int x = 0; x < this.maxX; x++)
{
// Use the single precision position to calculate correct bounding pixels
// otherwise we get rogue pixels outside of the bounds.
var point = Vector2.Transform(new Vector2(x, y), this.matrix);
AutomorphicTransformUtilities.Convolve(
in this.sampler,
point,
sourceBuffer,
span,
x,
ref yKernelSpanRef,
ref xKernelSpanRef,
this.radialExtents,
this.maxSourceExtents);
}
PixelOperations<TPixel>.Instance.FromVector4Destructive(
this.configuration,
span,
this.destination.GetPixelRowSpan(y));
}
}
}
}
}

20
src/ImageSharp/Processing/Processors/Transforms/AffineTransformProcessor{TPixel}.cs → src/ImageSharp/Processing/Processors/Transforms/Automorphic/AffineTransformProcessor{TPixel}.cs

@ -10,12 +10,14 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
/// Provides the base methods to perform affine transforms on an image. /// Provides the base methods to perform affine transforms on an image.
/// </summary> /// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam> /// <typeparam name="TPixel">The pixel format.</typeparam>
internal class AffineTransformProcessor<TPixel> : TransformProcessor<TPixel> internal partial class AffineTransformProcessor<TPixel> : TransformProcessor<TPixel>, IResamplingImageProcessor<TPixel>
where TPixel : struct, IPixel<TPixel> where TPixel : struct, IPixel<TPixel>
{ {
private readonly Size destinationSize; private readonly Size destinationSize;
private readonly Matrix3x2 transformMatrix; private readonly Matrix3x2 transformMatrix;
private readonly IResampler resampler; private readonly IResampler resampler;
private ImageFrame<TPixel> source;
private ImageFrame<TPixel> destination;
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="AffineTransformProcessor{TPixel}"/> class. /// Initializes a new instance of the <see cref="AffineTransformProcessor{TPixel}"/> class.
@ -36,6 +38,20 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
/// <inheritdoc/> /// <inheritdoc/>
protected override void OnFrameApply(ImageFrame<TPixel> source, ImageFrame<TPixel> destination) protected override void OnFrameApply(ImageFrame<TPixel> source, ImageFrame<TPixel> destination)
=> this.resampler.ApplyAffineTransform(this.Configuration, source, destination, this.transformMatrix); {
this.source = source;
this.destination = destination;
this.resampler.ApplyTransform(this);
}
/// <inheritdoc/>
public void ApplyTransform<TResampler>(in TResampler sampler)
where TResampler : struct, IResampler
=> ApplyAffineTransform(
this.Configuration,
in sampler,
this.source,
this.destination,
this.transformMatrix);
} }
} }

0
src/ImageSharp/Processing/Processors/Transforms/AutoOrientProcessor.cs → src/ImageSharp/Processing/Processors/Transforms/Automorphic/AutoOrientProcessor.cs

0
src/ImageSharp/Processing/Processors/Transforms/AutoOrientProcessor{TPixel}.cs → src/ImageSharp/Processing/Processors/Transforms/Automorphic/AutoOrientProcessor{TPixel}.cs

104
src/ImageSharp/Processing/Processors/Transforms/Automorphic/AutomorphicTransformUtilities.cs

@ -0,0 +1,104 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
using System.Numerics;
using System.Runtime.CompilerServices;
using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp.Processing.Processors.Transforms
{
/// <summary>
/// Utility methods for affine and projective transforms.
/// </summary>
internal static class AutomorphicTransformUtilities
{
[MethodImpl(InliningOptions.ShortMethod)]
internal static int GetSamplingRadius<TResampler>(in TResampler sampler, int sourceSize, int destinationSize)
where TResampler : struct, IResampler
{
double scale = sourceSize / destinationSize;
if (scale < 1)
{
scale = 1;
}
return (int)Math.Ceiling(scale * sampler.Radius);
}
[MethodImpl(InliningOptions.ShortMethod)]
internal static void Convolve<TResampler, TPixel>(
in TResampler sampler,
Vector2 transformedPoint,
Buffer2D<TPixel> sourcePixels,
Span<Vector4> targetRow,
int column,
ref float yKernelSpanRef,
ref float xKernelSpanRef,
Vector2 radialExtents,
Vector4 maxSourceExtents)
where TResampler : struct, IResampler
where TPixel : struct, IPixel<TPixel>
{
// Clamp sampling pixel radial extents to the source image edges
Vector2 minXY = transformedPoint - radialExtents;
Vector2 maxXY = transformedPoint + radialExtents;
// left, top, right, bottom
var sourceExtents = new Vector4(
MathF.Ceiling(minXY.X),
MathF.Ceiling(minXY.Y),
MathF.Floor(maxXY.X),
MathF.Floor(maxXY.Y));
sourceExtents = Vector4.Clamp(sourceExtents, Vector4.Zero, maxSourceExtents);
int left = (int)sourceExtents.X;
int top = (int)sourceExtents.Y;
int right = (int)sourceExtents.Z;
int bottom = (int)sourceExtents.W;
if (left == right || top == bottom)
{
return;
}
CalculateWeights(in sampler, top, bottom, transformedPoint.Y, ref yKernelSpanRef);
CalculateWeights(in sampler, left, right, transformedPoint.X, ref xKernelSpanRef);
Vector4 sum = Vector4.Zero;
for (int kernelY = 0, y = top; y <= bottom; y++, kernelY++)
{
float yWeight = Unsafe.Add(ref yKernelSpanRef, kernelY);
for (int kernelX = 0, x = left; x <= right; x++, kernelX++)
{
float xWeight = Unsafe.Add(ref xKernelSpanRef, kernelX);
// Values are first premultiplied to prevent darkening of edge pixels.
var current = sourcePixels[x, y].ToVector4();
Vector4Utils.Premultiply(ref current);
sum += current * xWeight * yWeight;
}
}
// Reverse the premultiplication
Vector4Utils.UnPremultiply(ref sum);
targetRow[column] = sum;
}
[MethodImpl(InliningOptions.ShortMethod)]
private static void CalculateWeights<TResampler>(in TResampler sampler, int min, int max, float point, ref float weightsRef)
where TResampler : struct, IResampler
{
float sum = 0;
for (int x = 0, i = min; i <= max; i++, x++)
{
float weight = sampler.GetValue(i - point);
sum += weight;
Unsafe.Add(ref weightsRef, x) = weight;
}
}
}
}

0
src/ImageSharp/Processing/Processors/Transforms/FlipProcessor.cs → src/ImageSharp/Processing/Processors/Transforms/Automorphic/FlipProcessor.cs

0
src/ImageSharp/Processing/Processors/Transforms/FlipProcessor{TPixel}.cs → src/ImageSharp/Processing/Processors/Transforms/Automorphic/FlipProcessor{TPixel}.cs

2
src/ImageSharp/Processing/Processors/Transforms/ProjectiveTransformProcessor.cs → src/ImageSharp/Processing/Processors/Transforms/Automorphic/ProjectiveTransformProcessor.cs

@ -19,6 +19,8 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
public ProjectiveTransformProcessor(Matrix4x4 matrix, IResampler sampler, Size targetDimensions) public ProjectiveTransformProcessor(Matrix4x4 matrix, IResampler sampler, Size targetDimensions)
{ {
Guard.NotNull(sampler, nameof(sampler)); Guard.NotNull(sampler, nameof(sampler));
Guard.MustBeValueType(sampler, nameof(sampler));
this.Sampler = sampler; this.Sampler = sampler;
this.TransformMatrix = matrix; this.TransformMatrix = matrix;
this.DestinationSize = targetDimensions; this.DestinationSize = targetDimensions;

209
src/ImageSharp/Processing/Processors/Transforms/Automorphic/ProjectiveTransformProcessor{TPixel}.Transforms.cs

@ -0,0 +1,209 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
using System.Numerics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp.Processing.Processors.Transforms
{
/// <content>
/// Contains the application code for performing a projective transform.
/// </content>
internal partial class ProjectiveTransformProcessor<TPixel>
{
/// <summary>
/// Applies a projective transformation upon an image.
/// </summary>
/// <typeparam name="TResampler">The type of sampler.</typeparam>
/// <param name="configuration">The configuration.</param>
/// <param name="sampler">The pixel sampler.</param>
/// <param name="source">The source image frame.</param>
/// <param name="destination">The destination image frame.</param>
/// <param name="matrix">The transform matrix.</param>
public static void ApplyProjectiveTransform<TResampler>(
Configuration configuration,
in TResampler sampler,
ImageFrame<TPixel> source,
ImageFrame<TPixel> destination,
Matrix4x4 matrix)
where TResampler : struct, IResampler
{
// Handle transforms that result in output identical to the original.
if (matrix.Equals(default) || matrix.Equals(Matrix4x4.Identity))
{
// The clone will be blank here copy all the pixel data over
source.GetPixelMemoryGroup().CopyTo(destination.GetPixelMemoryGroup());
return;
}
// Convert from screen to world space.
Matrix4x4.Invert(matrix, out matrix);
if (sampler is NearestNeighborResampler)
{
var nnOperation = new NNProjectiveOperation(source, destination, matrix);
ParallelRowIterator.IterateRows(
configuration,
destination.Bounds(),
in nnOperation);
return;
}
int yRadius = AutomorphicTransformUtilities.GetSamplingRadius(in sampler, source.Height, destination.Height);
int xRadius = AutomorphicTransformUtilities.GetSamplingRadius(in sampler, source.Width, destination.Width);
var radialExtents = new Vector2(xRadius, yRadius);
int yLength = (yRadius * 2) + 1;
int xLength = (xRadius * 2) + 1;
// We use 2D buffers so that we can access the weight spans per row in parallel.
using Buffer2D<float> yKernelBuffer = configuration.MemoryAllocator.Allocate2D<float>(yLength, destination.Height);
using Buffer2D<float> xKernelBuffer = configuration.MemoryAllocator.Allocate2D<float>(xLength, destination.Height);
int maxX = source.Width - 1;
int maxY = source.Height - 1;
var maxSourceExtents = new Vector4(maxX, maxY, maxX, maxY);
var operation = new ProjectiveOperation<TResampler>(
configuration,
source,
destination,
yKernelBuffer,
xKernelBuffer,
in sampler,
matrix,
radialExtents,
maxSourceExtents);
ParallelRowIterator.IterateRows<ProjectiveOperation<TResampler>, Vector4>(
configuration,
destination.Bounds(),
in operation);
}
private readonly struct NNProjectiveOperation : IRowIntervalOperation
{
private readonly ImageFrame<TPixel> source;
private readonly ImageFrame<TPixel> destination;
private readonly Rectangle bounds;
private readonly Matrix4x4 matrix;
private readonly int maxX;
[MethodImpl(InliningOptions.ShortMethod)]
public NNProjectiveOperation(
ImageFrame<TPixel> source,
ImageFrame<TPixel> destination,
Matrix4x4 matrix)
{
this.source = source;
this.destination = destination;
this.bounds = source.Bounds();
this.matrix = matrix;
this.maxX = destination.Width;
}
[MethodImpl(InliningOptions.ShortMethod)]
public void Invoke(in RowInterval rows)
{
for (int y = rows.Min; y < rows.Max; y++)
{
Span<TPixel> destRow = this.destination.GetPixelRowSpan(y);
for (int x = 0; x < this.maxX; x++)
{
Vector2 point = TransformUtilities.ProjectiveTransform2D(x, y, this.matrix);
int px = (int)MathF.Round(point.X);
int py = (int)MathF.Round(point.Y);
if (this.bounds.Contains(px, py))
{
destRow[x] = this.source[px, py];
}
}
}
}
}
private readonly struct ProjectiveOperation<TResampler> : IRowIntervalOperation<Vector4>
where TResampler : struct, IResampler
{
private readonly Configuration configuration;
private readonly ImageFrame<TPixel> source;
private readonly ImageFrame<TPixel> destination;
private readonly Buffer2D<float> yKernelBuffer;
private readonly Buffer2D<float> xKernelBuffer;
private readonly TResampler sampler;
private readonly Matrix4x4 matrix;
private readonly Vector2 radialExtents;
private readonly Vector4 maxSourceExtents;
private readonly int maxX;
[MethodImpl(InliningOptions.ShortMethod)]
public ProjectiveOperation(
Configuration configuration,
ImageFrame<TPixel> source,
ImageFrame<TPixel> destination,
Buffer2D<float> yKernelBuffer,
Buffer2D<float> xKernelBuffer,
in TResampler sampler,
Matrix4x4 matrix,
Vector2 radialExtents,
Vector4 maxSourceExtents)
{
this.configuration = configuration;
this.source = source;
this.destination = destination;
this.yKernelBuffer = yKernelBuffer;
this.xKernelBuffer = xKernelBuffer;
this.sampler = sampler;
this.matrix = matrix;
this.radialExtents = radialExtents;
this.maxSourceExtents = maxSourceExtents;
this.maxX = destination.Width;
}
[MethodImpl(InliningOptions.ShortMethod)]
public void Invoke(in RowInterval rows, Span<Vector4> span)
{
Buffer2D<TPixel> sourceBuffer = this.source.PixelBuffer;
for (int y = rows.Min; y < rows.Max; y++)
{
PixelOperations<TPixel>.Instance.ToVector4(
this.configuration,
this.destination.GetPixelRowSpan(y),
span);
ref float yKernelSpanRef = ref MemoryMarshal.GetReference(this.yKernelBuffer.GetRowSpan(y));
ref float xKernelSpanRef = ref MemoryMarshal.GetReference(this.xKernelBuffer.GetRowSpan(y));
for (int x = 0; x < this.maxX; x++)
{
// Use the single precision position to calculate correct bounding pixels
// otherwise we get rogue pixels outside of the bounds.
Vector2 point = TransformUtilities.ProjectiveTransform2D(x, y, this.matrix);
AutomorphicTransformUtilities.Convolve(
in this.sampler,
point,
sourceBuffer,
span,
x,
ref yKernelSpanRef,
ref xKernelSpanRef,
this.radialExtents,
this.maxSourceExtents);
}
PixelOperations<TPixel>.Instance.FromVector4Destructive(
this.configuration,
span,
this.destination.GetPixelRowSpan(y));
}
}
}
}
}

20
src/ImageSharp/Processing/Processors/Transforms/ProjectiveTransformProcessor{TPixel}.cs → src/ImageSharp/Processing/Processors/Transforms/Automorphic/ProjectiveTransformProcessor{TPixel}.cs

@ -10,12 +10,14 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
/// Provides the base methods to perform non-affine transforms on an image. /// Provides the base methods to perform non-affine transforms on an image.
/// </summary> /// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam> /// <typeparam name="TPixel">The pixel format.</typeparam>
internal class ProjectiveTransformProcessor<TPixel> : TransformProcessor<TPixel> internal partial class ProjectiveTransformProcessor<TPixel> : TransformProcessor<TPixel>, IResamplingImageProcessor<TPixel>
where TPixel : struct, IPixel<TPixel> where TPixel : struct, IPixel<TPixel>
{ {
private readonly Size destinationSize; private readonly Size destinationSize;
private readonly IResampler resampler; private readonly IResampler resampler;
private readonly Matrix4x4 transformMatrix; private readonly Matrix4x4 transformMatrix;
private ImageFrame<TPixel> source;
private ImageFrame<TPixel> destination;
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="ProjectiveTransformProcessor{TPixel}"/> class. /// Initializes a new instance of the <see cref="ProjectiveTransformProcessor{TPixel}"/> class.
@ -36,6 +38,20 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
/// <inheritdoc/> /// <inheritdoc/>
protected override void OnFrameApply(ImageFrame<TPixel> source, ImageFrame<TPixel> destination) protected override void OnFrameApply(ImageFrame<TPixel> source, ImageFrame<TPixel> destination)
=> this.resampler.ApplyProjectiveTransform(this.Configuration, source, destination, this.transformMatrix); {
this.source = source;
this.destination = destination;
this.resampler.ApplyTransform(this);
}
/// <inheritdoc/>
public void ApplyTransform<TResampler>(in TResampler sampler)
where TResampler : struct, IResampler
=> ApplyProjectiveTransform(
this.Configuration,
in sampler,
this.source,
this.destination,
this.transformMatrix);
} }
} }

0
src/ImageSharp/Processing/Processors/Transforms/RotateProcessor.cs → src/ImageSharp/Processing/Processors/Transforms/Automorphic/RotateProcessor.cs

0
src/ImageSharp/Processing/Processors/Transforms/RotateProcessor{TPixel}.cs → src/ImageSharp/Processing/Processors/Transforms/Automorphic/RotateProcessor{TPixel}.cs

0
src/ImageSharp/Processing/Processors/Transforms/SkewProcessor.cs → src/ImageSharp/Processing/Processors/Transforms/Automorphic/SkewProcessor.cs

48
src/ImageSharp/Processing/Processors/Transforms/IResampler.cs

@ -1,7 +1,6 @@
// Copyright (c) Six Labors and contributors. // Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
using System.Numerics;
using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp.Processing.Processors.Transforms namespace SixLabors.ImageSharp.Processing.Processors.Transforms
@ -26,52 +25,11 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
float GetValue(float x); float GetValue(float x);
/// <summary> /// <summary>
/// Applies an resizing transformation upon an image. /// Applies a transformation upon an image.
/// </summary> /// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam> /// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="configuration">The configuration.</param> /// <param name="processor">The transforming image processor.</param>
/// <param name="source">The source image.</param> void ApplyTransform<TPixel>(IResamplingImageProcessor<TPixel> processor)
/// <param name="destination">The destination image.</param>
/// <param name="sourceRectangle">The source bounds.</param>
/// <param name="targetRectangle">The target location.</param>
/// <param name="compand">Whether to compress or expand individual pixel color values on processing.</param>
void ApplyResizeTransform<TPixel>(
Configuration configuration,
Image<TPixel> source,
Image<TPixel> destination,
Rectangle sourceRectangle,
Rectangle targetRectangle,
bool compand)
where TPixel : struct, IPixel<TPixel>;
/// <summary>
/// Applies an affine transformation upon an image.
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="configuration">The configuration.</param>
/// <param name="source">The source image frame.</param>
/// <param name="destination">The destination image frame.</param>
/// <param name="matrix">The transform matrix.</param>
void ApplyAffineTransform<TPixel>(
Configuration configuration,
ImageFrame<TPixel> source,
ImageFrame<TPixel> destination,
Matrix3x2 matrix)
where TPixel : struct, IPixel<TPixel>;
/// <summary>
/// Applies a projective transformation upon an image.
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="configuration">The configuration.</param>
/// <param name="source">The source image frame.</param>
/// <param name="destination">The destination image frame.</param>
/// <param name="matrix">The transform matrix.</param>
void ApplyProjectiveTransform<TPixel>(
Configuration configuration,
ImageFrame<TPixel> source,
ImageFrame<TPixel> destination,
Matrix4x4 matrix)
where TPixel : struct, IPixel<TPixel>; where TPixel : struct, IPixel<TPixel>;
} }
} }

23
src/ImageSharp/Processing/Processors/Transforms/IResamplingImageProcessor{TPixel}.cs

@ -0,0 +1,23 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp.Processing.Processors.Transforms
{
/// <summary>
/// Implements an algorithm to alter the pixels of an image via a resampling transforms.
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
public interface IResamplingImageProcessor<TPixel> : IImageProcessor<TPixel>
where TPixel : struct, IPixel<TPixel>
{
/// <summary>
/// Applies a resampling transform with the given sampler.
/// </summary>
/// <typeparam name="TResampler">The type of sampler.</typeparam>
/// <param name="sampler">The sampler to use.</param>
void ApplyTransform<TResampler>(in TResampler sampler)
where TResampler : struct, IResampler;
}
}

263
src/ImageSharp/Processing/Processors/Transforms/ResamplerExtensions.Operations.cs

@ -1,263 +0,0 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
using System.Numerics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp.Processing.Processors.Transforms
{
/// <content>
/// Extensions for <see cref="IResampler"/>.
/// </content>
public static partial class ResamplerExtensions
{
private readonly struct NNAffineOperation<TPixel> : IRowIntervalOperation
where TPixel : struct, IPixel<TPixel>
{
private readonly ImageFrame<TPixel> source;
private readonly ImageFrame<TPixel> destination;
private readonly Rectangle bounds;
private readonly Matrix3x2 matrix;
private readonly int maxX;
[MethodImpl(InliningOptions.ShortMethod)]
public NNAffineOperation(
ImageFrame<TPixel> source,
ImageFrame<TPixel> destination,
Matrix3x2 matrix)
{
this.source = source;
this.destination = destination;
this.bounds = source.Bounds();
this.matrix = matrix;
this.maxX = destination.Width;
}
[MethodImpl(InliningOptions.ShortMethod)]
public void Invoke(in RowInterval rows)
{
for (int y = rows.Min; y < rows.Max; y++)
{
Span<TPixel> destRow = this.destination.GetPixelRowSpan(y);
for (int x = 0; x < this.maxX; x++)
{
var point = Vector2.Transform(new Vector2(x, y), this.matrix);
int px = (int)MathF.Round(point.X);
int py = (int)MathF.Round(point.Y);
if (this.bounds.Contains(px, py))
{
destRow[x] = this.source[px, py];
}
}
}
}
}
private readonly struct NNProjectiveOperation<TPixel> : IRowIntervalOperation
where TPixel : struct, IPixel<TPixel>
{
private readonly ImageFrame<TPixel> source;
private readonly ImageFrame<TPixel> destination;
private readonly Rectangle bounds;
private readonly Matrix4x4 matrix;
private readonly int maxX;
[MethodImpl(InliningOptions.ShortMethod)]
public NNProjectiveOperation(
ImageFrame<TPixel> source,
ImageFrame<TPixel> destination,
Matrix4x4 matrix)
{
this.source = source;
this.destination = destination;
this.bounds = source.Bounds();
this.matrix = matrix;
this.maxX = destination.Width;
}
[MethodImpl(InliningOptions.ShortMethod)]
public void Invoke(in RowInterval rows)
{
for (int y = rows.Min; y < rows.Max; y++)
{
Span<TPixel> destRow = this.destination.GetPixelRowSpan(y);
for (int x = 0; x < this.maxX; x++)
{
Vector2 point = TransformUtilities.ProjectiveTransform2D(x, y, this.matrix);
int px = (int)MathF.Round(point.X);
int py = (int)MathF.Round(point.Y);
if (this.bounds.Contains(px, py))
{
destRow[x] = this.source[px, py];
}
}
}
}
}
private readonly struct AffineOperation<TResampler, TPixel> : IRowIntervalOperation<Vector4>
where TResampler : unmanaged, IResampler
where TPixel : struct, IPixel<TPixel>
{
private readonly Configuration configuration;
private readonly ImageFrame<TPixel> source;
private readonly ImageFrame<TPixel> destination;
private readonly Buffer2D<float> yKernelBuffer;
private readonly Buffer2D<float> xKernelBuffer;
private readonly TResampler sampler;
private readonly Matrix3x2 matrix;
private readonly Vector2 radialExtents;
private readonly Vector4 maxSourceExtents;
private readonly int maxX;
[MethodImpl(InliningOptions.ShortMethod)]
public AffineOperation(
Configuration configuration,
ImageFrame<TPixel> source,
ImageFrame<TPixel> destination,
Buffer2D<float> yKernelBuffer,
Buffer2D<float> xKernelBuffer,
in TResampler sampler,
Matrix3x2 matrix,
Vector2 radialExtents,
Vector4 maxSourceExtents)
{
this.configuration = configuration;
this.source = source;
this.destination = destination;
this.yKernelBuffer = yKernelBuffer;
this.xKernelBuffer = xKernelBuffer;
this.sampler = sampler;
this.matrix = matrix;
this.radialExtents = radialExtents;
this.maxSourceExtents = maxSourceExtents;
this.maxX = destination.Width;
}
[MethodImpl(InliningOptions.ShortMethod)]
public void Invoke(in RowInterval rows, Span<Vector4> span)
{
Buffer2D<TPixel> sourceBuffer = this.source.PixelBuffer;
for (int y = rows.Min; y < rows.Max; y++)
{
PixelOperations<TPixel>.Instance.ToVector4(
this.configuration,
this.destination.GetPixelRowSpan(y),
span);
ref float yKernelSpanRef = ref MemoryMarshal.GetReference(this.yKernelBuffer.GetRowSpan(y));
ref float xKernelSpanRef = ref MemoryMarshal.GetReference(this.xKernelBuffer.GetRowSpan(y));
for (int x = 0; x < this.maxX; x++)
{
// Use the single precision position to calculate correct bounding pixels
// otherwise we get rogue pixels outside of the bounds.
var point = Vector2.Transform(new Vector2(x, y), this.matrix);
Convolve(
in this.sampler,
point,
sourceBuffer,
span,
x,
ref yKernelSpanRef,
ref xKernelSpanRef,
this.radialExtents,
this.maxSourceExtents);
}
PixelOperations<TPixel>.Instance.FromVector4Destructive(
this.configuration,
span,
this.destination.GetPixelRowSpan(y));
}
}
}
private readonly struct ProjectiveOperation<TResampler, TPixel> : IRowIntervalOperation<Vector4>
where TResampler : unmanaged, IResampler
where TPixel : struct, IPixel<TPixel>
{
private readonly Configuration configuration;
private readonly ImageFrame<TPixel> source;
private readonly ImageFrame<TPixel> destination;
private readonly Buffer2D<float> yKernelBuffer;
private readonly Buffer2D<float> xKernelBuffer;
private readonly TResampler sampler;
private readonly Matrix4x4 matrix;
private readonly Vector2 radialExtents;
private readonly Vector4 maxSourceExtents;
private readonly int maxX;
[MethodImpl(InliningOptions.ShortMethod)]
public ProjectiveOperation(
Configuration configuration,
ImageFrame<TPixel> source,
ImageFrame<TPixel> destination,
Buffer2D<float> yKernelBuffer,
Buffer2D<float> xKernelBuffer,
in TResampler sampler,
Matrix4x4 matrix,
Vector2 radialExtents,
Vector4 maxSourceExtents)
{
this.configuration = configuration;
this.source = source;
this.destination = destination;
this.yKernelBuffer = yKernelBuffer;
this.xKernelBuffer = xKernelBuffer;
this.sampler = sampler;
this.matrix = matrix;
this.radialExtents = radialExtents;
this.maxSourceExtents = maxSourceExtents;
this.maxX = destination.Width;
}
[MethodImpl(InliningOptions.ShortMethod)]
public void Invoke(in RowInterval rows, Span<Vector4> span)
{
Buffer2D<TPixel> sourceBuffer = this.source.PixelBuffer;
for (int y = rows.Min; y < rows.Max; y++)
{
PixelOperations<TPixel>.Instance.ToVector4(
this.configuration,
this.destination.GetPixelRowSpan(y),
span);
ref float yKernelSpanRef = ref MemoryMarshal.GetReference(this.yKernelBuffer.GetRowSpan(y));
ref float xKernelSpanRef = ref MemoryMarshal.GetReference(this.xKernelBuffer.GetRowSpan(y));
for (int x = 0; x < this.maxX; x++)
{
// Use the single precision position to calculate correct bounding pixels
// otherwise we get rogue pixels outside of the bounds.
Vector2 point = TransformUtilities.ProjectiveTransform2D(x, y, this.matrix);
Convolve(
in this.sampler,
point,
sourceBuffer,
span,
x,
ref yKernelSpanRef,
ref xKernelSpanRef,
this.radialExtents,
this.maxSourceExtents);
}
PixelOperations<TPixel>.Instance.FromVector4Destructive(
this.configuration,
span,
this.destination.GetPixelRowSpan(y));
}
}
}
}
}

249
src/ImageSharp/Processing/Processors/Transforms/ResamplerExtensions.cs

@ -1,249 +0,0 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
using System.Numerics;
using System.Runtime.CompilerServices;
using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp.Processing.Processors.Transforms
{
/// <summary>
/// Extensions for <see cref="IResampler"/>.
/// </summary>
public static partial class ResamplerExtensions
{
/// <summary>
/// Applies an affine transformation upon an image.
/// </summary>
/// <typeparam name="TResampler">The type of sampler.</typeparam>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="configuration">The configuration.</param>
/// <param name="sampler">The pixel sampler.</param>
/// <param name="source">The source image frame.</param>
/// <param name="destination">The destination image frame.</param>
/// <param name="matrix">The transform matrix.</param>
public static void ApplyAffineTransform<TResampler, TPixel>(
Configuration configuration,
in TResampler sampler,
ImageFrame<TPixel> source,
ImageFrame<TPixel> destination,
Matrix3x2 matrix)
where TResampler : unmanaged, IResampler
where TPixel : struct, IPixel<TPixel>
{
// Handle transforms that result in output identical to the original.
if (matrix.Equals(default) || matrix.Equals(Matrix3x2.Identity))
{
// The clone will be blank here copy all the pixel data over
source.GetPixelMemoryGroup().CopyTo(destination.GetPixelMemoryGroup());
return;
}
// Convert from screen to world space.
Matrix3x2.Invert(matrix, out matrix);
if (sampler is NearestNeighborResampler)
{
var nnOperation = new NNAffineOperation<TPixel>(source, destination, matrix);
ParallelRowIterator.IterateRows(
configuration,
destination.Bounds(),
in nnOperation);
return;
}
int yRadius = GetSamplingRadius(in sampler, source.Height, destination.Height);
int xRadius = GetSamplingRadius(in sampler, source.Width, destination.Width);
var radialExtents = new Vector2(xRadius, yRadius);
int yLength = (yRadius * 2) + 1;
int xLength = (xRadius * 2) + 1;
// We use 2D buffers so that we can access the weight spans per row in parallel.
using Buffer2D<float> yKernelBuffer = configuration.MemoryAllocator.Allocate2D<float>(yLength, destination.Height);
using Buffer2D<float> xKernelBuffer = configuration.MemoryAllocator.Allocate2D<float>(xLength, destination.Height);
int maxX = source.Width - 1;
int maxY = source.Height - 1;
var maxSourceExtents = new Vector4(maxX, maxY, maxX, maxY);
var operation = new AffineOperation<TResampler, TPixel>(
configuration,
source,
destination,
yKernelBuffer,
xKernelBuffer,
in sampler,
matrix,
radialExtents,
maxSourceExtents);
ParallelRowIterator.IterateRows<AffineOperation<TResampler, TPixel>, Vector4>(
configuration,
destination.Bounds(),
in operation);
}
/// <summary>
/// Applies a projective transformation upon an image.
/// </summary>
/// <typeparam name="TResampler">The type of sampler.</typeparam>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="configuration">The configuration.</param>
/// <param name="sampler">The pixel sampler.</param>
/// <param name="source">The source image frame.</param>
/// <param name="destination">The destination image frame.</param>
/// <param name="matrix">The transform matrix.</param>
public static void ApplyProjectiveTransform<TResampler, TPixel>(
Configuration configuration,
in TResampler sampler,
ImageFrame<TPixel> source,
ImageFrame<TPixel> destination,
Matrix4x4 matrix)
where TResampler : unmanaged, IResampler
where TPixel : struct, IPixel<TPixel>
{
// Handle transforms that result in output identical to the original.
if (matrix.Equals(default) || matrix.Equals(Matrix4x4.Identity))
{
// The clone will be blank here copy all the pixel data over
source.GetPixelMemoryGroup().CopyTo(destination.GetPixelMemoryGroup());
return;
}
// Convert from screen to world space.
Matrix4x4.Invert(matrix, out matrix);
if (sampler is NearestNeighborResampler)
{
var nnOperation = new NNProjectiveOperation<TPixel>(source, destination, matrix);
ParallelRowIterator.IterateRows(
configuration,
destination.Bounds(),
in nnOperation);
return;
}
int yRadius = GetSamplingRadius(in sampler, source.Height, destination.Height);
int xRadius = GetSamplingRadius(in sampler, source.Width, destination.Width);
var radialExtents = new Vector2(xRadius, yRadius);
int yLength = (yRadius * 2) + 1;
int xLength = (xRadius * 2) + 1;
// We use 2D buffers so that we can access the weight spans per row in parallel.
using Buffer2D<float> yKernelBuffer = configuration.MemoryAllocator.Allocate2D<float>(yLength, destination.Height);
using Buffer2D<float> xKernelBuffer = configuration.MemoryAllocator.Allocate2D<float>(xLength, destination.Height);
int maxX = source.Width - 1;
int maxY = source.Height - 1;
var maxSourceExtents = new Vector4(maxX, maxY, maxX, maxY);
var operation = new ProjectiveOperation<TResampler, TPixel>(
configuration,
source,
destination,
yKernelBuffer,
xKernelBuffer,
in sampler,
matrix,
radialExtents,
maxSourceExtents);
ParallelRowIterator.IterateRows<ProjectiveOperation<TResampler, TPixel>, Vector4>(
configuration,
destination.Bounds(),
in operation);
}
[MethodImpl(InliningOptions.ShortMethod)]
internal static void Convolve<TResampler, TPixel>(
in TResampler sampler,
Vector2 transformedPoint,
Buffer2D<TPixel> sourcePixels,
Span<Vector4> targetRow,
int column,
ref float yKernelSpanRef,
ref float xKernelSpanRef,
Vector2 radialExtents,
Vector4 maxSourceExtents)
where TResampler : unmanaged, IResampler
where TPixel : struct, IPixel<TPixel>
{
// Clamp sampling pixel radial extents to the source image edges
Vector2 minXY = transformedPoint - radialExtents;
Vector2 maxXY = transformedPoint + radialExtents;
// left, top, right, bottom
var sourceExtents = new Vector4(
MathF.Ceiling(minXY.X),
MathF.Ceiling(minXY.Y),
MathF.Floor(maxXY.X),
MathF.Floor(maxXY.Y));
sourceExtents = Vector4.Clamp(sourceExtents, Vector4.Zero, maxSourceExtents);
int left = (int)sourceExtents.X;
int top = (int)sourceExtents.Y;
int right = (int)sourceExtents.Z;
int bottom = (int)sourceExtents.W;
if (left == right || top == bottom)
{
return;
}
CalculateWeights(in sampler, top, bottom, transformedPoint.Y, ref yKernelSpanRef);
CalculateWeights(in sampler, left, right, transformedPoint.X, ref xKernelSpanRef);
Vector4 sum = Vector4.Zero;
for (int kernelY = 0, y = top; y <= bottom; y++, kernelY++)
{
float yWeight = Unsafe.Add(ref yKernelSpanRef, kernelY);
for (int kernelX = 0, x = left; x <= right; x++, kernelX++)
{
float xWeight = Unsafe.Add(ref xKernelSpanRef, kernelX);
// Values are first premultiplied to prevent darkening of edge pixels.
var current = sourcePixels[x, y].ToVector4();
Vector4Utils.Premultiply(ref current);
sum += current * xWeight * yWeight;
}
}
// Reverse the premultiplication
Vector4Utils.UnPremultiply(ref sum);
targetRow[column] = sum;
}
[MethodImpl(InliningOptions.ShortMethod)]
private static void CalculateWeights<TResampler>(in TResampler sampler, int min, int max, float point, ref float weightsRef)
where TResampler : unmanaged, IResampler
{
float sum = 0;
for (int x = 0, i = min; i <= max; i++, x++)
{
float weight = sampler.GetValue(i - point);
sum += weight;
Unsafe.Add(ref weightsRef, x) = weight;
}
}
[MethodImpl(InliningOptions.ShortMethod)]
private static int GetSamplingRadius<TResampler>(in TResampler sampler, int sourceSize, int destinationSize)
where TResampler : unmanaged, IResampler
{
double scale = sourceSize / destinationSize;
if (scale < 1)
{
scale = 1;
}
return (int)Math.Ceiling(scale * sampler.Radius);
}
}
}

47
src/ImageSharp/Processing/Processors/Transforms/Resamplers/BicubicResampler.cs

@ -1,7 +1,6 @@
// Copyright (c) Six Labors and contributors. // Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
using System.Numerics;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.PixelFormats;
@ -43,48 +42,8 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
/// <inheritdoc/> /// <inheritdoc/>
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
public void ApplyResizeTransform<TPixel>( public void ApplyTransform<TPixel>(IResamplingImageProcessor<TPixel> processor)
Configuration configuration, where TPixel : struct, IPixel<TPixel>
Image<TPixel> source, => processor.ApplyTransform(in this);
Image<TPixel> destination,
Rectangle sourceRectangle,
Rectangle destinationRectangle,
bool compand)
where TPixel : struct, IPixel<TPixel> => ResamplerExtensions.ApplyResizeTransform(
configuration,
in this,
source,
destination,
sourceRectangle,
destinationRectangle,
compand);
/// <inheritdoc/>
[MethodImpl(InliningOptions.ShortMethod)]
public void ApplyAffineTransform<TPixel>(
Configuration configuration,
ImageFrame<TPixel> source,
ImageFrame<TPixel> destination,
Matrix3x2 matrix)
where TPixel : struct, IPixel<TPixel> => ResamplerExtensions.ApplyAffineTransform(
configuration,
in this,
source,
destination,
matrix);
/// <inheritdoc/>
[MethodImpl(InliningOptions.ShortMethod)]
public void ApplyProjectiveTransform<TPixel>(
Configuration configuration,
ImageFrame<TPixel> source,
ImageFrame<TPixel> destination,
Matrix4x4 matrix)
where TPixel : struct, IPixel<TPixel> => ResamplerExtensions.ApplyProjectiveTransform(
configuration,
in this,
source,
destination,
matrix);
} }
} }

47
src/ImageSharp/Processing/Processors/Transforms/Resamplers/BoxResampler.cs

@ -1,7 +1,6 @@
// Copyright (c) Six Labors and contributors. // Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
using System.Numerics;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.PixelFormats;
@ -30,48 +29,8 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
/// <inheritdoc/> /// <inheritdoc/>
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
public void ApplyResizeTransform<TPixel>( public void ApplyTransform<TPixel>(IResamplingImageProcessor<TPixel> processor)
Configuration configuration, where TPixel : struct, IPixel<TPixel>
Image<TPixel> source, => processor.ApplyTransform(in this);
Image<TPixel> destination,
Rectangle sourceRectangle,
Rectangle destinationRectangle,
bool compand)
where TPixel : struct, IPixel<TPixel> => ResamplerExtensions.ApplyResizeTransform(
configuration,
in this,
source,
destination,
sourceRectangle,
destinationRectangle,
compand);
/// <inheritdoc/>
[MethodImpl(InliningOptions.ShortMethod)]
public void ApplyAffineTransform<TPixel>(
Configuration configuration,
ImageFrame<TPixel> source,
ImageFrame<TPixel> destination,
Matrix3x2 matrix)
where TPixel : struct, IPixel<TPixel> => ResamplerExtensions.ApplyAffineTransform(
configuration,
in this,
source,
destination,
matrix);
/// <inheritdoc/>
[MethodImpl(InliningOptions.ShortMethod)]
public void ApplyProjectiveTransform<TPixel>(
Configuration configuration,
ImageFrame<TPixel> source,
ImageFrame<TPixel> destination,
Matrix4x4 matrix)
where TPixel : struct, IPixel<TPixel> => ResamplerExtensions.ApplyProjectiveTransform(
configuration,
in this,
source,
destination,
matrix);
} }
} }

47
src/ImageSharp/Processing/Processors/Transforms/Resamplers/CubicResampler.cs

@ -1,7 +1,6 @@
// Copyright (c) Six Labors and contributors. // Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
using System.Numerics;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.PixelFormats;
@ -106,48 +105,8 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
/// <inheritdoc/> /// <inheritdoc/>
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
public void ApplyResizeTransform<TPixel>( public void ApplyTransform<TPixel>(IResamplingImageProcessor<TPixel> processor)
Configuration configuration, where TPixel : struct, IPixel<TPixel>
Image<TPixel> source, => processor.ApplyTransform(in this);
Image<TPixel> destination,
Rectangle sourceRectangle,
Rectangle destinationRectangle,
bool compand)
where TPixel : struct, IPixel<TPixel> => ResamplerExtensions.ApplyResizeTransform(
configuration,
in this,
source,
destination,
sourceRectangle,
destinationRectangle,
compand);
/// <inheritdoc/>
[MethodImpl(InliningOptions.ShortMethod)]
public void ApplyAffineTransform<TPixel>(
Configuration configuration,
ImageFrame<TPixel> source,
ImageFrame<TPixel> destination,
Matrix3x2 matrix)
where TPixel : struct, IPixel<TPixel> => ResamplerExtensions.ApplyAffineTransform(
configuration,
in this,
source,
destination,
matrix);
/// <inheritdoc/>
[MethodImpl(InliningOptions.ShortMethod)]
public void ApplyProjectiveTransform<TPixel>(
Configuration configuration,
ImageFrame<TPixel> source,
ImageFrame<TPixel> destination,
Matrix4x4 matrix)
where TPixel : struct, IPixel<TPixel> => ResamplerExtensions.ApplyProjectiveTransform(
configuration,
in this,
source,
destination,
matrix);
} }
} }

47
src/ImageSharp/Processing/Processors/Transforms/Resamplers/LanczosResampler.cs

@ -1,7 +1,6 @@
// Copyright (c) Six Labors and contributors. // Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
using System.Numerics;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.PixelFormats;
@ -62,48 +61,8 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
/// <inheritdoc/> /// <inheritdoc/>
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
public void ApplyResizeTransform<TPixel>( public void ApplyTransform<TPixel>(IResamplingImageProcessor<TPixel> processor)
Configuration configuration, where TPixel : struct, IPixel<TPixel>
Image<TPixel> source, => processor.ApplyTransform(in this);
Image<TPixel> destination,
Rectangle sourceRectangle,
Rectangle destinationRectangle,
bool compand)
where TPixel : struct, IPixel<TPixel> => ResamplerExtensions.ApplyResizeTransform(
configuration,
in this,
source,
destination,
sourceRectangle,
destinationRectangle,
compand);
/// <inheritdoc/>
[MethodImpl(InliningOptions.ShortMethod)]
public void ApplyAffineTransform<TPixel>(
Configuration configuration,
ImageFrame<TPixel> source,
ImageFrame<TPixel> destination,
Matrix3x2 matrix)
where TPixel : struct, IPixel<TPixel> => ResamplerExtensions.ApplyAffineTransform(
configuration,
in this,
source,
destination,
matrix);
/// <inheritdoc/>
[MethodImpl(InliningOptions.ShortMethod)]
public void ApplyProjectiveTransform<TPixel>(
Configuration configuration,
ImageFrame<TPixel> source,
ImageFrame<TPixel> destination,
Matrix4x4 matrix)
where TPixel : struct, IPixel<TPixel> => ResamplerExtensions.ApplyProjectiveTransform(
configuration,
in this,
source,
destination,
matrix);
} }
} }

47
src/ImageSharp/Processing/Processors/Transforms/Resamplers/NearestNeighborResampler.cs

@ -1,7 +1,6 @@
// Copyright (c) Six Labors and contributors. // Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
using System.Numerics;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.PixelFormats;
@ -22,48 +21,8 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
/// <inheritdoc/> /// <inheritdoc/>
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
public void ApplyResizeTransform<TPixel>( public void ApplyTransform<TPixel>(IResamplingImageProcessor<TPixel> processor)
Configuration configuration, where TPixel : struct, IPixel<TPixel>
Image<TPixel> source, => processor.ApplyTransform(in this);
Image<TPixel> destination,
Rectangle sourceRectangle,
Rectangle destinationRectangle,
bool compand)
where TPixel : struct, IPixel<TPixel> => ResamplerExtensions.ApplyResizeTransform(
configuration,
in this,
source,
destination,
sourceRectangle,
destinationRectangle,
compand);
/// <inheritdoc/>
[MethodImpl(InliningOptions.ShortMethod)]
public void ApplyAffineTransform<TPixel>(
Configuration configuration,
ImageFrame<TPixel> source,
ImageFrame<TPixel> destination,
Matrix3x2 matrix)
where TPixel : struct, IPixel<TPixel> => ResamplerExtensions.ApplyAffineTransform(
configuration,
in this,
source,
destination,
matrix);
/// <inheritdoc/>
[MethodImpl(InliningOptions.ShortMethod)]
public void ApplyProjectiveTransform<TPixel>(
Configuration configuration,
ImageFrame<TPixel> source,
ImageFrame<TPixel> destination,
Matrix4x4 matrix)
where TPixel : struct, IPixel<TPixel> => ResamplerExtensions.ApplyProjectiveTransform(
configuration,
in this,
source,
destination,
matrix);
} }
} }

47
src/ImageSharp/Processing/Processors/Transforms/Resamplers/TriangleResampler.cs

@ -1,7 +1,6 @@
// Copyright (c) Six Labors and contributors. // Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
using System.Numerics;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.PixelFormats;
@ -36,48 +35,8 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
/// <inheritdoc/> /// <inheritdoc/>
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
public void ApplyResizeTransform<TPixel>( public void ApplyTransform<TPixel>(IResamplingImageProcessor<TPixel> processor)
Configuration configuration, where TPixel : struct, IPixel<TPixel>
Image<TPixel> source, => processor.ApplyTransform(in this);
Image<TPixel> destination,
Rectangle sourceRectangle,
Rectangle destinationRectangle,
bool compand)
where TPixel : struct, IPixel<TPixel> => ResamplerExtensions.ApplyResizeTransform(
configuration,
in this,
source,
destination,
sourceRectangle,
destinationRectangle,
compand);
/// <inheritdoc/>
[MethodImpl(InliningOptions.ShortMethod)]
public void ApplyAffineTransform<TPixel>(
Configuration configuration,
ImageFrame<TPixel> source,
ImageFrame<TPixel> destination,
Matrix3x2 matrix)
where TPixel : struct, IPixel<TPixel> => ResamplerExtensions.ApplyAffineTransform(
configuration,
in this,
source,
destination,
matrix);
/// <inheritdoc/>
[MethodImpl(InliningOptions.ShortMethod)]
public void ApplyProjectiveTransform<TPixel>(
Configuration configuration,
ImageFrame<TPixel> source,
ImageFrame<TPixel> destination,
Matrix4x4 matrix)
where TPixel : struct, IPixel<TPixel> => ResamplerExtensions.ApplyProjectiveTransform(
configuration,
in this,
source,
destination,
matrix);
} }
} }

47
src/ImageSharp/Processing/Processors/Transforms/Resamplers/WelchResampler.cs

@ -1,7 +1,6 @@
// Copyright (c) Six Labors and contributors. // Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
using System.Numerics;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.PixelFormats;
@ -35,48 +34,8 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
/// <inheritdoc/> /// <inheritdoc/>
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
public void ApplyResizeTransform<TPixel>( public void ApplyTransform<TPixel>(IResamplingImageProcessor<TPixel> processor)
Configuration configuration, where TPixel : struct, IPixel<TPixel>
Image<TPixel> source, => processor.ApplyTransform(in this);
Image<TPixel> destination,
Rectangle sourceRectangle,
Rectangle destinationRectangle,
bool compand)
where TPixel : struct, IPixel<TPixel> => ResamplerExtensions.ApplyResizeTransform(
configuration,
in this,
source,
destination,
sourceRectangle,
destinationRectangle,
compand);
/// <inheritdoc/>
[MethodImpl(InliningOptions.ShortMethod)]
public void ApplyAffineTransform<TPixel>(
Configuration configuration,
ImageFrame<TPixel> source,
ImageFrame<TPixel> destination,
Matrix3x2 matrix)
where TPixel : struct, IPixel<TPixel> => ResamplerExtensions.ApplyAffineTransform(
configuration,
in this,
source,
destination,
matrix);
/// <inheritdoc/>
[MethodImpl(InliningOptions.ShortMethod)]
public void ApplyProjectiveTransform<TPixel>(
Configuration configuration,
ImageFrame<TPixel> source,
ImageFrame<TPixel> destination,
Matrix4x4 matrix)
where TPixel : struct, IPixel<TPixel> => ResamplerExtensions.ApplyProjectiveTransform(
configuration,
in this,
source,
destination,
matrix);
} }
} }

6
src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeKernelMap.cs

@ -116,7 +116,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
int destinationSize, int destinationSize,
int sourceSize, int sourceSize,
MemoryAllocator memoryAllocator) MemoryAllocator memoryAllocator)
where TResampler : unmanaged, IResampler where TResampler : struct, IResampler
{ {
double ratio = (double)sourceSize / destinationSize; double ratio = (double)sourceSize / destinationSize;
double scale = ratio; double scale = ratio;
@ -182,7 +182,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
/// Initializes the kernel map. /// Initializes the kernel map.
/// </summary> /// </summary>
protected internal virtual void Initialize<TResampler>(in TResampler sampler) protected internal virtual void Initialize<TResampler>(in TResampler sampler)
where TResampler : unmanaged, IResampler where TResampler : struct, IResampler
{ {
for (int i = 0; i < this.DestinationLength; i++) for (int i = 0; i < this.DestinationLength; i++)
{ {
@ -196,7 +196,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
/// so the data reusable by other data rows. /// so the data reusable by other data rows.
/// </summary> /// </summary>
private ResizeKernel BuildKernel<TResampler>(in TResampler sampler, int destRowIndex, int dataRowIndex) private ResizeKernel BuildKernel<TResampler>(in TResampler sampler, int destRowIndex, int dataRowIndex)
where TResampler : unmanaged, IResampler where TResampler : struct, IResampler
{ {
double center = ((destRowIndex + .5) * this.ratio) - .5; double center = ((destRowIndex + .5) * this.ratio) - .5;

1
src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor.cs

@ -17,6 +17,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
{ {
Guard.NotNull(options, nameof(options)); Guard.NotNull(options, nameof(options));
Guard.NotNull(options.Sampler, nameof(options.Sampler)); Guard.NotNull(options.Sampler, nameof(options.Sampler));
Guard.MustBeValueType(options.Sampler, nameof(options.Sampler));
(Size size, Rectangle rectangle) = ResizeHelper.CalculateTargetLocationAndBounds(sourceSize, options); (Size size, Rectangle rectangle) = ResizeHelper.CalculateTargetLocationAndBounds(sourceSize, options);

22
src/ImageSharp/Processing/Processors/Transforms/Resize/ResamplerExtensions.cs → src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor{TPixel}.Transforms.cs

@ -10,15 +10,15 @@ using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp.Processing.Processors.Transforms namespace SixLabors.ImageSharp.Processing.Processors.Transforms
{ {
/// <content> /// <content>
/// Extensions for <see cref="IResampler"/>. /// Contains the application code for resizing.
/// </content> /// </content>
public static partial class ResamplerExtensions internal partial class ResizeProcessor<TPixel>
where TPixel : struct, IPixel<TPixel>
{ {
/// <summary> /// <summary>
/// Applies an resizing transformation upon an image. /// Applies an resizing transformation upon an image.
/// </summary> /// </summary>
/// <typeparam name="TResampler">The type of sampler.</typeparam> /// <typeparam name="TResampler">The type of sampler.</typeparam>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="configuration">The configuration.</param> /// <param name="configuration">The configuration.</param>
/// <param name="sampler">The pixel sampler.</param> /// <param name="sampler">The pixel sampler.</param>
/// <param name="source">The source image.</param> /// <param name="source">The source image.</param>
@ -26,7 +26,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
/// <param name="sourceRectangle">The source bounds.</param> /// <param name="sourceRectangle">The source bounds.</param>
/// <param name="destinationRectangle">The destination location.</param> /// <param name="destinationRectangle">The destination location.</param>
/// <param name="compand">Whether to compress or expand individual pixel color values on processing.</param> /// <param name="compand">Whether to compress or expand individual pixel color values on processing.</param>
public static void ApplyResizeTransform<TResampler, TPixel>( public static void ApplyResizeTransform<TResampler>(
Configuration configuration, Configuration configuration,
in TResampler sampler, in TResampler sampler,
Image<TPixel> source, Image<TPixel> source,
@ -34,8 +34,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
Rectangle sourceRectangle, Rectangle sourceRectangle,
Rectangle destinationRectangle, Rectangle destinationRectangle,
bool compand) bool compand)
where TResampler : unmanaged, IResampler where TResampler : struct, IResampler
where TPixel : struct, IPixel<TPixel>
{ {
// Handle resize dimensions identical to the original // Handle resize dimensions identical to the original
if (source.Width == destination.Width if (source.Width == destination.Width
@ -108,20 +107,19 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
} }
} }
private static void ApplyNNResizeFrameTransform<TPixel>( private static void ApplyNNResizeFrameTransform(
Configuration configuration, Configuration configuration,
ImageFrame<TPixel> source, ImageFrame<TPixel> source,
ImageFrame<TPixel> destination, ImageFrame<TPixel> destination,
Rectangle sourceRectangle, Rectangle sourceRectangle,
Rectangle destinationRectangle, Rectangle destinationRectangle,
Rectangle interest) Rectangle interest)
where TPixel : struct, IPixel<TPixel>
{ {
// Scaling factors // Scaling factors
float widthFactor = sourceRectangle.Width / (float)destinationRectangle.Width; float widthFactor = sourceRectangle.Width / (float)destinationRectangle.Width;
float heightFactor = sourceRectangle.Height / (float)destinationRectangle.Height; float heightFactor = sourceRectangle.Height / (float)destinationRectangle.Height;
var operation = new NNRowIntervalOperation<TPixel>( var operation = new NNRowIntervalOperation(
sourceRectangle, sourceRectangle,
destinationRectangle, destinationRectangle,
widthFactor, widthFactor,
@ -135,7 +133,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
in operation); in operation);
} }
private static void ApplyResizeFrameTransform<TPixel>( private static void ApplyResizeFrameTransform(
Configuration configuration, Configuration configuration,
ImageFrame<TPixel> source, ImageFrame<TPixel> source,
ImageFrame<TPixel> destination, ImageFrame<TPixel> destination,
@ -145,7 +143,6 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
Rectangle destinationRectangle, Rectangle destinationRectangle,
Rectangle interest, Rectangle interest,
bool compand) bool compand)
where TPixel : struct, IPixel<TPixel>
{ {
PixelConversionModifiers conversionModifiers = PixelConversionModifiers conversionModifiers =
PixelConversionModifiers.Premultiply.ApplyCompanding(compand); PixelConversionModifiers.Premultiply.ApplyCompanding(compand);
@ -171,8 +168,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
} }
} }
private readonly struct NNRowIntervalOperation<TPixel> : IRowIntervalOperation private readonly struct NNRowIntervalOperation : IRowIntervalOperation
where TPixel : struct, IPixel<TPixel>
{ {
private readonly Rectangle sourceBounds; private readonly Rectangle sourceBounds;
private readonly Rectangle destinationBounds; private readonly Rectangle destinationBounds;

23
src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor{TPixel}.cs

@ -9,7 +9,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
/// Implements resizing of images using various resamplers. /// Implements resizing of images using various resamplers.
/// </summary> /// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam> /// <typeparam name="TPixel">The pixel format.</typeparam>
internal class ResizeProcessor<TPixel> : TransformProcessor<TPixel> internal partial class ResizeProcessor<TPixel> : TransformProcessor<TPixel>, IResamplingImageProcessor<TPixel>
where TPixel : struct, IPixel<TPixel> where TPixel : struct, IPixel<TPixel>
{ {
private readonly int destinationWidth; private readonly int destinationWidth;
@ -17,6 +17,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
private readonly IResampler resampler; private readonly IResampler resampler;
private readonly Rectangle destinationRectangle; private readonly Rectangle destinationRectangle;
private readonly bool compand; private readonly bool compand;
private Image<TPixel> destination;
public ResizeProcessor(Configuration configuration, ResizeProcessor definition, Image<TPixel> source, Rectangle sourceRectangle) public ResizeProcessor(Configuration configuration, ResizeProcessor definition, Image<TPixel> source, Rectangle sourceRectangle)
: base(configuration, source, sourceRectangle) : base(configuration, source, sourceRectangle)
@ -34,13 +35,8 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
/// <inheritdoc/> /// <inheritdoc/>
protected override void BeforeImageApply(Image<TPixel> destination) protected override void BeforeImageApply(Image<TPixel> destination)
{ {
this.resampler.ApplyResizeTransform( this.destination = destination;
this.Configuration, this.resampler.ApplyTransform(this);
this.Source,
destination,
this.SourceRectangle,
this.destinationRectangle,
this.compand);
base.BeforeImageApply(destination); base.BeforeImageApply(destination);
} }
@ -50,5 +46,16 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
{ {
// Everything happens in BeforeImageApply. // Everything happens in BeforeImageApply.
} }
public void ApplyTransform<TResampler>(in TResampler sampler)
where TResampler : struct, IResampler =>
ApplyResizeTransform(
this.Configuration,
in sampler,
this.Source,
this.destination,
this.SourceRectangle,
this.destinationRectangle,
this.compand);
} }
} }

2
tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeKernelMapTests.ReferenceKernelMap.cs

@ -27,7 +27,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Transforms
public ReferenceKernel GetKernel(int destinationIndex) => this.kernels[destinationIndex]; public ReferenceKernel GetKernel(int destinationIndex) => this.kernels[destinationIndex];
public static ReferenceKernelMap Calculate<TResampler>(in TResampler sampler, int destinationSize, int sourceSize, bool normalize = true) public static ReferenceKernelMap Calculate<TResampler>(in TResampler sampler, int destinationSize, int sourceSize, bool normalize = true)
where TResampler : unmanaged, IResampler where TResampler : struct, IResampler
{ {
double ratio = (double)sourceSize / destinationSize; double ratio = (double)sourceSize / destinationSize;
double scale = ratio; double scale = ratio;

6
tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeKernelMapTests.cs

@ -87,7 +87,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Transforms
[Theory(Skip = "Only for debugging and development")] [Theory(Skip = "Only for debugging and development")]
[MemberData(nameof(KernelMapData))] [MemberData(nameof(KernelMapData))]
public void PrintNonNormalizedKernelMap<TResampler>(TResampler resampler, int srcSize, int destSize) public void PrintNonNormalizedKernelMap<TResampler>(TResampler resampler, int srcSize, int destSize)
where TResampler : unmanaged, IResampler where TResampler : struct, IResampler
{ {
var kernelMap = ReferenceKernelMap.Calculate<TResampler>(in resampler, destSize, srcSize, false); var kernelMap = ReferenceKernelMap.Calculate<TResampler>(in resampler, destSize, srcSize, false);
@ -97,7 +97,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Transforms
[Theory] [Theory]
[MemberData(nameof(KernelMapData))] [MemberData(nameof(KernelMapData))]
public void KernelMapContentIsCorrect<TResampler>(TResampler resampler, int srcSize, int destSize) public void KernelMapContentIsCorrect<TResampler>(TResampler resampler, int srcSize, int destSize)
where TResampler : unmanaged, IResampler where TResampler : struct, IResampler
{ {
this.VerifyKernelMapContentIsCorrect(resampler, srcSize, destSize); this.VerifyKernelMapContentIsCorrect(resampler, srcSize, destSize);
} }
@ -115,7 +115,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Transforms
#endif #endif
private void VerifyKernelMapContentIsCorrect<TResampler>(TResampler resampler, int srcSize, int destSize) private void VerifyKernelMapContentIsCorrect<TResampler>(TResampler resampler, int srcSize, int destSize)
where TResampler : unmanaged, IResampler where TResampler : struct, IResampler
{ {
var referenceMap = ReferenceKernelMap.Calculate(in resampler, destSize, srcSize); var referenceMap = ReferenceKernelMap.Calculate(in resampler, destSize, srcSize);
var kernelMap = ResizeKernelMap.Calculate(in resampler, destSize, srcSize, Configuration.Default.MemoryAllocator); var kernelMap = ResizeKernelMap.Calculate(in resampler, destSize, srcSize, Configuration.Default.MemoryAllocator);

Loading…
Cancel
Save