Browse Source

Fix affine transforms and cleanup

pull/386/head
James Jackson-South 8 years ago
parent
commit
cd0d443e72
  1. 32
      src/ImageSharp/Processing/Processors/Transforms/AffineProcessor.cs
  2. 14
      src/ImageSharp/Processing/Processors/Transforms/ResizeProcessor.cs
  3. 10
      src/ImageSharp/Processing/Processors/Transforms/RotateProcessor.cs
  4. 5
      src/ImageSharp/Processing/Processors/Transforms/SkewProcessor.cs
  5. 2
      src/ImageSharp/Processing/Transforms/Options/ResizeOptions.cs
  6. 96
      src/ImageSharp/Processing/Transforms/Resamplers/KnownResamplers.cs
  7. 8
      src/ImageSharp/Processing/Transforms/Resize.cs
  8. 31
      src/ImageSharp/Processing/Transforms/Rotate.cs
  9. 33
      src/ImageSharp/Processing/Transforms/Skew.cs
  10. 2
      tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeProfilingBenchmarks.cs
  11. 37
      tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeTests.cs
  12. 13
      tests/ImageSharp.Tests/Processing/Processors/Transforms/RotateTests.cs
  13. 14
      tests/ImageSharp.Tests/Processing/Processors/Transforms/SkewTest.cs

32
src/ImageSharp/Processing/Processors/Transforms/AffineProcessor.cs

@ -2,6 +2,7 @@
// Licensed under the Apache License, Version 2.0.
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Numerics;
using System.Runtime.CompilerServices;
@ -18,15 +19,12 @@ namespace SixLabors.ImageSharp.Processing.Processors
internal abstract class AffineProcessor<TPixel> : ResamplingWeightedProcessor<TPixel>
where TPixel : struct, IPixel<TPixel>
{
// TODO: Move to constants somewhere else to prevent generic type duplication.
private static readonly Rectangle DefaultRectangle = new Rectangle(0, 0, 1, 1);
/// <summary>
/// Initializes a new instance of the <see cref="AffineProcessor{TPixel}"/> class.
/// </summary>
/// <param name="sampler">The sampler to perform the resize operation.</param>
protected AffineProcessor(IResampler sampler)
: base(sampler, 1, 1, DefaultRectangle) // Hack to prevent Guard throwing in base, we always set the canvas
: base(sampler, 1, 1, Rectangles.DefaultRectangle) // Hack to prevent Guard throwing in base, we always set the canvas
{
}
@ -47,7 +45,7 @@ namespace SixLabors.ImageSharp.Processing.Processors
/// <param name="sourceRectangle">The source rectangle.</param>
protected virtual void CreateNewCanvas(Rectangle sourceRectangle)
{
if (this.ResizeRectangle == DefaultRectangle)
if (this.ResizeRectangle == Rectangles.DefaultRectangle)
{
if (this.Expand)
{
@ -82,15 +80,14 @@ namespace SixLabors.ImageSharp.Processing.Processors
/// Gets a transform matrix adjusted to center upon the target image bounds.
/// </summary>
/// <param name="source">The source image.</param>
/// <param name="matrix">The transform matrix.</param>
/// <returns>
/// The <see cref="Matrix3x2"/>.
/// </returns>
protected Matrix3x2 GetCenteredMatrix(ImageFrame<TPixel> source, Matrix3x2 matrix)
protected Matrix3x2 GetCenteredMatrix(ImageFrame<TPixel> source)
{
var translationToTargetCenter = Matrix3x2.CreateTranslation(-this.ResizeRectangle.Width * .5F, -this.ResizeRectangle.Height * .5F);
var translateToSourceCenter = Matrix3x2.CreateTranslation(source.Width * .5F, source.Height * .5F);
return (translationToTargetCenter * matrix) * translateToSourceCenter;
return (translationToTargetCenter * this.CreateProcessingMatrix()) * translateToSourceCenter;
}
/// <summary>
@ -108,25 +105,20 @@ namespace SixLabors.ImageSharp.Processing.Processors
{
ref float horizontalValues = ref windowX.GetStartReference();
ref float verticalValues = ref windowY.GetStartReference();
int xLeft = windowX.Left;
int yLeft = windowY.Left;
int xLength = windowX.Length;
int yLength = windowY.Length;
Vector4 result = Vector4.Zero;
// TODO: Fix this.
// The output for skew is shrunken, offset, with right/bottom banding.
// For rotate values are offset
for (int y = 0; y < yLength; y++)
{
float yweight = Unsafe.Add(ref verticalValues, y);
int offsetY = yLeft + y + point.Y;
int offsetY = y + point.Y;
offsetY = offsetY.Clamp(0, maxY);
for (int x = 0; x < xLength; x++)
{
float xweight = Unsafe.Add(ref horizontalValues, x);
int offsetX = xLeft + x + point.X;
int offsetX = x + point.X;
offsetX = offsetX.Clamp(0, maxX);
float weight = yweight * xweight;
@ -137,4 +129,14 @@ namespace SixLabors.ImageSharp.Processing.Processors
return result;
}
}
/// <summary>
/// Contains a static rectangle used for comparison when creating a new canvas.
/// We do this so we can inherit from the resampling weights class and pass the guard in the constructor and also avoid creating a new rectangle each time.
/// </summary>
[SuppressMessage("StyleCop.CSharp.MaintainabilityRules", "SA1402:FileMayOnlyContainASingleType", Justification = "I'm using this only here to prevent duplication in generic types.")]
internal static class Rectangles
{
public static Rectangle DefaultRectangle { get; } = new Rectangle(0, 0, 1, 1);
}
}

14
src/ImageSharp/Processing/Processors/Transforms/ResizeProcessor.cs

@ -53,16 +53,12 @@ namespace SixLabors.ImageSharp.Processing.Processors
/// <inheritdoc/>
protected override Image<TPixel> CreateDestination(Image<TPixel> source, Rectangle sourceRectangle)
{
Configuration config = source.GetConfiguration();
// We will always be creating the clone even for mutate because we may need to resize the canvas
IEnumerable<ImageFrame<TPixel>> frames =
source.Frames.Select(x => new ImageFrame<TPixel>(this.Width, this.Height, x.MetaData.Clone()));
// We will always be creating the clone even for mutate because thats the way this base processor works
// ------------
// For resize we know we are going to populate every pixel with fresh data and we want a different target size so
// let's manually clone an empty set of images at the correct target and then have the base class processs them in turn.
IEnumerable<ImageFrame<TPixel>> frames = source.Frames.Select(x => new ImageFrame<TPixel>(this.Width, this.Height, x.MetaData.Clone())); // this will create places holders
var image = new Image<TPixel>(config, source.MetaData.Clone(), frames); // base the place holder images in to prevet a extra frame being added
return image;
// Use the overload to prevent an extra frame being added
return new Image<TPixel>(source.GetConfiguration(), source.MetaData.Clone(), frames);
}
/// <inheritdoc/>

10
src/ImageSharp/Processing/Processors/Transforms/RotateProcessor.cs

@ -25,14 +25,14 @@ namespace SixLabors.ImageSharp.Processing.Processors
/// Initializes a new instance of the <see cref="RotateProcessor{TPixel}"/> class.
/// </summary>
public RotateProcessor()
: base(new BicubicResampler())
: base(KnownResamplers.NearestNeighbor)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="RotateProcessor{TPixel}"/> class.
/// </summary>
/// <param name="sampler">The sampler to perform the resize operation.</param>
/// <param name="sampler">The sampler to perform the rotating operation.</param>
public RotateProcessor(IResampler sampler)
: base(sampler)
{
@ -48,7 +48,7 @@ namespace SixLabors.ImageSharp.Processing.Processors
{
if (this.transformMatrix == default(Matrix3x2))
{
this.transformMatrix = Matrix3x2Extensions.CreateRotationDegrees(-this.Angle, new Point(0, 0));
this.transformMatrix = Matrix3x2Extensions.CreateRotationDegrees(-this.Angle, PointF.Empty);
}
return this.transformMatrix;
@ -83,7 +83,7 @@ namespace SixLabors.ImageSharp.Processing.Processors
int height = this.ResizeRectangle.Height;
int width = this.ResizeRectangle.Width;
Matrix3x2 matrix = this.GetCenteredMatrix(source, this.CreateProcessingMatrix());
Matrix3x2 matrix = this.GetCenteredMatrix(source);
Rectangle sourceBounds = source.Bounds();
if (this.Sampler is NearestNeighborResampler)
@ -99,7 +99,6 @@ namespace SixLabors.ImageSharp.Processing.Processors
for (int x = 0; x < width; x++)
{
var transformedPoint = Point.Rotate(new Point(x, y), matrix);
if (sourceBounds.Contains(transformedPoint.X, transformedPoint.Y))
{
destRow[x] = source[transformedPoint.X, transformedPoint.Y];
@ -127,6 +126,7 @@ namespace SixLabors.ImageSharp.Processing.Processors
{
WeightsWindow windowX = this.HorizontalWeights.Weights[transformedPoint.X];
WeightsWindow windowY = this.VerticalWeights.Weights[transformedPoint.Y];
Vector4 dXY = this.ComputeWeightedSumAtPosition(source, maxX, maxY, ref windowX, ref windowY, ref transformedPoint);
ref TPixel dest = ref destRow[x];
dest.PackFromVector4(dXY);

5
src/ImageSharp/Processing/Processors/Transforms/SkewProcessor.cs

@ -44,7 +44,7 @@ namespace SixLabors.ImageSharp.Processing.Processors
{
if (this.transformMatrix == default(Matrix3x2))
{
this.transformMatrix = Matrix3x2Extensions.CreateSkewDegrees(-this.AngleX, -this.AngleY, new Point(0, 0));
this.transformMatrix = Matrix3x2Extensions.CreateSkewDegrees(-this.AngleX, -this.AngleY, PointF.Empty);
}
return this.transformMatrix;
@ -55,7 +55,7 @@ namespace SixLabors.ImageSharp.Processing.Processors
{
int height = this.ResizeRectangle.Height;
int width = this.ResizeRectangle.Width;
Matrix3x2 matrix = this.GetCenteredMatrix(source, this.CreateProcessingMatrix());
Matrix3x2 matrix = this.GetCenteredMatrix(source);
Rectangle sourceBounds = source.Bounds();
if (this.Sampler is NearestNeighborResampler)
@ -71,7 +71,6 @@ namespace SixLabors.ImageSharp.Processing.Processors
for (int x = 0; x < width; x++)
{
var transformedPoint = Point.Skew(new Point(x, y), matrix);
if (sourceBounds.Contains(transformedPoint.X, transformedPoint.Y))
{
destRow[x] = source[transformedPoint.X, transformedPoint.Y];

2
src/ImageSharp/Processing/Transforms/Options/ResizeOptions.cs

@ -35,7 +35,7 @@ namespace SixLabors.ImageSharp.Processing
/// <summary>
/// Gets or sets the sampler to perform the resize operation.
/// </summary>
public IResampler Sampler { get; set; } = new BicubicResampler();
public IResampler Sampler { get; set; } = KnownResamplers.Bicubic;
/// <summary>
/// Gets or sets a value indicating whether to compress

96
src/ImageSharp/Processing/Transforms/Resamplers/KnownResamplers.cs

@ -0,0 +1,96 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
namespace SixLabors.ImageSharp.Processing
{
/// <summary>
/// Contains reusable static instances of known resampling algorithms
/// </summary>
public static class KnownResamplers
{
/// <summary>
/// Gets the Bicubic sampler that implements the bicubic kernel algorithm W(x)
/// </summary>
public static IResampler Bicubic { get; } = new BicubicResampler();
/// <summary>
/// Gets the Box sampler that implements the box algorithm. Similar to nearest neighbor when upscaling.
/// When downscaling the pixels will average, merging pixels together.
/// </summary>
public static IResampler Box { get; } = new BoxResampler();
/// <summary>
/// Gets the Catmull-Rom sampler, a well known standard Cubic Filter often used as a interpolation function
/// </summary>
public static IResampler CatmullRom { get; } = new CatmullRomResampler();
/// <summary>
/// Gets the Hermite sampler. A type of smoothed triangular interpolation filter that rounds off strong edges while
/// preserving flat 'color levels' in the original image.
/// </summary>
public static IResampler Hermite { get; } = new HermiteResampler();
/// <summary>
/// Gets the Lanczos kernel sampler that implements smooth interpolation with a radius of 2 pixels.
/// This algorithm provides sharpened results when compared to others when downsampling.
/// </summary>
public static IResampler Lanczos2 { get; } = new Lanczos2Resampler();
/// <summary>
/// Gets the Lanczos kernel sampler that implements smooth interpolation with a radius of 3 pixels
/// This algorithm provides sharpened results when compared to others when downsampling.
/// </summary>
public static IResampler Lanczos3 { get; } = new Lanczos3Resampler();
/// <summary>
/// Gets the Lanczos kernel sampler that implements smooth interpolation with a radius of 5 pixels
/// This algorithm provides sharpened results when compared to others when downsampling.
/// </summary>
public static IResampler Lanczos5 { get; } = new Lanczos5Resampler();
/// <summary>
/// Gets the Lanczos kernel sampler that implements smooth interpolation with a radius of 8 pixels
/// This algorithm provides sharpened results when compared to others when downsampling.
/// </summary>
public static IResampler Lanczos8 { get; } = new Lanczos8Resampler();
/// <summary>
/// Gets the Mitchell-Netravali sampler. This seperable cubic algorithm yields a very good equilibrium between
/// detail preservation (sharpness) and smoothness.
/// </summary>
public static IResampler MitchellNetravali { get; } = new MitchellNetravaliResampler();
/// <summary>
/// Gets the Nearest-Neighbour sampler that implements the nearest neighbor algorithm. This uses a very fast, unscaled filter
/// which will select the closest pixel to the new pixels position.
/// </summary>
public static IResampler NearestNeighbor { get; } = new NearestNeighborResampler();
/// <summary>
/// Gets the Robidoux sampler. This algorithm developed by Nicolas Robidoux providing a very good equilibrium between
/// detail preservation (sharpness) and smoothness comprable to <see cref="MitchellNetravali"/>.
/// </summary>
public static IResampler Robidoux { get; } = new RobidouxResampler();
/// <summary>
/// Gets the Robidoux Sharp sampler. A sharpend form of the <see cref="Robidoux"/> sampler
/// </summary>
public static IResampler RobidouxSharp { get; } = new RobidouxResampler();
/// <summary>
/// Gets the Spline sampler. A seperable cubic algorithm similar to <see cref="MitchellNetravali"/> but yielding smoother results.
/// </summary>
public static IResampler Spline { get; } = new SplineResampler();
/// <summary>
/// Gets the Triangle sampler, otherwise known as Bilinear. This interpolation algorithm can be used where perfect image transformation
/// with pixel matching is impossible, so that one can calculate and assign appropriate intensity values to pixels
/// </summary>
public static IResampler Triangle { get; } = new TriangleResampler();
/// <summary>
/// Gets the Welch sampler. A high speed algorthm that delivers very sharpened results.
/// </summary>
public static IResampler Welch { get; } = new WelchResampler();
}
}

8
src/ImageSharp/Processing/Transforms/Resize.cs

@ -56,7 +56,7 @@ namespace SixLabors.ImageSharp
public static IImageProcessingContext<TPixel> Resize<TPixel>(this IImageProcessingContext<TPixel> source, Size size)
where TPixel : struct, IPixel<TPixel>
{
return Resize(source, size.Width, size.Height, new BicubicResampler(), false);
return Resize(source, size.Width, size.Height, KnownResamplers.Bicubic, false);
}
/// <summary>
@ -71,7 +71,7 @@ namespace SixLabors.ImageSharp
public static IImageProcessingContext<TPixel> Resize<TPixel>(this IImageProcessingContext<TPixel> source, Size size, bool compand)
where TPixel : struct, IPixel<TPixel>
{
return Resize(source, size.Width, size.Height, new BicubicResampler(), compand);
return Resize(source, size.Width, size.Height, KnownResamplers.Bicubic, compand);
}
/// <summary>
@ -86,7 +86,7 @@ namespace SixLabors.ImageSharp
public static IImageProcessingContext<TPixel> Resize<TPixel>(this IImageProcessingContext<TPixel> source, int width, int height)
where TPixel : struct, IPixel<TPixel>
{
return Resize(source, width, height, new BicubicResampler(), false);
return Resize(source, width, height, KnownResamplers.Bicubic, false);
}
/// <summary>
@ -102,7 +102,7 @@ namespace SixLabors.ImageSharp
public static IImageProcessingContext<TPixel> Resize<TPixel>(this IImageProcessingContext<TPixel> source, int width, int height, bool compand)
where TPixel : struct, IPixel<TPixel>
{
return Resize(source, width, height, new BicubicResampler(), compand);
return Resize(source, width, height, KnownResamplers.Bicubic, compand);
}
/// <summary>

31
src/ImageSharp/Processing/Transforms/Rotate.cs

@ -21,9 +21,19 @@ namespace SixLabors.ImageSharp
/// <returns>The <see cref="Image{TPixel}"/></returns>
public static IImageProcessingContext<TPixel> Rotate<TPixel>(this IImageProcessingContext<TPixel> source, float degrees)
where TPixel : struct, IPixel<TPixel>
{
return Rotate(source, degrees, true);
}
=> Rotate(source, degrees, true);
/// <summary>
/// Rotates an image by the given angle in degrees, expanding the image to fit the rotated result.
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="source">The image to rotate.</param>
/// <param name="degrees">The angle in degrees to perform the rotation.</param>
/// <param name="sampler">The <see cref="IResampler"/> to perform the resampling.</param>
/// <returns>The <see cref="Image{TPixel}"/></returns>
public static IImageProcessingContext<TPixel> Rotate<TPixel>(this IImageProcessingContext<TPixel> source, float degrees, IResampler sampler)
where TPixel : struct, IPixel<TPixel>
=> Rotate(source, degrees, sampler, true);
/// <summary>
/// Rotates and flips an image by the given instructions.
@ -46,6 +56,19 @@ namespace SixLabors.ImageSharp
/// <returns>The <see cref="Image{TPixel}"/></returns>
public static IImageProcessingContext<TPixel> Rotate<TPixel>(this IImageProcessingContext<TPixel> source, float degrees, bool expand)
where TPixel : struct, IPixel<TPixel>
=> source.ApplyProcessor(new RotateProcessor<TPixel>(new BicubicResampler()) { Angle = degrees, Expand = expand });
=> Rotate(source, degrees, KnownResamplers.NearestNeighbor, expand);
/// <summary>
/// Rotates an image by the given angle in degrees using the specified sampling algorithm.
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="source">The image to rotate.</param>
/// <param name="degrees">The angle in degrees to perform the rotation.</param>
/// <param name="sampler">The <see cref="IResampler"/> to perform the resampling.</param>
/// <param name="expand">Whether to expand the image to fit the rotated result.</param>
/// <returns>The <see cref="Image{TPixel}"/></returns>
public static IImageProcessingContext<TPixel> Rotate<TPixel>(this IImageProcessingContext<TPixel> source, float degrees, IResampler sampler, bool expand)
where TPixel : struct, IPixel<TPixel>
=> source.ApplyProcessor(new RotateProcessor<TPixel>(sampler) { Angle = degrees, Expand = expand });
}
}

33
src/ImageSharp/Processing/Transforms/Skew.cs

@ -22,9 +22,20 @@ namespace SixLabors.ImageSharp
/// <returns>The <see cref="Image{TPixel}"/></returns>
public static IImageProcessingContext<TPixel> Skew<TPixel>(this IImageProcessingContext<TPixel> source, float degreesX, float degreesY)
where TPixel : struct, IPixel<TPixel>
{
return Skew(source, degreesX, degreesY, true);
}
=> Skew(source, degreesX, degreesY, true);
/// <summary>
/// Skews an image by the given angles in degrees using the given sampler, expanding the image to fit the skewed result.
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="source">The image to skew.</param>
/// <param name="degreesX">The angle in degrees to perform the rotation along the x-axis.</param>
/// <param name="degreesY">The angle in degrees to perform the rotation along the y-axis.</param>
/// <param name="sampler">The <see cref="IResampler"/> to perform the resampling.</param>
/// <returns>The <see cref="Image{TPixel}"/></returns>
public static IImageProcessingContext<TPixel> Skew<TPixel>(this IImageProcessingContext<TPixel> source, float degreesX, float degreesY, IResampler sampler)
where TPixel : struct, IPixel<TPixel>
=> Skew(source, degreesX, degreesY, sampler, true);
/// <summary>
/// Skews an image by the given angles in degrees.
@ -37,6 +48,20 @@ namespace SixLabors.ImageSharp
/// <returns>The <see cref="Image{TPixel}"/></returns>
public static IImageProcessingContext<TPixel> Skew<TPixel>(this IImageProcessingContext<TPixel> source, float degreesX, float degreesY, bool expand)
where TPixel : struct, IPixel<TPixel>
=> source.ApplyProcessor(new SkewProcessor<TPixel>(new BicubicResampler()) { AngleX = degreesX, AngleY = degreesY, Expand = expand });
=> Skew(source, degreesX, degreesY, KnownResamplers.NearestNeighbor, expand);
/// <summary>
/// Skews an image by the given angles in degrees using the specified sampling algorithm.
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="source">The image to skew.</param>
/// <param name="degreesX">The angle in degrees to perform the rotation along the x-axis.</param>
/// <param name="degreesY">The angle in degrees to perform the rotation along the y-axis.</param>
/// <param name="sampler">The <see cref="IResampler"/> to perform the resampling.</param>
/// <param name="expand">Whether to expand the image to fit the skewed result.</param>
/// <returns>The <see cref="Image{TPixel}"/></returns>
public static IImageProcessingContext<TPixel> Skew<TPixel>(this IImageProcessingContext<TPixel> source, float degreesX, float degreesY, IResampler sampler, bool expand)
where TPixel : struct, IPixel<TPixel>
=> source.ApplyProcessor(new SkewProcessor<TPixel>(sampler) { AngleX = degreesX, AngleY = degreesY, Expand = expand });
}
}

2
tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeProfilingBenchmarks.cs

@ -38,7 +38,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Transforms
// [Fact]
public void PrintWeightsData()
{
var proc = new ResizeProcessor<Rgba32>(new BicubicResampler(), 200, 200);
var proc = new ResizeProcessor<Rgba32>(KnownResamplers.Bicubic, 200, 200);
ResamplingWeightedProcessor<Rgba32>.WeightsBuffer weights = proc.PrecomputeWeights(200, 500);

37
tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeTests.cs

@ -20,19 +20,20 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Transforms
public static readonly TheoryData<string, IResampler> AllReSamplers =
new TheoryData<string, IResampler>
{
{ "Bicubic", new BicubicResampler() },
{ "Triangle", new TriangleResampler() },
{ "NearestNeighbor", new NearestNeighborResampler() },
{ "Box", new BoxResampler() },
{ "Lanczos3", new Lanczos3Resampler() },
{ "Lanczos5", new Lanczos5Resampler() },
{ "MitchellNetravali", new MitchellNetravaliResampler() },
{ "Lanczos8", new Lanczos8Resampler() },
{ "Hermite", new HermiteResampler() },
{ "Spline", new SplineResampler() },
{ "Robidoux", new RobidouxResampler() },
{ "RobidouxSharp", new RobidouxSharpResampler() },
{ "Welch", new WelchResampler() }
{ "Bicubic", KnownResamplers.Bicubic },
{ "Triangle", KnownResamplers.Triangle},
{ "NearestNeighbor", KnownResamplers.NearestNeighbor },
{ "Box", KnownResamplers.Box },
{ "Lanczos2", KnownResamplers.Lanczos2 },
{ "Lanczos3", KnownResamplers.Lanczos3 },
{ "Lanczos5", KnownResamplers.Lanczos5 },
{ "MitchellNetravali", KnownResamplers.MitchellNetravali },
{ "Lanczos8", KnownResamplers.Lanczos8 },
{ "Hermite", KnownResamplers.Hermite },
{ "Spline", KnownResamplers.Spline },
{ "Robidoux", KnownResamplers.Robidoux },
{ "RobidouxSharp", KnownResamplers.RobidouxSharp },
{ "Welch", KnownResamplers.Welch }
};
[Theory]
@ -105,7 +106,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Transforms
var sourceRectangle = new Rectangle(image.Width / 8, image.Height / 8, image.Width / 4, image.Height / 4);
var destRectangle = new Rectangle(image.Width / 4, image.Height / 4, image.Width / 2, image.Height / 2);
image.Mutate(x => x.Resize(image.Width, image.Height, new BicubicResampler(), sourceRectangle, destRectangle, false));
image.Mutate(x => x.Resize(image.Width, image.Height, KnownResamplers.Bicubic, sourceRectangle, destRectangle, false));
image.DebugSave(provider);
image.CompareToReferenceOutput(provider);
@ -286,7 +287,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Transforms
[InlineData(2, 0)]
public static void BicubicWindowOscillatesCorrectly(float x, float expected)
{
var sampler = new BicubicResampler();
var sampler = KnownResamplers.Bicubic;
float result = sampler.GetValue(x);
Assert.Equal(result, expected);
@ -300,7 +301,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Transforms
[InlineData(2, 0)]
public static void TriangleWindowOscillatesCorrectly(float x, float expected)
{
var sampler = new TriangleResampler();
var sampler = KnownResamplers.Triangle;
float result = sampler.GetValue(x);
Assert.Equal(result, expected);
@ -314,7 +315,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Transforms
[InlineData(2, 0)]
public static void Lanczos3WindowOscillatesCorrectly(float x, float expected)
{
var sampler = new Lanczos3Resampler();
var sampler = KnownResamplers.Lanczos3;
float result = sampler.GetValue(x);
Assert.Equal(result, expected);
@ -328,7 +329,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Transforms
[InlineData(4, 0)]
public static void Lanczos5WindowOscillatesCorrectly(float x, float expected)
{
var sampler = new Lanczos5Resampler();
var sampler = KnownResamplers.Lanczos5;
float result = sampler.GetValue(x);
Assert.Equal(result, expected);

13
tests/ImageSharp.Tests/Processing/Processors/Transforms/RotateTests.cs

@ -39,6 +39,19 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Transforms
}
}
[Theory]
[WithTestPatternImages(nameof(RotateFloatValues), 100, 50, DefaultPixelType)]
[WithTestPatternImages(nameof(RotateFloatValues), 50, 100, DefaultPixelType)]
public void RotateWithSampler<TPixel>(TestImageProvider<TPixel> provider, float value)
where TPixel : struct, IPixel<TPixel>
{
using (Image<TPixel> image = provider.GetImage())
{
image.Mutate(x => x.Rotate(value, KnownResamplers.Triangle));
image.DebugSave(provider, string.Join("_", value, "triangle"));
}
}
[Theory]
[WithTestPatternImages(nameof(RotateEnumValues), 100, 50, DefaultPixelType)]
[WithTestPatternImages(nameof(RotateEnumValues), 50, 100, DefaultPixelType)]

14
tests/ImageSharp.Tests/Processing/Processors/Transforms/SkewTest.cs

@ -6,6 +6,8 @@ using Xunit;
namespace SixLabors.ImageSharp.Tests.Processing.Processors.Transforms
{
using SixLabors.ImageSharp.Processing;
public class SkewTest : FileTestBase
{
public static readonly TheoryData<float, float> SkewValues
@ -26,5 +28,17 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Transforms
image.DebugSave(provider, string.Join("_", x, y));
}
}
[Theory]
[WithFileCollection(nameof(DefaultFiles), nameof(SkewValues), DefaultPixelType)]
public void ImageShouldSkewWithSampler<TPixel>(TestImageProvider<TPixel> provider, float x, float y)
where TPixel : struct, IPixel<TPixel>
{
using (Image<TPixel> image = provider.GetImage())
{
image.Mutate(i => i.Skew(x, y, KnownResamplers.Triangle));
image.DebugSave(provider, string.Join("_", x, y, "triangle"));
}
}
}
}
Loading…
Cancel
Save