Browse Source

Add Skew, Enhance Rotate

Former-commit-id: 55c3fee46aa082cc2cca3232580ed42d6d40a1e1
Former-commit-id: ab1b89f66aca40b6d2afe262ab937d768cae67af
Former-commit-id: ef13eccfc48a91a3adc99eaf0b746486d069bac1
af/merge-core
James South 10 years ago
parent
commit
fb6ac13e17
  1. 5
      README.md
  2. 15
      src/ImageProcessorCore/Numerics/Point.cs
  3. 56
      src/ImageProcessorCore/Samplers/ImageSamplerExtensions.cs
  4. 9
      src/ImageProcessorCore/Samplers/Rotate.cs
  5. 124
      src/ImageProcessorCore/Samplers/Skew.cs
  6. 27
      tests/ImageProcessorCore.Tests/Processors/Samplers/SamplerTests.cs

5
README.md

@ -92,9 +92,10 @@ git clone https://github.com/JimBobSquarePants/ImageProcessor
- [x] Rectangular Crop
- [ ] Elliptical Crop
- [x] Entropy Crop
- Rotation
- Rotation/Skew
- [x] Flip (90, 270, FlipType etc)
- [x] Rotate by angle
- [x] Rotate by angle and center point.
- [x] Skew by x/y angles and center point.
- ColorMatrix operations (Uses Matrix4x4)
- [x] BlackWhite
- [x] Greyscale BT709

15
src/ImageProcessorCore/Numerics/Point.cs

@ -171,6 +171,21 @@ namespace ImageProcessorCore
return new Point(Vector2.Transform(point.backingVector, Matrix3x2.CreateRotation(radians, origin.backingVector)));
}
/// <summary>
/// Skews a point around a given origin by the specified angles in degrees.
/// </summary>
/// <param name="point">The point to rotate</param>
/// <param name="origin">The center point to rotate around.</param>
/// <param name="degreesX">The x-angle in degrees.</param>
/// <param name="degreesY">The y-angle in degrees.</param>
/// <returns></returns>
public static Point Skew(Point point, Point origin, float degreesX, float degreesY)
{
float radiansX = (float)ImageMaths.DegreesToRadians(degreesX);
float radiansY = (float)ImageMaths.DegreesToRadians(degreesY);
return new Point(Vector2.Transform(point.backingVector, Matrix3x2.CreateSkew(degreesX, degreesY, origin.backingVector)));
}
/// <inheritdoc/>
public override int GetHashCode()
{

56
src/ImageProcessorCore/Samplers/ImageSamplerExtensions.cs

@ -170,13 +170,26 @@ namespace ImageProcessorCore.Samplers
/// <summary>
/// Rotates an image by the given angle in degrees.
/// </summary>
/// <param name="source">The image to resize.</param>
/// <param name="source">The image to rotate.</param>
/// <param name="degrees">The angle in degrees to perform the rotation.</param>
/// <param name="progressHandler">A delegate which is called as progress is made processing the image.</param>
/// <returns>The <see cref="Image"/></returns>
public static Image Rotate(this Image source, float degrees, ProgressEventHandler progressHandler = null)
{
Rotate processor = new Rotate { Angle = degrees };
return Rotate(source, degrees, Rectangle.Center(source.Bounds), progressHandler);
}
/// <summary>
/// Rotates an image by the given angle in degrees around the given center point.
/// </summary>
/// <param name="source">The image to rotate.</param>
/// <param name="degrees">The angle in degrees to perform the rotation.</param>
/// <param name="center">The center point at which to skew the image.</param>
/// <param name="progressHandler">A delegate which is called as progress is made processing the image.</param>
/// <returns>The <see cref="Image"/></returns>
public static Image Rotate(this Image source, float degrees, Point center, ProgressEventHandler progressHandler = null)
{
Rotate processor = new Rotate { Angle = degrees, Center = center };
processor.OnProgress += progressHandler;
try
@ -192,7 +205,7 @@ namespace ImageProcessorCore.Samplers
/// <summary>
/// Rotates and flips an image by the given instructions.
/// </summary>
/// <param name="source">The image to resize.</param>
/// <param name="source">The image to rotate, flip, or both.</param>
/// <param name="rotateType">The <see cref="RotateType"/> to perform the rotation.</param>
/// <param name="flipType">The <see cref="FlipType"/> to perform the flip.</param>
/// <param name="progressHandler">A delegate which is called as progress is made processing the image.</param>
@ -211,5 +224,42 @@ namespace ImageProcessorCore.Samplers
processor.OnProgress -= progressHandler;
}
}
/// <summary>
/// Skews an image by the given angles in degrees.
/// </summary>
/// <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="progressHandler">A delegate which is called as progress is made processing the image.</param>
/// <returns>The <see cref="Image"/></returns>
public static Image Skew(this Image source, float degreesX, float degreesY, ProgressEventHandler progressHandler = null)
{
return Skew(source, degreesX, degreesY, Rectangle.Center(source.Bounds), progressHandler);
}
/// <summary>
/// Skews an image by the given angles in degrees around the given center point.
/// </summary>
/// <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="center">The center point at which to skew the image.</param>
/// <param name="progressHandler">A delegate which is called as progress is made processing the image.</param>
/// <returns>The <see cref="Image"/></returns>
public static Image Skew(this Image source, float degreesX, float degreesY, Point center, ProgressEventHandler progressHandler = null)
{
Skew processor = new Skew { AngleX = degreesX, AngleY = degreesY, Center = center };
processor.OnProgress += progressHandler;
try
{
return source.Process(source.Width, source.Height, source.Bounds, source.Bounds, processor);
}
finally
{
processor.OnProgress -= progressHandler;
}
}
}
}

9
src/ImageProcessorCore/Samplers/Rotate.cs

@ -8,7 +8,7 @@ namespace ImageProcessorCore.Samplers
using System.Threading.Tasks;
/// <summary>
/// Provides methods that allow the rotating of images using various algorithms.
/// Provides methods that allow the rotating of images.
/// </summary>
public class Rotate : ImageSampler
{
@ -43,6 +43,11 @@ namespace ImageProcessorCore.Samplers
}
}
/// <summary>
/// Gets or sets the center point.
/// </summary>
public Point Center { get; set; }
/// <inheritdoc/>
protected override void Apply(ImageBase target, ImageBase source, Rectangle targetRectangle, Rectangle sourceRectangle, int startY, int endY)
{
@ -51,7 +56,7 @@ namespace ImageProcessorCore.Samplers
int startX = targetRectangle.X;
int endX = targetRectangle.Right;
float negativeAngle = -this.angle;
Point centre = Rectangle.Center(sourceRectangle);
Point centre = this.Center == Point.Empty ? Rectangle.Center(sourceRectangle) : this.Center;
// Scaling factors
float widthFactor = source.Width / (float)target.Width;

124
src/ImageProcessorCore/Samplers/Skew.cs

@ -0,0 +1,124 @@
// <copyright file="Skew.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageProcessorCore.Samplers
{
using System.Threading.Tasks;
/// <summary>
/// Provides methods that allow the skewing of images.
/// </summary>
public class Skew : ImageSampler
{
/// <summary>
/// The angle of rotation along the x-axis.
/// </summary>
private float angleX;
/// <summary>
/// The angle of rotation along the y-axis.
/// </summary>
private float angleY;
/// <summary>
/// Gets or sets the angle of rotation along the x-axis in degrees.
/// </summary>
public float AngleX
{
get
{
return this.angleX;
}
set
{
if (value > 360)
{
value -= 360;
}
if (value < 0)
{
value += 360;
}
this.angleX = value;
}
}
/// <summary>
/// Gets or sets the angle of rotation along the y-axis in degrees.
/// </summary>
public float AngleY
{
get
{
return this.angleY;
}
set
{
if (value > 360)
{
value -= 360;
}
if (value < 0)
{
value += 360;
}
this.angleY = value;
}
}
/// <summary>
/// Gets or sets the center point.
/// </summary>
public Point Center { get; set; }
/// <inheritdoc/>
protected override void Apply(ImageBase target, ImageBase source, Rectangle targetRectangle, Rectangle sourceRectangle, int startY, int endY)
{
int targetY = targetRectangle.Y;
int targetBottom = targetRectangle.Bottom;
int startX = targetRectangle.X;
int endX = targetRectangle.Right;
float negativeAngleX = -this.angleX;
float negativeAngleY = -this.angleY;
Point centre = this.Center == Point.Empty ? Rectangle.Center(sourceRectangle) : this.Center;
// Scaling factors
float widthFactor = source.Width / (float)target.Width;
float heightFactor = source.Height / (float)target.Height;
Parallel.For(
startY,
endY,
y =>
{
if (y >= targetY && y < targetBottom)
{
// Y coordinates of source points
int originY = (int)((y - targetY) * heightFactor);
for (int x = startX; x < endX; x++)
{
// X coordinates of source points
int originX = (int)((x - startX) * widthFactor);
// Skew at the centre point
Point skewed = Point.Skew(new Point(originX, originY), centre, negativeAngleX, negativeAngleY);
if (sourceRectangle.Contains(skewed.X, skewed.Y))
{
target[x, y] = source[skewed.X, skewed.Y];
}
}
this.OnRowProcessed();
}
});
}
}
}

27
tests/ImageProcessorCore.Tests/Processors/Samplers/SamplerTests.cs

@ -202,7 +202,32 @@
using (FileStream output = File.OpenWrite($"TestOutput/Rotate/{filename}"))
{
image.Rotate(45, this.ProgressUpdate)
.BackgroundColor(Color.Pink)
.Save(output);
}
Trace.WriteLine($"{watch.ElapsedMilliseconds}ms");
}
}
}
[Fact]
public void ImageShouldSkew()
{
if (!Directory.Exists("TestOutput/Skew"))
{
Directory.CreateDirectory("TestOutput/Skew");
}
foreach (string file in Files)
{
using (FileStream stream = File.OpenRead(file))
{
Stopwatch watch = Stopwatch.StartNew();
Image image = new Image(stream);
string filename = Path.GetFileName(file);
using (FileStream output = File.OpenWrite($"TestOutput/Skew/{filename}"))
{
image.Skew(45, 45, this.ProgressUpdate)
.Save(output);
}

Loading…
Cancel
Save