Browse Source

Add rotation to resampler

- TODO: Better edge handling.


Former-commit-id: c4c65b5dd47357ff419119ddf1f3d4f0e3a5c5e4
Former-commit-id: 22ecdf450bc106f6b1c3e0f3c4cb2cd5a2a010ab
Former-commit-id: 95089f4cd166de28c8133aa873ae4195b1b8ffbf
af/merge-core
James Jackson-South 10 years ago
parent
commit
53a7d2768d
  1. 38
      src/ImageProcessor/Common/Helpers/ImageMaths.cs
  2. 10
      src/ImageProcessor/Numerics/Rectangle.cs
  3. 2
      src/ImageProcessor/Samplers/ImageSampleExtensions.cs
  4. 89
      src/ImageProcessor/Samplers/Resampler.cs
  5. 6
      src/ImageProcessor/project.json
  6. 2
      src/ImageProcessor/project.lock.json.REMOVED.git-id
  7. 2
      tests/ImageProcessor.Tests/project.lock.json.REMOVED.git-id

38
src/ImageProcessor/Common/Helpers/ImageMaths.cs

@ -95,6 +95,44 @@ namespace ImageProcessor
return 1.0f;
}
/// <summary>
/// Returns the given degrees converted to radians.
/// </summary>
/// <param name="angleInDegrees">
/// The angle in degrees.
/// </param>
/// <returns>
/// The <see cref="double"/> representing the degree as radians.
/// </returns>
public static double DegreesToRadians(double angleInDegrees)
{
return angleInDegrees * (PI / 180);
}
/// <summary>
/// Rotates one point around another
/// <see href="http://stackoverflow.com/a/13695630/82333"/>
/// </summary>
/// <param name="pointToRotate">The point to rotate.</param>
/// <param name="angleInDegrees">The rotation angle in degrees.</param>
/// <param name="centerPoint">The centre point of rotation. If not set the point will equal
/// <see cref="Point.Empty"/>
/// </param>
/// <returns>Rotated point</returns>
public static Point RotatePoint(Point pointToRotate, double angleInDegrees, Point? centerPoint = null)
{
Point center = centerPoint ?? Point.Empty;
double angleInRadians = DegreesToRadians(angleInDegrees);
double cosTheta = Math.Cos(angleInRadians);
double sinTheta = Math.Sin(angleInRadians);
return new Point
{
X = (int)((cosTheta * (pointToRotate.X - center.X)) - (sinTheta * (pointToRotate.Y - center.Y)) + center.X),
Y = (int)((sinTheta * (pointToRotate.X - center.X)) + (cosTheta * (pointToRotate.Y - center.Y)) + center.Y)
};
}
/// <summary>
/// Ensures that any passed double is correctly rounded to zero
/// </summary>

10
src/ImageProcessor/Numerics/Rectangle.cs

@ -155,6 +155,16 @@ namespace ImageProcessor
&& y < this.Y + this.Height;
}
/// <summary>
/// Returns the center point of the given <see cref="Rectangle"/>
/// </summary>
/// <param name="rectangle">The rectangle</param>
/// <returns><see cref="Point"/></returns>
public static Point Center(Rectangle rectangle)
{
return new Point(rectangle.Left + rectangle.Width / 2, rectangle.Top + rectangle.Height / 2);
}
/// <summary>
/// Indicates whether this instance and a specified object are equal.
/// </summary>

2
src/ImageProcessor/Samplers/ImageSampleExtensions.cs

@ -49,7 +49,7 @@ namespace ImageProcessor.Samplers
/// <returns>The <see cref="Image"/></returns>
public static Image Resize(this Image source, int width, int height, IResampler sampler, Rectangle sourceRectangle)
{
return source.Process(width, height, sourceRectangle, new Rectangle(0, 0, width, height), new Resize(sampler));
return source.Process(width, height, sourceRectangle, new Rectangle(0, 0, width, height), new Resampler(sampler));
}
/// <summary>

89
src/ImageProcessor/Samplers/Resize.cs → src/ImageProcessor/Samplers/Resampler.cs

@ -1,4 +1,4 @@
// <copyright file="Resize.cs" company="James Jackson-South">
// <copyright file="Resampler.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
@ -10,14 +10,19 @@ namespace ImageProcessor.Samplers
using System.Threading.Tasks;
/// <summary>
/// Provides methods that allow the resizing of images using various resampling algorithms.
/// Provides methods that allow the resampling of images using various algorithms.
/// </summary>
public class Resize : ParallelImageProcessor
public class Resampler : ParallelImageProcessor
{
/// <summary>
/// The epsilon for comparing floating point numbers.
/// </summary>
private const float Epsilon = 0.01f;
private const float Epsilon = 0.0001f;
/// <summary>
/// The angle of rotation.
/// </summary>
private double angle;
/// <summary>
/// The horizontal weights.
@ -30,12 +35,12 @@ namespace ImageProcessor.Samplers
private Weights[] verticalWeights;
/// <summary>
/// Initializes a new instance of the <see cref="Resize"/> class.
/// Initializes a new instance of the <see cref="Resampler"/> class.
/// </summary>
/// <param name="sampler">
/// The sampler to perform the resize operation.
/// </param>
public Resize(IResampler sampler)
public Resampler(IResampler sampler)
{
Guard.NotNull(sampler, nameof(sampler));
@ -47,6 +52,32 @@ namespace ImageProcessor.Samplers
/// </summary>
public IResampler Sampler { get; }
/// <summary>
/// Gets or sets the angle of rotation.
/// </summary>
public double Angle
{
get
{
return this.angle;
}
set
{
if (value > 360)
{
value -= 360;
}
if (value < 0)
{
value += 360;
}
this.angle = value;
}
}
/// <inheritdoc/>
protected override void OnApply(ImageBase source, Rectangle targetRectangle, Rectangle sourceRectangle)
{
@ -61,6 +92,8 @@ namespace ImageProcessor.Samplers
int targetBottom = targetRectangle.Bottom;
int startX = targetRectangle.X;
int endX = targetRectangle.Right;
Point centre = Rectangle.Center(sourceRectangle);
bool rotate = this.angle > 0 && this.angle < 360;
Parallel.For(
startY,
@ -82,28 +115,38 @@ namespace ImageProcessor.Samplers
foreach (Weight yw in verticalValues)
{
if (Math.Abs(yw.Value) < Epsilon)
{
continue;
}
int originY = yw.Index;
foreach (Weight xw in horizontalValues)
{
if (Math.Abs(xw.Value) < Epsilon)
{
continue;
}
int originX = xw.Index;
Color sourceColor = Color.InverseCompand(source[originX, originY]);
if (Math.Abs(sourceColor.A) < Epsilon)
Color sourceColor;
float weight;
if (rotate)
{
continue;
// Rotating at the centre point
Point rotated = ImageMaths.RotatePoint(new Point(originX, originY), this.angle, centre);
originX = rotated.X;
originY = rotated.Y;
// TODO: This can't work. We're not normalising properly since weights are skipped.
// Also... This is so slow!
if (sourceRectangle.Contains(originX, originY))
{
sourceColor = Color.InverseCompand(source[originX, originY]);
weight = (yw.Value / verticalSum) * (xw.Value / horizontalSum);
destination.R += sourceColor.R * weight;
destination.G += sourceColor.G * weight;
destination.B += sourceColor.B * weight;
destination.A += sourceColor.A * weight;
}
}
float weight = (yw.Value / verticalSum) * (xw.Value / horizontalSum);
sourceColor = Color.InverseCompand(source[originX, originY]);
weight = (yw.Value / verticalSum) * (xw.Value / horizontalSum);
destination.R += sourceColor.R * weight;
destination.G += sourceColor.G * weight;
@ -112,9 +155,12 @@ namespace ImageProcessor.Samplers
}
}
destination = Color.Compand(destination);
// Ensure are alpha values only reflect possible values to prevent bleed.
destination.A = (float)Math.Round(destination.A, 2);
target[x, y] = Color.Compand(destination);
target[x, y] = destination;
}
}
});
@ -176,6 +222,7 @@ namespace ImageProcessor.Samplers
builder.Add(new Weight(a, w));
}
}
result[i].Values = builder.ToImmutable();
result[i].Sum = sum;
});

6
src/ImageProcessor/project.json

@ -19,11 +19,11 @@
"System.Runtime.Extensions": "4.0.10",
"System.Reflection": "4.0.10",
"System.IO": "4.0.10",
"StyleCop.Analyzers": "1.0.0-beta016",
"Microsoft.NETCore": "5.0.1-beta-23409",
"Microsoft.NETCore.Platforms": "1.0.1-beta-23409"
"Microsoft.NETCore.Platforms": "1.0.1-beta-23409",
"StyleCop.Analyzers": "1.0.0-beta016"
},
"frameworks": {
"dotnet": {}
"dotnet": { }
}
}

2
src/ImageProcessor/project.lock.json.REMOVED.git-id

@ -1 +1 @@
0b32043447c786c20468c00c3c0924348b389517
e9c58eb8b9733d48d157d8bf0852daae4f60dab0

2
tests/ImageProcessor.Tests/project.lock.json.REMOVED.git-id

@ -1 +1 @@
9ead0f07f8529db4e80eda95477601f6d3eb9583
11091fe7d5fc9cff9aec6ef60bf1c47e0e01d066
Loading…
Cancel
Save