mirror of https://github.com/SixLabors/ImageSharp
25 changed files with 296 additions and 396 deletions
@ -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); |
|
||||
} |
|
||||
} |
|
||||
@ -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); |
|
||||
} |
|
||||
} |
|
||||
@ -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)]; |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
Loading…
Reference in new issue