mirror of https://github.com/SixLabors/ImageSharp
Browse Source
Former-commit-id: 59a0db810427fcfd9c7426392f5de94317d928bd Former-commit-id: 89d27c015fbc972372f3697c06864dd71f458574 Former-commit-id: 160b946dcbfc868be3d014c2ef6410cbb40d4ef7af/merge-core
7 changed files with 321 additions and 255 deletions
@ -0,0 +1,41 @@ |
|||||
|
// <copyright file="RotateFlip.cs" company="James Jackson-South">
|
||||
|
// Copyright (c) James Jackson-South and contributors.
|
||||
|
// Licensed under the Apache License, Version 2.0.
|
||||
|
// </copyright>
|
||||
|
|
||||
|
namespace ImageProcessorCore |
||||
|
{ |
||||
|
using Processors; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Extension methods for the <see cref="Image{T,TP}"/> type.
|
||||
|
/// </summary>
|
||||
|
public static partial class ImageExtensions |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Flips an image by the given instructions.
|
||||
|
/// </summary>
|
||||
|
/// <typeparam name="T">The pixel format.</typeparam>
|
||||
|
/// <typeparam name="TP">The packed format. <example>long, float.</example></typeparam>
|
||||
|
/// <param name="source">The image to rotate, flip, or both.</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>
|
||||
|
/// <returns>The <see cref="Image"/></returns>
|
||||
|
public static Image<T, TP> Flip<T, TP>(this Image<T, TP> source, FlipType flipType, ProgressEventHandler progressHandler = null) |
||||
|
where T : IPackedVector<TP> |
||||
|
where TP : struct |
||||
|
{ |
||||
|
FlipProcessor<T, TP> processor = new FlipProcessor<T, TP>(flipType); |
||||
|
processor.OnProgress += progressHandler; |
||||
|
|
||||
|
try |
||||
|
{ |
||||
|
return source.Process(source.Width, source.Height, source.Bounds, source.Bounds, processor); |
||||
|
} |
||||
|
finally |
||||
|
{ |
||||
|
processor.OnProgress -= progressHandler; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,119 @@ |
|||||
|
// <copyright file="RotateFlipProcessor.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; |
||||
|
using System.Threading.Tasks; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Provides methods that allow the flipping of an image around its center point.
|
||||
|
/// </summary>
|
||||
|
/// <typeparam name="T">The pixel format.</typeparam>
|
||||
|
/// <typeparam name="TP">The packed format. <example>long, float.</example></typeparam>
|
||||
|
public class FlipProcessor<T, TP> : ImageSampler<T, TP> |
||||
|
where T : IPackedVector<TP> |
||||
|
where TP : struct |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Initializes a new instance of the <see cref="FlipProcessor{T,TP}"/> class.
|
||||
|
/// </summary>
|
||||
|
/// <param name="flipType">The <see cref="FlipType"/> used to perform flipping.</param>
|
||||
|
public FlipProcessor(FlipType flipType) |
||||
|
{ |
||||
|
this.FlipType = flipType; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets the <see cref="FlipType"/> used to perform flipping.
|
||||
|
/// </summary>
|
||||
|
public FlipType FlipType { get; } |
||||
|
|
||||
|
/// <inheritdoc/>
|
||||
|
protected override void Apply(ImageBase<T, TP> target, ImageBase<T, TP> source, Rectangle targetRectangle, Rectangle sourceRectangle, int startY, int endY) |
||||
|
{ |
||||
|
target.ClonePixels(target.Width, target.Height, source.Pixels); |
||||
|
|
||||
|
switch (this.FlipType) |
||||
|
{ |
||||
|
// No default needed as we have already set the pixels.
|
||||
|
case FlipType.Vertical: |
||||
|
this.FlipX(target); |
||||
|
break; |
||||
|
case FlipType.Horizontal: |
||||
|
this.FlipY(target); |
||||
|
break; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Swaps the image at the X-axis, which goes horizontally through the middle
|
||||
|
/// at half the height of the image.
|
||||
|
/// </summary>
|
||||
|
/// <param name="target">Target image to apply the process to.</param>
|
||||
|
private void FlipX(ImageBase<T, TP> target) |
||||
|
{ |
||||
|
int width = target.Width; |
||||
|
int height = target.Height; |
||||
|
int halfHeight = (int)Math.Ceiling(target.Height * .5F); |
||||
|
Image<T, TP> temp = new Image<T, TP>(width, height); |
||||
|
temp.ClonePixels(width, height, target.Pixels); |
||||
|
|
||||
|
using (IPixelAccessor<T, TP> targetPixels = target.Lock()) |
||||
|
using (IPixelAccessor<T, TP> tempPixels = temp.Lock()) |
||||
|
{ |
||||
|
Parallel.For( |
||||
|
0, |
||||
|
halfHeight, |
||||
|
this.ParallelOptions, |
||||
|
y => |
||||
|
{ |
||||
|
for (int x = 0; x < width; x++) |
||||
|
{ |
||||
|
int newY = height - y - 1; |
||||
|
targetPixels[x, y] = tempPixels[x, newY]; |
||||
|
targetPixels[x, newY] = tempPixels[x, y]; |
||||
|
} |
||||
|
|
||||
|
this.OnRowProcessed(); |
||||
|
}); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Swaps the image at the Y-axis, which goes vertically through the middle
|
||||
|
/// at half of the width of the image.
|
||||
|
/// </summary>
|
||||
|
/// <param name="target">Target image to apply the process to.</param>
|
||||
|
private void FlipY(ImageBase<T, TP> target) |
||||
|
{ |
||||
|
int width = target.Width; |
||||
|
int height = target.Height; |
||||
|
int halfWidth = (int)Math.Ceiling(width * .5F); |
||||
|
Image<T, TP> temp = new Image<T, TP>(width, height); |
||||
|
temp.ClonePixels(width, height, target.Pixels); |
||||
|
|
||||
|
using (IPixelAccessor<T, TP> targetPixels = target.Lock()) |
||||
|
using (IPixelAccessor<T, TP> tempPixels = temp.Lock()) |
||||
|
{ |
||||
|
Parallel.For( |
||||
|
0, |
||||
|
height, |
||||
|
this.ParallelOptions, |
||||
|
y => |
||||
|
{ |
||||
|
for (int x = 0; x < halfWidth; x++) |
||||
|
{ |
||||
|
int newX = width - x - 1; |
||||
|
targetPixels[x, y] = tempPixels[newX, y]; |
||||
|
targetPixels[newX, y] = tempPixels[x, y]; |
||||
|
} |
||||
|
|
||||
|
this.OnRowProcessed(); |
||||
|
}); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -1,240 +0,0 @@ |
|||||
// <copyright file="RotateFlipProcessor.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; |
|
||||
using System.Threading.Tasks; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Provides methods that allow the rotation and flipping of an image around its center point.
|
|
||||
/// </summary>
|
|
||||
/// <typeparam name="T">The pixel format.</typeparam>
|
|
||||
/// <typeparam name="TP">The packed format. <example>long, float.</example></typeparam>
|
|
||||
public class RotateFlipProcessor<T, TP> : ImageSampler<T, TP> |
|
||||
where T : IPackedVector<TP> |
|
||||
where TP : struct |
|
||||
{ |
|
||||
/// <summary>
|
|
||||
/// Initializes a new instance of the <see cref="RotateFlipProcessor{T,TP}"/> class.
|
|
||||
/// </summary>
|
|
||||
/// <param name="rotateType">The <see cref="RotateType"/> used to perform rotation.</param>
|
|
||||
/// <param name="flipType">The <see cref="FlipType"/> used to perform flipping.</param>
|
|
||||
public RotateFlipProcessor(RotateType rotateType, FlipType flipType) |
|
||||
{ |
|
||||
this.RotateType = rotateType; |
|
||||
this.FlipType = flipType; |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets the <see cref="FlipType"/> used to perform flipping.
|
|
||||
/// </summary>
|
|
||||
public FlipType FlipType { get; } |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets the <see cref="RotateType"/> used to perform rotation.
|
|
||||
/// </summary>
|
|
||||
public RotateType RotateType { get; } |
|
||||
|
|
||||
/// <inheritdoc/>
|
|
||||
protected override void Apply(ImageBase<T, TP> target, ImageBase<T, TP> source, Rectangle targetRectangle, Rectangle sourceRectangle, int startY, int endY) |
|
||||
{ |
|
||||
switch (this.RotateType) |
|
||||
{ |
|
||||
case RotateType.Rotate90: |
|
||||
this.Rotate90(target, source); |
|
||||
break; |
|
||||
case RotateType.Rotate180: |
|
||||
this.Rotate180(target, source); |
|
||||
break; |
|
||||
case RotateType.Rotate270: |
|
||||
this.Rotate270(target, source); |
|
||||
break; |
|
||||
default: |
|
||||
target.ClonePixels(target.Width, target.Height, source.Pixels); |
|
||||
break; |
|
||||
} |
|
||||
|
|
||||
switch (this.FlipType) |
|
||||
{ |
|
||||
// No default needed as we have already set the pixels.
|
|
||||
case FlipType.Vertical: |
|
||||
this.FlipX(target); |
|
||||
break; |
|
||||
case FlipType.Horizontal: |
|
||||
this.FlipY(target); |
|
||||
break; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Rotates the image 270 degrees clockwise at the centre point.
|
|
||||
/// </summary>
|
|
||||
/// <param name="target">The target image.</param>
|
|
||||
/// <param name="source">The source image.</param>
|
|
||||
private void Rotate270(ImageBase<T, TP> target, ImageBase<T, TP> source) |
|
||||
{ |
|
||||
int width = source.Width; |
|
||||
int height = source.Height; |
|
||||
Image<T, TP> temp = new Image<T, TP>(height, width); |
|
||||
|
|
||||
using (IPixelAccessor<T, TP> sourcePixels = source.Lock()) |
|
||||
using (IPixelAccessor<T, TP> tempPixels = temp.Lock()) |
|
||||
{ |
|
||||
Parallel.For( |
|
||||
0, |
|
||||
height, |
|
||||
this.ParallelOptions, |
|
||||
y => |
|
||||
{ |
|
||||
for (int x = 0; x < width; x++) |
|
||||
{ |
|
||||
int newX = height - y - 1; |
|
||||
newX = height - newX - 1; |
|
||||
int newY = width - x - 1; |
|
||||
newY = width - newY - 1; |
|
||||
tempPixels[newX, newY] = sourcePixels[x, y]; |
|
||||
} |
|
||||
|
|
||||
this.OnRowProcessed(); |
|
||||
}); |
|
||||
} |
|
||||
|
|
||||
target.SetPixels(height, width, temp.Pixels); |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Rotates the image 180 degrees clockwise at the centre point.
|
|
||||
/// </summary>
|
|
||||
/// <param name="target">The target image.</param>
|
|
||||
/// <param name="source">The source image.</param>
|
|
||||
private void Rotate180(ImageBase<T, TP> target, ImageBase<T, TP> source) |
|
||||
{ |
|
||||
int width = source.Width; |
|
||||
int height = source.Height; |
|
||||
|
|
||||
using (IPixelAccessor<T, TP> sourcePixels = source.Lock()) |
|
||||
using (IPixelAccessor<T, TP> targetPixels = target.Lock()) |
|
||||
{ |
|
||||
Parallel.For( |
|
||||
0, |
|
||||
height, |
|
||||
this.ParallelOptions, |
|
||||
y => |
|
||||
{ |
|
||||
for (int x = 0; x < width; x++) |
|
||||
{ |
|
||||
int newX = width - x - 1; |
|
||||
int newY = height - y - 1; |
|
||||
targetPixels[newX, newY] = sourcePixels[x, y]; |
|
||||
} |
|
||||
|
|
||||
this.OnRowProcessed(); |
|
||||
}); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Rotates the image 90 degrees clockwise at the centre point.
|
|
||||
/// </summary>
|
|
||||
/// <param name="target">The target image.</param>
|
|
||||
/// <param name="source">The source image.</param>
|
|
||||
private void Rotate90(ImageBase<T, TP> target, ImageBase<T, TP> source) |
|
||||
{ |
|
||||
int width = source.Width; |
|
||||
int height = source.Height; |
|
||||
Image<T, TP> temp = new Image<T, TP>(height, width); |
|
||||
|
|
||||
using (IPixelAccessor<T, TP> sourcePixels = source.Lock()) |
|
||||
using (IPixelAccessor<T, TP> tempPixels = temp.Lock()) |
|
||||
{ |
|
||||
Parallel.For( |
|
||||
0, |
|
||||
height, |
|
||||
this.ParallelOptions, |
|
||||
y => |
|
||||
{ |
|
||||
for (int x = 0; x < width; x++) |
|
||||
{ |
|
||||
int newX = height - y - 1; |
|
||||
tempPixels[newX, x] = sourcePixels[x, y]; |
|
||||
} |
|
||||
|
|
||||
this.OnRowProcessed(); |
|
||||
}); |
|
||||
} |
|
||||
|
|
||||
target.SetPixels(height, width, temp.Pixels); |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Swaps the image at the X-axis, which goes horizontally through the middle
|
|
||||
/// at half the height of the image.
|
|
||||
/// </summary>
|
|
||||
/// <param name="target">Target image to apply the process to.</param>
|
|
||||
private void FlipX(ImageBase<T, TP> target) |
|
||||
{ |
|
||||
int width = target.Width; |
|
||||
int height = target.Height; |
|
||||
int halfHeight = (int)Math.Ceiling(target.Height * .5F); |
|
||||
Image<T, TP> temp = new Image<T, TP>(width, height); |
|
||||
temp.ClonePixels(width, height, target.Pixels); |
|
||||
|
|
||||
using (IPixelAccessor<T, TP> targetPixels = target.Lock()) |
|
||||
using (IPixelAccessor<T, TP> tempPixels = temp.Lock()) |
|
||||
{ |
|
||||
Parallel.For( |
|
||||
0, |
|
||||
halfHeight, |
|
||||
this.ParallelOptions, |
|
||||
y => |
|
||||
{ |
|
||||
for (int x = 0; x < width; x++) |
|
||||
{ |
|
||||
int newY = height - y - 1; |
|
||||
targetPixels[x, y] = tempPixels[x, newY]; |
|
||||
targetPixels[x, newY] = tempPixels[x, y]; |
|
||||
} |
|
||||
|
|
||||
this.OnRowProcessed(); |
|
||||
}); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Swaps the image at the Y-axis, which goes vertically through the middle
|
|
||||
/// at half of the width of the image.
|
|
||||
/// </summary>
|
|
||||
/// <param name="target">Target image to apply the process to.</param>
|
|
||||
private void FlipY(ImageBase<T, TP> target) |
|
||||
{ |
|
||||
int width = target.Width; |
|
||||
int height = target.Height; |
|
||||
int halfWidth = (int)Math.Ceiling(width * .5F); |
|
||||
Image<T, TP> temp = new Image<T, TP>(width, height); |
|
||||
temp.ClonePixels(width, height, target.Pixels); |
|
||||
|
|
||||
using (IPixelAccessor<T, TP> targetPixels = target.Lock()) |
|
||||
using (IPixelAccessor<T, TP> tempPixels = temp.Lock()) |
|
||||
{ |
|
||||
Parallel.For( |
|
||||
0, |
|
||||
height, |
|
||||
this.ParallelOptions, |
|
||||
y => |
|
||||
{ |
|
||||
for (int x = 0; x < halfWidth; x++) |
|
||||
{ |
|
||||
int newX = width - x - 1; |
|
||||
targetPixels[x, y] = tempPixels[newX, y]; |
|
||||
targetPixels[newX, y] = tempPixels[x, y]; |
|
||||
} |
|
||||
|
|
||||
this.OnRowProcessed(); |
|
||||
}); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
Loading…
Reference in new issue