mirror of https://github.com/SixLabors/ImageSharp
Browse Source
Former-commit-id: f7dddf44418638397c4d3639a08fc1058d720470 Former-commit-id: 5a08b1e1bdedc910a00f66917bf1ac3eadaa7a31 Former-commit-id: f3be022b70d7e2edf4c1741566008abe7b034a1eaf/merge-core
7 changed files with 368 additions and 323 deletions
@ -0,0 +1,158 @@ |
|||
// <copyright file="Resize.cs" company="James Jackson-South">
|
|||
// Copyright (c) James Jackson-South and contributors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
// </copyright>
|
|||
|
|||
namespace ImageProcessor.Samplers |
|||
{ |
|||
using System.Threading.Tasks; |
|||
|
|||
/// <summary>
|
|||
/// Provides methods that allow the resizing of images using various algorithms.
|
|||
/// </summary>
|
|||
public class Resize : Resampler |
|||
{ |
|||
/// <summary>
|
|||
/// The image used for storing the first pass pixels.
|
|||
/// </summary>
|
|||
private Image firstPass; |
|||
|
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="Resize"/> class.
|
|||
/// </summary>
|
|||
/// <param name="sampler">
|
|||
/// The sampler to perform the resize operation.
|
|||
/// </param>
|
|||
public Resize(IResampler sampler) |
|||
: base(sampler) |
|||
{ |
|||
} |
|||
|
|||
/// <inheritdoc/>
|
|||
public override int Parallelism { get; set; } = 1; |
|||
|
|||
/// <inheritdoc/>
|
|||
protected override void OnApply(ImageBase source, ImageBase target, Rectangle targetRectangle, Rectangle sourceRectangle) |
|||
{ |
|||
if (!(this.Sampler is NearestNeighborResampler)) |
|||
{ |
|||
this.HorizontalWeights = this.PrecomputeWeights(targetRectangle.Width, sourceRectangle.Width); |
|||
this.VerticalWeights = this.PrecomputeWeights(targetRectangle.Height, sourceRectangle.Height); |
|||
} |
|||
|
|||
this.firstPass = new Image(target.Width, source.Height); |
|||
} |
|||
|
|||
/// <inheritdoc/>
|
|||
protected override void Apply(ImageBase target, ImageBase source, Rectangle targetRectangle, Rectangle sourceRectangle, int startY, int endY) |
|||
{ |
|||
// Jump out, we'll deal with that later.
|
|||
if (source.Bounds == target.Bounds) |
|||
{ |
|||
return; |
|||
} |
|||
|
|||
int sourceBottom = source.Bounds.Bottom; |
|||
int targetY = targetRectangle.Y; |
|||
int targetBottom = targetRectangle.Bottom; |
|||
int startX = targetRectangle.X; |
|||
int endX = targetRectangle.Right; |
|||
|
|||
if (this.Sampler is NearestNeighborResampler) |
|||
{ |
|||
// 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); |
|||
|
|||
target[x, y] = source[originX, originY]; |
|||
} |
|||
} |
|||
}); |
|||
|
|||
// Break out now.
|
|||
return; |
|||
} |
|||
|
|||
// Interpolate the image using the calculated weights.
|
|||
// A 2-pass 1D algorithm appears to be faster than splitting a 1-pass 2D algorithm
|
|||
// First process the columns.
|
|||
Parallel.For( |
|||
0, |
|||
sourceBottom, |
|||
y => |
|||
{ |
|||
for (int x = startX; x < endX; x++) |
|||
{ |
|||
Weight[] horizontalValues = this.HorizontalWeights[x].Values; |
|||
|
|||
// Destination color components
|
|||
Color destination = new Color(); |
|||
|
|||
foreach (Weight xw in horizontalValues) |
|||
{ |
|||
int originX = xw.Index; |
|||
Color sourceColor = Color.Expand(source[originX, y]); |
|||
destination += sourceColor * xw.Value; |
|||
} |
|||
|
|||
destination = Color.Compress(destination); |
|||
this.firstPass[x, y] = destination; |
|||
} |
|||
}); |
|||
|
|||
// Now process the rows.
|
|||
Parallel.For( |
|||
startY, |
|||
endY, |
|||
y => |
|||
{ |
|||
if (y >= targetY && y < targetBottom) |
|||
{ |
|||
Weight[] verticalValues = this.VerticalWeights[y].Values; |
|||
|
|||
for (int x = startX; x < endX; x++) |
|||
{ |
|||
// Destination color components
|
|||
Color destination = new Color(); |
|||
|
|||
foreach (Weight yw in verticalValues) |
|||
{ |
|||
int originY = yw.Index; |
|||
int originX = x; |
|||
Color sourceColor = Color.Expand(this.firstPass[originX, originY]); |
|||
destination += sourceColor * yw.Value; |
|||
} |
|||
|
|||
destination = Color.Compress(destination); |
|||
target[x, y] = destination; |
|||
} |
|||
} |
|||
}); |
|||
} |
|||
|
|||
/// <inheritdoc/>
|
|||
protected override void AfterApply(ImageBase source, ImageBase target, Rectangle targetRectangle, Rectangle sourceRectangle) |
|||
{ |
|||
// Copy the pixels over.
|
|||
if (source.Bounds == target.Bounds) |
|||
{ |
|||
target.ClonePixels(target.Width, target.Height, source.Pixels); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,159 @@ |
|||
// <copyright file="Rotate.cs" company="James Jackson-South">
|
|||
// Copyright (c) James Jackson-South and contributors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
// </copyright>
|
|||
|
|||
namespace ImageProcessor.Samplers |
|||
{ |
|||
using System.Threading.Tasks; |
|||
|
|||
/// <summary>
|
|||
/// Provides methods that allow the rotating of images using various algorithms.
|
|||
/// </summary>
|
|||
public class Rotate : Resampler |
|||
{ |
|||
/// <summary>
|
|||
/// The angle of rotation.
|
|||
/// </summary>
|
|||
private float angle; |
|||
|
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="Rotate"/> class.
|
|||
/// </summary>
|
|||
/// <param name="sampler">
|
|||
/// The sampler to perform the resize operation.
|
|||
/// </param>
|
|||
public Rotate(IResampler sampler) |
|||
: base(sampler) |
|||
{ |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets or sets the angle of rotation.
|
|||
/// </summary>
|
|||
public float 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, ImageBase target, Rectangle targetRectangle, Rectangle sourceRectangle) |
|||
{ |
|||
if (!(this.Sampler is NearestNeighborResampler)) |
|||
{ |
|||
this.HorizontalWeights = this.PrecomputeWeights(targetRectangle.Width, sourceRectangle.Width); |
|||
this.VerticalWeights = this.PrecomputeWeights(targetRectangle.Height, sourceRectangle.Height); |
|||
} |
|||
} |
|||
|
|||
/// <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 negativeAngle = -this.angle; |
|||
Point centre = Rectangle.Center(sourceRectangle); |
|||
|
|||
if (this.Sampler is NearestNeighborResampler) |
|||
{ |
|||
// 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); |
|||
|
|||
// Rotate at the centre point
|
|||
Point rotated = Point.Rotate(new Point(originX, originY), centre, negativeAngle); |
|||
if (sourceRectangle.Contains(rotated.X, rotated.Y)) |
|||
{ |
|||
target[x, y] = source[rotated.X, rotated.Y]; |
|||
} |
|||
} |
|||
} |
|||
}); |
|||
|
|||
// Break out now.
|
|||
return; |
|||
} |
|||
|
|||
// Interpolate the image using the calculated weights.
|
|||
Parallel.For( |
|||
startY, |
|||
endY, |
|||
y => |
|||
{ |
|||
if (y >= targetY && y < targetBottom) |
|||
{ |
|||
Weight[] verticalValues = this.VerticalWeights[y].Values; |
|||
|
|||
for (int x = startX; x < endX; x++) |
|||
{ |
|||
Weight[] horizontalValues = this.HorizontalWeights[x].Values; |
|||
|
|||
// Destination color components
|
|||
Color destination = new Color(); |
|||
|
|||
foreach (Weight yw in verticalValues) |
|||
{ |
|||
int originY = yw.Index; |
|||
|
|||
foreach (Weight xw in horizontalValues) |
|||
{ |
|||
int originX = xw.Index; |
|||
|
|||
// Rotate at the centre point
|
|||
Point rotated = Point.Rotate(new Point(originX, originY), centre, negativeAngle); |
|||
if (sourceRectangle.Contains(rotated.X, rotated.Y)) |
|||
{ |
|||
target[x, y] = source[rotated.X, rotated.Y]; |
|||
} |
|||
|
|||
if (sourceRectangle.Contains(rotated.X, rotated.Y)) |
|||
{ |
|||
Color sourceColor = Color.Expand(source[rotated.X, rotated.Y]); |
|||
destination += sourceColor * yw.Value * xw.Value; |
|||
} |
|||
} |
|||
} |
|||
|
|||
destination = Color.Compress(destination); |
|||
target[x, y] = destination; |
|||
} |
|||
} |
|||
}); |
|||
} |
|||
} |
|||
} |
|||
Loading…
Reference in new issue