Browse Source

Update default behaviours

af/merge-core
James Jackson-South 8 years ago
parent
commit
02f97c93b3
  1. 29
      src/ImageSharp/Common/Helpers/ImageMaths.cs
  2. 16
      src/ImageSharp/Processing/Processors/Transforms/AffineProcessor.cs
  3. 8
      src/ImageSharp/Processing/Processors/Transforms/CenteredAffineProcessor.cs
  4. 4
      src/ImageSharp/Processing/Processors/Transforms/RotateProcessor.cs
  5. 2
      src/ImageSharp/Processing/Processors/Transforms/SkewProcessor.cs
  6. 2
      src/ImageSharp/Processing/Transforms/Rotate.cs
  7. 2
      src/ImageSharp/Processing/Transforms/Skew.cs
  8. 43
      src/ImageSharp/Processing/Transforms/TransformHelpers.cs
  9. 9
      tests/ImageSharp.Tests/Processing/Transforms/TransformTests.cs

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

@ -139,35 +139,6 @@ namespace SixLabors.ImageSharp
return new Rectangle(topLeft.X, topLeft.Y, bottomRight.X - topLeft.X, bottomRight.Y - topLeft.Y);
}
/// <summary>
/// Gets the bounding <see cref="Rectangle"/> from the given matrix.
/// </summary>
/// <param name="rectangle">The source rectangle.</param>
/// <param name="matrix">The transformation matrix.</param>
/// <returns>
/// The <see cref="Rectangle"/>.
/// </returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Rectangle GetBoundingRectangle(Rectangle rectangle, Matrix3x2 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);
// 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)));
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));
}
/// <summary>
/// Finds the bounding rectangle based on the first instance of any color component other
/// than the given one.

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

@ -67,9 +67,7 @@ namespace SixLabors.ImageSharp.Processing.Processors
{
if (this.targetRectangle == Rectangle.Empty)
{
this.targetRectangle = Matrix3x2.Invert(this.TransformMatrix, out Matrix3x2 sizeMatrix)
? ImageMaths.GetBoundingRectangle(sourceRectangle, sizeMatrix)
: sourceRectangle;
this.targetRectangle = this.GetTransformedBoundingRectangle(sourceRectangle, this.TransformMatrix);
}
// We will always be creating the clone even for mutate because we may need to resize the canvas
@ -148,6 +146,7 @@ namespace SixLabors.ImageSharp.Processing.Processors
Vector2 maxXY = point + radius;
Vector2 minXY = point - radius;
// max, maxY, minX, minY
var extents = new Vector4(
MathF.Floor(maxXY.X + .5F),
MathF.Floor(maxXY.Y + .5F),
@ -245,6 +244,17 @@ namespace SixLabors.ImageSharp.Processing.Processors
return this.TransformMatrix;
}
/// <summary>
/// Gets the bounding <see cref="Rectangle"/> relative to the source for the given transformation matrix.
/// </summary>
/// <param name="sourceRectangle">The source rectangle.</param>
/// <param name="matrix">The transformation matrix.</param>
/// <returns>The <see cref="Rectangle"/></returns>
protected virtual Rectangle GetTransformedBoundingRectangle(Rectangle sourceRectangle, Matrix3x2 matrix)
{
return sourceRectangle;
}
/// <summary>
/// Calculated the weights for the given point.
/// This method uses more samples than the upscaled version to ensure edge pixels are correctly rendered.

8
src/ImageSharp/Processing/Processors/Transforms/CenteredAffineProcessor.cs

@ -27,5 +27,13 @@ namespace SixLabors.ImageSharp.Processing.Processors
var translateToSourceCenter = Matrix3x2.CreateTranslation(sourceRectangle.Width * .5F, sourceRectangle.Height * .5F);
return translationToTargetCenter * this.TransformMatrix * translateToSourceCenter;
}
/// <inheritdoc/>
protected override Rectangle GetTransformedBoundingRectangle(Rectangle sourceRectangle, Matrix3x2 matrix)
{
return Matrix3x2.Invert(this.TransformMatrix, out Matrix3x2 sizeMatrix)
? TransformHelpers.GetTransformedBoundingRectangle(sourceRectangle, sizeMatrix)
: sourceRectangle;
}
}
}

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

@ -23,7 +23,7 @@ namespace SixLabors.ImageSharp.Processing.Processors
/// </summary>
/// <param name="degrees">The angle of rotation in degrees.</param>
public RotateProcessor(float degrees)
: this(degrees, KnownResamplers.NearestNeighbor)
: this(degrees, KnownResamplers.Bicubic)
{
}
@ -83,7 +83,7 @@ namespace SixLabors.ImageSharp.Processing.Processors
{
degrees = degrees % 360;
if (degrees < 0)
while (degrees < 0)
{
degrees += 360;
}

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

@ -19,7 +19,7 @@ namespace SixLabors.ImageSharp.Processing.Processors
/// <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)
: this(degreesX, degreesY, KnownResamplers.Bicubic)
{
}

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

@ -32,7 +32,7 @@ namespace SixLabors.ImageSharp
/// <returns>The <see cref="Image{TPixel}"/></returns>
public static IImageProcessingContext<TPixel> Rotate<TPixel>(this IImageProcessingContext<TPixel> source, float degrees)
where TPixel : struct, IPixel<TPixel>
=> Rotate(source, degrees, KnownResamplers.NearestNeighbor);
=> Rotate(source, degrees, KnownResamplers.Bicubic);
/// <summary>
/// Rotates an image by the given angle in degrees using the specified sampling algorithm.

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

@ -22,7 +22,7 @@ namespace SixLabors.ImageSharp
/// <returns>The <see cref="Image{TPixel}"/></returns>
public static IImageProcessingContext<TPixel> Skew<TPixel>(this IImageProcessingContext<TPixel> source, float degreesX, float degreesY)
where TPixel : struct, IPixel<TPixel>
=> Skew(source, degreesX, degreesY, KnownResamplers.NearestNeighbor);
=> Skew(source, degreesX, degreesY, KnownResamplers.Bicubic);
/// <summary>
/// Skews an image by the given angles in degrees using the specified sampling algorithm.

43
src/ImageSharp/Processing/Transforms/TransformHelpers.cs

@ -0,0 +1,43 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
using System.Numerics;
using SixLabors.Primitives;
namespace SixLabors.ImageSharp
{
/// <summary>
/// Contains helper methods for working with affine transforms
/// </summary>
public class TransformHelpers
{
/// <summary>
/// Returns the bounding <see cref="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, Matrix3x2 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);
// 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)));
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));
}
}
}

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

@ -72,7 +72,12 @@ namespace SixLabors.ImageSharp.Tests.Processing.Transforms
var rotate = Matrix3x2.CreateRotation((float)Math.PI / 4F, new Vector2(5 / 2F, 5 / 2F));
var translate = Matrix3x2.CreateTranslation((7 - 5) / 2F, (7 - 5) / 2F);
image.Mutate(c => c.Transform(rotate * translate, resampler));
Rectangle sourceRectangle = image.Bounds();
Matrix3x2 matrix = rotate * translate;
Rectangle destRectangle = TransformHelpers.GetTransformedBoundingRectangle(sourceRectangle, matrix);
image.Mutate(c => c.Transform(matrix, resampler, destRectangle));
image.DebugSave(provider, resamplerName);
VerifyAllPixelsAreWhiteOrTransparent(image);
@ -96,7 +101,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Transforms
Matrix3x2 m = rotate * scale * translate;
this.PrintMatrix(m);
image.Mutate(i => i.Transform(m, KnownResamplers.Bicubic));
image.DebugSave(provider, $"R({angleDeg})_S({sx},{sy})_T({tx},{ty})");
}

Loading…
Cancel
Save