From cd0d443e72cc7df7938586e8507a91d07b6424fa Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Thu, 23 Nov 2017 12:11:02 +1100 Subject: [PATCH] Fix affine transforms and cleanup --- .../Processors/Transforms/AffineProcessor.cs | 32 ++++--- .../Processors/Transforms/ResizeProcessor.cs | 14 +-- .../Processors/Transforms/RotateProcessor.cs | 10 +- .../Processors/Transforms/SkewProcessor.cs | 5 +- .../Transforms/Options/ResizeOptions.cs | 2 +- .../Transforms/Resamplers/KnownResamplers.cs | 96 +++++++++++++++++++ .../Processing/Transforms/Resize.cs | 8 +- .../Processing/Transforms/Rotate.cs | 31 +++++- src/ImageSharp/Processing/Transforms/Skew.cs | 33 ++++++- .../Transforms/ResizeProfilingBenchmarks.cs | 2 +- .../Processors/Transforms/ResizeTests.cs | 37 +++---- .../Processors/Transforms/RotateTests.cs | 13 +++ .../Processors/Transforms/SkewTest.cs | 14 +++ 13 files changed, 233 insertions(+), 64 deletions(-) create mode 100644 src/ImageSharp/Processing/Transforms/Resamplers/KnownResamplers.cs diff --git a/src/ImageSharp/Processing/Processors/Transforms/AffineProcessor.cs b/src/ImageSharp/Processing/Processors/Transforms/AffineProcessor.cs index 93a97267c..9fcc3f4e4 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/AffineProcessor.cs +++ b/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 : ResamplingWeightedProcessor where TPixel : struct, IPixel { - // TODO: Move to constants somewhere else to prevent generic type duplication. - private static readonly Rectangle DefaultRectangle = new Rectangle(0, 0, 1, 1); - /// /// Initializes a new instance of the class. /// /// The sampler to perform the resize operation. 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 /// The source rectangle. 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. /// /// The source image. - /// The transform matrix. /// /// The . /// - protected Matrix3x2 GetCenteredMatrix(ImageFrame source, Matrix3x2 matrix) + protected Matrix3x2 GetCenteredMatrix(ImageFrame 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; } /// @@ -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; } } + + /// + /// 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. + /// + [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); + } } \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/Transforms/ResizeProcessor.cs b/src/ImageSharp/Processing/Processors/Transforms/ResizeProcessor.cs index a4fdb1a1b..ecfcc7dd2 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/ResizeProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/ResizeProcessor.cs @@ -53,16 +53,12 @@ namespace SixLabors.ImageSharp.Processing.Processors /// protected override Image CreateDestination(Image 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> frames = + source.Frames.Select(x => new ImageFrame(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> frames = source.Frames.Select(x => new ImageFrame(this.Width, this.Height, x.MetaData.Clone())); // this will create places holders - var image = new Image(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(source.GetConfiguration(), source.MetaData.Clone(), frames); } /// diff --git a/src/ImageSharp/Processing/Processors/Transforms/RotateProcessor.cs b/src/ImageSharp/Processing/Processors/Transforms/RotateProcessor.cs index 1bcd26d8d..35ce8ce63 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/RotateProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/RotateProcessor.cs @@ -25,14 +25,14 @@ namespace SixLabors.ImageSharp.Processing.Processors /// Initializes a new instance of the class. /// public RotateProcessor() - : base(new BicubicResampler()) + : base(KnownResamplers.NearestNeighbor) { } /// /// Initializes a new instance of the class. /// - /// The sampler to perform the resize operation. + /// The sampler to perform the rotating operation. 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); diff --git a/src/ImageSharp/Processing/Processors/Transforms/SkewProcessor.cs b/src/ImageSharp/Processing/Processors/Transforms/SkewProcessor.cs index ba156c674..0daf6acdd 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/SkewProcessor.cs +++ b/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]; diff --git a/src/ImageSharp/Processing/Transforms/Options/ResizeOptions.cs b/src/ImageSharp/Processing/Transforms/Options/ResizeOptions.cs index 8f2d3db0a..d20eaefb1 100644 --- a/src/ImageSharp/Processing/Transforms/Options/ResizeOptions.cs +++ b/src/ImageSharp/Processing/Transforms/Options/ResizeOptions.cs @@ -35,7 +35,7 @@ namespace SixLabors.ImageSharp.Processing /// /// Gets or sets the sampler to perform the resize operation. /// - public IResampler Sampler { get; set; } = new BicubicResampler(); + public IResampler Sampler { get; set; } = KnownResamplers.Bicubic; /// /// Gets or sets a value indicating whether to compress diff --git a/src/ImageSharp/Processing/Transforms/Resamplers/KnownResamplers.cs b/src/ImageSharp/Processing/Transforms/Resamplers/KnownResamplers.cs new file mode 100644 index 000000000..b4a9b648a --- /dev/null +++ b/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 +{ + /// + /// Contains reusable static instances of known resampling algorithms + /// + public static class KnownResamplers + { + /// + /// Gets the Bicubic sampler that implements the bicubic kernel algorithm W(x) + /// + public static IResampler Bicubic { get; } = new BicubicResampler(); + + /// + /// Gets the Box sampler that implements the box algorithm. Similar to nearest neighbor when upscaling. + /// When downscaling the pixels will average, merging pixels together. + /// + public static IResampler Box { get; } = new BoxResampler(); + + /// + /// Gets the Catmull-Rom sampler, a well known standard Cubic Filter often used as a interpolation function + /// + public static IResampler CatmullRom { get; } = new CatmullRomResampler(); + + /// + /// 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. + /// + public static IResampler Hermite { get; } = new HermiteResampler(); + + /// + /// 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. + /// + public static IResampler Lanczos2 { get; } = new Lanczos2Resampler(); + + /// + /// 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. + /// + public static IResampler Lanczos3 { get; } = new Lanczos3Resampler(); + + /// + /// 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. + /// + public static IResampler Lanczos5 { get; } = new Lanczos5Resampler(); + + /// + /// 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. + /// + public static IResampler Lanczos8 { get; } = new Lanczos8Resampler(); + + /// + /// Gets the Mitchell-Netravali sampler. This seperable cubic algorithm yields a very good equilibrium between + /// detail preservation (sharpness) and smoothness. + /// + public static IResampler MitchellNetravali { get; } = new MitchellNetravaliResampler(); + + /// + /// 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. + /// + public static IResampler NearestNeighbor { get; } = new NearestNeighborResampler(); + + /// + /// Gets the Robidoux sampler. This algorithm developed by Nicolas Robidoux providing a very good equilibrium between + /// detail preservation (sharpness) and smoothness comprable to . + /// + public static IResampler Robidoux { get; } = new RobidouxResampler(); + + /// + /// Gets the Robidoux Sharp sampler. A sharpend form of the sampler + /// + public static IResampler RobidouxSharp { get; } = new RobidouxResampler(); + + /// + /// Gets the Spline sampler. A seperable cubic algorithm similar to but yielding smoother results. + /// + public static IResampler Spline { get; } = new SplineResampler(); + + /// + /// 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 + /// + public static IResampler Triangle { get; } = new TriangleResampler(); + + /// + /// Gets the Welch sampler. A high speed algorthm that delivers very sharpened results. + /// + public static IResampler Welch { get; } = new WelchResampler(); + } +} \ No newline at end of file diff --git a/src/ImageSharp/Processing/Transforms/Resize.cs b/src/ImageSharp/Processing/Transforms/Resize.cs index 3c7cbca31..832b02dea 100644 --- a/src/ImageSharp/Processing/Transforms/Resize.cs +++ b/src/ImageSharp/Processing/Transforms/Resize.cs @@ -56,7 +56,7 @@ namespace SixLabors.ImageSharp public static IImageProcessingContext Resize(this IImageProcessingContext source, Size size) where TPixel : struct, IPixel { - return Resize(source, size.Width, size.Height, new BicubicResampler(), false); + return Resize(source, size.Width, size.Height, KnownResamplers.Bicubic, false); } /// @@ -71,7 +71,7 @@ namespace SixLabors.ImageSharp public static IImageProcessingContext Resize(this IImageProcessingContext source, Size size, bool compand) where TPixel : struct, IPixel { - return Resize(source, size.Width, size.Height, new BicubicResampler(), compand); + return Resize(source, size.Width, size.Height, KnownResamplers.Bicubic, compand); } /// @@ -86,7 +86,7 @@ namespace SixLabors.ImageSharp public static IImageProcessingContext Resize(this IImageProcessingContext source, int width, int height) where TPixel : struct, IPixel { - return Resize(source, width, height, new BicubicResampler(), false); + return Resize(source, width, height, KnownResamplers.Bicubic, false); } /// @@ -102,7 +102,7 @@ namespace SixLabors.ImageSharp public static IImageProcessingContext Resize(this IImageProcessingContext source, int width, int height, bool compand) where TPixel : struct, IPixel { - return Resize(source, width, height, new BicubicResampler(), compand); + return Resize(source, width, height, KnownResamplers.Bicubic, compand); } /// diff --git a/src/ImageSharp/Processing/Transforms/Rotate.cs b/src/ImageSharp/Processing/Transforms/Rotate.cs index 436aa3cd9..2ec1385bb 100644 --- a/src/ImageSharp/Processing/Transforms/Rotate.cs +++ b/src/ImageSharp/Processing/Transforms/Rotate.cs @@ -21,9 +21,19 @@ namespace SixLabors.ImageSharp /// The public static IImageProcessingContext Rotate(this IImageProcessingContext source, float degrees) where TPixel : struct, IPixel - { - return Rotate(source, degrees, true); - } + => Rotate(source, degrees, true); + + /// + /// Rotates an image by the given angle in degrees, expanding the image to fit the rotated result. + /// + /// The pixel format. + /// The image to rotate. + /// The angle in degrees to perform the rotation. + /// The to perform the resampling. + /// The + public static IImageProcessingContext Rotate(this IImageProcessingContext source, float degrees, IResampler sampler) + where TPixel : struct, IPixel + => Rotate(source, degrees, sampler, true); /// /// Rotates and flips an image by the given instructions. @@ -46,6 +56,19 @@ namespace SixLabors.ImageSharp /// The public static IImageProcessingContext Rotate(this IImageProcessingContext source, float degrees, bool expand) where TPixel : struct, IPixel - => source.ApplyProcessor(new RotateProcessor(new BicubicResampler()) { Angle = degrees, Expand = expand }); + => Rotate(source, degrees, KnownResamplers.NearestNeighbor, expand); + + /// + /// Rotates an image by the given angle in degrees using the specified sampling algorithm. + /// + /// The pixel format. + /// The image to rotate. + /// The angle in degrees to perform the rotation. + /// The to perform the resampling. + /// Whether to expand the image to fit the rotated result. + /// The + public static IImageProcessingContext Rotate(this IImageProcessingContext source, float degrees, IResampler sampler, bool expand) + where TPixel : struct, IPixel + => source.ApplyProcessor(new RotateProcessor(sampler) { Angle = degrees, Expand = expand }); } } diff --git a/src/ImageSharp/Processing/Transforms/Skew.cs b/src/ImageSharp/Processing/Transforms/Skew.cs index 873dbe510..f03e60e3d 100644 --- a/src/ImageSharp/Processing/Transforms/Skew.cs +++ b/src/ImageSharp/Processing/Transforms/Skew.cs @@ -22,9 +22,20 @@ namespace SixLabors.ImageSharp /// The public static IImageProcessingContext Skew(this IImageProcessingContext source, float degreesX, float degreesY) where TPixel : struct, IPixel - { - return Skew(source, degreesX, degreesY, true); - } + => Skew(source, degreesX, degreesY, true); + + /// + /// Skews an image by the given angles in degrees using the given sampler, expanding the image to fit the skewed result. + /// + /// The pixel format. + /// The image to skew. + /// The angle in degrees to perform the rotation along the x-axis. + /// The angle in degrees to perform the rotation along the y-axis. + /// The to perform the resampling. + /// The + public static IImageProcessingContext Skew(this IImageProcessingContext source, float degreesX, float degreesY, IResampler sampler) + where TPixel : struct, IPixel + => Skew(source, degreesX, degreesY, sampler, true); /// /// Skews an image by the given angles in degrees. @@ -37,6 +48,20 @@ namespace SixLabors.ImageSharp /// The public static IImageProcessingContext Skew(this IImageProcessingContext source, float degreesX, float degreesY, bool expand) where TPixel : struct, IPixel - => source.ApplyProcessor(new SkewProcessor(new BicubicResampler()) { AngleX = degreesX, AngleY = degreesY, Expand = expand }); + => Skew(source, degreesX, degreesY, KnownResamplers.NearestNeighbor, expand); + + /// + /// Skews an image by the given angles in degrees using the specified sampling algorithm. + /// + /// The pixel format. + /// The image to skew. + /// The angle in degrees to perform the rotation along the x-axis. + /// The angle in degrees to perform the rotation along the y-axis. + /// The to perform the resampling. + /// Whether to expand the image to fit the skewed result. + /// The + public static IImageProcessingContext Skew(this IImageProcessingContext source, float degreesX, float degreesY, IResampler sampler, bool expand) + where TPixel : struct, IPixel + => source.ApplyProcessor(new SkewProcessor(sampler) { AngleX = degreesX, AngleY = degreesY, Expand = expand }); } } diff --git a/tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeProfilingBenchmarks.cs b/tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeProfilingBenchmarks.cs index 963b849d2..6dd636980 100644 --- a/tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeProfilingBenchmarks.cs +++ b/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(new BicubicResampler(), 200, 200); + var proc = new ResizeProcessor(KnownResamplers.Bicubic, 200, 200); ResamplingWeightedProcessor.WeightsBuffer weights = proc.PrecomputeWeights(200, 500); diff --git a/tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeTests.cs b/tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeTests.cs index a5e21b8ef..662d03d9c 100644 --- a/tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeTests.cs +++ b/tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeTests.cs @@ -20,19 +20,20 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Transforms public static readonly TheoryData AllReSamplers = new TheoryData { - { "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); diff --git a/tests/ImageSharp.Tests/Processing/Processors/Transforms/RotateTests.cs b/tests/ImageSharp.Tests/Processing/Processors/Transforms/RotateTests.cs index 161af43c9..49fe8952b 100644 --- a/tests/ImageSharp.Tests/Processing/Processors/Transforms/RotateTests.cs +++ b/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(TestImageProvider provider, float value) + where TPixel : struct, IPixel + { + using (Image 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)] diff --git a/tests/ImageSharp.Tests/Processing/Processors/Transforms/SkewTest.cs b/tests/ImageSharp.Tests/Processing/Processors/Transforms/SkewTest.cs index 5e83fa620..6e0d65149 100644 --- a/tests/ImageSharp.Tests/Processing/Processors/Transforms/SkewTest.cs +++ b/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 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(TestImageProvider provider, float x, float y) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage()) + { + image.Mutate(i => i.Skew(x, y, KnownResamplers.Triangle)); + image.DebugSave(provider, string.Join("_", x, y, "triangle")); + } + } } } \ No newline at end of file