From d59e569ce8af8657b18430ed4ce1925aaabda01b Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Fri, 9 Dec 2016 01:56:29 +1100 Subject: [PATCH] Move Resize and fix tests. --- .../Transforms/CompandingResizeProcessor.cs | 77 +++++++++++-------- .../Transforms/EntropyCropProcessor.cs | 2 +- .../Transforms/ResamplingWeightedProcessor.cs | 47 +++++++---- .../Processors/Transforms/ResizeProcessor.cs | 77 +++++++++++-------- .../Filters/Transforms/EntropyCrop.cs | 2 +- .../Transforms/Resize.cs | 6 +- .../Processors/Samplers/ResizeTests.cs | 5 +- 7 files changed, 128 insertions(+), 88 deletions(-) rename src/ImageSharp/{Samplers => Filters}/Processors/Transforms/CompandingResizeProcessor.cs (69%) rename src/ImageSharp/{Samplers => Filters}/Processors/Transforms/ResamplingWeightedProcessor.cs (80%) rename src/ImageSharp/{Samplers => Filters}/Processors/Transforms/ResizeProcessor.cs (69%) rename src/ImageSharp/{Samplers => Filters}/Transforms/Resize.cs (98%) diff --git a/src/ImageSharp/Samplers/Processors/Transforms/CompandingResizeProcessor.cs b/src/ImageSharp/Filters/Processors/Transforms/CompandingResizeProcessor.cs similarity index 69% rename from src/ImageSharp/Samplers/Processors/Transforms/CompandingResizeProcessor.cs rename to src/ImageSharp/Filters/Processors/Transforms/CompandingResizeProcessor.cs index 1028622ef..3c6562dc0 100644 --- a/src/ImageSharp/Samplers/Processors/Transforms/CompandingResizeProcessor.cs +++ b/src/ImageSharp/Filters/Processors/Transforms/CompandingResizeProcessor.cs @@ -22,11 +22,25 @@ namespace ImageSharp.Processors /// /// Initializes a new instance of the class. /// - /// - /// The sampler to perform the resize operation. + /// The sampler to perform the resize operation. + /// The target width. + /// The target height. + public CompandingResizeProcessor(IResampler sampler, int width, int height) + : base(sampler, width, height, new Rectangle(0, 0, width, height)) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The sampler to perform the resize operation. + /// The target width. + /// The target height. + /// + /// The structure that specifies the portion of the target image object to draw to. /// - public CompandingResizeProcessor(IResampler sampler) - : base(sampler) + public CompandingResizeProcessor(IResampler sampler, int width, int height, Rectangle resizeRectangle) + : base(sampler, width, height, resizeRectangle) { } @@ -34,37 +48,38 @@ namespace ImageSharp.Processors public override bool Compand { get; set; } = true; /// - public override void Apply(ImageBase target, ImageBase source, Rectangle targetRectangle, Rectangle sourceRectangle, int startY, int endY) + protected override void Apply(ImageBase source, Rectangle sourceRectangle, int startY, int endY) { // Jump out, we'll deal with that later. - if (source.Bounds == target.Bounds && sourceRectangle == targetRectangle) + if (source.Width == this.Width && source.Height == this.Height && sourceRectangle == this.ResizeRectangle) { return; } - int width = target.Width; - int height = target.Height; - int sourceHeight = sourceRectangle.Height; - int targetX = target.Bounds.X; - int targetY = target.Bounds.Y; - int targetRight = target.Bounds.Right; - int targetBottom = target.Bounds.Bottom; - int startX = targetRectangle.X; - int endX = targetRectangle.Right; - - int minX = Math.Max(targetX, startX); - int maxX = Math.Min(targetRight, endX); - int minY = Math.Max(targetY, startY); - int maxY = Math.Min(targetBottom, endY); + // Reset the values as the rectangle can be altered by ResizeRectangle. + startY = this.ResizeRectangle.Y; + endY = this.ResizeRectangle.Bottom; + + int width = this.Width; + int height = this.Height; + int startX = this.ResizeRectangle.X; + int endX = this.ResizeRectangle.Right; + + int minX = Math.Max(0, startX); + int maxX = Math.Min(width, endX); + int minY = Math.Max(0, startY); + int maxY = Math.Min(height, endY); + + TColor[] target = new TColor[width * height]; if (this.Sampler is NearestNeighborResampler) { // Scaling factors - float widthFactor = sourceRectangle.Width / (float)targetRectangle.Width; - float heightFactor = sourceRectangle.Height / (float)targetRectangle.Height; + float widthFactor = sourceRectangle.Width / (float)this.ResizeRectangle.Width; + float heightFactor = sourceRectangle.Height / (float)this.ResizeRectangle.Height; using (PixelAccessor sourcePixels = source.Lock()) - using (PixelAccessor targetPixels = target.Lock()) + using (PixelAccessor targetPixels = target.Lock(width, height)) { Parallel.For( minY, @@ -84,6 +99,7 @@ namespace ImageSharp.Processors } // Break out now. + source.SetPixels(width, height, target); return; } @@ -91,19 +107,14 @@ namespace ImageSharp.Processors // A 2-pass 1D algorithm appears to be faster than splitting a 1-pass 2D algorithm // First process the columns. Since we are not using multiple threads startY and endY // are the upper and lower bounds of the source rectangle. - Image firstPass = new Image(target.Width, source.Height); + TColor[] firstPass = new TColor[width * source.Height]; using (PixelAccessor sourcePixels = source.Lock()) - using (PixelAccessor firstPassPixels = firstPass.Lock()) - using (PixelAccessor targetPixels = target.Lock()) + using (PixelAccessor firstPassPixels = firstPass.Lock(width, source.Height)) + using (PixelAccessor targetPixels = target.Lock(width, height)) { - minX = Math.Max(0, startX); - maxX = Math.Min(width, endX); - minY = Math.Max(0, startY); - maxY = Math.Min(height, endY); - Parallel.For( 0, - sourceHeight, + sourceRectangle.Height, this.ParallelOptions, y => { @@ -154,6 +165,8 @@ namespace ImageSharp.Processors } }); } + + source.SetPixels(width, height, target); } } } \ No newline at end of file diff --git a/src/ImageSharp/Filters/Processors/Transforms/EntropyCropProcessor.cs b/src/ImageSharp/Filters/Processors/Transforms/EntropyCropProcessor.cs index 6a5fd867a..715ab49d5 100644 --- a/src/ImageSharp/Filters/Processors/Transforms/EntropyCropProcessor.cs +++ b/src/ImageSharp/Filters/Processors/Transforms/EntropyCropProcessor.cs @@ -1,4 +1,4 @@ -// +// // Copyright (c) James Jackson-South and contributors. // Licensed under the Apache License, Version 2.0. // diff --git a/src/ImageSharp/Samplers/Processors/Transforms/ResamplingWeightedProcessor.cs b/src/ImageSharp/Filters/Processors/Transforms/ResamplingWeightedProcessor.cs similarity index 80% rename from src/ImageSharp/Samplers/Processors/Transforms/ResamplingWeightedProcessor.cs rename to src/ImageSharp/Filters/Processors/Transforms/ResamplingWeightedProcessor.cs index 808440a0f..4f5efff64 100644 --- a/src/ImageSharp/Samplers/Processors/Transforms/ResamplingWeightedProcessor.cs +++ b/src/ImageSharp/Filters/Processors/Transforms/ResamplingWeightedProcessor.cs @@ -13,21 +13,29 @@ namespace ImageSharp.Processors /// /// The pixel format. /// The packed format. uint, long, float. - public abstract class ResamplingWeightedProcessor : ImageSamplingProcessor + public abstract class ResamplingWeightedProcessor : ImageFilteringProcessor where TColor : struct, IPackedPixel where TPacked : struct { /// /// Initializes a new instance of the class. /// - /// - /// The sampler to perform the resize operation. + /// The sampler to perform the resize operation. + /// The target width. + /// The target height. + /// + /// The structure that specifies the portion of the target image object to draw to. /// - protected ResamplingWeightedProcessor(IResampler sampler) + protected ResamplingWeightedProcessor(IResampler sampler, int width, int height, Rectangle resizeRectangle) { Guard.NotNull(sampler, nameof(sampler)); + Guard.MustBeGreaterThan(width, 0, nameof(width)); + Guard.MustBeGreaterThan(height, 0, nameof(height)); this.Sampler = sampler; + this.Width = width; + this.Height = height; + this.ResizeRectangle = resizeRectangle; } /// @@ -35,6 +43,21 @@ namespace ImageSharp.Processors /// public IResampler Sampler { get; } + /// + /// Gets the width. + /// + public int Width { get; } + + /// + /// Gets the height. + /// + public int Height { get; } + + /// + /// Gets the resize rectangle. + /// + public Rectangle ResizeRectangle { get; } + /// /// Gets or sets the horizontal weights. /// @@ -46,22 +69,12 @@ namespace ImageSharp.Processors protected Weights[] VerticalWeights { get; set; } /// - protected override void OnApply(ImageBase target, ImageBase source, Rectangle targetRectangle, Rectangle sourceRectangle) + protected override void OnApply(ImageBase source, Rectangle sourceRectangle) { if (!(this.Sampler is NearestNeighborResampler)) { - this.HorizontalWeights = this.PrecomputeWeights(targetRectangle.Width, sourceRectangle.Width); - this.VerticalWeights = this.PrecomputeWeights(targetRectangle.Height, sourceRectangle.Height); - } - } - - /// - protected override void AfterApply(ImageBase target, ImageBase source, Rectangle targetRectangle, Rectangle sourceRectangle) - { - // Copy the pixels over. - if (source.Bounds == target.Bounds && sourceRectangle == targetRectangle) - { - target.ClonePixels(target.Width, target.Height, source.Pixels); + this.HorizontalWeights = this.PrecomputeWeights(this.ResizeRectangle.Width, sourceRectangle.Width); + this.VerticalWeights = this.PrecomputeWeights(this.ResizeRectangle.Height, sourceRectangle.Height); } } diff --git a/src/ImageSharp/Samplers/Processors/Transforms/ResizeProcessor.cs b/src/ImageSharp/Filters/Processors/Transforms/ResizeProcessor.cs similarity index 69% rename from src/ImageSharp/Samplers/Processors/Transforms/ResizeProcessor.cs rename to src/ImageSharp/Filters/Processors/Transforms/ResizeProcessor.cs index 1ec56d6d5..cf0cf2a8f 100644 --- a/src/ImageSharp/Samplers/Processors/Transforms/ResizeProcessor.cs +++ b/src/ImageSharp/Filters/Processors/Transforms/ResizeProcessor.cs @@ -24,46 +24,61 @@ namespace ImageSharp.Processors /// /// Initializes a new instance of the class. /// - /// - /// The sampler to perform the resize operation. + /// The sampler to perform the resize operation. + /// The target width. + /// The target height. + public ResizeProcessor(IResampler sampler, int width, int height) + : base(sampler, width, height, new Rectangle(0, 0, width, height)) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The sampler to perform the resize operation. + /// The target width. + /// The target height. + /// + /// The structure that specifies the portion of the target image object to draw to. /// - public ResizeProcessor(IResampler sampler) - : base(sampler) + public ResizeProcessor(IResampler sampler, int width, int height, Rectangle resizeRectangle) + : base(sampler, width, height, resizeRectangle) { } /// - public override void Apply(ImageBase target, ImageBase source, Rectangle targetRectangle, Rectangle sourceRectangle, int startY, int endY) + protected override void Apply(ImageBase source, Rectangle sourceRectangle, int startY, int endY) { // Jump out, we'll deal with that later. - if (source.Bounds == target.Bounds && sourceRectangle == targetRectangle) + if (source.Width == this.Width && source.Height == this.Height && sourceRectangle == this.ResizeRectangle) { return; } - int width = target.Width; - int height = target.Height; - int sourceHeight = sourceRectangle.Height; - int targetX = target.Bounds.X; - int targetY = target.Bounds.Y; - int targetRight = target.Bounds.Right; - int targetBottom = target.Bounds.Bottom; - int startX = targetRectangle.X; - int endX = targetRectangle.Right; - - int minX = Math.Max(targetX, startX); - int maxX = Math.Min(targetRight, endX); - int minY = Math.Max(targetY, startY); - int maxY = Math.Min(targetBottom, endY); + // Reset the values as the rectangle can be altered by ResizeRectangle. + startY = this.ResizeRectangle.Y; + endY = this.ResizeRectangle.Bottom; + + int width = this.Width; + int height = this.Height; + int startX = this.ResizeRectangle.X; + int endX = this.ResizeRectangle.Right; + + int minX = Math.Max(0, startX); + int maxX = Math.Min(width, endX); + int minY = Math.Max(0, startY); + int maxY = Math.Min(height, endY); + + TColor[] target = new TColor[width * height]; if (this.Sampler is NearestNeighborResampler) { // Scaling factors - float widthFactor = sourceRectangle.Width / (float)targetRectangle.Width; - float heightFactor = sourceRectangle.Height / (float)targetRectangle.Height; + float widthFactor = sourceRectangle.Width / (float)this.ResizeRectangle.Width; + float heightFactor = sourceRectangle.Height / (float)this.ResizeRectangle.Height; using (PixelAccessor sourcePixels = source.Lock()) - using (PixelAccessor targetPixels = target.Lock()) + using (PixelAccessor targetPixels = target.Lock(width, height)) { Parallel.For( minY, @@ -83,6 +98,7 @@ namespace ImageSharp.Processors } // Break out now. + source.SetPixels(width, height, target); return; } @@ -90,19 +106,14 @@ namespace ImageSharp.Processors // A 2-pass 1D algorithm appears to be faster than splitting a 1-pass 2D algorithm // First process the columns. Since we are not using multiple threads startY and endY // are the upper and lower bounds of the source rectangle. - Image firstPass = new Image(target.Width, source.Height); + TColor[] firstPass = new TColor[width * source.Height]; using (PixelAccessor sourcePixels = source.Lock()) - using (PixelAccessor firstPassPixels = firstPass.Lock()) - using (PixelAccessor targetPixels = target.Lock()) + using (PixelAccessor firstPassPixels = firstPass.Lock(width, source.Height)) + using (PixelAccessor targetPixels = target.Lock(width, height)) { - minX = Math.Max(0, startX); - maxX = Math.Min(width, endX); - minY = Math.Max(0, startY); - maxY = Math.Min(height, endY); - Parallel.For( 0, - sourceHeight, + sourceRectangle.Height, this.ParallelOptions, y => { @@ -153,6 +164,8 @@ namespace ImageSharp.Processors } }); } + + source.SetPixels(width, height, target); } } } \ No newline at end of file diff --git a/src/ImageSharp/Filters/Transforms/EntropyCrop.cs b/src/ImageSharp/Filters/Transforms/EntropyCrop.cs index 17fb109ee..49757039f 100644 --- a/src/ImageSharp/Filters/Transforms/EntropyCrop.cs +++ b/src/ImageSharp/Filters/Transforms/EntropyCrop.cs @@ -1,4 +1,4 @@ -// +// // Copyright (c) James Jackson-South and contributors. // Licensed under the Apache License, Version 2.0. // diff --git a/src/ImageSharp/Samplers/Transforms/Resize.cs b/src/ImageSharp/Filters/Transforms/Resize.cs similarity index 98% rename from src/ImageSharp/Samplers/Transforms/Resize.cs rename to src/ImageSharp/Filters/Transforms/Resize.cs index ee3da5ec6..4c3543c83 100644 --- a/src/ImageSharp/Samplers/Transforms/Resize.cs +++ b/src/ImageSharp/Filters/Transforms/Resize.cs @@ -155,14 +155,14 @@ namespace ImageSharp if (compand) { - processor = new CompandingResizeProcessor(sampler); + processor = new CompandingResizeProcessor(sampler, width, height, targetRectangle); } else { - processor = new ResizeProcessor(sampler); + processor = new ResizeProcessor(sampler, width, height, targetRectangle); } - return source.Process(width, height, sourceRectangle, targetRectangle, processor); + return source.Process(sourceRectangle, processor); } } } diff --git a/tests/ImageSharp.Tests/Processors/Samplers/ResizeTests.cs b/tests/ImageSharp.Tests/Processors/Samplers/ResizeTests.cs index 876ba7ebc..b29ab0116 100644 --- a/tests/ImageSharp.Tests/Processors/Samplers/ResizeTests.cs +++ b/tests/ImageSharp.Tests/Processors/Samplers/ResizeTests.cs @@ -5,6 +5,7 @@ namespace ImageSharp.Tests { + using System; using System.IO; using Xunit; @@ -248,7 +249,7 @@ namespace ImageSharp.Tests ResizeOptions options = new ResizeOptions() { Sampler = sampler, - Size = new Size(image.Width - 50, image.Height - 25), + Size = new Size((int)Math.Round(image.Width * .75F), (int)Math.Round(image.Height * 95F)), Mode = ResizeMode.Min }; @@ -276,7 +277,7 @@ namespace ImageSharp.Tests ResizeOptions options = new ResizeOptions() { Sampler = sampler, - Size = new Size(image.Width - 200, image.Height), + Size = new Size(image.Width / 2, image.Height), Mode = ResizeMode.Stretch };