Browse Source

Merge branch 'refs/heads/pr/421' into Core

# Conflicts:
#	src/ImageProcessorCore/Samplers/Processors/RotateProcessor.cs
#	src/ImageProcessorCore/Samplers/Processors/SkewProcessor.cs
#	tests/ImageProcessorCore.Tests/Processors/Samplers/SamplerTests.cs


Former-commit-id: d39b16dbe66bf91d4d27242fa74819f08f1a5606
Former-commit-id: 0f19f90477be6e3e9f1f37789733ec2482d7522a
Former-commit-id: f7957d2ba8a8d325febf51f97c3f66c951b1d16a
af/merge-core
James Jackson-South 10 years ago
parent
commit
6fae0623e2
  1. 47
      src/ImageProcessorCore/Samplers/Processors/Matrix3x2Processor.cs
  2. 65
      src/ImageProcessorCore/Samplers/Processors/RotateProcessor.cs
  3. 67
      src/ImageProcessorCore/Samplers/Processors/SkewProcessor.cs
  4. 11
      src/ImageProcessorCore/Samplers/Rotate.cs
  5. 11
      src/ImageProcessorCore/Samplers/Skew.cs
  6. 4
      tests/ImageProcessorCore.Tests/Processors/Samplers/SamplerTests.cs

47
src/ImageProcessorCore/Samplers/Processors/Matrix3x2Processor.cs

@ -0,0 +1,47 @@
// <copyright file="Matrix3x2Processor.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageProcessorCore.Processors
{
using System.Numerics;
/// <summary>
/// Provides methods to transform an image using a <see cref="Matrix3x2"/>.
/// </summary>
public abstract class Matrix3x2Processor : ImageSampler
{
/// <summary>
/// Creates a new target to contain the results of the matrix transform.
/// </summary>
/// <param name="target">Target image to apply the process to.</param>
/// <param name="sourceRectangle">The source rectangle.</param>
/// <param name="processMatrix">The processing matrix.</param>
protected static void CreateNewTarget(ImageBase target, Rectangle sourceRectangle, Matrix3x2 processMatrix)
{
Matrix3x2 sizeMatrix;
if (Matrix3x2.Invert(processMatrix, out sizeMatrix))
{
Rectangle rectangle = ImageMaths.GetBoundingRectangle(sourceRectangle, sizeMatrix);
target.SetPixels(rectangle.Width, rectangle.Height, new float[rectangle.Width * rectangle.Height * 4]);
}
}
/// <summary>
/// Gets a transform matrix adjusted to center upon the target image bounds.
/// </summary>
/// <param name="target">Target image to apply the process to.</param>
/// <param name="source">The source image.</param>
/// <param name="matrix">The transform matrix.</param>
/// <returns>
/// The <see cref="Matrix3x2"/>.
/// </returns>
protected static Matrix3x2 GetCenteredMatrix(ImageBase target, ImageBase source, Matrix3x2 matrix)
{
Matrix3x2 translationToTargetCenter = Matrix3x2.CreateTranslation(-target.Width / 2f, -target.Height / 2f);
Matrix3x2 translateToSourceCenter = Matrix3x2.CreateTranslation(source.Width / 2f, source.Height / 2f);
return (translationToTargetCenter * matrix) * translateToSourceCenter;
}
}
}

65
src/ImageProcessorCore/Samplers/Processors/RotateProcessor.cs

@ -11,85 +11,60 @@ namespace ImageProcessorCore.Processors
/// <summary> /// <summary>
/// Provides methods that allow the rotating of images. /// Provides methods that allow the rotating of images.
/// </summary> /// </summary>
public class RotateProcessor : ImageSampler public class RotateProcessor : Matrix3x2Processor
{ {
/// <summary>
/// The tranform matrix to apply.
/// </summary>
private Matrix3x2 processMatrix;
/// <inheritdoc/> /// <inheritdoc/>
public override int Parallelism { get; set; } = 1; public override int Parallelism { get; set; } = 1;
/// <summary> /// <summary>
/// Gets or sets the angle of rotation in degrees. /// Gets or sets the angle of processMatrix in degrees.
/// </summary> /// </summary>
public float Angle { get; set; } public float Angle { get; set; }
/// <summary>
/// Gets or sets the center point.
/// </summary>
public Point Center { get; set; }
/// <summary> /// <summary>
/// Gets or sets a value indicating whether to expand the canvas to fit the rotated image. /// Gets or sets a value indicating whether to expand the canvas to fit the rotated image.
/// </summary> /// </summary>
public bool Expand { get; set; } public bool Expand { get; set; } = true;
/// <inheritdoc/> /// <inheritdoc/>
protected override void OnApply(ImageBase target, ImageBase source, Rectangle targetRectangle, Rectangle sourceRectangle) protected override void OnApply(ImageBase target, ImageBase source, Rectangle targetRectangle, Rectangle sourceRectangle)
{ {
processMatrix = Point.CreateRotation(new Point(0, 0), -this.Angle);
if (this.Expand) if (this.Expand)
{ {
Point centre = this.Center == Point.Empty ? Rectangle.Center(sourceRectangle) : this.Center; CreateNewTarget(target, sourceRectangle, processMatrix);
Matrix3x2 rotation = Point.CreateRotation(centre, -this.Angle);
Matrix3x2 invertedRotation;
Matrix3x2.Invert(rotation, out invertedRotation);
Rectangle bounds = ImageMaths.GetBoundingRectangle(source.Bounds, invertedRotation);
target.SetPixels(bounds.Width, bounds.Height, new float[bounds.Width * bounds.Height * 4]);
} }
} }
/// <inheritdoc/> /// <inheritdoc/>
protected override void Apply(ImageBase target, ImageBase source, Rectangle targetRectangle, Rectangle sourceRectangle, int startY, int endY) protected override void Apply(ImageBase target, ImageBase source, Rectangle targetRectangle, Rectangle sourceRectangle, int startY, int endY)
{ {
int height = target.Height; Matrix3x2 matrix = GetCenteredMatrix(target, source, this.processMatrix);
int startX = 0;
int endX = target.Width;
Point centre = this.Center == Point.Empty ? Rectangle.Center(target.Bounds) : this.Center;
//Matrix3x2 invertedRotation;
Matrix3x2 rotation = Point.CreateRotation(centre, -this.Angle);
//Matrix3x2.Invert(rotation, out invertedRotation);
//Vector2 rightTop = Vector2.Transform(new Vector2(source.Width, 0), invertedRotation);
//Vector2 leftBottom = Vector2.Transform(new Vector2(0, source.Height), invertedRotation);
//if (this.Angle < 0)
//{
// rotation = Point.CreateRotation(new Point((int)-leftBottom.X, (int)leftBottom.Y), -this.Angle);
//}
//if (this.Angle > 0)
//{
// rotation = Point.CreateRotation(new Point((int)rightTop.X, (int)-rightTop.Y), -this.Angle);
//}
// Since we are not working in parallel we use full height and width
// of the first pass image.
using (PixelAccessor sourcePixels = source.Lock()) using (PixelAccessor sourcePixels = source.Lock())
using (PixelAccessor targetPixels = target.Lock()) using (PixelAccessor targetPixels = target.Lock())
{ {
Parallel.For( Parallel.For(
0, 0,
height, target.Height,
y => y =>
{
for (int x = 0; x < target.Width; x++)
{ {
for (int x = startX; x < endX; x++) Point transformedPoint = Point.Rotate(new Point(x, y), matrix);
if (source.Bounds.Contains(transformedPoint.X, transformedPoint.Y))
{ {
Point rotated = Point.Rotate(new Point(x, y), rotation); targetPixels[x, y] = sourcePixels[transformedPoint.X, transformedPoint.Y];
if (source.Bounds.Contains(rotated.X, rotated.Y))
{
targetPixels[x, y] = sourcePixels[rotated.X, rotated.Y];
}
} }
}
this.OnRowProcessed(); OnRowProcessed();
}); });
} }
} }
} }

67
src/ImageProcessorCore/Samplers/Processors/SkewProcessor.cs

@ -11,8 +11,13 @@ namespace ImageProcessorCore.Processors
/// <summary> /// <summary>
/// Provides methods that allow the skewing of images. /// Provides methods that allow the skewing of images.
/// </summary> /// </summary>
public class SkewProcessor : ImageSampler public class SkewProcessor : Matrix3x2Processor
{ {
/// <summary>
/// The tranform matrix to apply.
/// </summary>
private Matrix3x2 processMatrix;
/// <inheritdoc/> /// <inheritdoc/>
public override int Parallelism { get; set; } = 1; public override int Parallelism { get; set; } = 1;
@ -26,77 +31,45 @@ namespace ImageProcessorCore.Processors
/// </summary> /// </summary>
public float AngleY { get; set; } public float AngleY { get; set; }
/// <summary>
/// Gets or sets the center point.
/// </summary>
public Point Center { get; set; }
/// <summary> /// <summary>
/// Gets or sets a value indicating whether to expand the canvas to fit the skewed image. /// Gets or sets a value indicating whether to expand the canvas to fit the skewed image.
/// </summary> /// </summary>
public bool Expand { get; set; } public bool Expand { get; set; } = true;
/// <inheritdoc/> /// <inheritdoc/>
protected override void OnApply(ImageBase target, ImageBase source, Rectangle targetRectangle, Rectangle sourceRectangle) protected override void OnApply(ImageBase target, ImageBase source, Rectangle targetRectangle, Rectangle sourceRectangle)
{ {
this.processMatrix = Point.CreateSkew(new Point(0, 0), -this.AngleX, -this.AngleY);
if (this.Expand) if (this.Expand)
{ {
Point centre = this.Center; CreateNewTarget(target, sourceRectangle, this.processMatrix);
Matrix3x2 skew = Point.CreateSkew(centre, -this.AngleX, -this.AngleY);
Matrix3x2 invertedSkew;
Matrix3x2.Invert(skew, out invertedSkew);
Rectangle bounds = ImageMaths.GetBoundingRectangle(source.Bounds, invertedSkew);
target.SetPixels(bounds.Width, bounds.Height, new float[bounds.Width * bounds.Height * 4]);
} }
} }
/// <inheritdoc/> /// <inheritdoc/>
protected override void Apply(ImageBase target, ImageBase source, Rectangle targetRectangle, Rectangle sourceRectangle, int startY, int endY) protected override void Apply(ImageBase target, ImageBase source, Rectangle targetRectangle, Rectangle sourceRectangle, int startY, int endY)
{ {
int height = target.Height; Matrix3x2 matrix = GetCenteredMatrix(target, source, this.processMatrix);
int startX = 0;
int endX = target.Width;
Point centre = this.Center;
Matrix3x2 invertedSkew;
Matrix3x2 skew = Point.CreateSkew(centre, -this.AngleX, -this.AngleY);
Matrix3x2.Invert(skew, out invertedSkew);
Vector2 rightTop = Vector2.Transform(new Vector2(source.Width, 0), invertedSkew);
Vector2 leftBottom = Vector2.Transform(new Vector2(0, source.Height), invertedSkew);
if (this.AngleX < 0 && this.AngleY > 0)
{
skew = Point.CreateSkew(new Point((int)-leftBottom.X, (int)leftBottom.Y), -this.AngleX, -this.AngleY);
}
if (this.AngleX > 0 && this.AngleY < 0)
{
skew = Point.CreateSkew(new Point((int)rightTop.X, (int)-rightTop.Y), -this.AngleX, -this.AngleY);
}
if (this.AngleX < 0 && this.AngleY < 0)
{
skew = Point.CreateSkew(new Point(target.Width - 1, target.Height - 1), -this.AngleX, -this.AngleY);
}
using (PixelAccessor sourcePixels = source.Lock()) using (PixelAccessor sourcePixels = source.Lock())
using (PixelAccessor targetPixels = target.Lock()) using (PixelAccessor targetPixels = target.Lock())
{ {
Parallel.For( Parallel.For(
0, 0,
height, target.Height,
y => y =>
{
for (int x = startX; x < endX; x++)
{ {
Point skewed = Point.Skew(new Point(x, y), skew); for (int x = 0; x < target.Width; x++)
if (source.Bounds.Contains(skewed.X, skewed.Y))
{ {
targetPixels[x, y] = sourcePixels[skewed.X, skewed.Y]; Point transformedPoint = Point.Skew(new Point(x, y), matrix);
if (source.Bounds.Contains(transformedPoint.X, transformedPoint.Y))
{
targetPixels[x, y] = sourcePixels[transformedPoint.X, transformedPoint.Y];
}
} }
}
this.OnRowProcessed(); OnRowProcessed();
}); });
} }
} }
} }

11
src/ImageProcessorCore/Samplers/Rotate.cs

@ -1,7 +1,7 @@
// <copyright file="Rotate.cs" company="James Jackson-South"> // <copyright file="Rotate.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors. // Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
// </copyright>------------------------------------------------------------------------------------------------------------------- // </copyright>
namespace ImageProcessorCore namespace ImageProcessorCore
{ {
@ -21,21 +21,20 @@ namespace ImageProcessorCore
/// <returns>The <see cref="Image"/></returns> /// <returns>The <see cref="Image"/></returns>
public static Image Rotate(this Image source, float degrees, ProgressEventHandler progressHandler = null) public static Image Rotate(this Image source, float degrees, ProgressEventHandler progressHandler = null)
{ {
return Rotate(source, degrees, Point.Empty, true, progressHandler); return Rotate(source, degrees, true, progressHandler);
} }
/// <summary> /// <summary>
/// Rotates an image by the given angle in degrees around the given center point. /// Rotates an image by the given angle in degrees.
/// </summary> /// </summary>
/// <param name="source">The image to rotate.</param> /// <param name="source">The image to rotate.</param>
/// <param name="degrees">The angle in degrees to perform the rotation.</param> /// <param name="degrees">The angle in degrees to perform the rotation.</param>
/// <param name="center">The center point at which to rotate the image.</param>
/// <param name="expand">Whether to expand the image to fit the rotated result.</param> /// <param name="expand">Whether to expand the image to fit the rotated result.</param>
/// <param name="progressHandler">A delegate which is called as progress is made processing 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> /// <returns>The <see cref="Image"/></returns>
public static Image Rotate(this Image source, float degrees, Point center, bool expand, ProgressEventHandler progressHandler = null) public static Image Rotate(this Image source, float degrees, bool expand, ProgressEventHandler progressHandler = null)
{ {
RotateProcessor processor = new RotateProcessor { Angle = degrees, Center = center, Expand = expand }; RotateProcessor processor = new RotateProcessor { Angle = degrees, Expand = expand };
processor.OnProgress += progressHandler; processor.OnProgress += progressHandler;
try try

11
src/ImageProcessorCore/Samplers/Skew.cs

@ -1,7 +1,7 @@
// <copyright file="Skew.cs" company="James Jackson-South"> // <copyright file="Skew.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors. // Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
// </copyright>------------------------------------------------------------------------------------------------------------------- // </copyright>
namespace ImageProcessorCore namespace ImageProcessorCore
{ {
@ -22,22 +22,21 @@ namespace ImageProcessorCore
/// <returns>The <see cref="Image"/></returns> /// <returns>The <see cref="Image"/></returns>
public static Image Skew(this Image source, float degreesX, float degreesY, ProgressEventHandler progressHandler = null) public static Image Skew(this Image source, float degreesX, float degreesY, ProgressEventHandler progressHandler = null)
{ {
return Skew(source, degreesX, degreesY, Point.Empty, true, progressHandler); return Skew(source, degreesX, degreesY, true, progressHandler);
} }
/// <summary> /// <summary>
/// Skews an image by the given angles in degrees around the given center point. /// Skews an image by the given angles in degrees.
/// </summary> /// </summary>
/// <param name="source">The image to skew.</param> /// <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="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="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="expand">Whether to expand the image to fit the skewed result.</param> /// <param name="expand">Whether to expand the image to fit the skewed result.</param>
/// <param name="progressHandler">A delegate which is called as progress is made processing 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> /// <returns>The <see cref="Image"/></returns>
public static Image Skew(this Image source, float degreesX, float degreesY, Point center, bool expand, ProgressEventHandler progressHandler = null) public static Image Skew(this Image source, float degreesX, float degreesY, bool expand, ProgressEventHandler progressHandler = null)
{ {
SkewProcessor processor = new SkewProcessor { AngleX = degreesX, AngleY = degreesY, Center = center, Expand = expand }; SkewProcessor processor = new SkewProcessor { AngleX = degreesX, AngleY = degreesY, Expand = expand };
processor.OnProgress += progressHandler; processor.OnProgress += progressHandler;
try try

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

@ -429,7 +429,7 @@ namespace ImageProcessorCore.Tests
Image image = new Image(stream); Image image = new Image(stream);
using (FileStream output = File.OpenWrite($"TestOutput/Rotate/{filename}")) using (FileStream output = File.OpenWrite($"TestOutput/Rotate/{filename}"))
{ {
image.Rotate(63, this.ProgressUpdate) image.Rotate(-170, this.ProgressUpdate)
.Save(output); .Save(output);
} }
} }
@ -454,7 +454,7 @@ namespace ImageProcessorCore.Tests
Image image = new Image(stream); Image image = new Image(stream);
using (FileStream output = File.OpenWrite($"TestOutput/Skew/{filename}")) using (FileStream output = File.OpenWrite($"TestOutput/Skew/{filename}"))
{ {
image.Skew(-15, 12, this.ProgressUpdate) image.Skew(-20, -10, this.ProgressUpdate)
.Save(output); .Save(output);
} }
} }

Loading…
Cancel
Save