Browse Source

Update based on feedback.

pull/1118/head
James Jackson-South 6 years ago
parent
commit
b25e102fe6
  1. 57
      src/ImageSharp/Processing/Processors/Transforms/Automorphic/AffineTransformProcessor{TPixel}.cs
  2. 57
      src/ImageSharp/Processing/Processors/Transforms/Automorphic/ProjectiveTransformProcessor{TPixel}.cs
  3. 2
      src/ImageSharp/Processing/Processors/Transforms/IResampler.cs
  4. 4
      src/ImageSharp/Processing/Processors/Transforms/IResamplingTransformImageProcessor{TPixel}.cs
  5. 0
      src/ImageSharp/Processing/Processors/Transforms/Linear/AffineTransformProcessor.cs
  6. 65
      src/ImageSharp/Processing/Processors/Transforms/Linear/AffineTransformProcessor{TPixel}.cs
  7. 0
      src/ImageSharp/Processing/Processors/Transforms/Linear/AutoOrientProcessor.cs
  8. 0
      src/ImageSharp/Processing/Processors/Transforms/Linear/AutoOrientProcessor{TPixel}.cs
  9. 0
      src/ImageSharp/Processing/Processors/Transforms/Linear/FlipProcessor.cs
  10. 0
      src/ImageSharp/Processing/Processors/Transforms/Linear/FlipProcessor{TPixel}.cs
  11. 2
      src/ImageSharp/Processing/Processors/Transforms/Linear/LinearTransformUtilities.cs
  12. 0
      src/ImageSharp/Processing/Processors/Transforms/Linear/ProjectiveTransformProcessor.cs
  13. 65
      src/ImageSharp/Processing/Processors/Transforms/Linear/ProjectiveTransformProcessor{TPixel}.cs
  14. 0
      src/ImageSharp/Processing/Processors/Transforms/Linear/RotateProcessor.cs
  15. 0
      src/ImageSharp/Processing/Processors/Transforms/Linear/RotateProcessor{TPixel}.cs
  16. 0
      src/ImageSharp/Processing/Processors/Transforms/Linear/SkewProcessor.cs
  17. 2
      src/ImageSharp/Processing/Processors/Transforms/Resamplers/BicubicResampler.cs
  18. 2
      src/ImageSharp/Processing/Processors/Transforms/Resamplers/BoxResampler.cs
  19. 2
      src/ImageSharp/Processing/Processors/Transforms/Resamplers/CubicResampler.cs
  20. 2
      src/ImageSharp/Processing/Processors/Transforms/Resamplers/LanczosResampler.cs
  21. 2
      src/ImageSharp/Processing/Processors/Transforms/Resamplers/NearestNeighborResampler.cs
  22. 2
      src/ImageSharp/Processing/Processors/Transforms/Resamplers/TriangleResampler.cs
  23. 2
      src/ImageSharp/Processing/Processors/Transforms/Resamplers/WelchResampler.cs
  24. 222
      src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor{TPixel}.Transforms.cs
  25. 204
      src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor{TPixel}.cs

57
src/ImageSharp/Processing/Processors/Transforms/Automorphic/AffineTransformProcessor{TPixel}.cs

@ -1,57 +0,0 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System.Numerics;
using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp.Processing.Processors.Transforms
{
/// <summary>
/// Provides the base methods to perform affine transforms on an image.
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
internal partial class AffineTransformProcessor<TPixel> : TransformProcessor<TPixel>, IResamplingImageProcessor<TPixel>
where TPixel : struct, IPixel<TPixel>
{
private readonly Size destinationSize;
private readonly Matrix3x2 transformMatrix;
private readonly IResampler resampler;
private ImageFrame<TPixel> source;
private ImageFrame<TPixel> destination;
/// <summary>
/// Initializes a new instance of the <see cref="AffineTransformProcessor{TPixel}"/> class.
/// </summary>
/// <param name="configuration">The configuration which allows altering default behaviour or extending the library.</param>
/// <param name="definition">The <see cref="AffineTransformProcessor"/> defining the processor parameters.</param>
/// <param name="source">The source <see cref="Image{TPixel}"/> for the current processor instance.</param>
/// <param name="sourceRectangle">The source area to process for the current processor instance.</param>
public AffineTransformProcessor(Configuration configuration, AffineTransformProcessor definition, Image<TPixel> source, Rectangle sourceRectangle)
: base(configuration, source, sourceRectangle)
{
this.destinationSize = definition.DestinationSize;
this.transformMatrix = definition.TransformMatrix;
this.resampler = definition.Sampler;
}
protected override Size GetDestinationSize() => this.destinationSize;
/// <inheritdoc/>
protected override void OnFrameApply(ImageFrame<TPixel> source, ImageFrame<TPixel> destination)
{
this.source = source;
this.destination = destination;
this.resampler.ApplyTransform(this);
}
/// <inheritdoc/>
public void ApplyTransform<TResampler>(in TResampler sampler)
where TResampler : struct, IResampler
=> ApplyAffineTransform(
this.Configuration,
in sampler,
this.source,
this.destination,
this.transformMatrix);
}
}

57
src/ImageSharp/Processing/Processors/Transforms/Automorphic/ProjectiveTransformProcessor{TPixel}.cs

@ -1,57 +0,0 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System.Numerics;
using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp.Processing.Processors.Transforms
{
/// <summary>
/// Provides the base methods to perform non-affine transforms on an image.
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
internal partial class ProjectiveTransformProcessor<TPixel> : TransformProcessor<TPixel>, IResamplingImageProcessor<TPixel>
where TPixel : struct, IPixel<TPixel>
{
private readonly Size destinationSize;
private readonly IResampler resampler;
private readonly Matrix4x4 transformMatrix;
private ImageFrame<TPixel> source;
private ImageFrame<TPixel> destination;
/// <summary>
/// Initializes a new instance of the <see cref="ProjectiveTransformProcessor{TPixel}"/> class.
/// </summary>
/// <param name="configuration">The configuration which allows altering default behaviour or extending the library.</param>
/// <param name="definition">The <see cref="ProjectiveTransformProcessor"/> defining the processor parameters.</param>
/// <param name="source">The source <see cref="Image{TPixel}"/> for the current processor instance.</param>
/// <param name="sourceRectangle">The source area to process for the current processor instance.</param>
public ProjectiveTransformProcessor(Configuration configuration, ProjectiveTransformProcessor definition, Image<TPixel> source, Rectangle sourceRectangle)
: base(configuration, source, sourceRectangle)
{
this.destinationSize = definition.DestinationSize;
this.transformMatrix = definition.TransformMatrix;
this.resampler = definition.Sampler;
}
protected override Size GetDestinationSize() => this.destinationSize;
/// <inheritdoc/>
protected override void OnFrameApply(ImageFrame<TPixel> source, ImageFrame<TPixel> destination)
{
this.source = source;
this.destination = destination;
this.resampler.ApplyTransform(this);
}
/// <inheritdoc/>
public void ApplyTransform<TResampler>(in TResampler sampler)
where TResampler : struct, IResampler
=> ApplyProjectiveTransform(
this.Configuration,
in sampler,
this.source,
this.destination,
this.transformMatrix);
}
}

2
src/ImageSharp/Processing/Processors/Transforms/IResampler.cs

@ -29,7 +29,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="processor">The transforming image processor.</param>
void ApplyTransform<TPixel>(IResamplingImageProcessor<TPixel> processor)
void ApplyTransform<TPixel>(IResamplingTransformImageProcessor<TPixel> processor)
where TPixel : struct, IPixel<TPixel>;
}
}

4
src/ImageSharp/Processing/Processors/Transforms/IResamplingImageProcessor{TPixel}.cs → src/ImageSharp/Processing/Processors/Transforms/IResamplingTransformImageProcessor{TPixel}.cs

@ -6,10 +6,10 @@ using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp.Processing.Processors.Transforms
{
/// <summary>
/// Implements an algorithm to alter the pixels of an image via a resampling transforms.
/// Implements an algorithm to alter the pixels of an image via resampling transforms.
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
public interface IResamplingImageProcessor<TPixel> : IImageProcessor<TPixel>
public interface IResamplingTransformImageProcessor<TPixel> : IImageProcessor<TPixel>
where TPixel : struct, IPixel<TPixel>
{
/// <summary>

0
src/ImageSharp/Processing/Processors/Transforms/Automorphic/AffineTransformProcessor.cs → src/ImageSharp/Processing/Processors/Transforms/Linear/AffineTransformProcessor.cs

65
src/ImageSharp/Processing/Processors/Transforms/Automorphic/AffineTransformProcessor{TPixel}.Transforms.cs → src/ImageSharp/Processing/Processors/Transforms/Linear/AffineTransformProcessor{TPixel}.cs

@ -11,28 +11,53 @@ using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp.Processing.Processors.Transforms
{
/// <content>
/// Contains the application code for performing an affine transform.
/// </content>
internal partial class AffineTransformProcessor<TPixel>
/// <summary>
/// Provides the base methods to perform affine transforms on an image.
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
internal class AffineTransformProcessor<TPixel> : TransformProcessor<TPixel>, IResamplingTransformImageProcessor<TPixel>
where TPixel : struct, IPixel<TPixel>
{
private readonly Size destinationSize;
private readonly Matrix3x2 transformMatrix;
private readonly IResampler resampler;
private ImageFrame<TPixel> source;
private ImageFrame<TPixel> destination;
/// <summary>
/// Applies an affine transformation upon an image.
/// Initializes a new instance of the <see cref="AffineTransformProcessor{TPixel}"/> class.
/// </summary>
/// <typeparam name="TResampler">The type of sampler.</typeparam>
/// <param name="configuration">The configuration.</param>
/// <param name="sampler">The pixel sampler.</param>
/// <param name="source">The source image frame.</param>
/// <param name="destination">The destination image frame.</param>
/// <param name="matrix">The transform matrix.</param>
public static void ApplyAffineTransform<TResampler>(
Configuration configuration,
in TResampler sampler,
ImageFrame<TPixel> source,
ImageFrame<TPixel> destination,
Matrix3x2 matrix)
/// <param name="configuration">The configuration which allows altering default behaviour or extending the library.</param>
/// <param name="definition">The <see cref="AffineTransformProcessor"/> defining the processor parameters.</param>
/// <param name="source">The source <see cref="Image{TPixel}"/> for the current processor instance.</param>
/// <param name="sourceRectangle">The source area to process for the current processor instance.</param>
public AffineTransformProcessor(Configuration configuration, AffineTransformProcessor definition, Image<TPixel> source, Rectangle sourceRectangle)
: base(configuration, source, sourceRectangle)
{
this.destinationSize = definition.DestinationSize;
this.transformMatrix = definition.TransformMatrix;
this.resampler = definition.Sampler;
}
protected override Size GetDestinationSize() => this.destinationSize;
/// <inheritdoc/>
protected override void OnFrameApply(ImageFrame<TPixel> source, ImageFrame<TPixel> destination)
{
this.source = source;
this.destination = destination;
this.resampler.ApplyTransform(this);
}
/// <inheritdoc/>
public void ApplyTransform<TResampler>(in TResampler sampler)
where TResampler : struct, IResampler
{
Configuration configuration = this.Configuration;
ImageFrame<TPixel> source = this.source;
ImageFrame<TPixel> destination = this.destination;
Matrix3x2 matrix = this.transformMatrix;
// Handle transforms that result in output identical to the original.
if (matrix.Equals(default) || matrix.Equals(Matrix3x2.Identity))
{
@ -55,8 +80,8 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
return;
}
int yRadius = AutomorphicTransformUtilities.GetSamplingRadius(in sampler, source.Height, destination.Height);
int xRadius = AutomorphicTransformUtilities.GetSamplingRadius(in sampler, source.Width, destination.Width);
int yRadius = LinearTransformUtilities.GetSamplingRadius(in sampler, source.Height, destination.Height);
int xRadius = LinearTransformUtilities.GetSamplingRadius(in sampler, source.Width, destination.Width);
var radialExtents = new Vector2(xRadius, yRadius);
int yLength = (yRadius * 2) + 1;
int xLength = (xRadius * 2) + 1;
@ -186,7 +211,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
// Use the single precision position to calculate correct bounding pixels
// otherwise we get rogue pixels outside of the bounds.
var point = Vector2.Transform(new Vector2(x, y), this.matrix);
AutomorphicTransformUtilities.Convolve(
LinearTransformUtilities.Convolve(
in this.sampler,
point,
sourceBuffer,

0
src/ImageSharp/Processing/Processors/Transforms/Automorphic/AutoOrientProcessor.cs → src/ImageSharp/Processing/Processors/Transforms/Linear/AutoOrientProcessor.cs

0
src/ImageSharp/Processing/Processors/Transforms/Automorphic/AutoOrientProcessor{TPixel}.cs → src/ImageSharp/Processing/Processors/Transforms/Linear/AutoOrientProcessor{TPixel}.cs

0
src/ImageSharp/Processing/Processors/Transforms/Automorphic/FlipProcessor.cs → src/ImageSharp/Processing/Processors/Transforms/Linear/FlipProcessor.cs

0
src/ImageSharp/Processing/Processors/Transforms/Automorphic/FlipProcessor{TPixel}.cs → src/ImageSharp/Processing/Processors/Transforms/Linear/FlipProcessor{TPixel}.cs

2
src/ImageSharp/Processing/Processors/Transforms/Automorphic/AutomorphicTransformUtilities.cs → src/ImageSharp/Processing/Processors/Transforms/Linear/LinearTransformUtilities.cs

@ -12,7 +12,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
/// <summary>
/// Utility methods for affine and projective transforms.
/// </summary>
internal static class AutomorphicTransformUtilities
internal static class LinearTransformUtilities
{
[MethodImpl(InliningOptions.ShortMethod)]
internal static int GetSamplingRadius<TResampler>(in TResampler sampler, int sourceSize, int destinationSize)

0
src/ImageSharp/Processing/Processors/Transforms/Automorphic/ProjectiveTransformProcessor.cs → src/ImageSharp/Processing/Processors/Transforms/Linear/ProjectiveTransformProcessor.cs

65
src/ImageSharp/Processing/Processors/Transforms/Automorphic/ProjectiveTransformProcessor{TPixel}.Transforms.cs → src/ImageSharp/Processing/Processors/Transforms/Linear/ProjectiveTransformProcessor{TPixel}.cs

@ -11,28 +11,53 @@ using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp.Processing.Processors.Transforms
{
/// <content>
/// Contains the application code for performing a projective transform.
/// </content>
internal partial class ProjectiveTransformProcessor<TPixel>
/// <summary>
/// Provides the base methods to perform non-affine transforms on an image.
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
internal class ProjectiveTransformProcessor<TPixel> : TransformProcessor<TPixel>, IResamplingTransformImageProcessor<TPixel>
where TPixel : struct, IPixel<TPixel>
{
private readonly Size destinationSize;
private readonly IResampler resampler;
private readonly Matrix4x4 transformMatrix;
private ImageFrame<TPixel> source;
private ImageFrame<TPixel> destination;
/// <summary>
/// Applies a projective transformation upon an image.
/// Initializes a new instance of the <see cref="ProjectiveTransformProcessor{TPixel}"/> class.
/// </summary>
/// <typeparam name="TResampler">The type of sampler.</typeparam>
/// <param name="configuration">The configuration.</param>
/// <param name="sampler">The pixel sampler.</param>
/// <param name="source">The source image frame.</param>
/// <param name="destination">The destination image frame.</param>
/// <param name="matrix">The transform matrix.</param>
public static void ApplyProjectiveTransform<TResampler>(
Configuration configuration,
in TResampler sampler,
ImageFrame<TPixel> source,
ImageFrame<TPixel> destination,
Matrix4x4 matrix)
/// <param name="configuration">The configuration which allows altering default behaviour or extending the library.</param>
/// <param name="definition">The <see cref="ProjectiveTransformProcessor"/> defining the processor parameters.</param>
/// <param name="source">The source <see cref="Image{TPixel}"/> for the current processor instance.</param>
/// <param name="sourceRectangle">The source area to process for the current processor instance.</param>
public ProjectiveTransformProcessor(Configuration configuration, ProjectiveTransformProcessor definition, Image<TPixel> source, Rectangle sourceRectangle)
: base(configuration, source, sourceRectangle)
{
this.destinationSize = definition.DestinationSize;
this.transformMatrix = definition.TransformMatrix;
this.resampler = definition.Sampler;
}
protected override Size GetDestinationSize() => this.destinationSize;
/// <inheritdoc/>
protected override void OnFrameApply(ImageFrame<TPixel> source, ImageFrame<TPixel> destination)
{
this.source = source;
this.destination = destination;
this.resampler.ApplyTransform(this);
}
/// <inheritdoc/>
public void ApplyTransform<TResampler>(in TResampler sampler)
where TResampler : struct, IResampler
{
Configuration configuration = this.Configuration;
ImageFrame<TPixel> source = this.source;
ImageFrame<TPixel> destination = this.destination;
Matrix4x4 matrix = this.transformMatrix;
// Handle transforms that result in output identical to the original.
if (matrix.Equals(default) || matrix.Equals(Matrix4x4.Identity))
{
@ -55,8 +80,8 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
return;
}
int yRadius = AutomorphicTransformUtilities.GetSamplingRadius(in sampler, source.Height, destination.Height);
int xRadius = AutomorphicTransformUtilities.GetSamplingRadius(in sampler, source.Width, destination.Width);
int yRadius = LinearTransformUtilities.GetSamplingRadius(in sampler, source.Height, destination.Height);
int xRadius = LinearTransformUtilities.GetSamplingRadius(in sampler, source.Width, destination.Width);
var radialExtents = new Vector2(xRadius, yRadius);
int yLength = (yRadius * 2) + 1;
int xLength = (xRadius * 2) + 1;
@ -186,7 +211,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
// Use the single precision position to calculate correct bounding pixels
// otherwise we get rogue pixels outside of the bounds.
Vector2 point = TransformUtilities.ProjectiveTransform2D(x, y, this.matrix);
AutomorphicTransformUtilities.Convolve(
LinearTransformUtilities.Convolve(
in this.sampler,
point,
sourceBuffer,

0
src/ImageSharp/Processing/Processors/Transforms/Automorphic/RotateProcessor.cs → src/ImageSharp/Processing/Processors/Transforms/Linear/RotateProcessor.cs

0
src/ImageSharp/Processing/Processors/Transforms/Automorphic/RotateProcessor{TPixel}.cs → src/ImageSharp/Processing/Processors/Transforms/Linear/RotateProcessor{TPixel}.cs

0
src/ImageSharp/Processing/Processors/Transforms/Automorphic/SkewProcessor.cs → src/ImageSharp/Processing/Processors/Transforms/Linear/SkewProcessor.cs

2
src/ImageSharp/Processing/Processors/Transforms/Resamplers/BicubicResampler.cs

@ -42,7 +42,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
/// <inheritdoc/>
[MethodImpl(InliningOptions.ShortMethod)]
public void ApplyTransform<TPixel>(IResamplingImageProcessor<TPixel> processor)
public void ApplyTransform<TPixel>(IResamplingTransformImageProcessor<TPixel> processor)
where TPixel : struct, IPixel<TPixel>
=> processor.ApplyTransform(in this);
}

2
src/ImageSharp/Processing/Processors/Transforms/Resamplers/BoxResampler.cs

@ -29,7 +29,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
/// <inheritdoc/>
[MethodImpl(InliningOptions.ShortMethod)]
public void ApplyTransform<TPixel>(IResamplingImageProcessor<TPixel> processor)
public void ApplyTransform<TPixel>(IResamplingTransformImageProcessor<TPixel> processor)
where TPixel : struct, IPixel<TPixel>
=> processor.ApplyTransform(in this);
}

2
src/ImageSharp/Processing/Processors/Transforms/Resamplers/CubicResampler.cs

@ -105,7 +105,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
/// <inheritdoc/>
[MethodImpl(InliningOptions.ShortMethod)]
public void ApplyTransform<TPixel>(IResamplingImageProcessor<TPixel> processor)
public void ApplyTransform<TPixel>(IResamplingTransformImageProcessor<TPixel> processor)
where TPixel : struct, IPixel<TPixel>
=> processor.ApplyTransform(in this);
}

2
src/ImageSharp/Processing/Processors/Transforms/Resamplers/LanczosResampler.cs

@ -61,7 +61,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
/// <inheritdoc/>
[MethodImpl(InliningOptions.ShortMethod)]
public void ApplyTransform<TPixel>(IResamplingImageProcessor<TPixel> processor)
public void ApplyTransform<TPixel>(IResamplingTransformImageProcessor<TPixel> processor)
where TPixel : struct, IPixel<TPixel>
=> processor.ApplyTransform(in this);
}

2
src/ImageSharp/Processing/Processors/Transforms/Resamplers/NearestNeighborResampler.cs

@ -21,7 +21,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
/// <inheritdoc/>
[MethodImpl(InliningOptions.ShortMethod)]
public void ApplyTransform<TPixel>(IResamplingImageProcessor<TPixel> processor)
public void ApplyTransform<TPixel>(IResamplingTransformImageProcessor<TPixel> processor)
where TPixel : struct, IPixel<TPixel>
=> processor.ApplyTransform(in this);
}

2
src/ImageSharp/Processing/Processors/Transforms/Resamplers/TriangleResampler.cs

@ -35,7 +35,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
/// <inheritdoc/>
[MethodImpl(InliningOptions.ShortMethod)]
public void ApplyTransform<TPixel>(IResamplingImageProcessor<TPixel> processor)
public void ApplyTransform<TPixel>(IResamplingTransformImageProcessor<TPixel> processor)
where TPixel : struct, IPixel<TPixel>
=> processor.ApplyTransform(in this);
}

2
src/ImageSharp/Processing/Processors/Transforms/Resamplers/WelchResampler.cs

@ -34,7 +34,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
/// <inheritdoc/>
[MethodImpl(InliningOptions.ShortMethod)]
public void ApplyTransform<TPixel>(IResamplingImageProcessor<TPixel> processor)
public void ApplyTransform<TPixel>(IResamplingTransformImageProcessor<TPixel> processor)
where TPixel : struct, IPixel<TPixel>
=> processor.ApplyTransform(in this);
}

222
src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor{TPixel}.Transforms.cs

@ -1,222 +0,0 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
using System.Runtime.CompilerServices;
using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp.Processing.Processors.Transforms
{
/// <content>
/// Contains the application code for resizing.
/// </content>
internal partial class ResizeProcessor<TPixel>
where TPixel : struct, IPixel<TPixel>
{
/// <summary>
/// Applies an resizing transformation upon an image.
/// </summary>
/// <typeparam name="TResampler">The type of sampler.</typeparam>
/// <param name="configuration">The configuration.</param>
/// <param name="sampler">The pixel sampler.</param>
/// <param name="source">The source image.</param>
/// <param name="destination">The destination image.</param>
/// <param name="sourceRectangle">The source bounds.</param>
/// <param name="destinationRectangle">The destination location.</param>
/// <param name="compand">Whether to compress or expand individual pixel color values on processing.</param>
public static void ApplyResizeTransform<TResampler>(
Configuration configuration,
in TResampler sampler,
Image<TPixel> source,
Image<TPixel> destination,
Rectangle sourceRectangle,
Rectangle destinationRectangle,
bool compand)
where TResampler : struct, IResampler
{
// Handle resize dimensions identical to the original
if (source.Width == destination.Width
&& source.Height == destination.Height
&& sourceRectangle == destinationRectangle)
{
for (int i = 0; i < source.Frames.Count; i++)
{
ImageFrame<TPixel> sourceFrame = source.Frames[i];
ImageFrame<TPixel> destinationFrame = destination.Frames[i];
// The cloned will be blank here copy all the pixel data over
sourceFrame.GetPixelMemoryGroup().CopyTo(destinationFrame.GetPixelMemoryGroup());
}
return;
}
var interest = Rectangle.Intersect(destinationRectangle, destination.Bounds());
if (sampler is NearestNeighborResampler)
{
for (int i = 0; i < source.Frames.Count; i++)
{
ImageFrame<TPixel> sourceFrame = source.Frames[i];
ImageFrame<TPixel> destinationFrame = destination.Frames[i];
ApplyNNResizeFrameTransform(
configuration,
sourceFrame,
destinationFrame,
sourceRectangle,
destinationRectangle,
interest);
}
return;
}
// Since all image frame dimensions have to be the same we can calculate
// the kernel maps and reuse for all frames.
MemoryAllocator allocator = configuration.MemoryAllocator;
using var horizontalKernelMap = ResizeKernelMap.Calculate(
in sampler,
destinationRectangle.Width,
sourceRectangle.Width,
allocator);
using var verticalKernelMap = ResizeKernelMap.Calculate(
in sampler,
destinationRectangle.Height,
sourceRectangle.Height,
allocator);
for (int i = 0; i < source.Frames.Count; i++)
{
ImageFrame<TPixel> sourceFrame = source.Frames[i];
ImageFrame<TPixel> destinationFrame = destination.Frames[i];
ApplyResizeFrameTransform(
configuration,
sourceFrame,
destinationFrame,
horizontalKernelMap,
verticalKernelMap,
sourceRectangle,
destinationRectangle,
interest,
compand);
}
}
private static void ApplyNNResizeFrameTransform(
Configuration configuration,
ImageFrame<TPixel> source,
ImageFrame<TPixel> destination,
Rectangle sourceRectangle,
Rectangle destinationRectangle,
Rectangle interest)
{
// Scaling factors
float widthFactor = sourceRectangle.Width / (float)destinationRectangle.Width;
float heightFactor = sourceRectangle.Height / (float)destinationRectangle.Height;
var operation = new NNRowIntervalOperation(
sourceRectangle,
destinationRectangle,
widthFactor,
heightFactor,
source,
destination);
ParallelRowIterator.IterateRows(
configuration,
interest,
in operation);
}
private static void ApplyResizeFrameTransform(
Configuration configuration,
ImageFrame<TPixel> source,
ImageFrame<TPixel> destination,
ResizeKernelMap horizontalKernelMap,
ResizeKernelMap verticalKernelMap,
Rectangle sourceRectangle,
Rectangle destinationRectangle,
Rectangle interest,
bool compand)
{
PixelConversionModifiers conversionModifiers =
PixelConversionModifiers.Premultiply.ApplyCompanding(compand);
BufferArea<TPixel> sourceArea = source.PixelBuffer.GetArea(sourceRectangle);
// To reintroduce parallel processing, we would launch multiple workers
// for different row intervals of the image.
using (var worker = new ResizeWorker<TPixel>(
configuration,
sourceArea,
conversionModifiers,
horizontalKernelMap,
verticalKernelMap,
destination.Width,
interest,
destinationRectangle.Location))
{
worker.Initialize();
var workingInterval = new RowInterval(interest.Top, interest.Bottom);
worker.FillDestinationPixels(workingInterval, destination.PixelBuffer);
}
}
private readonly struct NNRowIntervalOperation : IRowIntervalOperation
{
private readonly Rectangle sourceBounds;
private readonly Rectangle destinationBounds;
private readonly float widthFactor;
private readonly float heightFactor;
private readonly ImageFrame<TPixel> source;
private readonly ImageFrame<TPixel> destination;
[MethodImpl(InliningOptions.ShortMethod)]
public NNRowIntervalOperation(
Rectangle sourceBounds,
Rectangle destinationBounds,
float widthFactor,
float heightFactor,
ImageFrame<TPixel> source,
ImageFrame<TPixel> destination)
{
this.sourceBounds = sourceBounds;
this.destinationBounds = destinationBounds;
this.widthFactor = widthFactor;
this.heightFactor = heightFactor;
this.source = source;
this.destination = destination;
}
[MethodImpl(InliningOptions.ShortMethod)]
public void Invoke(in RowInterval rows)
{
int sourceX = this.sourceBounds.X;
int sourceY = this.sourceBounds.Y;
int destX = this.destinationBounds.X;
int destY = this.destinationBounds.Y;
int destLeft = this.destinationBounds.Left;
int destRight = this.destinationBounds.Right;
for (int y = rows.Min; y < rows.Max; y++)
{
// Y coordinates of source points
Span<TPixel> sourceRow = this.source.GetPixelRowSpan((int)(((y - destY) * this.heightFactor) + sourceY));
Span<TPixel> targetRow = this.destination.GetPixelRowSpan(y);
for (int x = destLeft; x < destRight; x++)
{
// X coordinates of source points
targetRow[x] = sourceRow[(int)(((x - destX) * this.widthFactor) + sourceX)];
}
}
}
}
}
}

204
src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor{TPixel}.cs

@ -1,6 +1,10 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
using System.Runtime.CompilerServices;
using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp.Processing.Processors.Transforms
@ -9,7 +13,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
/// Implements resizing of images using various resamplers.
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
internal partial class ResizeProcessor<TPixel> : TransformProcessor<TPixel>, IResamplingImageProcessor<TPixel>
internal partial class ResizeProcessor<TPixel> : TransformProcessor<TPixel>, IResamplingTransformImageProcessor<TPixel>
where TPixel : struct, IPixel<TPixel>
{
private readonly int destinationWidth;
@ -48,14 +52,196 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
}
public void ApplyTransform<TResampler>(in TResampler sampler)
where TResampler : struct, IResampler =>
ApplyResizeTransform(
this.Configuration,
where TResampler : struct, IResampler
{
Configuration configuration = this.Configuration;
Image<TPixel> source = this.Source;
Image<TPixel> destination = this.destination;
Rectangle sourceRectangle = this.SourceRectangle;
Rectangle destinationRectangle = this.destinationRectangle;
bool compand = this.compand;
// Handle resize dimensions identical to the original
if (source.Width == destination.Width
&& source.Height == destination.Height
&& sourceRectangle == destinationRectangle)
{
for (int i = 0; i < source.Frames.Count; i++)
{
ImageFrame<TPixel> sourceFrame = source.Frames[i];
ImageFrame<TPixel> destinationFrame = destination.Frames[i];
// The cloned will be blank here copy all the pixel data over
sourceFrame.GetPixelMemoryGroup().CopyTo(destinationFrame.GetPixelMemoryGroup());
}
return;
}
var interest = Rectangle.Intersect(destinationRectangle, destination.Bounds());
if (sampler is NearestNeighborResampler)
{
for (int i = 0; i < source.Frames.Count; i++)
{
ImageFrame<TPixel> sourceFrame = source.Frames[i];
ImageFrame<TPixel> destinationFrame = destination.Frames[i];
ApplyNNResizeFrameTransform(
configuration,
sourceFrame,
destinationFrame,
sourceRectangle,
destinationRectangle,
interest);
}
return;
}
// Since all image frame dimensions have to be the same we can calculate
// the kernel maps and reuse for all frames.
MemoryAllocator allocator = configuration.MemoryAllocator;
using var horizontalKernelMap = ResizeKernelMap.Calculate(
in sampler,
destinationRectangle.Width,
sourceRectangle.Width,
allocator);
using var verticalKernelMap = ResizeKernelMap.Calculate(
in sampler,
this.Source,
this.destination,
this.SourceRectangle,
this.destinationRectangle,
this.compand);
destinationRectangle.Height,
sourceRectangle.Height,
allocator);
for (int i = 0; i < source.Frames.Count; i++)
{
ImageFrame<TPixel> sourceFrame = source.Frames[i];
ImageFrame<TPixel> destinationFrame = destination.Frames[i];
ApplyResizeFrameTransform(
configuration,
sourceFrame,
destinationFrame,
horizontalKernelMap,
verticalKernelMap,
sourceRectangle,
destinationRectangle,
interest,
compand);
}
}
private static void ApplyNNResizeFrameTransform(
Configuration configuration,
ImageFrame<TPixel> source,
ImageFrame<TPixel> destination,
Rectangle sourceRectangle,
Rectangle destinationRectangle,
Rectangle interest)
{
// Scaling factors
float widthFactor = sourceRectangle.Width / (float)destinationRectangle.Width;
float heightFactor = sourceRectangle.Height / (float)destinationRectangle.Height;
var operation = new NNRowIntervalOperation(
sourceRectangle,
destinationRectangle,
widthFactor,
heightFactor,
source,
destination);
ParallelRowIterator.IterateRows(
configuration,
interest,
in operation);
}
private static void ApplyResizeFrameTransform(
Configuration configuration,
ImageFrame<TPixel> source,
ImageFrame<TPixel> destination,
ResizeKernelMap horizontalKernelMap,
ResizeKernelMap verticalKernelMap,
Rectangle sourceRectangle,
Rectangle destinationRectangle,
Rectangle interest,
bool compand)
{
PixelConversionModifiers conversionModifiers =
PixelConversionModifiers.Premultiply.ApplyCompanding(compand);
BufferArea<TPixel> sourceArea = source.PixelBuffer.GetArea(sourceRectangle);
// To reintroduce parallel processing, we would launch multiple workers
// for different row intervals of the image.
using (var worker = new ResizeWorker<TPixel>(
configuration,
sourceArea,
conversionModifiers,
horizontalKernelMap,
verticalKernelMap,
destination.Width,
interest,
destinationRectangle.Location))
{
worker.Initialize();
var workingInterval = new RowInterval(interest.Top, interest.Bottom);
worker.FillDestinationPixels(workingInterval, destination.PixelBuffer);
}
}
private readonly struct NNRowIntervalOperation : IRowIntervalOperation
{
private readonly Rectangle sourceBounds;
private readonly Rectangle destinationBounds;
private readonly float widthFactor;
private readonly float heightFactor;
private readonly ImageFrame<TPixel> source;
private readonly ImageFrame<TPixel> destination;
[MethodImpl(InliningOptions.ShortMethod)]
public NNRowIntervalOperation(
Rectangle sourceBounds,
Rectangle destinationBounds,
float widthFactor,
float heightFactor,
ImageFrame<TPixel> source,
ImageFrame<TPixel> destination)
{
this.sourceBounds = sourceBounds;
this.destinationBounds = destinationBounds;
this.widthFactor = widthFactor;
this.heightFactor = heightFactor;
this.source = source;
this.destination = destination;
}
[MethodImpl(InliningOptions.ShortMethod)]
public void Invoke(in RowInterval rows)
{
int sourceX = this.sourceBounds.X;
int sourceY = this.sourceBounds.Y;
int destX = this.destinationBounds.X;
int destY = this.destinationBounds.Y;
int destLeft = this.destinationBounds.Left;
int destRight = this.destinationBounds.Right;
for (int y = rows.Min; y < rows.Max; y++)
{
// Y coordinates of source points
Span<TPixel> sourceRow = this.source.GetPixelRowSpan((int)(((y - destY) * this.heightFactor) + sourceY));
Span<TPixel> targetRow = this.destination.GetPixelRowSpan(y);
for (int x = destLeft; x < destRight; x++)
{
// X coordinates of source points
targetRow[x] = sourceRow[(int)(((x - destX) * this.widthFactor) + sourceX)];
}
}
}
}
}
}

Loading…
Cancel
Save