diff --git a/src/ImageSharp/Processing/Processors/CloningImageProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/CloningImageProcessor{TPixel}.cs index c539861f9b..1b6438bf3f 100644 --- a/src/ImageSharp/Processing/Processors/CloningImageProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/CloningImageProcessor{TPixel}.cs @@ -110,10 +110,10 @@ namespace SixLabors.ImageSharp.Processing.Processors } /// - /// Gets the size of the target image. + /// Gets the size of the destination image. /// /// The . - protected abstract Size GetTargetSize(); + protected abstract Size GetDestinationSize(); /// /// This method is called before the process is applied to prepare the processor. @@ -168,21 +168,21 @@ namespace SixLabors.ImageSharp.Processing.Processors private Image CreateTarget() { Image source = this.Source; - Size targetSize = this.GetTargetSize(); + Size destinationSize = this.GetDestinationSize(); // We will always be creating the clone even for mutate because we may need to resize the canvas. - var targetFrames = new ImageFrame[source.Frames.Count]; - for (int i = 0; i < targetFrames.Length; i++) + var destinationFrames = new ImageFrame[source.Frames.Count]; + for (int i = 0; i < destinationFrames.Length; i++) { - targetFrames[i] = new ImageFrame( + destinationFrames[i] = new ImageFrame( this.Configuration, - targetSize.Width, - targetSize.Height, + destinationSize.Width, + destinationSize.Height, source.Frames[i].Metadata.DeepClone()); } // Use the overload to prevent an extra frame being added. - return new Image(this.Configuration, source.Metadata.DeepClone(), targetFrames); + return new Image(this.Configuration, source.Metadata.DeepClone(), destinationFrames); } private void CheckFrameCount(Image a, Image b) diff --git a/src/ImageSharp/Processing/Processors/Transforms/AffineTransformProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Transforms/AffineTransformProcessor{TPixel}.cs index 130cc1bf05..9d01c049c2 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/AffineTransformProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/AffineTransformProcessor{TPixel}.cs @@ -32,7 +32,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms this.resampler = definition.Sampler; } - protected override Size GetTargetSize() => this.targetSize; + protected override Size GetDestinationSize() => this.targetSize; /// protected override void OnFrameApply(ImageFrame source, ImageFrame destination) diff --git a/src/ImageSharp/Processing/Processors/Transforms/CropProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Transforms/CropProcessor{TPixel}.cs index e8eeea3cb1..a80eef2bcd 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/CropProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/CropProcessor{TPixel}.cs @@ -30,7 +30,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms => this.cropRectangle = definition.CropRectangle; /// - protected override Size GetTargetSize() => new Size(this.cropRectangle.Width, this.cropRectangle.Height); + protected override Size GetDestinationSize() => new Size(this.cropRectangle.Width, this.cropRectangle.Height); /// protected override void OnFrameApply(ImageFrame source, ImageFrame destination) diff --git a/src/ImageSharp/Processing/Processors/Transforms/IResampler.cs b/src/ImageSharp/Processing/Processors/Transforms/IResampler.cs index fb095b70ab..c7557461a8 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/IResampler.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/IResampler.cs @@ -25,6 +25,25 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms /// float GetValue(float x); + /// + /// Applies an resizing transformation upon an image. + /// + /// The pixel format. + /// The configuration. + /// The source image. + /// The destination image. + /// The source bounds. + /// The target location. + /// Whether to compress or expand individual pixel color values on processing. + void ApplyResizeTransform( + Configuration configuration, + Image source, + Image destination, + Rectangle sourceRectangle, + Rectangle targetRectangle, + bool compand) + where TPixel : struct, IPixel; + /// /// Applies an affine transformation upon an image. /// diff --git a/src/ImageSharp/Processing/Processors/Transforms/ProjectiveTransformProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Transforms/ProjectiveTransformProcessor{TPixel}.cs index 50315ac95c..afc4658b44 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/ProjectiveTransformProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/ProjectiveTransformProcessor{TPixel}.cs @@ -32,7 +32,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms this.resampler = definition.Sampler; } - protected override Size GetTargetSize() => this.targetSize; + protected override Size GetDestinationSize() => this.targetSize; /// protected override void OnFrameApply(ImageFrame source, ImageFrame destination) diff --git a/src/ImageSharp/Processing/Processors/Transforms/Resamplers/BicubicResampler.cs b/src/ImageSharp/Processing/Processors/Transforms/Resamplers/BicubicResampler.cs index ea68715753..5f9669f6f8 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/Resamplers/BicubicResampler.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/Resamplers/BicubicResampler.cs @@ -41,6 +41,24 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms return 0; } + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void ApplyResizeTransform( + Configuration configuration, + Image source, + Image destination, + Rectangle sourceRectangle, + Rectangle destinationRectangle, + bool compand) + where TPixel : struct, IPixel => ResamplerExtensions.ApplyResizeTransform( + configuration, + in this, + source, + destination, + sourceRectangle, + destinationRectangle, + compand); + /// [MethodImpl(InliningOptions.ShortMethod)] public void ApplyAffineTransform( diff --git a/src/ImageSharp/Processing/Processors/Transforms/Resamplers/BoxResampler.cs b/src/ImageSharp/Processing/Processors/Transforms/Resamplers/BoxResampler.cs index 49b53378f9..ecaa28c804 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/Resamplers/BoxResampler.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/Resamplers/BoxResampler.cs @@ -28,6 +28,24 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms return 0; } + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void ApplyResizeTransform( + Configuration configuration, + Image source, + Image destination, + Rectangle sourceRectangle, + Rectangle destinationRectangle, + bool compand) + where TPixel : struct, IPixel => ResamplerExtensions.ApplyResizeTransform( + configuration, + in this, + source, + destination, + sourceRectangle, + destinationRectangle, + compand); + /// [MethodImpl(InliningOptions.ShortMethod)] public void ApplyAffineTransform( diff --git a/src/ImageSharp/Processing/Processors/Transforms/Resamplers/CatmullRomResampler.cs b/src/ImageSharp/Processing/Processors/Transforms/Resamplers/CatmullRomResampler.cs index 5a2992595d..f62b7d989c 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/Resamplers/CatmullRomResampler.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/Resamplers/CatmullRomResampler.cs @@ -28,6 +28,24 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms return ImageMaths.GetBcValue(x, B, C); } + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void ApplyResizeTransform( + Configuration configuration, + Image source, + Image destination, + Rectangle sourceRectangle, + Rectangle destinationRectangle, + bool compand) + where TPixel : struct, IPixel => ResamplerExtensions.ApplyResizeTransform( + configuration, + in this, + source, + destination, + sourceRectangle, + destinationRectangle, + compand); + /// [MethodImpl(InliningOptions.ShortMethod)] public void ApplyAffineTransform( diff --git a/src/ImageSharp/Processing/Processors/Transforms/Resamplers/HermiteResampler.cs b/src/ImageSharp/Processing/Processors/Transforms/Resamplers/HermiteResampler.cs index 80aa69acd6..883d4b684a 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/Resamplers/HermiteResampler.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/Resamplers/HermiteResampler.cs @@ -27,6 +27,24 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms return ImageMaths.GetBcValue(x, B, C); } + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void ApplyResizeTransform( + Configuration configuration, + Image source, + Image destination, + Rectangle sourceRectangle, + Rectangle destinationRectangle, + bool compand) + where TPixel : struct, IPixel => ResamplerExtensions.ApplyResizeTransform( + configuration, + in this, + source, + destination, + sourceRectangle, + destinationRectangle, + compand); + /// [MethodImpl(InliningOptions.ShortMethod)] public void ApplyAffineTransform( diff --git a/src/ImageSharp/Processing/Processors/Transforms/Resamplers/Lanczos2Resampler.cs b/src/ImageSharp/Processing/Processors/Transforms/Resamplers/Lanczos2Resampler.cs index 3228a709d2..93174a23a1 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/Resamplers/Lanczos2Resampler.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/Resamplers/Lanczos2Resampler.cs @@ -34,6 +34,24 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms return 0F; } + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void ApplyResizeTransform( + Configuration configuration, + Image source, + Image destination, + Rectangle sourceRectangle, + Rectangle destinationRectangle, + bool compand) + where TPixel : struct, IPixel => ResamplerExtensions.ApplyResizeTransform( + configuration, + in this, + source, + destination, + sourceRectangle, + destinationRectangle, + compand); + /// [MethodImpl(InliningOptions.ShortMethod)] public void ApplyAffineTransform( diff --git a/src/ImageSharp/Processing/Processors/Transforms/Resamplers/Lanczos3Resampler.cs b/src/ImageSharp/Processing/Processors/Transforms/Resamplers/Lanczos3Resampler.cs index a9388575be..6c008be630 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/Resamplers/Lanczos3Resampler.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/Resamplers/Lanczos3Resampler.cs @@ -34,6 +34,24 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms return 0F; } + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void ApplyResizeTransform( + Configuration configuration, + Image source, + Image destination, + Rectangle sourceRectangle, + Rectangle destinationRectangle, + bool compand) + where TPixel : struct, IPixel => ResamplerExtensions.ApplyResizeTransform( + configuration, + in this, + source, + destination, + sourceRectangle, + destinationRectangle, + compand); + /// [MethodImpl(InliningOptions.ShortMethod)] public void ApplyAffineTransform( diff --git a/src/ImageSharp/Processing/Processors/Transforms/Resamplers/Lanczos5Resampler.cs b/src/ImageSharp/Processing/Processors/Transforms/Resamplers/Lanczos5Resampler.cs index 7662f26160..56da01fb27 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/Resamplers/Lanczos5Resampler.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/Resamplers/Lanczos5Resampler.cs @@ -34,6 +34,24 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms return 0F; } + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void ApplyResizeTransform( + Configuration configuration, + Image source, + Image destination, + Rectangle sourceRectangle, + Rectangle destinationRectangle, + bool compand) + where TPixel : struct, IPixel => ResamplerExtensions.ApplyResizeTransform( + configuration, + in this, + source, + destination, + sourceRectangle, + destinationRectangle, + compand); + /// [MethodImpl(InliningOptions.ShortMethod)] public void ApplyAffineTransform( diff --git a/src/ImageSharp/Processing/Processors/Transforms/Resamplers/Lanczos8Resampler.cs b/src/ImageSharp/Processing/Processors/Transforms/Resamplers/Lanczos8Resampler.cs index e886f41db9..d24c7a1f08 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/Resamplers/Lanczos8Resampler.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/Resamplers/Lanczos8Resampler.cs @@ -34,6 +34,24 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms return 0F; } + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void ApplyResizeTransform( + Configuration configuration, + Image source, + Image destination, + Rectangle sourceRectangle, + Rectangle destinationRectangle, + bool compand) + where TPixel : struct, IPixel => ResamplerExtensions.ApplyResizeTransform( + configuration, + in this, + source, + destination, + sourceRectangle, + destinationRectangle, + compand); + /// [MethodImpl(InliningOptions.ShortMethod)] public void ApplyAffineTransform( diff --git a/src/ImageSharp/Processing/Processors/Transforms/Resamplers/MitchellNetravaliResampler.cs b/src/ImageSharp/Processing/Processors/Transforms/Resamplers/MitchellNetravaliResampler.cs index ef97be92b5..e67e50ab0b 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/Resamplers/MitchellNetravaliResampler.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/Resamplers/MitchellNetravaliResampler.cs @@ -25,6 +25,24 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms return ImageMaths.GetBcValue(x, B, C); } + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void ApplyResizeTransform( + Configuration configuration, + Image source, + Image destination, + Rectangle sourceRectangle, + Rectangle destinationRectangle, + bool compand) + where TPixel : struct, IPixel => ResamplerExtensions.ApplyResizeTransform( + configuration, + in this, + source, + destination, + sourceRectangle, + destinationRectangle, + compand); + /// [MethodImpl(InliningOptions.ShortMethod)] public void ApplyAffineTransform( diff --git a/src/ImageSharp/Processing/Processors/Transforms/Resamplers/NearestNeighborResampler.cs b/src/ImageSharp/Processing/Processors/Transforms/Resamplers/NearestNeighborResampler.cs index e4cec5f4d5..94b0b0405f 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/Resamplers/NearestNeighborResampler.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/Resamplers/NearestNeighborResampler.cs @@ -20,6 +20,24 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms [MethodImpl(InliningOptions.ShortMethod)] public float GetValue(float x) => x; + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void ApplyResizeTransform( + Configuration configuration, + Image source, + Image destination, + Rectangle sourceRectangle, + Rectangle destinationRectangle, + bool compand) + where TPixel : struct, IPixel => ResamplerExtensions.ApplyResizeTransform( + configuration, + in this, + source, + destination, + sourceRectangle, + destinationRectangle, + compand); + /// [MethodImpl(InliningOptions.ShortMethod)] public void ApplyAffineTransform( diff --git a/src/ImageSharp/Processing/Processors/Transforms/Resamplers/RobidouxResampler.cs b/src/ImageSharp/Processing/Processors/Transforms/Resamplers/RobidouxResampler.cs index 6d9e7641e0..1b4d84e5a0 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/Resamplers/RobidouxResampler.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/Resamplers/RobidouxResampler.cs @@ -26,6 +26,24 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms return ImageMaths.GetBcValue(x, B, C); } + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void ApplyResizeTransform( + Configuration configuration, + Image source, + Image destination, + Rectangle sourceRectangle, + Rectangle destinationRectangle, + bool compand) + where TPixel : struct, IPixel => ResamplerExtensions.ApplyResizeTransform( + configuration, + in this, + source, + destination, + sourceRectangle, + destinationRectangle, + compand); + /// [MethodImpl(InliningOptions.ShortMethod)] public void ApplyAffineTransform( diff --git a/src/ImageSharp/Processing/Processors/Transforms/Resamplers/RobidouxSharpResampler.cs b/src/ImageSharp/Processing/Processors/Transforms/Resamplers/RobidouxSharpResampler.cs index eaf5fa6e80..52991a6493 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/Resamplers/RobidouxSharpResampler.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/Resamplers/RobidouxSharpResampler.cs @@ -26,6 +26,24 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms return ImageMaths.GetBcValue(x, B, C); } + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void ApplyResizeTransform( + Configuration configuration, + Image source, + Image destination, + Rectangle sourceRectangle, + Rectangle destinationRectangle, + bool compand) + where TPixel : struct, IPixel => ResamplerExtensions.ApplyResizeTransform( + configuration, + in this, + source, + destination, + sourceRectangle, + destinationRectangle, + compand); + /// [MethodImpl(InliningOptions.ShortMethod)] public void ApplyAffineTransform( diff --git a/src/ImageSharp/Processing/Processors/Transforms/Resamplers/SplineResampler.cs b/src/ImageSharp/Processing/Processors/Transforms/Resamplers/SplineResampler.cs index d2608b2faa..a21ed495bc 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/Resamplers/SplineResampler.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/Resamplers/SplineResampler.cs @@ -26,6 +26,24 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms return ImageMaths.GetBcValue(x, B, C); } + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void ApplyResizeTransform( + Configuration configuration, + Image source, + Image destination, + Rectangle sourceRectangle, + Rectangle destinationRectangle, + bool compand) + where TPixel : struct, IPixel => ResamplerExtensions.ApplyResizeTransform( + configuration, + in this, + source, + destination, + sourceRectangle, + destinationRectangle, + compand); + /// [MethodImpl(InliningOptions.ShortMethod)] public void ApplyAffineTransform( diff --git a/src/ImageSharp/Processing/Processors/Transforms/Resamplers/TriangleResampler.cs b/src/ImageSharp/Processing/Processors/Transforms/Resamplers/TriangleResampler.cs index b403621241..c8409e1859 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/Resamplers/TriangleResampler.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/Resamplers/TriangleResampler.cs @@ -34,6 +34,24 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms return 0F; } + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void ApplyResizeTransform( + Configuration configuration, + Image source, + Image destination, + Rectangle sourceRectangle, + Rectangle destinationRectangle, + bool compand) + where TPixel : struct, IPixel => ResamplerExtensions.ApplyResizeTransform( + configuration, + in this, + source, + destination, + sourceRectangle, + destinationRectangle, + compand); + /// [MethodImpl(InliningOptions.ShortMethod)] public void ApplyAffineTransform( diff --git a/src/ImageSharp/Processing/Processors/Transforms/Resamplers/WelchResampler.cs b/src/ImageSharp/Processing/Processors/Transforms/Resamplers/WelchResampler.cs index 8a92ea7c00..673cbd5d74 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/Resamplers/WelchResampler.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/Resamplers/WelchResampler.cs @@ -33,6 +33,24 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms return 0F; } + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void ApplyResizeTransform( + Configuration configuration, + Image source, + Image destination, + Rectangle sourceRectangle, + Rectangle destinationRectangle, + bool compand) + where TPixel : struct, IPixel => ResamplerExtensions.ApplyResizeTransform( + configuration, + in this, + source, + destination, + sourceRectangle, + destinationRectangle, + compand); + /// [MethodImpl(InliningOptions.ShortMethod)] public void ApplyAffineTransform( diff --git a/src/ImageSharp/Processing/Processors/Transforms/Resize/ResamplerExtensions.cs b/src/ImageSharp/Processing/Processors/Transforms/Resize/ResamplerExtensions.cs new file mode 100644 index 0000000000..b681a436cd --- /dev/null +++ b/src/ImageSharp/Processing/Processors/Transforms/Resize/ResamplerExtensions.cs @@ -0,0 +1,227 @@ +// 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 +{ + /// + /// Extensions for . + /// + public static partial class ResamplerExtensions + { + /// + /// Applies an resizing transformation upon an image. + /// + /// The type of sampler. + /// The pixel format. + /// The configuration. + /// The pixel sampler. + /// The source image. + /// The destination image. + /// The source bounds. + /// The destination location. + /// Whether to compress or expand individual pixel color values on processing. + public static void ApplyResizeTransform( + Configuration configuration, + in TResampler sampler, + Image source, + Image destination, + Rectangle sourceRectangle, + Rectangle destinationRectangle, + bool compand) + where TResampler : unmanaged, IResampler + where TPixel : struct, IPixel + { + // 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 sourceFrame = source.Frames[i]; + ImageFrame 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 sourceFrame = source.Frames[i]; + ImageFrame 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 sourceFrame = source.Frames[i]; + ImageFrame destinationFrame = destination.Frames[i]; + + ApplyResizeFrameTransform( + configuration, + sourceFrame, + destinationFrame, + horizontalKernelMap, + verticalKernelMap, + sourceRectangle, + destinationRectangle, + interest, + compand); + } + } + + private static void ApplyNNResizeFrameTransform( + Configuration configuration, + ImageFrame source, + ImageFrame destination, + Rectangle sourceRectangle, + Rectangle destinationRectangle, + Rectangle interest) + where TPixel : struct, IPixel + { + // 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 source, + ImageFrame destination, + ResizeKernelMap horizontalKernelMap, + ResizeKernelMap verticalKernelMap, + Rectangle sourceRectangle, + Rectangle destinationRectangle, + Rectangle interest, + bool compand) + where TResampler : unmanaged, IResampler + where TPixel : struct, IPixel + { + PixelConversionModifiers conversionModifiers = + PixelConversionModifiers.Premultiply.ApplyCompanding(compand); + + BufferArea 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( + 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 + where TPixel : struct, IPixel + { + private readonly Rectangle sourceBounds; + private readonly Rectangle destinationBounds; + private readonly float widthFactor; + private readonly float heightFactor; + private readonly ImageFrame source; + private readonly ImageFrame destination; + + [MethodImpl(InliningOptions.ShortMethod)] + public NNRowIntervalOperation( + Rectangle sourceBounds, + Rectangle destinationBounds, + float widthFactor, + float heightFactor, + ImageFrame source, + ImageFrame 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 sourceRow = this.source.GetPixelRowSpan((int)(((y - destY) * this.heightFactor) + sourceY)); + Span 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)]; + } + } + } + } + } +} diff --git a/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeKernel.cs b/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeKernel.cs index 14bf552b9d..83bee91114 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeKernel.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeKernel.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. using System; @@ -8,7 +8,7 @@ using System.Runtime.CompilerServices; namespace SixLabors.ImageSharp.Processing.Processors.Transforms { /// - /// Points to a collection of of weights allocated in . + /// Points to a collection of of weights allocated in . /// internal readonly unsafe struct ResizeKernel { @@ -28,15 +28,23 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms /// /// Gets the start index for the destination row. /// - public int StartIndex { get; } + public int StartIndex + { + [MethodImpl(InliningOptions.ShortMethod)] + get; + } /// /// Gets the the length of the kernel. /// - public int Length { get; } + public int Length + { + [MethodImpl(InliningOptions.ShortMethod)] + get; + } /// - /// Gets the span representing the portion of the that this window covers. + /// Gets the span representing the portion of the that this window covers. /// /// The . /// @@ -81,6 +89,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms /// Copy the contents of altering /// to the value . /// + [MethodImpl(InliningOptions.ShortMethod)] internal ResizeKernel AlterLeftValue(int left) { return new ResizeKernel(left, this.bufferPtr, this.Length); @@ -96,4 +105,4 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms } } } -} \ No newline at end of file +} diff --git a/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeKernelMap.PeriodicKernelMap.cs b/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeKernelMap.PeriodicKernelMap.cs index be2546369e..52a308cf2b 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeKernelMap.PeriodicKernelMap.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeKernelMap.PeriodicKernelMap.cs @@ -1,19 +1,17 @@ -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. using SixLabors.ImageSharp.Memory; namespace SixLabors.ImageSharp.Processing.Processors.Transforms { - /// - /// Contains - /// - internal partial class ResizeKernelMap + internal partial class ResizeKernelMap + where TResampler : unmanaged, IResampler { /// - /// Memory-optimized where repeating rows are stored only once. + /// Memory-optimized where repeating rows are stored only once. /// - private sealed class PeriodicKernelMap : ResizeKernelMap + private sealed class PeriodicKernelMap : ResizeKernelMap { private readonly int period; @@ -21,7 +19,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms public PeriodicKernelMap( MemoryAllocator memoryAllocator, - IResampler sampler, + TResampler sampler, int sourceLength, int destinationLength, double ratio, @@ -45,15 +43,14 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms internal override string Info => base.Info + $"|period:{this.period}|cornerInterval:{this.cornerInterval}"; - protected override void Initialize() + protected internal override void Initialize() { // Build top corner data + one period of the mosaic data: int startOfFirstRepeatedMosaic = this.cornerInterval + this.period; for (int i = 0; i < startOfFirstRepeatedMosaic; i++) { - ResizeKernel kernel = this.BuildKernel(i, i); - this.kernels[i] = kernel; + this.kernels[i] = this.BuildKernel(i, i); } // Copy the mosaics: @@ -70,10 +67,9 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms int bottomStartData = this.cornerInterval + this.period; for (int i = 0; i < this.cornerInterval; i++) { - ResizeKernel kernel = this.BuildKernel(bottomStartDest + i, bottomStartData + i); - this.kernels[bottomStartDest + i] = kernel; + this.kernels[bottomStartDest + i] = this.BuildKernel(bottomStartDest + i, bottomStartData + i); } } } } -} \ No newline at end of file +} diff --git a/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeKernelMap.cs b/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeKernelMap.cs index cc95169564..8432eb654c 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeKernelMap.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeKernelMap.cs @@ -5,20 +5,20 @@ using System; using System.Buffers; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; - using SixLabors.ImageSharp.Memory; namespace SixLabors.ImageSharp.Processing.Processors.Transforms { /// - /// Provides values from an optimized, - /// contiguous memory region. + /// Provides resize kernel values from an optimized contiguous memory region. /// - internal partial class ResizeKernelMap : IDisposable + /// The type of sampler. + internal partial class ResizeKernelMap : IDisposable + where TResampler : unmanaged, IResampler { private static readonly TolerantMath TolerantMath = TolerantMath.Default; - private readonly IResampler sampler; + private readonly TResampler sampler; private readonly int sourceLength; @@ -34,12 +34,14 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms private readonly ResizeKernel[] kernels; + private bool isDisposed; + // To avoid both GC allocations, and MemoryAllocator ceremony: private readonly double[] tempValues; private ResizeKernelMap( MemoryAllocator memoryAllocator, - IResampler sampler, + TResampler sampler, int sourceLength, int destinationLength, int bufferHeight, @@ -77,19 +79,34 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms $"radius:{this.radius}|sourceSize:{this.sourceLength}|destinationSize:{this.DestinationLength}|ratio:{this.ratio}|scale:{this.scale}"; /// - /// Disposes instance releasing it's backing buffer. + /// Disposes instance releasing it's backing buffer. /// public void Dispose() + => this.Dispose(true); + + /// + /// Disposes the object and frees resources for the Garbage Collector. + /// + /// Whether to dispose of managed and unmanaged objects. + protected virtual void Dispose(bool disposing) { - this.pinHandle.Dispose(); - this.data.Dispose(); + if (!this.isDisposed) + { + this.isDisposed = true; + + if (disposing) + { + this.pinHandle.Dispose(); + this.data.Dispose(); + } + } } /// /// Returns a for an index value between 0 and DestinationSize - 1. /// [MethodImpl(InliningOptions.ShortMethod)] - public ref ResizeKernel GetKernel(int destIdx) => ref this.kernels[destIdx]; + internal ref ResizeKernel GetKernel(int destIdx) => ref this.kernels[destIdx]; /// /// Computes the weights to apply at each pixel when resizing. @@ -98,9 +115,9 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms /// The destination size /// The source size /// The to use for buffer allocations - /// The - public static ResizeKernelMap Calculate( - IResampler sampler, + /// The + public static ResizeKernelMap Calculate( + in TResampler sampler, int destinationSize, int sourceSize, MemoryAllocator memoryAllocator) @@ -141,7 +158,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms // If we don't have at least 2 periods, we go with the basic implementation: bool hasAtLeast2Periods = 2 * (cornerInterval + period) < destinationSize; - ResizeKernelMap result = hasAtLeast2Periods + ResizeKernelMap result = hasAtLeast2Periods ? new PeriodicKernelMap( memoryAllocator, sampler, @@ -152,7 +169,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms radius, period, cornerInterval) - : new ResizeKernelMap( + : new ResizeKernelMap( memoryAllocator, sampler, sourceSize, @@ -167,12 +184,14 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms return result; } - protected virtual void Initialize() + /// + /// Initializes the kernel map. + /// + protected internal virtual void Initialize() { for (int i = 0; i < this.DestinationLength; i++) { - ResizeKernel kernel = this.BuildKernel(i, i); - this.kernels[i] = kernel; + this.kernels[i] = this.BuildKernel(i, i); } } diff --git a/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor.cs b/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor.cs index ec1f94c143..520370b6ef 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor.cs @@ -21,9 +21,9 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms (Size size, Rectangle rectangle) = ResizeHelper.CalculateTargetLocationAndBounds(sourceSize, options); this.Sampler = options.Sampler; - this.TargetWidth = size.Width; - this.TargetHeight = size.Height; - this.TargetRectangle = rectangle; + this.DestinationWidth = size.Width; + this.DestinationHeight = size.Height; + this.DestinationRectangle = rectangle; this.Compand = options.Compand; } @@ -33,19 +33,19 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms public IResampler Sampler { get; } /// - /// Gets the target width. + /// Gets the destination width. /// - public int TargetWidth { get; } + public int DestinationWidth { get; } /// - /// Gets the target height. + /// Gets the destination height. /// - public int TargetHeight { get; } + public int DestinationHeight { get; } /// /// Gets the resize rectangle. /// - public Rectangle TargetRectangle { get; } + public Rectangle DestinationRectangle { get; } /// /// Gets a value indicating whether to compress or expand individual pixel color values on processing. diff --git a/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor{TPixel}.cs index 92c1b71fe1..72064d0e36 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor{TPixel}.cs @@ -1,10 +1,6 @@ // 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 @@ -12,59 +8,39 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms /// /// Implements resizing of images using various resamplers. /// - /// - /// The original code has been adapted from . - /// /// The pixel format. internal class ResizeProcessor : TransformProcessor where TPixel : struct, IPixel { - private bool isDisposed; - private readonly int targetWidth; - private readonly int targetHeight; + private readonly int destinationWidth; + private readonly int destinationHeight; private readonly IResampler resampler; - private readonly Rectangle targetRectangle; + private readonly Rectangle destinationRectangle; private readonly bool compand; - // The following fields are not immutable but are optionally created on demand. - private ResizeKernelMap horizontalKernelMap; - private ResizeKernelMap verticalKernelMap; - public ResizeProcessor(Configuration configuration, ResizeProcessor definition, Image source, Rectangle sourceRectangle) : base(configuration, source, sourceRectangle) { - this.targetWidth = definition.TargetWidth; - this.targetHeight = definition.TargetHeight; - this.targetRectangle = definition.TargetRectangle; + this.destinationWidth = definition.DestinationWidth; + this.destinationHeight = definition.DestinationHeight; + this.destinationRectangle = definition.DestinationRectangle; this.resampler = definition.Sampler; this.compand = definition.Compand; } /// - protected override Size GetTargetSize() => new Size(this.targetWidth, this.targetHeight); + protected override Size GetDestinationSize() => new Size(this.destinationWidth, this.destinationHeight); /// protected override void BeforeImageApply(Image destination) { - if (!(this.resampler is NearestNeighborResampler)) - { - Image source = this.Source; - Rectangle sourceRectangle = this.SourceRectangle; - - // Since all image frame dimensions have to be the same we can calculate this for all frames. - MemoryAllocator memoryAllocator = source.GetMemoryAllocator(); - this.horizontalKernelMap = ResizeKernelMap.Calculate( - this.resampler, - this.targetRectangle.Width, - sourceRectangle.Width, - memoryAllocator); - - this.verticalKernelMap = ResizeKernelMap.Calculate( - this.resampler, - this.targetRectangle.Height, - sourceRectangle.Height, - memoryAllocator); - } + this.resampler.ApplyResizeTransform( + this.Configuration, + this.Source, + destination, + this.SourceRectangle, + this.destinationRectangle, + this.compand); base.BeforeImageApply(destination); } @@ -72,131 +48,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms /// protected override void OnFrameApply(ImageFrame source, ImageFrame destination) { - Rectangle sourceRectangle = this.SourceRectangle; - Configuration configuration = this.Configuration; - - // Handle resize dimensions identical to the original - if (source.Width == destination.Width - && source.Height == destination.Height - && sourceRectangle == this.targetRectangle) - { - // The cloned will be blank here copy all the pixel data over - source.GetPixelMemoryGroup().CopyTo(destination.GetPixelMemoryGroup()); - return; - } - - int width = this.targetWidth; - int height = this.targetHeight; - var interest = Rectangle.Intersect(this.targetRectangle, new Rectangle(0, 0, width, height)); - - if (this.resampler is NearestNeighborResampler) - { - // Scaling factors - float widthFactor = sourceRectangle.Width / (float)this.targetRectangle.Width; - float heightFactor = sourceRectangle.Height / (float)this.targetRectangle.Height; - - var operation = new RowIntervalOperation(sourceRectangle, this.targetRectangle, widthFactor, heightFactor, source, destination); - ParallelRowIterator.IterateRows( - configuration, - interest, - in operation); - - return; - } - - PixelConversionModifiers conversionModifiers = - PixelConversionModifiers.Premultiply.ApplyCompanding(this.compand); - - BufferArea sourceArea = source.PixelBuffer.GetArea(sourceRectangle); - - // To reintroduce parallel processing, we to launch multiple workers - // for different row intervals of the image. - using (var worker = new ResizeWorker( - configuration, - sourceArea, - conversionModifiers, - this.horizontalKernelMap, - this.verticalKernelMap, - width, - interest, - this.targetRectangle.Location)) - { - worker.Initialize(); - - var workingInterval = new RowInterval(interest.Top, interest.Bottom); - worker.FillDestinationPixels(workingInterval, destination.PixelBuffer); - } - } - - /// - protected override void Dispose(bool disposing) - { - if (this.isDisposed) - { - return; - } - - if (disposing) - { - this.horizontalKernelMap?.Dispose(); - this.horizontalKernelMap = null; - this.verticalKernelMap?.Dispose(); - this.verticalKernelMap = null; - } - - this.isDisposed = true; - base.Dispose(disposing); - } - - private readonly struct RowIntervalOperation : IRowIntervalOperation - { - private readonly Rectangle sourceBounds; - private readonly Rectangle destinationBounds; - private readonly float widthFactor; - private readonly float heightFactor; - private readonly ImageFrame source; - private readonly ImageFrame destination; - - [MethodImpl(InliningOptions.ShortMethod)] - public RowIntervalOperation( - Rectangle sourceBounds, - Rectangle destinationBounds, - float widthFactor, - float heightFactor, - ImageFrame source, - ImageFrame 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 sourceRow = this.source.GetPixelRowSpan((int)(((y - destY) * this.heightFactor) + sourceY)); - Span 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)]; - } - } - } + // Everything happens in BeforeImageApply. } } } diff --git a/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeWorker.cs b/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeWorker.cs index de339823e7..cbec5242c0 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeWorker.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeWorker.cs @@ -19,7 +19,8 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms /// When sliding the window, the contents of the bottom window band are copied to the new top band. /// For more details, and visual explanation, see "ResizeWorker.pptx". /// - internal sealed class ResizeWorker : IDisposable + internal sealed class ResizeWorker : IDisposable + where TResampler : unmanaged, IResampler where TPixel : struct, IPixel { private readonly Buffer2D transposedFirstPassBuffer; @@ -28,7 +29,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms private readonly PixelConversionModifiers conversionModifiers; - private readonly ResizeKernelMap horizontalKernelMap; + private readonly ResizeKernelMap horizontalKernelMap; private readonly BufferArea source; @@ -38,7 +39,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms private readonly IMemoryOwner tempColumnBuffer; - private readonly ResizeKernelMap verticalKernelMap; + private readonly ResizeKernelMap verticalKernelMap; private readonly int destWidth; @@ -56,8 +57,8 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms Configuration configuration, BufferArea source, PixelConversionModifiers conversionModifiers, - ResizeKernelMap horizontalKernelMap, - ResizeKernelMap verticalKernelMap, + ResizeKernelMap horizontalKernelMap, + ResizeKernelMap verticalKernelMap, int destWidth, Rectangle targetWorkingRect, Point targetOrigin) @@ -104,7 +105,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms this.tempColumnBuffer.Dispose(); } - [MethodImpl(MethodImplOptions.AggressiveInlining)] + [MethodImpl(InliningOptions.ShortMethod)] public Span GetColumnSpan(int x, int startY) { return this.transposedFirstPassBuffer.GetRowSpan(x).Slice(startY - this.currentWindow.Min); diff --git a/tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeKernelMapTests.ReferenceKernelMap.cs b/tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeKernelMapTests.ReferenceKernelMap.cs index beb7ebc9c3..d6fa075366 100644 --- a/tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeKernelMapTests.ReferenceKernelMap.cs +++ b/tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeKernelMapTests.ReferenceKernelMap.cs @@ -13,7 +13,8 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Transforms /// /// Simplified reference implementation for functionality. /// - internal class ReferenceKernelMap + internal class ReferenceKernelMap + where TResampler : unmanaged, IResampler { private readonly ReferenceKernel[] kernels; @@ -26,7 +27,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Transforms public ReferenceKernel GetKernel(int destinationIndex) => this.kernels[destinationIndex]; - public static ReferenceKernelMap Calculate(IResampler sampler, int destinationSize, int sourceSize, bool normalize = true) + public static ReferenceKernelMap Calculate(TResampler sampler, int destinationSize, int sourceSize, bool normalize = true) { double ratio = (double)sourceSize / destinationSize; double scale = ratio; @@ -84,7 +85,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Transforms result.Add(new ReferenceKernel(left, floatVals)); } - return new ReferenceKernelMap(result.ToArray()); + return new ReferenceKernelMap(result.ToArray()); } } diff --git a/tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeKernelMapTests.cs b/tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeKernelMapTests.cs index 08745d5701..6ca3c3bee3 100644 --- a/tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeKernelMapTests.cs +++ b/tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeKernelMapTests.cs @@ -25,59 +25,60 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Transforms /// /// resamplerName, srcSize, destSize /// - public static readonly TheoryData KernelMapData = new TheoryData + public static readonly TheoryData KernelMapData + = new TheoryData { - { nameof(KnownResamplers.Bicubic), 15, 10 }, - { nameof(KnownResamplers.Bicubic), 10, 15 }, - { nameof(KnownResamplers.Bicubic), 20, 20 }, - { nameof(KnownResamplers.Bicubic), 50, 40 }, - { nameof(KnownResamplers.Bicubic), 40, 50 }, - { nameof(KnownResamplers.Bicubic), 500, 200 }, - { nameof(KnownResamplers.Bicubic), 200, 500 }, - { nameof(KnownResamplers.Bicubic), 3032, 400 }, - { nameof(KnownResamplers.Bicubic), 10, 25 }, - { nameof(KnownResamplers.Lanczos3), 16, 12 }, - { nameof(KnownResamplers.Lanczos3), 12, 16 }, - { nameof(KnownResamplers.Lanczos3), 12, 9 }, - { nameof(KnownResamplers.Lanczos3), 9, 12 }, - { nameof(KnownResamplers.Lanczos3), 6, 8 }, - { nameof(KnownResamplers.Lanczos3), 8, 6 }, - { nameof(KnownResamplers.Lanczos3), 20, 12 }, - { nameof(KnownResamplers.Lanczos3), 5, 25 }, - { nameof(KnownResamplers.Lanczos3), 5, 50 }, - { nameof(KnownResamplers.Lanczos3), 25, 5 }, - { nameof(KnownResamplers.Lanczos3), 50, 5 }, - { nameof(KnownResamplers.Lanczos3), 49, 5 }, - { nameof(KnownResamplers.Lanczos3), 31, 5 }, - { nameof(KnownResamplers.Lanczos8), 500, 200 }, - { nameof(KnownResamplers.Lanczos8), 100, 10 }, - { nameof(KnownResamplers.Lanczos8), 100, 80 }, - { nameof(KnownResamplers.Lanczos8), 10, 100 }, + { KnownResamplers.Bicubic, 15, 10 }, + { KnownResamplers.Bicubic, 10, 15 }, + { KnownResamplers.Bicubic, 20, 20 }, + { KnownResamplers.Bicubic, 50, 40 }, + { KnownResamplers.Bicubic, 40, 50 }, + { KnownResamplers.Bicubic, 500, 200 }, + { KnownResamplers.Bicubic, 200, 500 }, + { KnownResamplers.Bicubic, 3032, 400 }, + { KnownResamplers.Bicubic, 10, 25 }, + { KnownResamplers.Lanczos3, 16, 12 }, + { KnownResamplers.Lanczos3, 12, 16 }, + { KnownResamplers.Lanczos3, 12, 9 }, + { KnownResamplers.Lanczos3, 9, 12 }, + { KnownResamplers.Lanczos3, 6, 8 }, + { KnownResamplers.Lanczos3, 8, 6 }, + { KnownResamplers.Lanczos3, 20, 12 }, + { KnownResamplers.Lanczos3, 5, 25 }, + { KnownResamplers.Lanczos3, 5, 50 }, + { KnownResamplers.Lanczos3, 25, 5 }, + { KnownResamplers.Lanczos3, 50, 5 }, + { KnownResamplers.Lanczos3, 49, 5 }, + { KnownResamplers.Lanczos3, 31, 5 }, + { KnownResamplers.Lanczos8, 500, 200 }, + { KnownResamplers.Lanczos8, 100, 10 }, + { KnownResamplers.Lanczos8, 100, 80 }, + { KnownResamplers.Lanczos8, 10, 100 }, // Resize_WorksWithAllResamplers_Rgba32_CalliphoraPartial_Box-0.5: - { nameof(KnownResamplers.Box), 378, 149 }, - { nameof(KnownResamplers.Box), 349, 174 }, + { KnownResamplers.Box, 378, 149 }, + { KnownResamplers.Box, 349, 174 }, // Accuracy-related regression-test cases cherry-picked from GeneratedImageResizeData - { nameof(KnownResamplers.Box), 201, 100 }, - { nameof(KnownResamplers.Box), 199, 99 }, - { nameof(KnownResamplers.Box), 10, 299 }, - { nameof(KnownResamplers.Box), 299, 10 }, - { nameof(KnownResamplers.Box), 301, 300 }, - { nameof(KnownResamplers.Box), 1180, 480 }, - { nameof(KnownResamplers.Lanczos2), 3264, 3032 }, - { nameof(KnownResamplers.Bicubic), 1280, 2240 }, - { nameof(KnownResamplers.Bicubic), 1920, 1680 }, - { nameof(KnownResamplers.Bicubic), 3072, 2240 }, - { nameof(KnownResamplers.Welch), 300, 2008 }, + { KnownResamplers.Box, 201, 100 }, + { KnownResamplers.Box, 199, 99 }, + { KnownResamplers.Box, 10, 299 }, + { KnownResamplers.Box, 299, 10 }, + { KnownResamplers.Box, 301, 300 }, + { KnownResamplers.Box, 1180, 480 }, + { KnownResamplers.Lanczos2, 3264, 3032 }, + { KnownResamplers.Bicubic, 1280, 2240 }, + { KnownResamplers.Bicubic, 1920, 1680 }, + { KnownResamplers.Bicubic, 3072, 2240 }, + { KnownResamplers.Welch, 300, 2008 }, // ResizeKernel.Length -related regression tests cherry-picked from GeneratedImageResizeData - { nameof(KnownResamplers.Bicubic), 10, 50 }, - { nameof(KnownResamplers.Bicubic), 49, 301 }, - { nameof(KnownResamplers.Bicubic), 301, 49 }, - { nameof(KnownResamplers.Bicubic), 1680, 1200 }, - { nameof(KnownResamplers.Box), 13, 299 }, - { nameof(KnownResamplers.Lanczos5), 3032, 600 }, + { KnownResamplers.Bicubic, 10, 50 }, + { KnownResamplers.Bicubic, 49, 301 }, + { KnownResamplers.Bicubic, 301, 49 }, + { KnownResamplers.Bicubic, 1680, 1200 }, + { KnownResamplers.Box, 13, 299 }, + { KnownResamplers.Lanczos5, 3032, 600 }, }; public static TheoryData GeneratedImageResizeData = @@ -85,20 +86,20 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Transforms [Theory(Skip = "Only for debugging and development")] [MemberData(nameof(KernelMapData))] - public void PrintNonNormalizedKernelMap(string resamplerName, int srcSize, int destSize) + public void PrintNonNormalizedKernelMap(TResampler resampler, int srcSize, int destSize) + where TResampler : unmanaged, IResampler { - IResampler resampler = TestUtils.GetResampler(resamplerName); - - var kernelMap = ReferenceKernelMap.Calculate(resampler, destSize, srcSize, false); + var kernelMap = ReferenceKernelMap.Calculate(resampler, destSize, srcSize, false); this.Output.WriteLine($"Actual KernelMap:\n{PrintKernelMap(kernelMap)}\n"); } [Theory] [MemberData(nameof(KernelMapData))] - public void KernelMapContentIsCorrect(string resamplerName, int srcSize, int destSize) + public void KernelMapContentIsCorrect(TResampler resampler, int srcSize, int destSize) + where TResampler : unmanaged, IResampler { - this.VerifyKernelMapContentIsCorrect(resamplerName, srcSize, destSize); + this.VerifyKernelMapContentIsCorrect(resampler, srcSize, destSize); } // Comprehensive but expensive tests, for ResizeKernelMap. @@ -113,12 +114,11 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Transforms } #endif - private void VerifyKernelMapContentIsCorrect(string resamplerName, int srcSize, int destSize) + private void VerifyKernelMapContentIsCorrect(TResampler resampler, int srcSize, int destSize) + where TResampler : unmanaged, IResampler { - IResampler resampler = TestUtils.GetResampler(resamplerName); - - var referenceMap = ReferenceKernelMap.Calculate(resampler, destSize, srcSize); - var kernelMap = ResizeKernelMap.Calculate(resampler, destSize, srcSize, Configuration.Default.MemoryAllocator); + var referenceMap = ReferenceKernelMap.Calculate(resampler, destSize, srcSize); + var kernelMap = ResizeKernelMap.Calculate(resampler, destSize, srcSize, Configuration.Default.MemoryAllocator); #if DEBUG this.Output.WriteLine(kernelMap.Info); @@ -153,20 +153,23 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Transforms } } - private static string PrintKernelMap(ResizeKernelMap kernelMap) => - PrintKernelMap(kernelMap, km => km.DestinationLength, (km, i) => km.GetKernel(i)); + private static string PrintKernelMap(ResizeKernelMap kernelMap) + where TResampler : unmanaged, IResampler + => PrintKernelMap>(kernelMap, km => km.DestinationLength, (km, i) => km.GetKernel(i)); - private static string PrintKernelMap(ReferenceKernelMap kernelMap) => - PrintKernelMap(kernelMap, km => km.DestinationSize, (km, i) => km.GetKernel(i)); + private static string PrintKernelMap(ReferenceKernelMap kernelMap) + where TResampler : unmanaged, IResampler + => PrintKernelMap>(kernelMap, km => km.DestinationSize, (km, i) => km.GetKernel(i)); - private static string PrintKernelMap( + private static string PrintKernelMap( TKernelMap kernelMap, Func getDestinationSize, Func getKernel) + where TResampler : unmanaged, IResampler { var bld = new StringBuilder(); - if (kernelMap is ResizeKernelMap actualMap) + if (kernelMap is ResizeKernelMap actualMap) { bld.AppendLine(actualMap.Info); } diff --git a/tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeTests.cs b/tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeTests.cs index 2cbffef47f..63c93596fb 100644 --- a/tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeTests.cs +++ b/tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeTests.cs @@ -121,8 +121,8 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Transforms configuration.MemoryAllocator = allocator; configuration.WorkingBufferSizeHintInBytes = workingBufferSizeHintInBytes; - var verticalKernelMap = ResizeKernelMap.Calculate( - KnownResamplers.Bicubic, + var verticalKernelMap = ResizeKernelMap.Calculate( + default, destSize.Height, image0.Height, Configuration.Default.MemoryAllocator); diff --git a/tests/ImageSharp.Tests/Processing/Transforms/PadTest.cs b/tests/ImageSharp.Tests/Processing/Transforms/PadTest.cs index 33da33c717..db1e76ae5d 100644 --- a/tests/ImageSharp.Tests/Processing/Transforms/PadTest.cs +++ b/tests/ImageSharp.Tests/Processing/Transforms/PadTest.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. using SixLabors.ImageSharp.Processing; @@ -20,9 +20,9 @@ namespace SixLabors.ImageSharp.Tests.Processing.Transforms this.operations.Pad(width, height); ResizeProcessor resizeProcessor = this.Verify(); - Assert.Equal(width, resizeProcessor.TargetWidth); - Assert.Equal(height, resizeProcessor.TargetHeight); + Assert.Equal(width, resizeProcessor.DestinationWidth); + Assert.Equal(height, resizeProcessor.DestinationHeight); Assert.Equal(sampler, resizeProcessor.Sampler); } } -} \ No newline at end of file +} diff --git a/tests/ImageSharp.Tests/Processing/Transforms/ResizeTests.cs b/tests/ImageSharp.Tests/Processing/Transforms/ResizeTests.cs index f87e17e060..e7b92b7b32 100644 --- a/tests/ImageSharp.Tests/Processing/Transforms/ResizeTests.cs +++ b/tests/ImageSharp.Tests/Processing/Transforms/ResizeTests.cs @@ -17,8 +17,8 @@ namespace SixLabors.ImageSharp.Tests.Processing.Transforms this.operations.Resize(width, height); ResizeProcessor resizeProcessor = this.Verify(); - Assert.Equal(width, resizeProcessor.TargetWidth); - Assert.Equal(height, resizeProcessor.TargetHeight); + Assert.Equal(width, resizeProcessor.DestinationWidth); + Assert.Equal(height, resizeProcessor.DestinationHeight); } [Fact] @@ -30,8 +30,8 @@ namespace SixLabors.ImageSharp.Tests.Processing.Transforms this.operations.Resize(width, height, sampler); ResizeProcessor resizeProcessor = this.Verify(); - Assert.Equal(width, resizeProcessor.TargetWidth); - Assert.Equal(height, resizeProcessor.TargetHeight); + Assert.Equal(width, resizeProcessor.DestinationWidth); + Assert.Equal(height, resizeProcessor.DestinationHeight); Assert.Equal(sampler, resizeProcessor.Sampler); } @@ -47,8 +47,8 @@ namespace SixLabors.ImageSharp.Tests.Processing.Transforms this.operations.Resize(width, height, sampler, compand); ResizeProcessor resizeProcessor = this.Verify(); - Assert.Equal(width, resizeProcessor.TargetWidth); - Assert.Equal(height, resizeProcessor.TargetHeight); + Assert.Equal(width, resizeProcessor.DestinationWidth); + Assert.Equal(height, resizeProcessor.DestinationHeight); Assert.Equal(sampler, resizeProcessor.Sampler); Assert.Equal(compand, resizeProcessor.Compand); } @@ -73,8 +73,8 @@ namespace SixLabors.ImageSharp.Tests.Processing.Transforms this.operations.Resize(resizeOptions); ResizeProcessor resizeProcessor = this.Verify(); - Assert.Equal(width, resizeProcessor.TargetWidth); - Assert.Equal(height, resizeProcessor.TargetHeight); + Assert.Equal(width, resizeProcessor.DestinationWidth); + Assert.Equal(height, resizeProcessor.DestinationHeight); Assert.Equal(sampler, resizeProcessor.Sampler); Assert.Equal(compand, resizeProcessor.Compand);