Browse Source

Fix inheritance and cleanup

af/merge-core
James Jackson-South 9 years ago
parent
commit
6b182faf80
  1. 27
      src/ImageSharp/Common/Helpers/ImageMaths.cs
  2. 79
      src/ImageSharp/Processing/Processors/Transforms/AffineProcessor.cs
  3. 18
      src/ImageSharp/Processing/Processors/Transforms/AutoOrientProcessor.cs
  4. 36
      src/ImageSharp/Processing/Processors/Transforms/RotateProcessor.cs
  5. 33
      src/ImageSharp/Processing/Processors/Transforms/SkewProcessor.cs
  6. 3
      src/ImageSharp/Processing/Transforms/Resamplers/BicubicResampler.cs
  7. 3
      src/ImageSharp/Processing/Transforms/Resamplers/BoxResampler.cs
  8. 3
      src/ImageSharp/Processing/Transforms/Resamplers/CatmullRomResampler.cs
  9. 3
      src/ImageSharp/Processing/Transforms/Resamplers/HermiteResampler.cs
  10. 3
      src/ImageSharp/Processing/Transforms/Resamplers/Lanczos2Resampler.cs
  11. 3
      src/ImageSharp/Processing/Transforms/Resamplers/Lanczos3Resampler.cs
  12. 3
      src/ImageSharp/Processing/Transforms/Resamplers/Lanczos5Resampler.cs
  13. 3
      src/ImageSharp/Processing/Transforms/Resamplers/Lanczos8Resampler.cs
  14. 3
      src/ImageSharp/Processing/Transforms/Resamplers/MitchellNetravaliResampler.cs
  15. 3
      src/ImageSharp/Processing/Transforms/Resamplers/NearestNeighborResampler.cs
  16. 3
      src/ImageSharp/Processing/Transforms/Resamplers/RobidouxSharpResampler.cs
  17. 3
      src/ImageSharp/Processing/Transforms/Resamplers/SplineResampler.cs
  18. 3
      src/ImageSharp/Processing/Transforms/Resamplers/TriangleResampler.cs
  19. 3
      src/ImageSharp/Processing/Transforms/Resamplers/WelchResampler.cs
  20. 37
      src/ImageSharp/Processing/Transforms/Rotate.cs
  21. 45
      src/ImageSharp/Processing/Transforms/Skew.cs
  22. 9
      tests/ImageSharp.Tests/Processing/Transforms/RotateFlipTests.cs
  23. 31
      tests/ImageSharp.Tests/Processing/Transforms/RotateTests.cs
  24. 21
      tests/ImageSharp.Tests/Processing/Transforms/SkewTest.cs

27
src/ImageSharp/Common/Helpers/ImageMaths.cs

@ -147,17 +147,26 @@ namespace SixLabors.ImageSharp
/// <returns>
/// The <see cref="Rectangle"/>.
/// </returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Rectangle GetBoundingRectangle(Rectangle rectangle, Matrix3x2 matrix)
{
var leftTop = Vector2.Transform(new Vector2(rectangle.Left, rectangle.Top), matrix);
var rightTop = Vector2.Transform(new Vector2(rectangle.Right, rectangle.Top), matrix);
var leftBottom = Vector2.Transform(new Vector2(rectangle.Left, rectangle.Bottom), matrix);
var rightBottom = Vector2.Transform(new Vector2(rectangle.Right, rectangle.Bottom), matrix);
Vector2[] allCorners = { leftTop, rightTop, leftBottom, rightBottom };
float extentX = allCorners.Select(v => v.X).Max() - allCorners.Select(v => v.X).Min();
float extentY = allCorners.Select(v => v.Y).Max() - allCorners.Select(v => v.Y).Min();
return new Rectangle(0, 0, (int)MathF.Ceiling(extentX), (int)MathF.Ceiling(extentY));
// Calculate the position of the four corners in world space by applying
// The world matrix to the four corners in object space (0, 0, width, height)
var tl = Vector2.Transform(Vector2.Zero, matrix);
var tr = Vector2.Transform(new Vector2(rectangle.Width, 0), matrix);
var bl = Vector2.Transform(new Vector2(0, rectangle.Height), matrix);
var br = Vector2.Transform(new Vector2(rectangle.Width, rectangle.Height), matrix);
// Find the minimum and maximum "corners" based on the ones above
float minX = MathF.Min(tl.X, MathF.Min(tr.X, MathF.Min(bl.X, br.X)));
float maxX = MathF.Max(tl.X, MathF.Max(tr.X, MathF.Max(bl.X, br.X)));
float minY = MathF.Min(tl.Y, MathF.Min(tr.Y, MathF.Min(bl.Y, br.Y)));
float maxY = MathF.Max(tl.Y, MathF.Max(tr.Y, MathF.Max(bl.Y, br.Y)));
var min = new Vector2(minX, minY);
var max = new Vector2(maxX, maxY);
Vector2 size = max - min;
return new Rectangle((int)MathF.Floor(minX), (int)MathF.Floor(minY), (int)MathF.Ceiling(size.X), (int)MathF.Ceiling(size.Y));
}
/// <summary>

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

@ -3,7 +3,6 @@
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Numerics;
using System.Threading.Tasks;
@ -18,52 +17,40 @@ namespace SixLabors.ImageSharp.Processing.Processors
/// Provides the base methods to perform affine transforms on an image.
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
internal abstract class AffineProcessor<TPixel> : ResamplingWeightedProcessor<TPixel>
internal abstract class AffineProcessor<TPixel> : CloningImageProcessor<TPixel>
where TPixel : struct, IPixel<TPixel>
{
private Rectangle targetRectangle;
private Matrix3x2 transformMatrix;
/// <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, Rectangles.DefaultRectangle) // Hack to prevent Guard throwing in base, we always set the canvas
{
this.Sampler = sampler;
}
/// <summary>
/// Gets or sets a value indicating whether to expand the canvas to fit the transformed image.
/// Gets the sampler to perform interpolation of the transform operation.
/// </summary>
public bool Expand { get; set; } = true;
public IResampler Sampler { get; }
/// <summary>
/// Returns the processing matrix used for transforming the image.
/// </summary>
/// <param name="rectangle">The rectangle bounds</param>
/// <returns>The <see cref="Matrix3x2"/></returns>
protected abstract Matrix3x2 CreateProcessingMatrix(Rectangle rectangle);
/// <summary>
/// Creates a new target canvas to contain the results of the matrix transform.
/// </summary>
/// <param name="sourceRectangle">The source rectangle.</param>
protected virtual void CreateNewCanvas(Rectangle sourceRectangle)
{
this.ResizeRectangle = Matrix3x2.Invert(this.CreateProcessingMatrix(sourceRectangle), out Matrix3x2 sizeMatrix)
? ImageMaths.GetBoundingRectangle(sourceRectangle, sizeMatrix)
: sourceRectangle;
this.Width = this.ResizeRectangle.Width;
this.Height = this.ResizeRectangle.Height;
}
protected abstract Matrix3x2 GetTransformMatrix();
/// <inheritdoc/>
protected override Image<TPixel> CreateDestination(Image<TPixel> source, Rectangle sourceRectangle)
{
this.CreateNewCanvas(sourceRectangle);
this.ResizeCanvas(sourceRectangle);
// 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.ResizeRectangle.Width, this.ResizeRectangle.Height, x.MetaData.Clone()));
source.Frames.Select(x => new ImageFrame<TPixel>(this.targetRectangle.Width, this.targetRectangle.Height, x.MetaData.Clone()));
// Use the overload to prevent an extra frame being added
return new Image<TPixel>(source.GetConfiguration(), source.MetaData.Clone(), frames);
@ -72,9 +59,11 @@ namespace SixLabors.ImageSharp.Processing.Processors
/// <inheritdoc/>
protected override void OnApply(ImageFrame<TPixel> source, ImageFrame<TPixel> destination, Rectangle sourceRectangle, Configuration configuration)
{
int height = this.ResizeRectangle.Height;
int width = this.ResizeRectangle.Width;
int height = this.targetRectangle.Height;
int width = this.targetRectangle.Width;
Rectangle sourceBounds = source.Bounds();
// Since could potentially be resizing the canvas we need to recenter the matrix
Matrix3x2 matrix = this.GetCenteredMatrix(source);
if (this.Sampler is NearestNeighborResampler)
@ -106,8 +95,7 @@ namespace SixLabors.ImageSharp.Processing.Processors
(float radius, float scale) yRadiusScale = this.GetSamplingRadius(source.Height, destination.Height);
float xScale = xRadiusScale.scale;
float yScale = yRadiusScale.scale;
float xRadius = xRadiusScale.radius;
float yRadius = yRadiusScale.radius;
var radius = new Vector2(xRadiusScale.radius, yRadiusScale.radius);
IResampler sampler = this.Sampler;
var maxSource = new Vector4(maxSourceX, maxSourceY, maxSourceX, maxSourceY);
@ -125,11 +113,14 @@ namespace SixLabors.ImageSharp.Processing.Processors
var point = Vector2.Transform(new Vector2(x, y), matrix);
// Clamp sampling pixel radial extents to the source image edges
Vector2 maxXY = point + radius;
Vector2 minXY = point - radius;
var extents = new Vector4(
MathF.Ceiling(point.X + xRadius),
MathF.Ceiling(point.Y + yRadius),
MathF.Floor(point.X - xRadius),
MathF.Floor(point.Y - yRadius));
MathF.Ceiling(maxXY.X),
MathF.Ceiling(maxXY.Y),
MathF.Floor(minXY.X),
MathF.Floor(minXY.Y));
extents = Vector4.Clamp(extents, Vector4.Zero, maxSource);
@ -172,9 +163,21 @@ namespace SixLabors.ImageSharp.Processing.Processors
/// </returns>
protected Matrix3x2 GetCenteredMatrix(ImageFrame<TPixel> source)
{
var translationToTargetCenter = Matrix3x2.CreateTranslation(-this.ResizeRectangle.Width * .5F, -this.ResizeRectangle.Height * .5F);
var translationToTargetCenter = Matrix3x2.CreateTranslation(-this.targetRectangle.Width * .5F, -this.targetRectangle.Height * .5F);
var translateToSourceCenter = Matrix3x2.CreateTranslation(source.Width * .5F, source.Height * .5F);
return (translationToTargetCenter * this.CreateProcessingMatrix(this.ResizeRectangle)) * translateToSourceCenter;
return translationToTargetCenter * this.transformMatrix * translateToSourceCenter;
}
/// <summary>
/// Creates a new target canvas to contain the results of the matrix transform.
/// </summary>
/// <param name="sourceRectangle">The source rectangle.</param>
private void ResizeCanvas(Rectangle sourceRectangle)
{
this.transformMatrix = this.GetTransformMatrix();
this.targetRectangle = Matrix3x2.Invert(this.transformMatrix, out Matrix3x2 sizeMatrix)
? ImageMaths.GetBoundingRectangle(sourceRectangle, sizeMatrix)
: sourceRectangle;
}
/// <summary>
@ -196,14 +199,4 @@ namespace SixLabors.ImageSharp.Processing.Processors
return (MathF.Ceiling(scale * this.Sampler.Radius), scale);
}
}
/// <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);
}
}

18
src/ImageSharp/Processing/Processors/Transforms/AutoOrientProcessor.cs

@ -15,13 +15,7 @@ namespace SixLabors.ImageSharp.Processing.Processors
internal class AutoOrientProcessor<TPixel> : ImageProcessor<TPixel>
where TPixel : struct, IPixel<TPixel>
{
/// <summary>
/// Initializes a new instance of the <see cref="AutoOrientProcessor{TPixel}"/> class.
/// </summary>
public AutoOrientProcessor()
{
}
/// <inheritdoc/>
protected override void BeforeImageApply(Image<TPixel> source, Rectangle sourceRectangle)
{
Orientation orientation = GetExifOrientation(source);
@ -33,7 +27,7 @@ namespace SixLabors.ImageSharp.Processing.Processors
break;
case Orientation.BottomRight:
new RotateProcessor<TPixel>() { Angle = (int)RotateType.Rotate180, Expand = false }.Apply(source, sourceRectangle);
new RotateProcessor<TPixel>((int)RotateType.Rotate180).Apply(source, sourceRectangle);
break;
case Orientation.BottomLeft:
@ -41,21 +35,21 @@ namespace SixLabors.ImageSharp.Processing.Processors
break;
case Orientation.LeftTop:
new RotateProcessor<TPixel>() { Angle = (int)RotateType.Rotate90, Expand = false }.Apply(source, sourceRectangle);
new RotateProcessor<TPixel>((int)RotateType.Rotate90).Apply(source, sourceRectangle);
new FlipProcessor<TPixel>(FlipType.Horizontal).Apply(source, sourceRectangle);
break;
case Orientation.RightTop:
new RotateProcessor<TPixel>() { Angle = (int)RotateType.Rotate90, Expand = false }.Apply(source, sourceRectangle);
new RotateProcessor<TPixel>((int)RotateType.Rotate90).Apply(source, sourceRectangle);
break;
case Orientation.RightBottom:
new FlipProcessor<TPixel>(FlipType.Vertical).Apply(source, sourceRectangle);
new RotateProcessor<TPixel>() { Angle = (int)RotateType.Rotate270, Expand = false }.Apply(source, sourceRectangle);
new RotateProcessor<TPixel>((int)RotateType.Rotate270).Apply(source, sourceRectangle);
break;
case Orientation.LeftBottom:
new RotateProcessor<TPixel>() { Angle = (int)RotateType.Rotate270, Expand = false }.Apply(source, sourceRectangle);
new RotateProcessor<TPixel>((int)RotateType.Rotate270).Apply(source, sourceRectangle);
break;
case Orientation.Unknown:

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

@ -19,39 +19,35 @@ namespace SixLabors.ImageSharp.Processing.Processors
internal class RotateProcessor<TPixel> : AffineProcessor<TPixel>
where TPixel : struct, IPixel<TPixel>
{
private Matrix3x2 transformMatrix;
/// <summary>
/// Initializes a new instance of the <see cref="RotateProcessor{TPixel}"/> class.
/// </summary>
public RotateProcessor()
: base(KnownResamplers.NearestNeighbor)
/// <param name="angle">The angle of rotation in degrees.</param>
public RotateProcessor(float angle)
: this(angle, KnownResamplers.NearestNeighbor)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="RotateProcessor{TPixel}"/> class.
/// </summary>
/// <param name="degrees">The angle of rotation in degrees.</param>
/// <param name="sampler">The sampler to perform the rotating operation.</param>
public RotateProcessor(IResampler sampler)
public RotateProcessor(float degrees, IResampler sampler)
: base(sampler)
{
this.Degrees = degrees;
}
/// <summary>
/// Gets or sets the angle of processMatrix in degrees.
/// Gets the angle of rotation in degrees.
/// </summary>
public float Angle { get; set; }
public float Degrees { get; }
/// <inheritdoc/>
protected override Matrix3x2 CreateProcessingMatrix(Rectangle rectangle)
protected override Matrix3x2 GetTransformMatrix()
{
if (this.transformMatrix == default(Matrix3x2))
{
this.transformMatrix = Matrix3x2Extensions.CreateRotationDegrees(-this.Angle, PointF.Empty);
}
return this.transformMatrix;
return Matrix3x2Extensions.CreateRotationDegrees(-this.Degrees, PointF.Empty);
}
/// <inheritdoc/>
@ -74,7 +70,7 @@ namespace SixLabors.ImageSharp.Processing.Processors
return;
}
if (MathF.Abs(this.Angle) < Constants.Epsilon)
if (MathF.Abs(this.Degrees) < Constants.Epsilon)
{
// No need to do anything so return.
return;
@ -82,7 +78,7 @@ namespace SixLabors.ImageSharp.Processing.Processors
profile.RemoveValue(ExifTag.Orientation);
if (this.Expand && profile.GetValue(ExifTag.PixelXDimension) != null)
if (profile.GetValue(ExifTag.PixelXDimension) != null)
{
profile.SetValue(ExifTag.PixelXDimension, source.Width);
profile.SetValue(ExifTag.PixelYDimension, source.Height);
@ -100,26 +96,26 @@ namespace SixLabors.ImageSharp.Processing.Processors
/// </returns>
private bool OptimizedApply(ImageFrame<TPixel> source, ImageFrame<TPixel> destination, Configuration configuration)
{
if (MathF.Abs(this.Angle) < Constants.Epsilon)
if (MathF.Abs(this.Degrees) < Constants.Epsilon)
{
// The destination will be blank here so copy all the pixel data over
source.GetPixelSpan().CopyTo(destination.GetPixelSpan());
return true;
}
if (MathF.Abs(this.Angle - 90) < Constants.Epsilon)
if (MathF.Abs(this.Degrees - 90) < Constants.Epsilon)
{
this.Rotate90(source, destination, configuration);
return true;
}
if (MathF.Abs(this.Angle - 180) < Constants.Epsilon)
if (MathF.Abs(this.Degrees - 180) < Constants.Epsilon)
{
this.Rotate180(source, destination, configuration);
return true;
}
if (MathF.Abs(this.Angle - 270) < Constants.Epsilon)
if (MathF.Abs(this.Degrees - 270) < Constants.Epsilon)
{
this.Rotate270(source, destination, configuration);
return true;

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

@ -14,36 +14,43 @@ namespace SixLabors.ImageSharp.Processing.Processors
internal class SkewProcessor<TPixel> : AffineProcessor<TPixel>
where TPixel : struct, IPixel<TPixel>
{
private Matrix3x2 transformMatrix;
/// <summary>
/// Initializes a new instance of the <see cref="SkewProcessor{TPixel}"/> class.
/// </summary>
/// <param name="degreesX">The angle in degrees to perform the skew along the x-axis.</param>
/// <param name="degreesY">The angle in degrees to perform the skew along the y-axis.</param>
public SkewProcessor(float degreesX, float degreesY)
: this(degreesX, degreesY, KnownResamplers.NearestNeighbor)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="SkewProcessor{TPixel}"/> class.
/// </summary>
/// <param name="degreesX">The angle in degrees to perform the skew along the x-axis.</param>
/// <param name="degreesY">The angle in degrees to perform the skew along the y-axis.</param>
/// <param name="sampler">The sampler to perform the skew operation.</param>
public SkewProcessor(IResampler sampler)
public SkewProcessor(float degreesX, float degreesY, IResampler sampler)
: base(sampler)
{
this.DegreesX = degreesX;
this.DegreesY = degreesY;
}
/// <summary>
/// Gets or sets the angle of rotation along the x-axis in degrees.
/// Gets the angle of rotation along the x-axis in degrees.
/// </summary>
public float AngleX { get; set; }
public float DegreesX { get; }
/// <summary>
/// Gets or sets the angle of rotation along the y-axis in degrees.
/// Gets the angle of rotation along the y-axis in degrees.
/// </summary>
public float AngleY { get; set; }
public float DegreesY { get; }
/// <inheritdoc/>
protected override Matrix3x2 CreateProcessingMatrix(Rectangle rectangle)
protected override Matrix3x2 GetTransformMatrix()
{
if (this.transformMatrix == default(Matrix3x2))
{
this.transformMatrix = Matrix3x2Extensions.CreateSkewDegrees(-this.AngleX, -this.AngleY, PointF.Empty);
}
return this.transformMatrix;
return Matrix3x2Extensions.CreateSkewDegrees(-this.DegreesX, -this.DegreesY, PointF.Empty);
}
}
}

3
src/ImageSharp/Processing/Transforms/Resamplers/BicubicResampler.cs

@ -1,8 +1,6 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System.Runtime.CompilerServices;
namespace SixLabors.ImageSharp.Processing
{
/// <summary>
@ -16,7 +14,6 @@ namespace SixLabors.ImageSharp.Processing
public float Radius => 2;
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public float GetValue(float x)
{
if (x < 0F)

3
src/ImageSharp/Processing/Transforms/Resamplers/BoxResampler.cs

@ -1,8 +1,6 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System.Runtime.CompilerServices;
namespace SixLabors.ImageSharp.Processing
{
/// <summary>
@ -15,7 +13,6 @@ namespace SixLabors.ImageSharp.Processing
public float Radius => 0.5F;
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public float GetValue(float x)
{
if (x > -0.5F && x <= 0.5F)

3
src/ImageSharp/Processing/Transforms/Resamplers/CatmullRomResampler.cs

@ -1,8 +1,6 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System.Runtime.CompilerServices;
namespace SixLabors.ImageSharp.Processing
{
/// <summary>
@ -17,7 +15,6 @@ namespace SixLabors.ImageSharp.Processing
public float Radius => 2;
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public float GetValue(float x)
{
const float B = 0;

3
src/ImageSharp/Processing/Transforms/Resamplers/HermiteResampler.cs

@ -1,8 +1,6 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System.Runtime.CompilerServices;
namespace SixLabors.ImageSharp.Processing
{
/// <summary>
@ -16,7 +14,6 @@ namespace SixLabors.ImageSharp.Processing
public float Radius => 2;
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public float GetValue(float x)
{
const float B = 0F;

3
src/ImageSharp/Processing/Transforms/Resamplers/Lanczos2Resampler.cs

@ -1,8 +1,6 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System.Runtime.CompilerServices;
namespace SixLabors.ImageSharp.Processing
{
/// <summary>
@ -16,7 +14,6 @@ namespace SixLabors.ImageSharp.Processing
public float Radius => 2;
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public float GetValue(float x)
{
if (x < 0F)

3
src/ImageSharp/Processing/Transforms/Resamplers/Lanczos3Resampler.cs

@ -1,8 +1,6 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System.Runtime.CompilerServices;
namespace SixLabors.ImageSharp.Processing
{
/// <summary>
@ -16,7 +14,6 @@ namespace SixLabors.ImageSharp.Processing
public float Radius => 3;
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public float GetValue(float x)
{
if (x < 0F)

3
src/ImageSharp/Processing/Transforms/Resamplers/Lanczos5Resampler.cs

@ -1,8 +1,6 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System.Runtime.CompilerServices;
namespace SixLabors.ImageSharp.Processing
{
/// <summary>
@ -16,7 +14,6 @@ namespace SixLabors.ImageSharp.Processing
public float Radius => 5;
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public float GetValue(float x)
{
if (x < 0F)

3
src/ImageSharp/Processing/Transforms/Resamplers/Lanczos8Resampler.cs

@ -1,8 +1,6 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System.Runtime.CompilerServices;
namespace SixLabors.ImageSharp.Processing
{
/// <summary>
@ -16,7 +14,6 @@ namespace SixLabors.ImageSharp.Processing
public float Radius => 8;
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public float GetValue(float x)
{
if (x < 0F)

3
src/ImageSharp/Processing/Transforms/Resamplers/MitchellNetravaliResampler.cs

@ -1,8 +1,6 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System.Runtime.CompilerServices;
namespace SixLabors.ImageSharp.Processing
{
/// <summary>
@ -15,7 +13,6 @@ namespace SixLabors.ImageSharp.Processing
public float Radius => 2;
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public float GetValue(float x)
{
const float B = 0.3333333F;

3
src/ImageSharp/Processing/Transforms/Resamplers/NearestNeighborResampler.cs

@ -1,8 +1,6 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System.Runtime.CompilerServices;
namespace SixLabors.ImageSharp.Processing
{
/// <summary>
@ -15,7 +13,6 @@ namespace SixLabors.ImageSharp.Processing
public float Radius => 1;
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public float GetValue(float x)
{
return x;

3
src/ImageSharp/Processing/Transforms/Resamplers/RobidouxSharpResampler.cs

@ -1,8 +1,6 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System.Runtime.CompilerServices;
namespace SixLabors.ImageSharp.Processing
{
/// <summary>
@ -15,7 +13,6 @@ namespace SixLabors.ImageSharp.Processing
public float Radius => 2;
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public float GetValue(float x)
{
const float B = 0.2620145123990142F;

3
src/ImageSharp/Processing/Transforms/Resamplers/SplineResampler.cs

@ -1,8 +1,6 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System.Runtime.CompilerServices;
namespace SixLabors.ImageSharp.Processing
{
/// <summary>
@ -15,7 +13,6 @@ namespace SixLabors.ImageSharp.Processing
public float Radius => 2;
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public float GetValue(float x)
{
const float B = 1F;

3
src/ImageSharp/Processing/Transforms/Resamplers/TriangleResampler.cs

@ -1,8 +1,6 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System.Runtime.CompilerServices;
namespace SixLabors.ImageSharp.Processing
{
/// <summary>
@ -16,7 +14,6 @@ namespace SixLabors.ImageSharp.Processing
public float Radius => 1;
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public float GetValue(float x)
{
if (x < 0F)

3
src/ImageSharp/Processing/Transforms/Resamplers/WelchResampler.cs

@ -1,8 +1,6 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System.Runtime.CompilerServices;
namespace SixLabors.ImageSharp.Processing
{
/// <summary>
@ -15,7 +13,6 @@ namespace SixLabors.ImageSharp.Processing
public float Radius => 3;
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public float GetValue(float x)
{
if (x < 0F)

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

@ -12,29 +12,6 @@ namespace SixLabors.ImageSharp
/// </summary>
public static partial class ImageExtensions
{
/// <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>
/// <returns>The <see cref="Image{TPixel}"/></returns>
public static IImageProcessingContext<TPixel> Rotate<TPixel>(this IImageProcessingContext<TPixel> source, float degrees)
where TPixel : struct, IPixel<TPixel>
=> 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.
/// </summary>
@ -44,7 +21,7 @@ namespace SixLabors.ImageSharp
/// <returns>The <see cref="Image{TPixel}"/></returns>
public static IImageProcessingContext<TPixel> Rotate<TPixel>(this IImageProcessingContext<TPixel> source, RotateType rotateType)
where TPixel : struct, IPixel<TPixel>
=> Rotate(source, (float)rotateType, false);
=> Rotate(source, (float)rotateType);
/// <summary>
/// Rotates an image by the given angle in degrees.
@ -52,11 +29,10 @@ namespace SixLabors.ImageSharp
/// <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="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, bool expand)
public static IImageProcessingContext<TPixel> Rotate<TPixel>(this IImageProcessingContext<TPixel> source, float degrees)
where TPixel : struct, IPixel<TPixel>
=> Rotate(source, degrees, KnownResamplers.NearestNeighbor, expand);
=> Rotate(source, degrees, KnownResamplers.NearestNeighbor);
/// <summary>
/// Rotates an image by the given angle in degrees using the specified sampling algorithm.
@ -65,10 +41,9 @@ namespace SixLabors.ImageSharp
/// <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)
public static IImageProcessingContext<TPixel> Rotate<TPixel>(this IImageProcessingContext<TPixel> source, float degrees, IResampler sampler)
where TPixel : struct, IPixel<TPixel>
=> source.ApplyProcessor(new RotateProcessor<TPixel>(sampler) { Angle = degrees, Expand = expand });
=> source.ApplyProcessor(new RotateProcessor<TPixel>(degrees, sampler));
}
}
}

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

@ -12,56 +12,29 @@ namespace SixLabors.ImageSharp
/// </summary>
public static partial class ImageExtensions
{
/// <summary>
/// Skews an image by the given angles in degrees, 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>
/// <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>
=> 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.
/// </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="expand">Whether to expand the image to fit the skewed result.</param>
/// <param name="degreesX">The angle in degrees to perform the skew along the x-axis.</param>
/// <param name="degreesY">The angle in degrees to perform the skew along the y-axis.</param>
/// <returns>The <see cref="Image{TPixel}"/></returns>
public static IImageProcessingContext<TPixel> Skew<TPixel>(this IImageProcessingContext<TPixel> source, float degreesX, float degreesY, bool expand)
public static IImageProcessingContext<TPixel> Skew<TPixel>(this IImageProcessingContext<TPixel> source, float degreesX, float degreesY)
where TPixel : struct, IPixel<TPixel>
=> Skew(source, degreesX, degreesY, KnownResamplers.NearestNeighbor, expand);
=> Skew(source, degreesX, degreesY, KnownResamplers.NearestNeighbor);
/// <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="degreesX">The angle in degrees to perform the skew along the x-axis.</param>
/// <param name="degreesY">The angle in degrees to perform the skew 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)
public static IImageProcessingContext<TPixel> Skew<TPixel>(this IImageProcessingContext<TPixel> source, float degreesX, float degreesY, IResampler sampler)
where TPixel : struct, IPixel<TPixel>
=> source.ApplyProcessor(new SkewProcessor<TPixel>(sampler) { AngleX = degreesX, AngleY = degreesY, Expand = expand });
=> source.ApplyProcessor(new SkewProcessor<TPixel>(degreesX, degreesY, sampler));
}
}
}

9
tests/ImageSharp.Tests/Processing/Transforms/RotateFlipTests.cs

@ -25,14 +25,13 @@ namespace SixLabors.ImageSharp.Tests.Processing.Transforms
[InlineData(RotateType.Rotate90, FlipType.Vertical, 90)]
[InlineData(RotateType.Rotate180, FlipType.Vertical, 180)]
[InlineData(RotateType.Rotate270, FlipType.Vertical, 270)]
public void Rotate_degreesFloat_RotateProcessorWithAnglesSetAndExpandTrue(RotateType angle, FlipType flip, float expectedAngle)
public void Rotate_degreesFloat_RotateProcessorWithAnglesSetrue(RotateType angle, FlipType flip, float expectedAngle)
{
this.operations.RotateFlip(angle, flip);
var rotateProcessor = this.Verify<RotateProcessor<Rgba32>>(0);
var flipProcessor = this.Verify<FlipProcessor<Rgba32>>(1);
RotateProcessor<Rgba32> rotateProcessor = this.Verify<RotateProcessor<Rgba32>>(0);
FlipProcessor<Rgba32> flipProcessor = this.Verify<FlipProcessor<Rgba32>>(1);
Assert.Equal(expectedAngle, rotateProcessor.Angle);
Assert.False(rotateProcessor.Expand);
Assert.Equal(expectedAngle, rotateProcessor.Degrees);
Assert.Equal(flip, flipProcessor.FlipType);
}
}

31
tests/ImageSharp.Tests/Processing/Transforms/RotateTests.cs

@ -1,7 +1,6 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Processing;
using SixLabors.ImageSharp.Processing.Processors;
using Xunit;
@ -10,46 +9,28 @@ namespace SixLabors.ImageSharp.Tests.Processing.Transforms
{
public class RotateTests : BaseImageOperationsExtensionTest
{
[Theory]
[InlineData(85.6f)]
[InlineData(21)]
public void Rotate_degreesFloat_RotateProcessorWithAnglesSetAndExpandTrue(float angle)
public void RotateDegreesFloatRotateProcessorWithAnglesSet(float angle)
{
this.operations.Rotate(angle);
var processor = this.Verify<RotateProcessor<Rgba32>>();
RotateProcessor<Rgba32> processor = this.Verify<RotateProcessor<Rgba32>>();
Assert.Equal(angle, processor.Angle);
Assert.True(processor.Expand);
Assert.Equal(angle, processor.Degrees);
}
[Theory]
[InlineData(RotateType.None, 0)]
[InlineData(RotateType.Rotate90, 90)]
[InlineData(RotateType.Rotate180, 180)]
[InlineData(RotateType.Rotate270, 270)]
public void Rotate_RotateType_RotateProcessorWithAnglesConvertedFromEnumAndExpandTrue(RotateType angle, float expectedangle)
public void RotateRotateTypeRotateProcessorWithAnglesConvertedFromEnum(RotateType angle, float expectedangle)
{
this.operations.Rotate(angle); // is this api needed ???
var processor = this.Verify<RotateProcessor<Rgba32>>();
Assert.Equal(expectedangle, processor.Angle);
Assert.False(processor.Expand);
}
[Theory]
[InlineData(85.6f, false)]
[InlineData(21, true)]
[InlineData(21, false)]
public void Rotate_degreesFloat_expand_RotateProcessorWithAnglesSetAndExpandSet(float angle, bool expand)
{
this.operations.Rotate(angle, expand);
var processor = this.Verify<RotateProcessor<Rgba32>>();
RotateProcessor<Rgba32> processor = this.Verify<RotateProcessor<Rgba32>>();
Assert.Equal(angle, processor.Angle);
Assert.Equal(expand, processor.Expand);
Assert.Equal(expectedangle, processor.Degrees);
}
}
}

21
tests/ImageSharp.Tests/Processing/Transforms/SkewTest.cs

@ -1,7 +1,6 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Processing.Processors;
using Xunit;
@ -10,25 +9,13 @@ namespace SixLabors.ImageSharp.Tests.Processing.Transforms
public class SkewTest : BaseImageOperationsExtensionTest
{
[Fact]
public void Skew_x_y_CreateSkewProcessorWithAnglesSetAndExpandTrue()
public void SkewXYCreateSkewProcessorWithAnglesSet()
{
this.operations.Skew(10, 20);
var processor = this.Verify<SkewProcessor<Rgba32>>();
SkewProcessor<Rgba32> processor = this.Verify<SkewProcessor<Rgba32>>();
Assert.Equal(10, processor.AngleX);
Assert.Equal(20, processor.AngleY);
Assert.True(processor.Expand);
}
[Fact]
public void Skew_x_y_expand_CreateSkewProcessorWithAnglesSetAndExpandTrue()
{
this.operations.Skew(10, 20, false);
var processor = this.Verify<SkewProcessor<Rgba32>>();
Assert.Equal(10, processor.AngleX);
Assert.Equal(20, processor.AngleY);
Assert.False(processor.Expand);
Assert.Equal(10, processor.DegreesX);
Assert.Equal(20, processor.DegreesY);
}
}
}
Loading…
Cancel
Save