mirror of https://github.com/SixLabors/ImageSharp
17 changed files with 475 additions and 661 deletions
@ -1,40 +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.Transforms |
|
||||
{ |
|
||||
/// <summary>
|
|
||||
/// A base class that provides methods to allow the automatic centering of non-affine transforms
|
|
||||
/// </summary>
|
|
||||
/// <typeparam name="TPixel">The pixel format.</typeparam>
|
|
||||
internal abstract class CenteredProjectiveTransformProcessor<TPixel> : ProjectiveTransformProcessor<TPixel> |
|
||||
where TPixel : struct, IPixel<TPixel> |
|
||||
{ |
|
||||
/// <summary>
|
|
||||
/// Initializes a new instance of the <see cref="CenteredProjectiveTransformProcessor{TPixel}"/> class.
|
|
||||
/// </summary>
|
|
||||
/// <param name="matrix">The transform matrix</param>
|
|
||||
/// <param name="sampler">The sampler to perform the transform operation.</param>
|
|
||||
/// <param name="sourceSize">The source image size</param>
|
|
||||
protected CenteredProjectiveTransformProcessor(Matrix4x4 matrix, IResampler sampler, Size sourceSize) |
|
||||
: base(matrix, sampler, GetTransformedDimensions(sourceSize, matrix)) |
|
||||
{ |
|
||||
} |
|
||||
|
|
||||
/// <inheritdoc/>
|
|
||||
protected override Matrix4x4 GetProcessingMatrix(Rectangle sourceRectangle, Rectangle destinationRectangle) |
|
||||
{ |
|
||||
return TransformHelpers.GetCenteredTransformMatrix(sourceRectangle, destinationRectangle, this.TransformMatrix); |
|
||||
} |
|
||||
|
|
||||
private static Size GetTransformedDimensions(Size sourceDimensions, Matrix4x4 matrix) |
|
||||
{ |
|
||||
var sourceRectangle = new Rectangle(0, 0, sourceDimensions.Width, sourceDimensions.Height); |
|
||||
return TransformHelpers.GetTransformedBoundingRectangle(sourceRectangle, matrix).Size; |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -1,116 +0,0 @@ |
|||||
// Copyright (c) Six Labors and contributors.
|
|
||||
// Licensed under the Apache License, Version 2.0.
|
|
||||
|
|
||||
using System; |
|
||||
using System.Runtime.CompilerServices; |
|
||||
using SixLabors.ImageSharp.PixelFormats; |
|
||||
|
|
||||
namespace SixLabors.ImageSharp.Processing.Processors.Transforms |
|
||||
{ |
|
||||
/// <summary>
|
|
||||
/// The base class for performing interpolated affine and non-affine transforms.
|
|
||||
/// </summary>
|
|
||||
/// <typeparam name="TPixel">The pixel format.</typeparam>
|
|
||||
internal abstract class InterpolatedTransformProcessorBase<TPixel> : TransformProcessorBase<TPixel> |
|
||||
where TPixel : struct, IPixel<TPixel> |
|
||||
{ |
|
||||
/// <summary>
|
|
||||
/// Initializes a new instance of the <see cref="InterpolatedTransformProcessorBase{TPixel}"/> class.
|
|
||||
/// </summary>
|
|
||||
/// <param name="sampler">The sampler to perform the transform operation.</param>
|
|
||||
protected InterpolatedTransformProcessorBase(IResampler sampler) |
|
||||
{ |
|
||||
Guard.NotNull(sampler, nameof(sampler)); |
|
||||
this.Sampler = sampler; |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets the sampler to perform interpolation of the transform operation.
|
|
||||
/// </summary>
|
|
||||
public IResampler Sampler { get; } |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Calculated the weights for the given point.
|
|
||||
/// This method uses more samples than the upscaled version to ensure edge pixels are correctly rendered.
|
|
||||
/// Additionally the weights are normalized.
|
|
||||
/// </summary>
|
|
||||
/// <param name="min">The minimum sampling offset</param>
|
|
||||
/// <param name="max">The maximum sampling offset</param>
|
|
||||
/// <param name="sourceMin">The minimum source bounds</param>
|
|
||||
/// <param name="sourceMax">The maximum source bounds</param>
|
|
||||
/// <param name="point">The transformed point dimension</param>
|
|
||||
/// <param name="sampler">The sampler</param>
|
|
||||
/// <param name="scale">The transformed image scale relative to the source</param>
|
|
||||
/// <param name="weightsRef">The reference to the collection of weights</param>
|
|
||||
/// <param name="length">The length of the weights collection</param>
|
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|
||||
protected static void CalculateWeightsDown(int min, int max, int sourceMin, int sourceMax, float point, IResampler sampler, float scale, ref float weightsRef, int length) |
|
||||
{ |
|
||||
float sum = 0; |
|
||||
|
|
||||
// Downsampling weights requires more edge sampling plus normalization of the weights
|
|
||||
for (int x = 0, i = min; i <= max; i++, x++) |
|
||||
{ |
|
||||
int index = i; |
|
||||
if (index < sourceMin) |
|
||||
{ |
|
||||
index = sourceMin; |
|
||||
} |
|
||||
|
|
||||
if (index > sourceMax) |
|
||||
{ |
|
||||
index = sourceMax; |
|
||||
} |
|
||||
|
|
||||
float weight = sampler.GetValue((index - point) / scale); |
|
||||
sum += weight; |
|
||||
Unsafe.Add(ref weightsRef, x) = weight; |
|
||||
} |
|
||||
|
|
||||
if (sum > 0) |
|
||||
{ |
|
||||
for (int i = 0; i < length; i++) |
|
||||
{ |
|
||||
ref float wRef = ref Unsafe.Add(ref weightsRef, i); |
|
||||
wRef /= sum; |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Calculated the weights for the given point.
|
|
||||
/// </summary>
|
|
||||
/// <param name="sourceMin">The minimum source bounds</param>
|
|
||||
/// <param name="sourceMax">The maximum source bounds</param>
|
|
||||
/// <param name="point">The transformed point dimension</param>
|
|
||||
/// <param name="sampler">The sampler</param>
|
|
||||
/// <param name="weightsRef">The reference to the collection of weights</param>
|
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|
||||
protected static void CalculateWeightsScaleUp(int sourceMin, int sourceMax, float point, IResampler sampler, ref float weightsRef) |
|
||||
{ |
|
||||
for (int x = 0, i = sourceMin; i <= sourceMax; i++, x++) |
|
||||
{ |
|
||||
Unsafe.Add(ref weightsRef, x) = sampler.GetValue(i - point); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Calculates the sampling radius for the current sampler
|
|
||||
/// </summary>
|
|
||||
/// <param name="sourceSize">The source dimension size</param>
|
|
||||
/// <param name="destinationSize">The destination dimension size</param>
|
|
||||
/// <returns>The radius, and scaling factor</returns>
|
|
||||
protected (float radius, float scale, float ratio) GetSamplingRadius(int sourceSize, int destinationSize) |
|
||||
{ |
|
||||
float ratio = (float)sourceSize / destinationSize; |
|
||||
float scale = ratio; |
|
||||
|
|
||||
if (scale < 1F) |
|
||||
{ |
|
||||
scale = 1F; |
|
||||
} |
|
||||
|
|
||||
return (MathF.Ceiling(scale * this.Sampler.Radius), scale, ratio); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -1,138 +0,0 @@ |
|||||
// Copyright (c) Six Labors and contributors.
|
|
||||
// Licensed under the Apache License, Version 2.0.
|
|
||||
|
|
||||
using System; |
|
||||
using System.Numerics; |
|
||||
using SixLabors.ImageSharp.MetaData.Profiles.Exif; |
|
||||
using SixLabors.ImageSharp.PixelFormats; |
|
||||
using SixLabors.Primitives; |
|
||||
|
|
||||
namespace SixLabors.ImageSharp.Processing.Processors.Transforms |
|
||||
{ |
|
||||
/// <summary>
|
|
||||
/// Contains helper methods for working with affine and non-affine transforms
|
|
||||
/// </summary>
|
|
||||
internal static class TransformHelpers |
|
||||
{ |
|
||||
/// <summary>
|
|
||||
/// Updates the dimensional metadata of a transformed image
|
|
||||
/// </summary>
|
|
||||
/// <typeparam name="TPixel">The pixel format.</typeparam>
|
|
||||
/// <param name="image">The image to update</param>
|
|
||||
public static void UpdateDimensionalMetData<TPixel>(Image<TPixel> image) |
|
||||
where TPixel : struct, IPixel<TPixel> |
|
||||
{ |
|
||||
ExifProfile profile = image.MetaData.ExifProfile; |
|
||||
if (profile is null) |
|
||||
{ |
|
||||
return; |
|
||||
} |
|
||||
|
|
||||
// Removing the previously stored value allows us to set a value with our own data tag if required.
|
|
||||
if (profile.GetValue(ExifTag.PixelXDimension) != null) |
|
||||
{ |
|
||||
profile.RemoveValue(ExifTag.PixelXDimension); |
|
||||
|
|
||||
if (image.Width <= ushort.MaxValue) |
|
||||
{ |
|
||||
profile.SetValue(ExifTag.PixelXDimension, (ushort)image.Width); |
|
||||
} |
|
||||
else |
|
||||
{ |
|
||||
profile.SetValue(ExifTag.PixelXDimension, (uint)image.Width); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
if (profile.GetValue(ExifTag.PixelYDimension) != null) |
|
||||
{ |
|
||||
profile.RemoveValue(ExifTag.PixelYDimension); |
|
||||
|
|
||||
if (image.Height <= ushort.MaxValue) |
|
||||
{ |
|
||||
profile.SetValue(ExifTag.PixelYDimension, (ushort)image.Height); |
|
||||
} |
|
||||
else |
|
||||
{ |
|
||||
profile.SetValue(ExifTag.PixelYDimension, (uint)image.Height); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets the centered transform matrix based upon the source and destination rectangles
|
|
||||
/// </summary>
|
|
||||
/// <param name="sourceRectangle">The source image bounds.</param>
|
|
||||
/// <param name="destinationRectangle">The destination image bounds.</param>
|
|
||||
/// <param name="matrix">The transformation matrix.</param>
|
|
||||
/// <returns>The <see cref="Matrix3x2"/></returns>
|
|
||||
public static Matrix3x2 GetCenteredTransformMatrix(Rectangle sourceRectangle, Rectangle destinationRectangle, Matrix3x2 matrix) |
|
||||
{ |
|
||||
// We invert the matrix to handle the transformation from screen to world space.
|
|
||||
// This ensures scaling matrices are correct.
|
|
||||
Matrix3x2.Invert(matrix, out Matrix3x2 inverted); |
|
||||
|
|
||||
var translationToTargetCenter = Matrix3x2.CreateTranslation(-destinationRectangle.Width * .5F, -destinationRectangle.Height * .5F); |
|
||||
var translateToSourceCenter = Matrix3x2.CreateTranslation(sourceRectangle.Width * .5F, sourceRectangle.Height * .5F); |
|
||||
|
|
||||
Matrix3x2.Invert(translationToTargetCenter * inverted * translateToSourceCenter, out Matrix3x2 centered); |
|
||||
|
|
||||
// Translate back to world to pass to the Transform method.
|
|
||||
return centered; |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets the centered transform matrix based upon the source and destination rectangles
|
|
||||
/// </summary>
|
|
||||
/// <param name="sourceRectangle">The source image bounds.</param>
|
|
||||
/// <param name="destinationRectangle">The destination image bounds.</param>
|
|
||||
/// <param name="matrix">The transformation matrix.</param>
|
|
||||
/// <returns>The <see cref="Matrix4x4"/></returns>
|
|
||||
public static Matrix4x4 GetCenteredTransformMatrix(Rectangle sourceRectangle, Rectangle destinationRectangle, Matrix4x4 matrix) |
|
||||
{ |
|
||||
// We invert the matrix to handle the transformation from screen to world space.
|
|
||||
// This ensures scaling matrices are correct.
|
|
||||
Matrix4x4.Invert(matrix, out Matrix4x4 inverted); |
|
||||
|
|
||||
var translationToTargetCenter = Matrix4x4.CreateTranslation(-destinationRectangle.Width * .5F, -destinationRectangle.Height * .5F, 0); |
|
||||
var translateToSourceCenter = Matrix4x4.CreateTranslation(sourceRectangle.Width * .5F, sourceRectangle.Height * .5F, 0); |
|
||||
|
|
||||
Matrix4x4.Invert(translationToTargetCenter * inverted * translateToSourceCenter, out Matrix4x4 centered); |
|
||||
|
|
||||
// Translate back to world to pass to the Transform method.
|
|
||||
return centered; |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Returns the bounding rectangle relative to the source for the given transformation matrix.
|
|
||||
/// </summary>
|
|
||||
/// <param name="rectangle">The source rectangle.</param>
|
|
||||
/// <param name="matrix">The transformation matrix.</param>
|
|
||||
/// <returns>
|
|
||||
/// The <see cref="Rectangle"/>.
|
|
||||
/// </returns>
|
|
||||
public static Rectangle GetTransformedBoundingRectangle(Rectangle rectangle, Matrix4x4 matrix) |
|
||||
{ |
|
||||
// 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); |
|
||||
|
|
||||
return GetBoundingRectangle(tl, tr, bl, br); |
|
||||
} |
|
||||
|
|
||||
private static Rectangle GetBoundingRectangle(Vector2 tl, Vector2 tr, Vector2 bl, Vector2 br) |
|
||||
{ |
|
||||
// Find the minimum and maximum "corners" based on the given vectors
|
|
||||
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))); |
|
||||
float sizeX = maxX - minX + .5F; |
|
||||
float sizeY = maxY - minY + .5F; |
|
||||
|
|
||||
return new Rectangle((int)(MathF.Ceiling(minX) - .5F), (int)(MathF.Ceiling(minY) - .5F), (int)MathF.Floor(sizeX), (int)MathF.Floor(sizeY)); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -0,0 +1,58 @@ |
|||||
|
// Copyright (c) Six Labors and contributors.
|
||||
|
// Licensed under the Apache License, Version 2.0.
|
||||
|
|
||||
|
using SixLabors.ImageSharp.MetaData.Profiles.Exif; |
||||
|
using SixLabors.ImageSharp.PixelFormats; |
||||
|
|
||||
|
namespace SixLabors.ImageSharp.Processing.Processors.Transforms |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Contains helper methods for working with transforms.
|
||||
|
/// </summary>
|
||||
|
internal static class TransformProcessorHelpers |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Updates the dimensional metadata of a transformed image
|
||||
|
/// </summary>
|
||||
|
/// <typeparam name="TPixel">The pixel format.</typeparam>
|
||||
|
/// <param name="image">The image to update</param>
|
||||
|
public static void UpdateDimensionalMetData<TPixel>(Image<TPixel> image) |
||||
|
where TPixel : struct, IPixel<TPixel> |
||||
|
{ |
||||
|
ExifProfile profile = image.MetaData.ExifProfile; |
||||
|
if (profile is null) |
||||
|
{ |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
// Removing the previously stored value allows us to set a value with our own data tag if required.
|
||||
|
if (profile.GetValue(ExifTag.PixelXDimension) != null) |
||||
|
{ |
||||
|
profile.RemoveValue(ExifTag.PixelXDimension); |
||||
|
|
||||
|
if (image.Width <= ushort.MaxValue) |
||||
|
{ |
||||
|
profile.SetValue(ExifTag.PixelXDimension, (ushort)image.Width); |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
profile.SetValue(ExifTag.PixelXDimension, (uint)image.Width); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
if (profile.GetValue(ExifTag.PixelYDimension) != null) |
||||
|
{ |
||||
|
profile.RemoveValue(ExifTag.PixelYDimension); |
||||
|
|
||||
|
if (image.Height <= ushort.MaxValue) |
||||
|
{ |
||||
|
profile.SetValue(ExifTag.PixelYDimension, (ushort)image.Height); |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
profile.SetValue(ExifTag.PixelYDimension, (uint)image.Height); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,113 @@ |
|||||
|
// Copyright (c) Six Labors and contributors.
|
||||
|
// Licensed under the Apache License, Version 2.0.
|
||||
|
|
||||
|
using System.Collections.Generic; |
||||
|
using System.Numerics; |
||||
|
using SixLabors.ImageSharp.Processing.Processors.Transforms; |
||||
|
using SixLabors.Primitives; |
||||
|
|
||||
|
namespace SixLabors.ImageSharp.Processing |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// A helper class for constructing <see cref="Matrix4x4"/> instances for use in projective transforms.
|
||||
|
/// </summary>
|
||||
|
public class ProjectiveTransformBuilder |
||||
|
{ |
||||
|
private readonly List<Matrix4x4> matrices = new List<Matrix4x4>(); |
||||
|
private Rectangle rectangle; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Initializes a new instance of the <see cref="ProjectiveTransformBuilder"/> class.
|
||||
|
/// </summary>
|
||||
|
/// <param name="sourceSize">The source image size.</param>
|
||||
|
public ProjectiveTransformBuilder(Size sourceSize) => this.Size = sourceSize; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Initializes a new instance of the <see cref="ProjectiveTransformBuilder"/> class.
|
||||
|
/// </summary>
|
||||
|
/// <param name="sourceRectangle">The source rectangle.</param>
|
||||
|
public ProjectiveTransformBuilder(Rectangle sourceRectangle) |
||||
|
: this(sourceRectangle.Size) |
||||
|
=> this.rectangle = sourceRectangle; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets the source image size.
|
||||
|
/// </summary>
|
||||
|
internal Size Size { get; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Prepends a matrix that performs a tapering projective transform.
|
||||
|
/// </summary>
|
||||
|
/// <param name="side">An enumeration that indicates the side of the rectangle that tapers.</param>
|
||||
|
/// <param name="corner">An enumeration that indicates on which corners to taper the rectangle.</param>
|
||||
|
/// <param name="fraction">The amount to taper.</param>
|
||||
|
/// <returns>The <see cref="ProjectiveTransformBuilder"/>.</returns>
|
||||
|
public ProjectiveTransformBuilder PrependTaperMatrix(TaperSide side, TaperCorner corner, float fraction) |
||||
|
{ |
||||
|
this.matrices.Insert(0, TransformUtils.CreateTaperMatrix(this.Size, side, corner, fraction)); |
||||
|
return this; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Appends a matrix that performs a tapering projective transform.
|
||||
|
/// </summary>
|
||||
|
/// <param name="side">An enumeration that indicates the side of the rectangle that tapers.</param>
|
||||
|
/// <param name="corner">An enumeration that indicates on which corners to taper the rectangle.</param>
|
||||
|
/// <param name="fraction">The amount to taper.</param>
|
||||
|
/// <returns>The <see cref="ProjectiveTransformBuilder"/>.</returns>
|
||||
|
public ProjectiveTransformBuilder AppendTaperMatrix(TaperSide side, TaperCorner corner, float fraction) |
||||
|
{ |
||||
|
this.matrices.Add(TransformUtils.CreateTaperMatrix(this.Size, side, corner, fraction)); |
||||
|
return this; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Prepends a raw matrix.
|
||||
|
/// </summary>
|
||||
|
/// <param name="matrix">The matrix to prepend.</param>
|
||||
|
/// <returns>The <see cref="ProjectiveTransformBuilder"/>.</returns>
|
||||
|
public ProjectiveTransformBuilder PrependMatrix(Matrix4x4 matrix) |
||||
|
{ |
||||
|
this.matrices.Insert(0, matrix); |
||||
|
return this; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Appends a raw matrix.
|
||||
|
/// </summary>
|
||||
|
/// <param name="matrix">The matrix to append.</param>
|
||||
|
/// <returns>The <see cref="ProjectiveTransformBuilder"/>.</returns>
|
||||
|
public ProjectiveTransformBuilder AppendMatrix(Matrix4x4 matrix) |
||||
|
{ |
||||
|
this.matrices.Add(matrix); |
||||
|
return this; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Returns the combined matrix.
|
||||
|
/// </summary>
|
||||
|
/// <returns>The <see cref="Matrix4x4"/>.</returns>
|
||||
|
public Matrix4x4 BuildMatrix() |
||||
|
{ |
||||
|
Matrix4x4 matrix = Matrix4x4.Identity; |
||||
|
|
||||
|
// Translate the origin matrix to cater for source rectangle offsets.
|
||||
|
if (!this.rectangle.Equals(default)) |
||||
|
{ |
||||
|
matrix *= Matrix4x4.CreateTranslation(new Vector3(-this.rectangle.Location, 0)); |
||||
|
} |
||||
|
|
||||
|
foreach (Matrix4x4 m in this.matrices) |
||||
|
{ |
||||
|
matrix *= m; |
||||
|
} |
||||
|
|
||||
|
return matrix; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Removes all matrices from the builder.
|
||||
|
/// </summary>
|
||||
|
public void Clear() => this.matrices.Clear(); |
||||
|
} |
||||
|
} |
||||
@ -1,166 +0,0 @@ |
|||||
// Copyright (c) Six Labors and contributors.
|
|
||||
// Licensed under the Apache License, Version 2.0.
|
|
||||
|
|
||||
using System.Numerics; |
|
||||
using SixLabors.Primitives; |
|
||||
|
|
||||
namespace SixLabors.ImageSharp.Processing |
|
||||
{ |
|
||||
/// <summary>
|
|
||||
/// Enumerates the various options which determine which side to taper
|
|
||||
/// </summary>
|
|
||||
public enum TaperSide |
|
||||
{ |
|
||||
/// <summary>
|
|
||||
/// Taper the left side
|
|
||||
/// </summary>
|
|
||||
Left, |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Taper the top side
|
|
||||
/// </summary>
|
|
||||
Top, |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Taper the right side
|
|
||||
/// </summary>
|
|
||||
Right, |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Taper the bottom side
|
|
||||
/// </summary>
|
|
||||
Bottom |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Enumerates the various options which determine how to taper corners
|
|
||||
/// </summary>
|
|
||||
public enum TaperCorner |
|
||||
{ |
|
||||
/// <summary>
|
|
||||
/// Taper the left or top corner
|
|
||||
/// </summary>
|
|
||||
LeftOrTop, |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Taper the right or bottom corner
|
|
||||
/// </summary>
|
|
||||
RightOrBottom, |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Taper the both sets of corners
|
|
||||
/// </summary>
|
|
||||
Both |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Provides helper methods for working with generalized projective transforms.
|
|
||||
/// </summary>
|
|
||||
public static class ProjectiveTransformHelper |
|
||||
{ |
|
||||
/// <summary>
|
|
||||
/// Creates a matrix that performs a tapering projective transform.
|
|
||||
/// <see href="https://docs.microsoft.com/en-us/xamarin/xamarin-forms/user-interface/graphics/skiasharp/transforms/non-affine"/>
|
|
||||
/// </summary>
|
|
||||
/// <param name="size">The rectangular size of the image being transformed.</param>
|
|
||||
/// <param name="taperSide">An enumeration that indicates the side of the rectangle that tapers.</param>
|
|
||||
/// <param name="taperCorner">An enumeration that indicates on which corners to taper the rectangle.</param>
|
|
||||
/// <param name="taperFraction">The amount to taper.</param>
|
|
||||
/// <returns>The <see cref="Matrix4x4"/></returns>
|
|
||||
public static Matrix4x4 CreateTaperMatrix(Size size, TaperSide taperSide, TaperCorner taperCorner, float taperFraction) |
|
||||
{ |
|
||||
Matrix4x4 matrix = Matrix4x4.Identity; |
|
||||
|
|
||||
switch (taperSide) |
|
||||
{ |
|
||||
case TaperSide.Left: |
|
||||
matrix.M11 = taperFraction; |
|
||||
matrix.M22 = taperFraction; |
|
||||
matrix.M13 = (taperFraction - 1) / size.Width; |
|
||||
|
|
||||
switch (taperCorner) |
|
||||
{ |
|
||||
case TaperCorner.RightOrBottom: |
|
||||
break; |
|
||||
|
|
||||
case TaperCorner.LeftOrTop: |
|
||||
matrix.M12 = size.Height * matrix.M13; |
|
||||
matrix.M32 = size.Height * (1 - taperFraction); |
|
||||
break; |
|
||||
|
|
||||
case TaperCorner.Both: |
|
||||
matrix.M12 = (size.Height * 0.5f) * matrix.M13; |
|
||||
matrix.M32 = size.Height * (1 - taperFraction) / 2; |
|
||||
break; |
|
||||
} |
|
||||
|
|
||||
break; |
|
||||
|
|
||||
case TaperSide.Top: |
|
||||
matrix.M11 = taperFraction; |
|
||||
matrix.M22 = taperFraction; |
|
||||
matrix.M23 = (taperFraction - 1) / size.Height; |
|
||||
|
|
||||
switch (taperCorner) |
|
||||
{ |
|
||||
case TaperCorner.RightOrBottom: |
|
||||
break; |
|
||||
|
|
||||
case TaperCorner.LeftOrTop: |
|
||||
matrix.M21 = size.Width * matrix.M23; |
|
||||
matrix.M31 = size.Width * (1 - taperFraction); |
|
||||
break; |
|
||||
|
|
||||
case TaperCorner.Both: |
|
||||
matrix.M21 = (size.Width * 0.5f) * matrix.M23; |
|
||||
matrix.M31 = size.Width * (1 - taperFraction) / 2; |
|
||||
break; |
|
||||
} |
|
||||
|
|
||||
break; |
|
||||
|
|
||||
case TaperSide.Right: |
|
||||
matrix.M11 = 1 / taperFraction; |
|
||||
matrix.M13 = (1 - taperFraction) / (size.Width * taperFraction); |
|
||||
|
|
||||
switch (taperCorner) |
|
||||
{ |
|
||||
case TaperCorner.RightOrBottom: |
|
||||
break; |
|
||||
|
|
||||
case TaperCorner.LeftOrTop: |
|
||||
matrix.M12 = size.Height * matrix.M13; |
|
||||
break; |
|
||||
|
|
||||
case TaperCorner.Both: |
|
||||
matrix.M12 = (size.Height * 0.5f) * matrix.M13; |
|
||||
break; |
|
||||
} |
|
||||
|
|
||||
break; |
|
||||
|
|
||||
case TaperSide.Bottom: |
|
||||
matrix.M22 = 1 / taperFraction; |
|
||||
matrix.M23 = (1 - taperFraction) / (size.Height * taperFraction); |
|
||||
|
|
||||
switch (taperCorner) |
|
||||
{ |
|
||||
case TaperCorner.RightOrBottom: |
|
||||
break; |
|
||||
|
|
||||
case TaperCorner.LeftOrTop: |
|
||||
matrix.M21 = size.Width * matrix.M23; |
|
||||
break; |
|
||||
|
|
||||
case TaperCorner.Both: |
|
||||
matrix.M21 = (size.Width * 0.5f) * matrix.M23; |
|
||||
break; |
|
||||
} |
|
||||
|
|
||||
break; |
|
||||
} |
|
||||
|
|
||||
return matrix; |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -0,0 +1,26 @@ |
|||||
|
// Copyright (c) Six Labors and contributors.
|
||||
|
// Licensed under the Apache License, Version 2.0.
|
||||
|
|
||||
|
namespace SixLabors.ImageSharp.Processing |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Enumerates the various options which determine how to taper corners
|
||||
|
/// </summary>
|
||||
|
public enum TaperCorner |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Taper the left or top corner
|
||||
|
/// </summary>
|
||||
|
LeftOrTop, |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Taper the right or bottom corner
|
||||
|
/// </summary>
|
||||
|
RightOrBottom, |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Taper the both sets of corners
|
||||
|
/// </summary>
|
||||
|
Both |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,31 @@ |
|||||
|
// Copyright (c) Six Labors and contributors.
|
||||
|
// Licensed under the Apache License, Version 2.0.
|
||||
|
|
||||
|
namespace SixLabors.ImageSharp.Processing |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Enumerates the various options which determine which side to taper
|
||||
|
/// </summary>
|
||||
|
public enum TaperSide |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Taper the left side
|
||||
|
/// </summary>
|
||||
|
Left, |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Taper the top side
|
||||
|
/// </summary>
|
||||
|
Top, |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Taper the right side
|
||||
|
/// </summary>
|
||||
|
Right, |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Taper the bottom side
|
||||
|
/// </summary>
|
||||
|
Bottom |
||||
|
} |
||||
|
} |
||||
Loading…
Reference in new issue