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>
/// Provides methods that allow the rotating of images.
/// </summary>
public class RotateProcessor : ImageSampler
public class RotateProcessor : Matrix3x2Processor
{
/// <summary>
/// The tranform matrix to apply.
/// </summary>
private Matrix3x2 processMatrix;
/// <inheritdoc/>
public override int Parallelism { get; set; } = 1;
/// <summary>
/// Gets or sets the angle of rotation in degrees.
/// Gets or sets the angle of processMatrix in degrees.
/// </summary>
public float Angle { get; set; }
/// <summary>
/// Gets or sets the center point.
/// </summary>
public Point Center { get; set; }
/// <summary>
/// Gets or sets a value indicating whether to expand the canvas to fit the rotated image.
/// </summary>
public bool Expand { get; set; }
public bool Expand { get; set; } = true;
/// <inheritdoc/>
protected override void OnApply(ImageBase target, ImageBase source, Rectangle targetRectangle, Rectangle sourceRectangle)
{
processMatrix = Point.CreateRotation(new Point(0, 0), -this.Angle);
if (this.Expand)
{
Point centre = this.Center == Point.Empty ? Rectangle.Center(sourceRectangle) : this.Center;
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]);
CreateNewTarget(target, sourceRectangle, processMatrix);
}
}
/// <inheritdoc/>
protected override void Apply(ImageBase target, ImageBase source, Rectangle targetRectangle, Rectangle sourceRectangle, int startY, int endY)
{
int height = target.Height;
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);
//}
Matrix3x2 matrix = GetCenteredMatrix(target, source, this.processMatrix);
// 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 targetPixels = target.Lock())
{
Parallel.For(
0,
height,
target.Height,
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);
if (source.Bounds.Contains(rotated.X, rotated.Y))
{
targetPixels[x, y] = sourcePixels[rotated.X, rotated.Y];
}
targetPixels[x, y] = sourcePixels[transformedPoint.X, transformedPoint.Y];
}
}
this.OnRowProcessed();
});
OnRowProcessed();
});
}
}
}

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

@ -11,8 +11,13 @@ namespace ImageProcessorCore.Processors
/// <summary>
/// Provides methods that allow the skewing of images.
/// </summary>
public class SkewProcessor : ImageSampler
public class SkewProcessor : Matrix3x2Processor
{
/// <summary>
/// The tranform matrix to apply.
/// </summary>
private Matrix3x2 processMatrix;
/// <inheritdoc/>
public override int Parallelism { get; set; } = 1;
@ -26,77 +31,45 @@ namespace ImageProcessorCore.Processors
/// </summary>
public float AngleY { get; set; }
/// <summary>
/// Gets or sets the center point.
/// </summary>
public Point Center { get; set; }
/// <summary>
/// Gets or sets a value indicating whether to expand the canvas to fit the skewed image.
/// </summary>
public bool Expand { get; set; }
public bool Expand { get; set; } = true;
/// <inheritdoc/>
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)
{
Point centre = this.Center;
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]);
CreateNewTarget(target, sourceRectangle, this.processMatrix);
}
}
/// <inheritdoc/>
protected override void Apply(ImageBase target, ImageBase source, Rectangle targetRectangle, Rectangle sourceRectangle, int startY, int endY)
{
int height = target.Height;
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);
}
Matrix3x2 matrix = GetCenteredMatrix(target, source, this.processMatrix);
using (PixelAccessor sourcePixels = source.Lock())
using (PixelAccessor targetPixels = target.Lock())
{
Parallel.For(
0,
height,
target.Height,
y =>
{
for (int x = startX; x < endX; x++)
{
Point skewed = Point.Skew(new Point(x, y), skew);
if (source.Bounds.Contains(skewed.X, skewed.Y))
for (int x = 0; x < target.Width; x++)
{
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 (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>-------------------------------------------------------------------------------------------------------------------
// </copyright>
namespace ImageProcessorCore
{
@ -21,21 +21,20 @@ namespace ImageProcessorCore
/// <returns>The <see cref="Image"/></returns>
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>
/// Rotates an image by the given angle in degrees around the given center point.
/// Rotates an image by the given angle in degrees.
/// </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 rotate the image.</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>
/// <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;
try

11
src/ImageProcessorCore/Samplers/Skew.cs

@ -1,7 +1,7 @@
// <copyright file="Skew.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>-------------------------------------------------------------------------------------------------------------------
// </copyright>
namespace ImageProcessorCore
{
@ -22,22 +22,21 @@ namespace ImageProcessorCore
/// <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, Point.Empty, true, progressHandler);
return Skew(source, degreesX, degreesY, true, progressHandler);
}
/// <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>
/// <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="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>
/// <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;
try

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

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

Loading…
Cancel
Save