Browse Source

Refactor affine classes to inherit from weighted

af/merge-core
James Jackson-South 9 years ago
parent
commit
f3fb47ba37
  1. 95
      src/ImageSharp/Processing/Processors/Transforms/AffineProcessor.cs
  2. 50
      src/ImageSharp/Processing/Processors/Transforms/Matrix3x2Processor.cs
  3. 218
      src/ImageSharp/Processing/Processors/Transforms/RotateProcessor.cs
  4. 79
      src/ImageSharp/Processing/Processors/Transforms/SkewProcessor.cs
  5. 3
      src/ImageSharp/Processing/Transforms/Rotate.cs
  6. 4
      src/ImageSharp/Processing/Transforms/Skew.cs

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

@ -0,0 +1,95 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System.Collections.Generic;
using System.Linq;
using System.Numerics;
using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.Primitives;
namespace SixLabors.ImageSharp.Processing.Processors
{
/// <summary>
/// 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>
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
{
}
/// <summary>
/// Gets or sets a value indicating whether to expand the canvas to fit the skewed image.
/// </summary>
public bool Expand { get; set; } = true;
/// <summary>
/// Returns the processing matrix used for transforming the image.
/// </summary>
/// <returns>The <see cref="Matrix3x2"/></returns>
protected abstract Matrix3x2 CreateProcessingMatrix();
/// <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)
{
if (this.ResizeRectangle == DefaultRectangle)
{
if (this.Expand)
{
this.ResizeRectangle = Matrix3x2.Invert(this.CreateProcessingMatrix(), out Matrix3x2 sizeMatrix)
? ImageMaths.GetBoundingRectangle(sourceRectangle, sizeMatrix)
: sourceRectangle;
}
else
{
this.ResizeRectangle = sourceRectangle;
}
}
this.Width = this.ResizeRectangle.Width;
this.Height = this.ResizeRectangle.Height;
}
/// <inheritdoc/>
protected override Image<TPixel> CreateDestination(Image<TPixel> source, Rectangle sourceRectangle)
{
this.CreateNewCanvas(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.Width, this.Height, x.MetaData.Clone()));
// Use the overload to prevent an extra frame being added
return new Image<TPixel>(source.GetConfiguration(), source.MetaData.Clone(), frames);
}
/// <summary>
/// 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)
{
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;
}
}
}

50
src/ImageSharp/Processing/Processors/Transforms/Matrix3x2Processor.cs

@ -1,50 +0,0 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System.Numerics;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.Primitives;
namespace SixLabors.ImageSharp.Processing.Processors
{
/// <summary>
/// Provides methods to transform an image using a <see cref="Matrix3x2"/>.
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
internal abstract class Matrix3x2Processor<TPixel> : ImageProcessor<TPixel>
where TPixel : struct, IPixel<TPixel>
{
/// <summary>
/// Gets the rectangle designating the target canvas.
/// </summary>
protected Rectangle CanvasRectangle { get; private set; }
/// <summary>
/// Creates a new target canvas to contain the results of the matrix transform.
/// </summary>
/// <param name="sourceRectangle">The source rectangle.</param>
/// <param name="processMatrix">The processing matrix.</param>
protected void CreateNewCanvas(Rectangle sourceRectangle, Matrix3x2 processMatrix)
{
Matrix3x2 sizeMatrix;
this.CanvasRectangle = Matrix3x2.Invert(processMatrix, out sizeMatrix)
? ImageMaths.GetBoundingRectangle(sourceRectangle, sizeMatrix)
: sourceRectangle;
}
/// <summary>
/// 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)
{
var translationToTargetCenter = Matrix3x2.CreateTranslation(-this.CanvasRectangle.Width * .5F, -this.CanvasRectangle.Height * .5F);
var translateToSourceCenter = Matrix3x2.CreateTranslation(source.Width * .5F, source.Height * .5F);
return (translationToTargetCenter * matrix) * translateToSourceCenter;
}
}
}

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

@ -6,7 +6,6 @@ using System.Numerics;
using System.Threading.Tasks; using System.Threading.Tasks;
using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.Helpers; using SixLabors.ImageSharp.Helpers;
using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.MetaData.Profiles.Exif; using SixLabors.ImageSharp.MetaData.Profiles.Exif;
using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.PixelFormats;
using SixLabors.Primitives; using SixLabors.Primitives;
@ -17,46 +16,84 @@ namespace SixLabors.ImageSharp.Processing.Processors
/// Provides methods that allow the rotating of images. /// Provides methods that allow the rotating of images.
/// </summary> /// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam> /// <typeparam name="TPixel">The pixel format.</typeparam>
internal class RotateProcessor<TPixel> : Matrix3x2Processor<TPixel> internal class RotateProcessor<TPixel> : AffineProcessor<TPixel>
where TPixel : struct, IPixel<TPixel> where TPixel : struct, IPixel<TPixel>
{ {
private Matrix3x2 transformMatrix;
/// <summary> /// <summary>
/// The transform matrix to apply. /// Initializes a new instance of the <see cref="RotateProcessor{TPixel}"/> class.
/// </summary> /// </summary>
private Matrix3x2 processMatrix; public RotateProcessor()
: base(new BicubicResampler())
{
}
/// <summary> /// <summary>
/// Gets or sets the angle of processMatrix in degrees. /// Initializes a new instance of the <see cref="RotateProcessor{TPixel}"/> class.
/// </summary> /// </summary>
public float Angle { get; set; } /// <param name="sampler">The sampler to perform the resize operation.</param>
public RotateProcessor(IResampler sampler)
: base(sampler)
{
}
/// <summary> /// <summary>
/// Gets or sets a value indicating whether to expand the canvas to fit the rotated image. /// Gets or sets the angle of processMatrix in degrees.
/// </summary> /// </summary>
public bool Expand { get; set; } = true; public float Angle { get; set; }
/// <inheritdoc/> /// <inheritdoc/>
protected override void OnApply(ImageFrame<TPixel> source, Rectangle sourceRectangle, Configuration configuration) protected override Matrix3x2 CreateProcessingMatrix()
{ {
if (this.OptimizedApply(source, configuration)) if (this.transformMatrix == default(Matrix3x2))
{
this.transformMatrix = Matrix3x2Extensions.CreateRotationDegrees(-this.Angle, new Point(0, 0));
}
return this.transformMatrix;
}
/// <inheritdoc/>
protected override void CreateNewCanvas(Rectangle sourceRectangle)
{
if (MathF.Abs(this.Angle) < Constants.Epsilon ||
MathF.Abs(this.Angle - 180) < Constants.Epsilon)
{
this.ResizeRectangle = sourceRectangle;
}
if (MathF.Abs(this.Angle - 90) < Constants.Epsilon ||
MathF.Abs(this.Angle - 270) < Constants.Epsilon)
{
// We always expand enumerated rectangle values
this.ResizeRectangle = new Rectangle(0, 0, sourceRectangle.Height, sourceRectangle.Width);
}
base.CreateNewCanvas(sourceRectangle);
}
/// <inheritdoc/>
protected override void OnApply(ImageFrame<TPixel> source, ImageFrame<TPixel> destination, Rectangle sourceRectangle, Configuration configuration)
{
if (this.OptimizedApply(source, destination, configuration))
{ {
return; return;
} }
int height = this.CanvasRectangle.Height; int height = this.ResizeRectangle.Height;
int width = this.CanvasRectangle.Width; int width = this.ResizeRectangle.Width;
Matrix3x2 matrix = this.GetCenteredMatrix(source, this.processMatrix); Matrix3x2 matrix = this.GetCenteredMatrix(source, this.CreateProcessingMatrix());
Rectangle sourceBounds = source.Bounds(); Rectangle sourceBounds = source.Bounds();
using (var targetPixels = new PixelAccessor<TPixel>(width, height)) // TODO: Use our new weights functionality to resample on transform
{ Parallel.For(
Parallel.For( 0,
0, height,
height, configuration.ParallelOptions,
configuration.ParallelOptions, y =>
y =>
{ {
Span<TPixel> targetRow = targetPixels.GetRowSpan(y); Span<TPixel> destRow = destination.GetPixelRowSpan(y);
for (int x = 0; x < width; x++) for (int x = 0; x < width; x++)
{ {
@ -64,34 +101,16 @@ namespace SixLabors.ImageSharp.Processing.Processors
if (sourceBounds.Contains(transformedPoint.X, transformedPoint.Y)) if (sourceBounds.Contains(transformedPoint.X, transformedPoint.Y))
{ {
targetRow[x] = source[transformedPoint.X, transformedPoint.Y]; destRow[x] = source[transformedPoint.X, transformedPoint.Y];
} }
} }
}); });
source.SwapPixelsBuffers(targetPixels);
}
}
/// <inheritdoc/>
protected override void BeforeApply(ImageFrame<TPixel> source, Rectangle sourceRectangle, Configuration configuration)
{
if (MathF.Abs(this.Angle) < Constants.Epsilon || MathF.Abs(this.Angle - 90) < Constants.Epsilon || MathF.Abs(this.Angle - 180) < Constants.Epsilon || MathF.Abs(this.Angle - 270) < Constants.Epsilon)
{
return;
}
this.processMatrix = Matrix3x2Extensions.CreateRotationDegrees(-this.Angle, new Point(0, 0));
if (this.Expand)
{
this.CreateNewCanvas(sourceRectangle, this.processMatrix);
}
} }
/// <inheritdoc/> /// <inheritdoc/>
protected override void AfterImageApply(Image<TPixel> source, Rectangle sourceRectangle) protected override void AfterImageApply(Image<TPixel> source, Image<TPixel> destination, Rectangle sourceRectangle)
{ {
ExifProfile profile = source.MetaData.ExifProfile; ExifProfile profile = destination.MetaData.ExifProfile;
if (profile == null) if (profile == null)
{ {
return; return;
@ -116,33 +135,35 @@ namespace SixLabors.ImageSharp.Processing.Processors
/// Rotates the images with an optimized method when the angle is 90, 180 or 270 degrees. /// Rotates the images with an optimized method when the angle is 90, 180 or 270 degrees.
/// </summary> /// </summary>
/// <param name="source">The source image.</param> /// <param name="source">The source image.</param>
/// <param name="destination">The destination image.</param>
/// <param name="configuration">The configuration.</param> /// <param name="configuration">The configuration.</param>
/// <returns> /// <returns>
/// The <see cref="bool" /> /// The <see cref="bool" />
/// </returns> /// </returns>
private bool OptimizedApply(ImageFrame<TPixel> source, Configuration configuration) private bool OptimizedApply(ImageFrame<TPixel> source, ImageFrame<TPixel> destination, Configuration configuration)
{ {
if (MathF.Abs(this.Angle) < Constants.Epsilon) if (MathF.Abs(this.Angle) < Constants.Epsilon)
{ {
// No need to do anything so return. // The destination will be blank here so copy all the pixel data over
source.GetPixelSpan().CopyTo(destination.GetPixelSpan());
return true; return true;
} }
if (MathF.Abs(this.Angle - 90) < Constants.Epsilon) if (MathF.Abs(this.Angle - 90) < Constants.Epsilon)
{ {
this.Rotate90(source, configuration); this.Rotate90(source, destination, configuration);
return true; return true;
} }
if (MathF.Abs(this.Angle - 180) < Constants.Epsilon) if (MathF.Abs(this.Angle - 180) < Constants.Epsilon)
{ {
this.Rotate180(source, configuration); this.Rotate180(source, destination, configuration);
return true; return true;
} }
if (MathF.Abs(this.Angle - 270) < Constants.Epsilon) if (MathF.Abs(this.Angle - 270) < Constants.Epsilon)
{ {
this.Rotate270(source, configuration); this.Rotate270(source, destination, configuration);
return true; return true;
} }
@ -153,95 +174,82 @@ namespace SixLabors.ImageSharp.Processing.Processors
/// Rotates the image 270 degrees clockwise at the centre point. /// Rotates the image 270 degrees clockwise at the centre point.
/// </summary> /// </summary>
/// <param name="source">The source image.</param> /// <param name="source">The source image.</param>
/// <param name="destination">The destination image.</param>
/// <param name="configuration">The configuration.</param> /// <param name="configuration">The configuration.</param>
private void Rotate270(ImageFrame<TPixel> source, Configuration configuration) private void Rotate270(ImageFrame<TPixel> source, ImageFrame<TPixel> destination, Configuration configuration)
{ {
int width = source.Width; int width = source.Width;
int height = source.Height; int height = source.Height;
using (var targetPixels = new PixelAccessor<TPixel>(height, width)) Parallel.For(
{ 0,
using (PixelAccessor<TPixel> sourcePixels = source.Lock()) height,
configuration.ParallelOptions,
y =>
{ {
Parallel.For( Span<TPixel> sourceRow = source.GetPixelRowSpan(y);
0, for (int x = 0; x < width; x++)
height, {
configuration.ParallelOptions, int newX = height - y - 1;
y => newX = height - newX - 1;
{ int newY = width - x - 1;
for (int x = 0; x < width; x++)
{
int newX = height - y - 1;
newX = height - newX - 1;
int newY = width - x - 1;
targetPixels[newX, newY] = sourcePixels[x, y];
}
});
}
source.SwapPixelsBuffers(targetPixels); destination[newX, newY] = sourceRow[x];
} }
});
} }
/// <summary> /// <summary>
/// Rotates the image 180 degrees clockwise at the centre point. /// Rotates the image 180 degrees clockwise at the centre point.
/// </summary> /// </summary>
/// <param name="source">The source image.</param> /// <param name="source">The source image.</param>
/// <param name="destination">The destination image.</param>
/// <param name="configuration">The configuration.</param> /// <param name="configuration">The configuration.</param>
private void Rotate180(ImageFrame<TPixel> source, Configuration configuration) private void Rotate180(ImageFrame<TPixel> source, ImageFrame<TPixel> destination, Configuration configuration)
{ {
int width = source.Width; int width = source.Width;
int height = source.Height; int height = source.Height;
using (var targetPixels = new PixelAccessor<TPixel>(width, height)) Parallel.For(
{ 0,
Parallel.For( height,
0, configuration.ParallelOptions,
height, y =>
configuration.ParallelOptions, {
y => Span<TPixel> sourceRow = source.GetPixelRowSpan(y);
{ Span<TPixel> targetRow = destination.GetPixelRowSpan(height - y - 1);
Span<TPixel> sourceRow = source.GetPixelRowSpan(y);
Span<TPixel> targetRow = targetPixels.GetRowSpan(height - y - 1);
for (int x = 0; x < width; x++)
{
targetRow[width - x - 1] = sourceRow[x];
}
});
source.SwapPixelsBuffers(targetPixels); for (int x = 0; x < width; x++)
} {
targetRow[width - x - 1] = sourceRow[x];
}
});
} }
/// <summary> /// <summary>
/// Rotates the image 90 degrees clockwise at the centre point. /// Rotates the image 90 degrees clockwise at the centre point.
/// </summary> /// </summary>
/// <param name="source">The source image.</param> /// <param name="source">The source image.</param>
/// <param name="destination">The destination image.</param>
/// <param name="configuration">The configuration.</param> /// <param name="configuration">The configuration.</param>
private void Rotate90(ImageFrame<TPixel> source, Configuration configuration) private void Rotate90(ImageFrame<TPixel> source, ImageFrame<TPixel> destination, Configuration configuration)
{ {
int width = source.Width; int width = source.Width;
int height = source.Height; int height = source.Height;
using (var targetPixels = new PixelAccessor<TPixel>(height, width)) Parallel.For(
{ 0,
Parallel.For( height,
0, configuration.ParallelOptions,
height, y =>
configuration.ParallelOptions, {
y => Span<TPixel> sourceRow = source.GetPixelRowSpan(y);
int newX = height - y - 1;
for (int x = 0; x < width; x++)
{ {
Span<TPixel> sourceRow = source.GetPixelRowSpan(y); destination[newX, x] = sourceRow[x];
int newX = height - y - 1; }
for (int x = 0; x < width; x++) });
{
targetPixels[newX, x] = sourceRow[x];
}
});
source.SwapPixelsBuffers(targetPixels);
}
} }
} }
} }

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

@ -6,7 +6,6 @@ using System.Numerics;
using System.Threading.Tasks; using System.Threading.Tasks;
using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.Helpers; using SixLabors.ImageSharp.Helpers;
using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.PixelFormats;
using SixLabors.Primitives; using SixLabors.Primitives;
@ -16,13 +15,19 @@ namespace SixLabors.ImageSharp.Processing.Processors
/// Provides methods that allow the skewing of images. /// Provides methods that allow the skewing of images.
/// </summary> /// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam> /// <typeparam name="TPixel">The pixel format.</typeparam>
internal class SkewProcessor<TPixel> : Matrix3x2Processor<TPixel> internal class SkewProcessor<TPixel> : AffineProcessor<TPixel>
where TPixel : struct, IPixel<TPixel> where TPixel : struct, IPixel<TPixel>
{ {
private Matrix3x2 transformMatrix;
/// <summary> /// <summary>
/// The transform matrix to apply. /// Initializes a new instance of the <see cref="SkewProcessor{TPixel}"/> class.
/// </summary> /// </summary>
private Matrix3x2 processMatrix; /// <param name="sampler">The sampler to perform the skew operation.</param>
public SkewProcessor(IResampler sampler)
: base(sampler)
{
}
/// <summary> /// <summary>
/// Gets or sets the angle of rotation along the x-axis in degrees. /// Gets or sets the angle of rotation along the x-axis in degrees.
@ -34,52 +39,44 @@ namespace SixLabors.ImageSharp.Processing.Processors
/// </summary> /// </summary>
public float AngleY { get; set; } public float AngleY { get; set; }
/// <summary> /// <inheritdoc/>
/// Gets or sets a value indicating whether to expand the canvas to fit the skewed image. protected override Matrix3x2 CreateProcessingMatrix()
/// </summary> {
public bool Expand { get; set; } = true; if (this.transformMatrix == default(Matrix3x2))
{
this.transformMatrix = Matrix3x2Extensions.CreateSkewDegrees(-this.AngleX, -this.AngleY, new Point(0, 0));
}
return this.transformMatrix;
}
/// <inheritdoc/> /// <inheritdoc/>
protected override void OnApply(ImageFrame<TPixel> source, Rectangle sourceRectangle, Configuration configuration) protected override void OnApply(ImageFrame<TPixel> source, ImageFrame<TPixel> destination, Rectangle sourceRectangle, Configuration configuration)
{ {
int height = this.CanvasRectangle.Height; int height = this.ResizeRectangle.Height;
int width = this.CanvasRectangle.Width; int width = this.ResizeRectangle.Width;
Matrix3x2 matrix = this.GetCenteredMatrix(source, this.processMatrix); Matrix3x2 matrix = this.GetCenteredMatrix(source, this.CreateProcessingMatrix());
Rectangle sourceBounds = source.Bounds(); Rectangle sourceBounds = source.Bounds();
using (var targetPixels = new PixelAccessor<TPixel>(width, height)) // TODO: Use our new weights functionality to resample on transform
{ Parallel.For(
Parallel.For( 0,
0, height,
height, configuration.ParallelOptions,
configuration.ParallelOptions, y =>
y => {
Span<TPixel> destRow = destination.GetPixelRowSpan(y);
for (int x = 0; x < width; x++)
{ {
Span<TPixel> targetRow = targetPixels.GetRowSpan(y); var transformedPoint = Point.Skew(new Point(x, y), matrix);
for (int x = 0; x < width; x++) if (sourceBounds.Contains(transformedPoint.X, transformedPoint.Y))
{ {
var transformedPoint = Point.Skew(new Point(x, y), matrix); destRow[x] = source[transformedPoint.X, transformedPoint.Y];
if (sourceBounds.Contains(transformedPoint.X, transformedPoint.Y))
{
targetRow[x] = source[transformedPoint.X, transformedPoint.Y];
}
} }
}); }
});
source.SwapPixelsBuffers(targetPixels);
}
}
/// <inheritdoc/>
protected override void BeforeApply(ImageFrame<TPixel> source, Rectangle sourceRectangle, Configuration configuration)
{
this.processMatrix = Matrix3x2Extensions.CreateSkewDegrees(-this.AngleX, -this.AngleY, new Point(0, 0));
if (this.Expand)
{
this.CreateNewCanvas(sourceRectangle, this.processMatrix);
}
} }
} }
} }

3
src/ImageSharp/Processing/Transforms/Rotate.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;
using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Processing; using SixLabors.ImageSharp.Processing;
using SixLabors.ImageSharp.Processing.Processors; using SixLabors.ImageSharp.Processing.Processors;
@ -47,6 +46,6 @@ namespace SixLabors.ImageSharp
/// <returns>The <see cref="Image{TPixel}"/></returns> /// <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, bool expand)
where TPixel : struct, IPixel<TPixel> where TPixel : struct, IPixel<TPixel>
=> source.ApplyProcessor(new RotateProcessor<TPixel> { Angle = degrees, Expand = expand }); => source.ApplyProcessor(new RotateProcessor<TPixel>(new BicubicResampler()) { Angle = degrees, Expand = expand });
} }
} }

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

@ -1,8 +1,8 @@
// 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;
using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Processing;
using SixLabors.ImageSharp.Processing.Processors; using SixLabors.ImageSharp.Processing.Processors;
namespace SixLabors.ImageSharp namespace SixLabors.ImageSharp
@ -37,6 +37,6 @@ namespace SixLabors.ImageSharp
/// <returns>The <see cref="Image{TPixel}"/></returns> /// <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, bool expand)
where TPixel : struct, IPixel<TPixel> where TPixel : struct, IPixel<TPixel>
=> source.ApplyProcessor(new SkewProcessor<TPixel> { AngleX = degreesX, AngleY = degreesY, Expand = expand }); => source.ApplyProcessor(new SkewProcessor<TPixel>(new BicubicResampler()) { AngleX = degreesX, AngleY = degreesY, Expand = expand });
} }
} }

Loading…
Cancel
Save