From d62fa6929beafa8a2d4c29cf5664fdf9c5d87b88 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Wed, 18 Sep 2019 00:06:40 +1000 Subject: [PATCH 1/2] Make processors public, refactor cloning. --- .../DrawImageProcessor{TPixelBg,TPixelFg}.cs | 16 ++--- .../DefaultImageProcessorContext{TPixel}.cs | 22 +++---- .../Processors/CloningImageProcessor.cs | 31 ++++++++++ .../CloningImageProcessor{TPixel}.cs | 45 +++++++++----- .../ICloningImageProcessor{TPixel}.cs | 15 ++--- .../Processing/Processors/IImageProcessor.cs | 2 +- .../Processors/IImageProcessor{TPixel}.cs | 11 +--- .../Processors/ImageProcessorExtensions.cs | 2 +- .../Processors/ImageProcessor{TPixel}.cs | 9 +-- .../Transforms/AffineTransformProcessor.cs | 13 ++-- .../Processors/Transforms/CropProcessor.cs | 11 ++-- .../ProjectiveTransformProcessor.cs | 11 +--- .../Transforms/Resize/ResizeProcessor.cs | 8 +-- .../Processors/Transforms/RotateProcessor.cs | 7 +-- .../Processors/Transforms/SkewProcessor.cs | 5 +- .../Processing/ImageProcessingContextTests.cs | 59 ++++++++++++------- 16 files changed, 149 insertions(+), 118 deletions(-) create mode 100644 src/ImageSharp/Processing/Processors/CloningImageProcessor.cs diff --git a/src/ImageSharp.Drawing/Processing/Processors/Drawing/DrawImageProcessor{TPixelBg,TPixelFg}.cs b/src/ImageSharp.Drawing/Processing/Processors/Drawing/DrawImageProcessor{TPixelBg,TPixelFg}.cs index 6cfa23cce..76082136c 100644 --- a/src/ImageSharp.Drawing/Processing/Processors/Drawing/DrawImageProcessor{TPixelBg,TPixelFg}.cs +++ b/src/ImageSharp.Drawing/Processing/Processors/Drawing/DrawImageProcessor{TPixelBg,TPixelFg}.cs @@ -91,7 +91,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Drawing var workingRect = Rectangle.FromLTRB(minX, minY, maxX, maxY); - // not a valid operation because rectangle does not overlap with this image. + // Not a valid operation because rectangle does not overlap with this image. if (workingRect.Width <= 0 || workingRect.Height <= 0) { throw new ImageProcessingException( @@ -102,14 +102,14 @@ namespace SixLabors.ImageSharp.Processing.Processors.Drawing workingRect, configuration, rows => + { + for (int y = rows.Min; y < rows.Max; y++) { - for (int y = rows.Min; y < rows.Max; y++) - { - Span background = source.GetPixelRowSpan(y).Slice(minX, width); - Span foreground = targetImage.GetPixelRowSpan(y - locationY).Slice(targetX, width); - blender.Blend(configuration, background, background, foreground, this.Opacity); - } - }); + Span background = source.GetPixelRowSpan(y).Slice(minX, width); + Span foreground = targetImage.GetPixelRowSpan(y - locationY).Slice(targetX, width); + blender.Blend(configuration, background, background, foreground, this.Opacity); + } + }); } } } diff --git a/src/ImageSharp/Processing/DefaultImageProcessorContext{TPixel}.cs b/src/ImageSharp/Processing/DefaultImageProcessorContext{TPixel}.cs index a6e3dc03a..7f7316694 100644 --- a/src/ImageSharp/Processing/DefaultImageProcessorContext{TPixel}.cs +++ b/src/ImageSharp/Processing/DefaultImageProcessorContext{TPixel}.cs @@ -29,6 +29,8 @@ namespace SixLabors.ImageSharp.Processing { this.mutate = mutate; this.source = source; + + // Mutate acts upon the source image only. if (this.mutate) { this.destination = source; @@ -43,7 +45,8 @@ namespace SixLabors.ImageSharp.Processing { if (!this.mutate && this.destination is null) { - // Ensure we have cloned it if we are not mutating as we might have failed to register any processors + // Ensure we have cloned the source if we are not mutating as we might have failed + // to register any processors. this.destination = this.source.Clone(); } @@ -64,26 +67,25 @@ namespace SixLabors.ImageSharp.Processing { if (!this.mutate && this.destination is null) { - // This will only work if the first processor applied is the cloning one thus - // realistically for this optimization to work the resize must the first processor - // applied any only up processors will take the double data path. - using (IImageProcessor specificProcessor = processor.CreatePixelSpecificProcessor(this.source, rectangle)) + // When cloning an image we can optimize the processing pipeline by avoiding an unnecessary + // interim clone if the first processor in the pipeline is a cloning processor. + if (processor is CloningImageProcessor cloningImageProcessor) { - // TODO: if 'specificProcessor' is not an ICloningImageProcessor we are unnecessarily disposing and recreating it. - // This should be solved in a future refactor. - if (specificProcessor is ICloningImageProcessor cloningImageProcessor) + using (ICloningImageProcessor pixelProcessor = cloningImageProcessor.CreatePixelSpecificProcessor(this.source, rectangle)) { - this.destination = cloningImageProcessor.CloneAndApply(); + this.destination = pixelProcessor.CloneAndExecute(); return this; } } + // Not a cloning processor? We need to create a clone to operate on. this.destination = this.source.Clone(); } + // Standard processing pipeline. using (IImageProcessor specificProcessor = processor.CreatePixelSpecificProcessor(this.destination, rectangle)) { - specificProcessor.Apply(); + specificProcessor.Execute(); } return this; diff --git a/src/ImageSharp/Processing/Processors/CloningImageProcessor.cs b/src/ImageSharp/Processing/Processors/CloningImageProcessor.cs new file mode 100644 index 000000000..6ab0fcb13 --- /dev/null +++ b/src/ImageSharp/Processing/Processors/CloningImageProcessor.cs @@ -0,0 +1,31 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.Primitives; + +namespace SixLabors.ImageSharp.Processing.Processors +{ + /// + /// The base class for all cloning image processors. + /// + public abstract class CloningImageProcessor : IImageProcessor + { + /// + /// Creates a pixel specific that is capable of executing + /// the processing algorithm on an . + /// + /// The pixel type. + /// The source image. Cannot be null. + /// + /// The structure that specifies the portion of the image object to draw. + /// + /// The + public abstract ICloningImageProcessor CreatePixelSpecificProcessor(Image source, Rectangle sourceRectangle) + where TPixel : struct, IPixel; + + /// + IImageProcessor IImageProcessor.CreatePixelSpecificProcessor(Image source, Rectangle sourceRectangle) + => this.CreatePixelSpecificProcessor(source, sourceRectangle); + } +} diff --git a/src/ImageSharp/Processing/Processors/CloningImageProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/CloningImageProcessor{TPixel}.cs index 8b3e1eb96..6b0329e77 100644 --- a/src/ImageSharp/Processing/Processors/CloningImageProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/CloningImageProcessor{TPixel}.cs @@ -9,10 +9,12 @@ using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing.Processors { /// - /// Allows the application of processing algorithms to a clone of the original image. + /// The base class for all pixel specific cloning image processors. + /// Allows the application of processing algorithms to the image. + /// The image is cloned before operating upon and the buffers swapped upon completion. /// /// The pixel format. - internal abstract class CloningImageProcessor : ICloningImageProcessor + public abstract class CloningImageProcessor : ICloningImageProcessor where TPixel : struct, IPixel { private bool isDisposed; @@ -45,16 +47,12 @@ namespace SixLabors.ImageSharp.Processing.Processors protected Configuration Configuration { get; } /// - public Image CloneAndApply() + public Image CloneAndExecute() { try { Image clone = this.CreateDestination(); - - if (clone.Frames.Count != this.Source.Frames.Count) - { - throw new ImageProcessingException($"An error occurred when processing the image using {this.GetType().Name}. The processor changed the number of frames."); - } + this.CheckFrameCount(this.Source, clone); Configuration configuration = this.Source.GetConfiguration(); this.BeforeImageApply(clone); @@ -86,17 +84,24 @@ namespace SixLabors.ImageSharp.Processing.Processors } /// - public void Apply() + public void Execute() { - using (Image cloned = this.CloneAndApply()) + // Create an interim clone of the source image to operate on. + // Doing this allows for the application of transforms that will alter + // the dimensions of the image. + Image clone = default; + try { - // we now need to move the pixel data/size data from one image base to another - if (cloned.Frames.Count != this.Source.Frames.Count) - { - throw new ImageProcessingException($"An error occurred when processing the image using {this.GetType().Name}. The processor changed the number of frames."); - } + clone = this.CloneAndExecute(); - this.Source.SwapOrCopyPixelsBuffersFrom(cloned); + // We now need to move the pixel data/size data from the clone to the source. + this.CheckFrameCount(this.Source, clone); + this.Source.SwapOrCopyPixelsBuffersFrom(clone); + } + finally + { + // Dispose of the clone now that we have swapped the pixel/size data. + clone?.Dispose(); } } @@ -165,5 +170,13 @@ namespace SixLabors.ImageSharp.Processing.Processors this.isDisposed = true; } } + + private void CheckFrameCount(Image a, Image b) + { + if (a.Frames.Count != b.Frames.Count) + { + throw new ImageProcessingException($"An error occurred when processing the image using {this.GetType().Name}. The processor changed the number of frames."); + } + } } } diff --git a/src/ImageSharp/Processing/Processors/ICloningImageProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/ICloningImageProcessor{TPixel}.cs index 1a21be1f9..c34bf60ae 100644 --- a/src/ImageSharp/Processing/Processors/ICloningImageProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/ICloningImageProcessor{TPixel}.cs @@ -2,7 +2,6 @@ // Licensed under the Apache License, Version 2.0. using SixLabors.ImageSharp.PixelFormats; -using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing.Processors { @@ -10,19 +9,13 @@ namespace SixLabors.ImageSharp.Processing.Processors /// Encapsulates methods to alter the pixels of a new image, cloned from the original image. /// /// The pixel format. - internal interface ICloningImageProcessor : IImageProcessor + public interface ICloningImageProcessor : IImageProcessor where TPixel : struct, IPixel { /// - /// Applies the process to the specified portion of the specified . + /// Clones the specified and executes the process against the clone. /// - /// - /// The target is null. - /// - /// - /// The target doesn't fit the dimension of the image. - /// - /// Returns the cloned image after there processor has been applied to it. - Image CloneAndApply(); + /// The . + Image CloneAndExecute(); } } diff --git a/src/ImageSharp/Processing/Processors/IImageProcessor.cs b/src/ImageSharp/Processing/Processors/IImageProcessor.cs index 4fff5273a..fb7a6a4d9 100644 --- a/src/ImageSharp/Processing/Processors/IImageProcessor.cs +++ b/src/ImageSharp/Processing/Processors/IImageProcessor.cs @@ -15,7 +15,7 @@ namespace SixLabors.ImageSharp.Processing.Processors public interface IImageProcessor { /// - /// Creates a pixel specific that is capable for executing + /// Creates a pixel specific that is capable of executing /// the processing algorithm on an . /// /// The pixel type. diff --git a/src/ImageSharp/Processing/Processors/IImageProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/IImageProcessor{TPixel}.cs index 3d6e0d765..1b874e4b9 100644 --- a/src/ImageSharp/Processing/Processors/IImageProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/IImageProcessor{TPixel}.cs @@ -3,7 +3,6 @@ using System; using SixLabors.ImageSharp.PixelFormats; -using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing.Processors { @@ -15,14 +14,8 @@ namespace SixLabors.ImageSharp.Processing.Processors where TPixel : struct, IPixel { /// - /// Applies the process to the specified portion of the specified . + /// Executes the process against the specified . /// - /// - /// The target is null. - /// - /// - /// The target doesn't fit the dimension of the image. - /// - void Apply(); + void Execute(); } } diff --git a/src/ImageSharp/Processing/Processors/ImageProcessorExtensions.cs b/src/ImageSharp/Processing/Processors/ImageProcessorExtensions.cs index 53eedfd20..9f41e0839 100644 --- a/src/ImageSharp/Processing/Processors/ImageProcessorExtensions.cs +++ b/src/ImageSharp/Processing/Processors/ImageProcessorExtensions.cs @@ -30,7 +30,7 @@ namespace SixLabors.ImageSharp.Processing.Processors { using (IImageProcessor processorImpl = this.processor.CreatePixelSpecificProcessor(image, this.sourceRectangle)) { - processorImpl.Apply(); + processorImpl.Execute(); } } } diff --git a/src/ImageSharp/Processing/Processors/ImageProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/ImageProcessor{TPixel}.cs index 8ac8cd67b..b224adc3f 100644 --- a/src/ImageSharp/Processing/Processors/ImageProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/ImageProcessor{TPixel}.cs @@ -9,10 +9,11 @@ using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing.Processors { /// - /// Allows the application of processors to images. + /// The base class for all pixel specific image processors. + /// Allows the application of processing algorithms to the image. /// /// The pixel format. - internal abstract class ImageProcessor : IImageProcessor + public abstract class ImageProcessor : IImageProcessor where TPixel : struct, IPixel { private bool isDisposed; @@ -45,7 +46,7 @@ namespace SixLabors.ImageSharp.Processing.Processors protected Configuration Configuration { get; } /// - public void Apply() + public void Execute() { try { @@ -71,7 +72,7 @@ namespace SixLabors.ImageSharp.Processing.Processors } /// - /// Applies the processor to just a single ImageBase. + /// Applies the processor to a single image frame. /// /// the source image. public void Apply(ImageFrame source) diff --git a/src/ImageSharp/Processing/Processors/Transforms/AffineTransformProcessor.cs b/src/ImageSharp/Processing/Processors/Transforms/AffineTransformProcessor.cs index 6e669e777..be5675578 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/AffineTransformProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/AffineTransformProcessor.cs @@ -2,8 +2,6 @@ // Licensed under the Apache License, Version 2.0. using System.Numerics; - -using SixLabors.ImageSharp.PixelFormats; using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing.Processors.Transforms @@ -11,7 +9,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms /// /// Defines an affine transformation applicable on an . /// - public class AffineTransformProcessor : IImageProcessor + public class AffineTransformProcessor : CloningImageProcessor { /// /// Initializes a new instance of the class. @@ -42,11 +40,8 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms /// public Size TargetDimensions { get; } - /// - public virtual IImageProcessor CreatePixelSpecificProcessor(Image source, Rectangle sourceRectangle) - where TPixel : struct, IPixel - { - return new AffineTransformProcessor(this, source, sourceRectangle); - } + /// + public override ICloningImageProcessor CreatePixelSpecificProcessor(Image source, Rectangle sourceRectangle) + => new AffineTransformProcessor(this, source, sourceRectangle); } } diff --git a/src/ImageSharp/Processing/Processors/Transforms/CropProcessor.cs b/src/ImageSharp/Processing/Processors/Transforms/CropProcessor.cs index 6105330df..025592a36 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/CropProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/CropProcessor.cs @@ -1,7 +1,6 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using SixLabors.ImageSharp.PixelFormats; using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing.Processors.Transforms @@ -9,7 +8,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms /// /// Defines a crop operation on an image. /// - public sealed class CropProcessor : IImageProcessor + public sealed class CropProcessor : CloningImageProcessor { /// /// Initializes a new instance of the class. @@ -23,6 +22,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms new Rectangle(Point.Empty, sourceSize).Contains(cropRectangle), nameof(cropRectangle), "Crop rectangle should be smaller than the source bounds."); + this.CropRectangle = cropRectangle; } @@ -32,10 +32,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms public Rectangle CropRectangle { get; } /// - public IImageProcessor CreatePixelSpecificProcessor(Image source, Rectangle sourceRectangle) - where TPixel : struct, IPixel - { - return new CropProcessor(this, source, sourceRectangle); - } + public override ICloningImageProcessor CreatePixelSpecificProcessor(Image source, Rectangle sourceRectangle) + => new CropProcessor(this, source, sourceRectangle); } } diff --git a/src/ImageSharp/Processing/Processors/Transforms/ProjectiveTransformProcessor.cs b/src/ImageSharp/Processing/Processors/Transforms/ProjectiveTransformProcessor.cs index 15a6e2d09..babdee593 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/ProjectiveTransformProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/ProjectiveTransformProcessor.cs @@ -2,8 +2,6 @@ // Licensed under the Apache License, Version 2.0. using System.Numerics; - -using SixLabors.ImageSharp.PixelFormats; using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing.Processors.Transforms @@ -11,7 +9,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms /// /// Defines a projective transformation applicable to an . /// - public sealed class ProjectiveTransformProcessor : IImageProcessor + public sealed class ProjectiveTransformProcessor : CloningImageProcessor { /// /// Initializes a new instance of the class. @@ -43,10 +41,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms public Size TargetDimensions { get; } /// - public IImageProcessor CreatePixelSpecificProcessor(Image source, Rectangle sourceRectangle) - where TPixel : struct, IPixel - { - return new ProjectiveTransformProcessor(this, source, sourceRectangle); - } + public override ICloningImageProcessor CreatePixelSpecificProcessor(Image source, Rectangle sourceRectangle) + => new ProjectiveTransformProcessor(this, source, sourceRectangle); } } diff --git a/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor.cs b/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor.cs index 35e22757c..5390fa4a4 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor.cs @@ -1,9 +1,6 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using System; - -using SixLabors.ImageSharp.PixelFormats; using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing.Processors.Transforms @@ -11,7 +8,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms /// /// Defines an image resizing operation with the given and dimensional parameters. /// - public class ResizeProcessor : IImageProcessor + public class ResizeProcessor : CloningImageProcessor { /// /// Initializes a new instance of the class. @@ -58,8 +55,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms public bool Compand { get; } /// - public IImageProcessor CreatePixelSpecificProcessor(Image source, Rectangle sourceRectangle) - where TPixel : struct, IPixel + public override ICloningImageProcessor CreatePixelSpecificProcessor(Image source, Rectangle sourceRectangle) => new ResizeProcessor(this, source, sourceRectangle); } } diff --git a/src/ImageSharp/Processing/Processors/Transforms/RotateProcessor.cs b/src/ImageSharp/Processing/Processors/Transforms/RotateProcessor.cs index 10d6cdc94..277cc05b2 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/RotateProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/RotateProcessor.cs @@ -2,7 +2,6 @@ // Licensed under the Apache License, Version 2.0. using System.Numerics; - using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing.Processors.Transforms @@ -47,9 +46,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms public float Degrees { get; } /// - public override IImageProcessor CreatePixelSpecificProcessor(Image source, Rectangle sourceRectangle) - { - return new RotateProcessor(this, source, sourceRectangle); - } + public override ICloningImageProcessor CreatePixelSpecificProcessor(Image source, Rectangle sourceRectangle) + => new RotateProcessor(this, source, sourceRectangle); } } diff --git a/src/ImageSharp/Processing/Processors/Transforms/SkewProcessor.cs b/src/ImageSharp/Processing/Processors/Transforms/SkewProcessor.cs index 4b87d6d2c..fb2114e03 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/SkewProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/SkewProcessor.cs @@ -1,8 +1,7 @@ -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. using System.Numerics; - using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing.Processors.Transforms @@ -56,4 +55,4 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms /// public float DegreesY { get; } } -} \ No newline at end of file +} diff --git a/tests/ImageSharp.Tests/Processing/ImageProcessingContextTests.cs b/tests/ImageSharp.Tests/Processing/ImageProcessingContextTests.cs index 589d59527..afb2cbecd 100644 --- a/tests/ImageSharp.Tests/Processing/ImageProcessingContextTests.cs +++ b/tests/ImageSharp.Tests/Processing/ImageProcessingContextTests.cs @@ -21,6 +21,8 @@ namespace SixLabors.ImageSharp.Tests.Processing private readonly Mock processorDefinition; + private readonly Mock cloningProcessorDefinition; + private readonly Mock> regularProcessorImpl; private readonly Mock> cloningProcessorImpl; @@ -30,18 +32,20 @@ namespace SixLabors.ImageSharp.Tests.Processing public ImageProcessingContextTests() { this.processorDefinition = new Mock(); + this.cloningProcessorDefinition = new Mock(); this.regularProcessorImpl = new Mock>(); this.cloningProcessorImpl = new Mock>(); } // bool throwException, bool useBounds public static readonly TheoryData ProcessorTestData = new TheoryData() - { - { false, false }, - { false, true }, - { true, false }, - { true, true } - }; + { + { false, false }, + { false, true }, + { true, false }, + { true, true } + }; + [Theory] [MemberData(nameof(ProcessorTestData))] public void Mutate_RegularProcessor(bool throwException, bool useBounds) @@ -57,7 +61,7 @@ namespace SixLabors.ImageSharp.Tests.Processing this.MutateApply(useBounds); } - this.regularProcessorImpl.Verify(p => p.Apply(), Times.Once()); + this.regularProcessorImpl.Verify(p => p.Execute(), Times.Once()); this.regularProcessorImpl.Verify(p => p.Dispose(), Times.Once()); } @@ -69,16 +73,15 @@ namespace SixLabors.ImageSharp.Tests.Processing if (throwException) { - Assert.Throws(() => this.CloneApply(useBounds)); + Assert.Throws(() => this.CloneRegularApply(useBounds)); } else { - this.CloneApply(useBounds); + this.CloneRegularApply(useBounds); } - // TODO: This should be Times.Once(). See comments in DefaultImageProcessingContext.ApplyProcessor() - this.regularProcessorImpl.Verify(p => p.Apply(), Times.AtLeast(1)); - this.regularProcessorImpl.Verify(p => p.Dispose(), Times.AtLeast(1)); + this.regularProcessorImpl.Verify(p => p.Execute(), Times.Once); + this.regularProcessorImpl.Verify(p => p.Dispose(), Times.Once); } [Theory] @@ -96,7 +99,7 @@ namespace SixLabors.ImageSharp.Tests.Processing this.MutateApply(useBounds); } - this.cloningProcessorImpl.Verify(p => p.Apply(), Times.Once()); + this.cloningProcessorImpl.Verify(p => p.Execute(), Times.Once()); this.cloningProcessorImpl.Verify(p => p.Dispose(), Times.Once()); } @@ -108,14 +111,14 @@ namespace SixLabors.ImageSharp.Tests.Processing if (throwException) { - Assert.Throws(() => this.CloneApply(useBounds)); + Assert.Throws(() => this.CloneCloneApply(useBounds)); } else { - this.CloneApply(useBounds); + this.CloneCloneApply(useBounds); } - this.cloningProcessorImpl.Verify(p => p.CloneAndApply(), Times.Once()); + this.cloningProcessorImpl.Verify(p => p.CloneAndExecute(), Times.Once()); this.cloningProcessorImpl.Verify(p => p.Dispose(), Times.Once()); } @@ -131,7 +134,7 @@ namespace SixLabors.ImageSharp.Tests.Processing } } - private void CloneApply(bool useBounds) + private void CloneRegularApply(bool useBounds) { if (useBounds) { @@ -143,11 +146,23 @@ namespace SixLabors.ImageSharp.Tests.Processing } } + private void CloneCloneApply(bool useBounds) + { + if (useBounds) + { + this.image.Clone(c => c.ApplyProcessor(this.cloningProcessorDefinition.Object, Bounds)).Dispose(); + } + else + { + this.image.Clone(c => c.ApplyProcessor(this.cloningProcessorDefinition.Object)).Dispose(); + } + } + private void SetupRegularProcessor(bool throwsException) { if (throwsException) { - this.regularProcessorImpl.Setup(p => p.Apply()).Throws(new ImageProcessingException("Test")); + this.regularProcessorImpl.Setup(p => p.Execute()).Throws(new ImageProcessingException("Test")); } this.processorDefinition @@ -159,13 +174,17 @@ namespace SixLabors.ImageSharp.Tests.Processing { if (throwsException) { - this.cloningProcessorImpl.Setup(p => p.Apply()).Throws(new ImageProcessingException("Test")); - this.cloningProcessorImpl.Setup(p => p.CloneAndApply()).Throws(new ImageProcessingException("Test")); + this.cloningProcessorImpl.Setup(p => p.Execute()).Throws(new ImageProcessingException("Test")); + this.cloningProcessorImpl.Setup(p => p.CloneAndExecute()).Throws(new ImageProcessingException("Test")); } this.processorDefinition .Setup(p => p.CreatePixelSpecificProcessor(It.IsAny>(), It.IsAny())) .Returns(this.cloningProcessorImpl.Object); + + this.cloningProcessorDefinition + .Setup(p => p.CreatePixelSpecificProcessor(It.IsAny>(), It.IsAny())) + .Returns(this.cloningProcessorImpl.Object); } } } From 40b69d7e9563fc361b874874b0eb3ef76f1871e1 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Fri, 20 Sep 2019 00:24:50 +1000 Subject: [PATCH 2/2] Refactor cloning processors and tests --- src/ImageSharp/Advanced/AotCompilerTools.cs | 36 +++---- .../DefaultImageProcessorContext{TPixel}.cs | 4 +- .../Processors/CloningImageProcessor.cs | 17 +-- .../CloningImageProcessor{TPixel}.cs | 35 ++++-- .../EdgeDetector2DProcessor{TPixel}.cs | 2 +- .../EdgeDetectorCompassProcessor{TPixel}.cs | 2 +- .../EdgeDetectorProcessor{TPixel}.cs | 2 +- .../Filters/LomographProcessor{TPixel}.cs | 2 +- .../Filters/PolaroidProcessor{TPixel}.cs | 4 +- .../Processors/ICloningImageProcessor.cs | 27 +++++ .../ICloningImageProcessor{TPixel}.cs | 2 +- .../Processors/ImageProcessorExtensions.cs | 17 +-- .../Processors/ImageProcessor{TPixel}.cs | 2 +- .../Transforms/AffineTransformProcessor.cs | 2 +- .../AffineTransformProcessor{TPixel}.cs | 55 ++++------ .../Transforms/AutoOrientProcessor{TPixel}.cs | 18 ++-- .../Processors/Transforms/CropProcessor.cs | 2 +- .../Transforms/CropProcessor{TPixel}.cs | 46 +++----- .../EntropyCropProcessor{TPixel}.cs | 6 +- .../ProjectiveTransformProcessor.cs | 2 +- .../ProjectiveTransformProcessor{TPixel}.cs | 56 ++++------ .../Transforms/Resize/ResizeProcessor.cs | 2 +- .../Resize/ResizeProcessor{TPixel}.cs | 101 +++++------------- .../Processors/Transforms/RotateProcessor.cs | 2 +- .../Drawing/FillRegionProcessorTests.cs | 4 +- .../Processing/ImageProcessingContextTests.cs | 32 ++++-- 26 files changed, 221 insertions(+), 259 deletions(-) create mode 100644 src/ImageSharp/Processing/Processors/ICloningImageProcessor.cs diff --git a/src/ImageSharp/Advanced/AotCompilerTools.cs b/src/ImageSharp/Advanced/AotCompilerTools.cs index 8d3a074b5..1ceba5f90 100644 --- a/src/ImageSharp/Advanced/AotCompilerTools.cs +++ b/src/ImageSharp/Advanced/AotCompilerTools.cs @@ -2,13 +2,12 @@ // Licensed under the Apache License, Version 2.0. using System.Numerics; +using System.Runtime.CompilerServices; using SixLabors.ImageSharp.Formats; using SixLabors.ImageSharp.Formats.Jpeg.Components; using SixLabors.ImageSharp.PixelFormats; -using SixLabors.ImageSharp.Processing; using SixLabors.ImageSharp.Processing.Processors.Dithering; using SixLabors.ImageSharp.Processing.Processors.Quantization; -using SixLabors.ImageSharp.Processing.Processors.Transforms; namespace SixLabors.ImageSharp.Advanced { @@ -81,9 +80,8 @@ namespace SixLabors.ImageSharp.Advanced AotCompileWuQuantizer(); AotCompileDithering(); AotCompilePixelOperations(); - AotCompileResizeOperations(); - System.Runtime.CompilerServices.Unsafe.SizeOf(); + Unsafe.SizeOf(); AotCodec(new Formats.Png.PngDecoder(), new Formats.Png.PngEncoder()); AotCodec(new Formats.Bmp.BmpDecoder(), new Formats.Bmp.BmpEncoder()); @@ -107,8 +105,10 @@ namespace SixLabors.ImageSharp.Advanced private static void AotCompileOctreeQuantizer() where TPixel : struct, IPixel { - var test = new OctreeFrameQuantizer(new OctreeQuantizer(false)); - test.AotGetPalette(); + using (var test = new OctreeFrameQuantizer(new OctreeQuantizer(false))) + { + test.AotGetPalette(); + } } /// @@ -118,9 +118,11 @@ namespace SixLabors.ImageSharp.Advanced private static void AotCompileWuQuantizer() where TPixel : struct, IPixel { - var test = new WuFrameQuantizer(Configuration.Default.MemoryAllocator, new WuQuantizer(false)); - test.QuantizeFrame(new ImageFrame(Configuration.Default, 1, 1)); - test.AotGetPalette(); + using (var test = new WuFrameQuantizer(Configuration.Default.MemoryAllocator, new WuQuantizer(false))) + { + test.QuantizeFrame(new ImageFrame(Configuration.Default, 1, 1)); + test.AotGetPalette(); + } } /// @@ -132,7 +134,10 @@ namespace SixLabors.ImageSharp.Advanced { var test = new FloydSteinbergDiffuser(); TPixel pixel = default; - test.Dither(new ImageFrame(Configuration.Default, 1, 1), pixel, pixel, 0, 0, 0, 0, 0, 0); + using (var image = new ImageFrame(Configuration.Default, 1, 1)) + { + test.Dither(image, pixel, pixel, 0, 0, 0, 0, 0, 0); + } } /// @@ -171,16 +176,5 @@ namespace SixLabors.ImageSharp.Advanced var pixelOp = new PixelOperations(); pixelOp.GetPixelBlender(PixelColorBlendingMode.Normal, PixelAlphaCompositionMode.Clear); } - - /// - /// This method pre-seeds the ResizeProcessor for the AoT compiler on iOS. - /// - /// The pixel format. - private static void AotCompileResizeOperations() - where TPixel : struct, IPixel - { - var genericResizeProcessor = (ResizeProcessor)new ResizeProcessor(new ResizeOptions(), default).CreatePixelSpecificProcessor(new Image(0, 0), default); - genericResizeProcessor.AotCreateDestination(); - } } } diff --git a/src/ImageSharp/Processing/DefaultImageProcessorContext{TPixel}.cs b/src/ImageSharp/Processing/DefaultImageProcessorContext{TPixel}.cs index 7f7316694..328ccdf94 100644 --- a/src/ImageSharp/Processing/DefaultImageProcessorContext{TPixel}.cs +++ b/src/ImageSharp/Processing/DefaultImageProcessorContext{TPixel}.cs @@ -69,9 +69,9 @@ namespace SixLabors.ImageSharp.Processing { // When cloning an image we can optimize the processing pipeline by avoiding an unnecessary // interim clone if the first processor in the pipeline is a cloning processor. - if (processor is CloningImageProcessor cloningImageProcessor) + if (processor is ICloningImageProcessor cloningImageProcessor) { - using (ICloningImageProcessor pixelProcessor = cloningImageProcessor.CreatePixelSpecificProcessor(this.source, rectangle)) + using (ICloningImageProcessor pixelProcessor = cloningImageProcessor.CreatePixelSpecificCloningProcessor(this.source, rectangle)) { this.destination = pixelProcessor.CloneAndExecute(); return this; diff --git a/src/ImageSharp/Processing/Processors/CloningImageProcessor.cs b/src/ImageSharp/Processing/Processors/CloningImageProcessor.cs index 6ab0fcb13..5e9ca2e54 100644 --- a/src/ImageSharp/Processing/Processors/CloningImageProcessor.cs +++ b/src/ImageSharp/Processing/Processors/CloningImageProcessor.cs @@ -9,23 +9,14 @@ namespace SixLabors.ImageSharp.Processing.Processors /// /// The base class for all cloning image processors. /// - public abstract class CloningImageProcessor : IImageProcessor + public abstract class CloningImageProcessor : ICloningImageProcessor { - /// - /// Creates a pixel specific that is capable of executing - /// the processing algorithm on an . - /// - /// The pixel type. - /// The source image. Cannot be null. - /// - /// The structure that specifies the portion of the image object to draw. - /// - /// The - public abstract ICloningImageProcessor CreatePixelSpecificProcessor(Image source, Rectangle sourceRectangle) + /// + public abstract ICloningImageProcessor CreatePixelSpecificCloningProcessor(Image source, Rectangle sourceRectangle) where TPixel : struct, IPixel; /// IImageProcessor IImageProcessor.CreatePixelSpecificProcessor(Image source, Rectangle sourceRectangle) - => this.CreatePixelSpecificProcessor(source, sourceRectangle); + => this.CreatePixelSpecificCloningProcessor(source, sourceRectangle); } } diff --git a/src/ImageSharp/Processing/Processors/CloningImageProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/CloningImageProcessor{TPixel}.cs index 1290a1032..42d2f0e1d 100644 --- a/src/ImageSharp/Processing/Processors/CloningImageProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/CloningImageProcessor{TPixel}.cs @@ -2,6 +2,8 @@ // Licensed under the Apache License, Version 2.0. using System; +using System.Collections.Generic; +using System.Linq; using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.PixelFormats; using SixLabors.Primitives; @@ -40,16 +42,16 @@ namespace SixLabors.ImageSharp.Processing.Processors protected Rectangle SourceRectangle { get; } /// - /// Gets the instance to use when performing operations. + /// Gets the instance to use when performing operations. /// protected Configuration Configuration { get; } /// - public Image CloneAndExecute() + Image ICloningImageProcessor.CloneAndExecute() { try { - Image clone = this.CreateDestination(); + Image clone = this.CreateTarget(); this.CheckFrameCount(this.Source, clone); Configuration configuration = this.Source.GetConfiguration(); @@ -82,7 +84,7 @@ namespace SixLabors.ImageSharp.Processing.Processors } /// - public void Execute() + void IImageProcessor.Execute() { // Create an interim clone of the source image to operate on. // Doing this allows for the application of transforms that will alter @@ -90,7 +92,7 @@ namespace SixLabors.ImageSharp.Processing.Processors Image clone = default; try { - clone = this.CloneAndExecute(); + clone = ((ICloningImageProcessor)this).CloneAndExecute(); // We now need to move the pixel data/size data from the clone to the source. this.CheckFrameCount(this.Source, clone); @@ -111,10 +113,10 @@ namespace SixLabors.ImageSharp.Processing.Processors } /// - /// Generates a deep clone of the source image that operations should be applied to. + /// Gets the size of the target image. /// - /// The cloned image. - protected virtual Image CreateDestination() => this.Source.Clone(); + /// The . + protected abstract Size GetTargetSize(); /// /// This method is called before the process is applied to prepare the processor. @@ -166,6 +168,23 @@ namespace SixLabors.ImageSharp.Processing.Processors { } + private Image CreateTarget() + { + Image source = this.Source; + Size targetSize = this.GetTargetSize(); + + // We will always be creating the clone even for mutate because we may need to resize the canvas + IEnumerable> frames = source.Frames.Select, ImageFrame>( + x => new ImageFrame( + source.GetConfiguration(), + targetSize.Width, + targetSize.Height, + x.Metadata.DeepClone())); + + // Use the overload to prevent an extra frame being added + return new Image(this.Configuration, source.Metadata.DeepClone(), frames); + } + private void CheckFrameCount(Image a, Image b) { if (a.Frames.Count != b.Frames.Count) diff --git a/src/ImageSharp/Processing/Processors/Convolution/EdgeDetector2DProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Convolution/EdgeDetector2DProcessor{TPixel}.cs index 7b070f99a..8358abe7d 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/EdgeDetector2DProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/EdgeDetector2DProcessor{TPixel}.cs @@ -54,7 +54,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution { if (this.Grayscale) { - new GrayscaleBt709Processor(1F).Apply(this.Source, this.SourceRectangle); + new GrayscaleBt709Processor(1F).Execute(this.Source, this.SourceRectangle); } base.BeforeImageApply(); diff --git a/src/ImageSharp/Processing/Processors/Convolution/EdgeDetectorCompassProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Convolution/EdgeDetectorCompassProcessor{TPixel}.cs index b7119ef44..dc9974c61 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/EdgeDetectorCompassProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/EdgeDetectorCompassProcessor{TPixel}.cs @@ -45,7 +45,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution { if (this.Grayscale) { - new GrayscaleBt709Processor(1F).Apply(this.Source, this.SourceRectangle); + new GrayscaleBt709Processor(1F).Execute(this.Source, this.SourceRectangle); } base.BeforeImageApply(); diff --git a/src/ImageSharp/Processing/Processors/Convolution/EdgeDetectorProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Convolution/EdgeDetectorProcessor{TPixel}.cs index fc762cf1b..5246dc3b7 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/EdgeDetectorProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/EdgeDetectorProcessor{TPixel}.cs @@ -41,7 +41,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution { if (this.Grayscale) { - new GrayscaleBt709Processor(1F).Apply(this.Source, this.SourceRectangle); + new GrayscaleBt709Processor(1F).Execute(this.Source, this.SourceRectangle); } base.BeforeImageApply(); diff --git a/src/ImageSharp/Processing/Processors/Filters/LomographProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Filters/LomographProcessor{TPixel}.cs index ab832a275..7d3a5bbc0 100644 --- a/src/ImageSharp/Processing/Processors/Filters/LomographProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Filters/LomographProcessor{TPixel}.cs @@ -29,7 +29,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Filters /// protected override void AfterImageApply() { - new VignetteProcessor(VeryDarkGreen).Apply(this.Source, this.SourceRectangle); + new VignetteProcessor(VeryDarkGreen).Execute(this.Source, this.SourceRectangle); base.AfterImageApply(); } } diff --git a/src/ImageSharp/Processing/Processors/Filters/PolaroidProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Filters/PolaroidProcessor{TPixel}.cs index 0be5bbb0d..f7ab1a1ec 100644 --- a/src/ImageSharp/Processing/Processors/Filters/PolaroidProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Filters/PolaroidProcessor{TPixel}.cs @@ -31,8 +31,8 @@ namespace SixLabors.ImageSharp.Processing.Processors.Filters /// protected override void AfterImageApply() { - new VignetteProcessor(VeryDarkOrange).Apply(this.Source, this.SourceRectangle); - new GlowProcessor(LightOrange, this.Source.Width / 4F).Apply(this.Source, this.SourceRectangle); + new VignetteProcessor(VeryDarkOrange).Execute(this.Source, this.SourceRectangle); + new GlowProcessor(LightOrange, this.Source.Width / 4F).Execute(this.Source, this.SourceRectangle); base.AfterImageApply(); } } diff --git a/src/ImageSharp/Processing/Processors/ICloningImageProcessor.cs b/src/ImageSharp/Processing/Processors/ICloningImageProcessor.cs new file mode 100644 index 000000000..554a4b886 --- /dev/null +++ b/src/ImageSharp/Processing/Processors/ICloningImageProcessor.cs @@ -0,0 +1,27 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.Primitives; + +namespace SixLabors.ImageSharp.Processing.Processors +{ + /// + /// Defines an algorithm to alter the pixels of a cloned image. + /// + public interface ICloningImageProcessor : IImageProcessor + { + /// + /// Creates a pixel specific that is capable of executing + /// the processing algorithm on an . + /// + /// The pixel type. + /// The source image. Cannot be null. + /// + /// The structure that specifies the portion of the image object to draw. + /// + /// The + ICloningImageProcessor CreatePixelSpecificCloningProcessor(Image source, Rectangle sourceRectangle) + where TPixel : struct, IPixel; + } +} diff --git a/src/ImageSharp/Processing/Processors/ICloningImageProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/ICloningImageProcessor{TPixel}.cs index c34bf60ae..84b126229 100644 --- a/src/ImageSharp/Processing/Processors/ICloningImageProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/ICloningImageProcessor{TPixel}.cs @@ -6,7 +6,7 @@ using SixLabors.ImageSharp.PixelFormats; namespace SixLabors.ImageSharp.Processing.Processors { /// - /// Encapsulates methods to alter the pixels of a new image, cloned from the original image. + /// Implements an algorithm to alter the pixels of a cloned image. /// /// The pixel format. public interface ICloningImageProcessor : IImageProcessor diff --git a/src/ImageSharp/Processing/Processors/ImageProcessorExtensions.cs b/src/ImageSharp/Processing/Processors/ImageProcessorExtensions.cs index 19e594a32..ce8ed813b 100644 --- a/src/ImageSharp/Processing/Processors/ImageProcessorExtensions.cs +++ b/src/ImageSharp/Processing/Processors/ImageProcessorExtensions.cs @@ -9,18 +9,21 @@ namespace SixLabors.ImageSharp.Processing.Processors { internal static class ImageProcessorExtensions { - public static void Apply(this IImageProcessor processor, Image source, Rectangle sourceRectangle) - { - source.AcceptVisitor(new ApplyVisitor(processor, sourceRectangle)); - } + /// + /// Executes the processor against the given source image and rectangle bounds. + /// + /// The processor. + /// The source image. + /// The source bounds. + public static void Execute(this IImageProcessor processor, Image source, Rectangle sourceRectangle) + => source.AcceptVisitor(new ExecuteVisitor(processor, sourceRectangle)); - private class ApplyVisitor : IImageVisitor + private class ExecuteVisitor : IImageVisitor { private readonly IImageProcessor processor; - private readonly Rectangle sourceRectangle; - public ApplyVisitor(IImageProcessor processor, Rectangle sourceRectangle) + public ExecuteVisitor(IImageProcessor processor, Rectangle sourceRectangle) { this.processor = processor; this.sourceRectangle = sourceRectangle; diff --git a/src/ImageSharp/Processing/Processors/ImageProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/ImageProcessor{TPixel}.cs index eb1dc4ba0..3e46e3c08 100644 --- a/src/ImageSharp/Processing/Processors/ImageProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/ImageProcessor{TPixel}.cs @@ -44,7 +44,7 @@ namespace SixLabors.ImageSharp.Processing.Processors protected Configuration Configuration { get; } /// - public void Execute() + void IImageProcessor.Execute() { try { diff --git a/src/ImageSharp/Processing/Processors/Transforms/AffineTransformProcessor.cs b/src/ImageSharp/Processing/Processors/Transforms/AffineTransformProcessor.cs index be5675578..6ca844fae 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/AffineTransformProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/AffineTransformProcessor.cs @@ -41,7 +41,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms public Size TargetDimensions { get; } /// - public override ICloningImageProcessor CreatePixelSpecificProcessor(Image source, Rectangle sourceRectangle) + public override ICloningImageProcessor CreatePixelSpecificCloningProcessor(Image source, Rectangle sourceRectangle) => new AffineTransformProcessor(this, source, sourceRectangle); } } diff --git a/src/ImageSharp/Processing/Processors/Transforms/AffineTransformProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Transforms/AffineTransformProcessor{TPixel}.cs index 7c50c04f3..97b8b009b 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/AffineTransformProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/AffineTransformProcessor{TPixel}.cs @@ -2,10 +2,7 @@ // Licensed under the Apache License, Version 2.0. using System; -using System.Collections.Generic; -using System.Linq; using System.Numerics; - using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.ParallelUtils; using SixLabors.ImageSharp.PixelFormats; @@ -20,6 +17,10 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms internal class AffineTransformProcessor : TransformProcessor where TPixel : struct, IPixel { + private Size targetSize; + private Matrix3x2 transformMatrix; + private readonly IResampler resampler; + /// /// Initializes a new instance of the class. /// @@ -29,50 +30,37 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms public AffineTransformProcessor(AffineTransformProcessor definition, Image source, Rectangle sourceRectangle) : base(source, sourceRectangle) { - this.Definition = definition; + this.targetSize = definition.TargetDimensions; + this.transformMatrix = definition.TransformMatrix; + this.resampler = definition.Sampler; } - protected AffineTransformProcessor Definition { get; } - - private Size TargetDimensions => this.Definition.TargetDimensions; - - private Matrix3x2 TransformMatrix => this.Definition.TransformMatrix; - - /// - protected override Image CreateDestination() - { - // We will always be creating the clone even for mutate because we may need to resize the canvas - IEnumerable> frames = this.Source.Frames.Select, ImageFrame>( - x => new ImageFrame(this.Configuration, this.TargetDimensions, x.Metadata.DeepClone())); - - // Use the overload to prevent an extra frame being added - return new Image(this.Configuration, this.Source.Metadata.DeepClone(), frames); - } + protected override Size GetTargetSize() => this.targetSize; /// protected override void OnFrameApply(ImageFrame source, ImageFrame destination) { // Handle transforms that result in output identical to the original. - if (this.TransformMatrix.Equals(default) || this.TransformMatrix.Equals(Matrix3x2.Identity)) + if (this.transformMatrix.Equals(default) || this.transformMatrix.Equals(Matrix3x2.Identity)) { // The clone will be blank here copy all the pixel data over source.GetPixelSpan().CopyTo(destination.GetPixelSpan()); return; } - int width = this.TargetDimensions.Width; - var targetBounds = new Rectangle(Point.Empty, this.TargetDimensions); + int width = this.targetSize.Width; + Rectangle sourceBounds = this.SourceRectangle; + var targetBounds = new Rectangle(Point.Empty, this.targetSize); + Configuration configuration = this.Configuration; // Convert from screen to world space. - Matrix3x2.Invert(this.TransformMatrix, out Matrix3x2 matrix); + Matrix3x2.Invert(this.transformMatrix, out Matrix3x2 matrix); - IResampler sampler = this.Definition.Sampler; - - if (sampler is NearestNeighborResampler) + if (this.resampler is NearestNeighborResampler) { ParallelHelper.IterateRows( targetBounds, - this.Configuration, + configuration, rows => { for (int y = rows.Min; y < rows.Max; y++) @@ -82,7 +70,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms for (int x = 0; x < width; x++) { var point = Point.Transform(new Point(x, y), matrix); - if (this.SourceRectangle.Contains(point.X, point.Y)) + if (sourceBounds.Contains(point.X, point.Y)) { destRow[x] = source[point.X, point.Y]; } @@ -93,19 +81,20 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms return; } - var kernel = new TransformKernelMap(this.Configuration, source.Size(), destination.Size(), sampler); + var kernel = new TransformKernelMap(configuration, source.Size(), destination.Size(), this.resampler); + try { ParallelHelper.IterateRowsWithTempBuffer( targetBounds, - this.Configuration, + configuration, (rows, vectorBuffer) => { Span vectorSpan = vectorBuffer.Span; for (int y = rows.Min; y < rows.Max; y++) { Span targetRowSpan = destination.GetPixelRowSpan(y); - PixelOperations.Instance.ToVector4(this.Configuration, targetRowSpan, vectorSpan); + PixelOperations.Instance.ToVector4(configuration, targetRowSpan, vectorSpan); ref float ySpanRef = ref kernel.GetYStartReference(y); ref float xSpanRef = ref kernel.GetXStartReference(y); @@ -124,7 +113,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms } PixelOperations.Instance.FromVector4Destructive( - this.Configuration, + configuration, vectorSpan, targetRowSpan); } diff --git a/src/ImageSharp/Processing/Processors/Transforms/AutoOrientProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Transforms/AutoOrientProcessor{TPixel}.cs index a5170c96a..b9952ac8f 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/AutoOrientProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/AutoOrientProcessor{TPixel}.cs @@ -34,33 +34,33 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms switch (orientation) { case OrientationMode.TopRight: - new FlipProcessor(FlipMode.Horizontal).Apply(this.Source, this.SourceRectangle); + new FlipProcessor(FlipMode.Horizontal).Execute(this.Source, this.SourceRectangle); break; case OrientationMode.BottomRight: - new RotateProcessor((int)RotateMode.Rotate180, size).Apply(this.Source, this.SourceRectangle); + new RotateProcessor((int)RotateMode.Rotate180, size).Execute(this.Source, this.SourceRectangle); break; case OrientationMode.BottomLeft: - new FlipProcessor(FlipMode.Vertical).Apply(this.Source, this.SourceRectangle); + new FlipProcessor(FlipMode.Vertical).Execute(this.Source, this.SourceRectangle); break; case OrientationMode.LeftTop: - new RotateProcessor((int)RotateMode.Rotate90, size).Apply(this.Source, this.SourceRectangle); - new FlipProcessor(FlipMode.Horizontal).Apply(this.Source, this.SourceRectangle); + new RotateProcessor((int)RotateMode.Rotate90, size).Execute(this.Source, this.SourceRectangle); + new FlipProcessor(FlipMode.Horizontal).Execute(this.Source, this.SourceRectangle); break; case OrientationMode.RightTop: - new RotateProcessor((int)RotateMode.Rotate90, size).Apply(this.Source, this.SourceRectangle); + new RotateProcessor((int)RotateMode.Rotate90, size).Execute(this.Source, this.SourceRectangle); break; case OrientationMode.RightBottom: - new FlipProcessor(FlipMode.Vertical).Apply(this.Source, this.SourceRectangle); - new RotateProcessor((int)RotateMode.Rotate270, size).Apply(this.Source, this.SourceRectangle); + new FlipProcessor(FlipMode.Vertical).Execute(this.Source, this.SourceRectangle); + new RotateProcessor((int)RotateMode.Rotate270, size).Execute(this.Source, this.SourceRectangle); break; case OrientationMode.LeftBottom: - new RotateProcessor((int)RotateMode.Rotate270, size).Apply(this.Source, this.SourceRectangle); + new RotateProcessor((int)RotateMode.Rotate270, size).Execute(this.Source, this.SourceRectangle); break; case OrientationMode.Unknown: diff --git a/src/ImageSharp/Processing/Processors/Transforms/CropProcessor.cs b/src/ImageSharp/Processing/Processors/Transforms/CropProcessor.cs index 025592a36..245a54208 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/CropProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/CropProcessor.cs @@ -32,7 +32,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms public Rectangle CropRectangle { get; } /// - public override ICloningImageProcessor CreatePixelSpecificProcessor(Image source, Rectangle sourceRectangle) + public override ICloningImageProcessor CreatePixelSpecificCloningProcessor(Image source, Rectangle sourceRectangle) => new CropProcessor(this, source, sourceRectangle); } } diff --git a/src/ImageSharp/Processing/Processors/Transforms/CropProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Transforms/CropProcessor{TPixel}.cs index 539a11f02..1bbdd0a16 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/CropProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/CropProcessor{TPixel}.cs @@ -2,9 +2,6 @@ // Licensed under the Apache License, Version 2.0. using System; -using System.Collections.Generic; -using System.Linq; - using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.ParallelUtils; using SixLabors.ImageSharp.PixelFormats; @@ -19,7 +16,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms internal class CropProcessor : TransformProcessor where TPixel : struct, IPixel { - private readonly CropProcessor definition; + private Rectangle cropRectangle; /// /// Initializes a new instance of the class. @@ -29,53 +26,42 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms /// The source area to process for the current processor instance. public CropProcessor(CropProcessor definition, Image source, Rectangle sourceRectangle) : base(source, sourceRectangle) - { - this.definition = definition; - } - - private Rectangle CropRectangle => this.definition.CropRectangle; + => this.cropRectangle = definition.CropRectangle; /// - protected override Image CreateDestination() - { - // We will always be creating the clone even for mutate because we may need to resize the canvas - IEnumerable> frames = this.Source.Frames.Select, ImageFrame>( - x => new ImageFrame( - this.Source.GetConfiguration(), - this.CropRectangle.Width, - this.CropRectangle.Height, - x.Metadata.DeepClone())); - - // Use the overload to prevent an extra frame being added - return new Image(this.Source.GetConfiguration(), this.Source.Metadata.DeepClone(), frames); - } + protected override Size GetTargetSize() => new Size(this.cropRectangle.Width, this.cropRectangle.Height); /// protected override void OnFrameApply(ImageFrame source, ImageFrame destination) { - // Handle resize dimensions identical to the original - if (source.Width == destination.Width && source.Height == destination.Height && this.SourceRectangle == this.CropRectangle) + // Handle crop dimensions identical to the original + if (source.Width == destination.Width + && source.Height == destination.Height + && this.SourceRectangle == this.cropRectangle) { // the cloned will be blank here copy all the pixel data over source.GetPixelSpan().CopyTo(destination.GetPixelSpan()); return; } - Rectangle rect = this.CropRectangle; + Rectangle bounds = this.cropRectangle; // Copying is cheap, we should process more pixels per task: - ParallelExecutionSettings parallelSettings = this.Configuration.GetParallelSettings().MultiplyMinimumPixelsPerTask(4); + ParallelExecutionSettings parallelSettings + = this.Configuration + .GetParallelSettings() + .MultiplyMinimumPixelsPerTask(4); ParallelHelper.IterateRows( - rect, + bounds, parallelSettings, rows => { for (int y = rows.Min; y < rows.Max; y++) { - Span sourceRow = source.GetPixelRowSpan(y).Slice(rect.Left); - Span targetRow = destination.GetPixelRowSpan(y - rect.Top); - sourceRow.Slice(0, rect.Width).CopyTo(targetRow); + Span sourceRow = source.GetPixelRowSpan(y).Slice(bounds.Left); + Span targetRow = destination.GetPixelRowSpan(y - bounds.Top); + sourceRow.Slice(0, bounds.Width).CopyTo(targetRow); } }); } diff --git a/src/ImageSharp/Processing/Processors/Transforms/EntropyCropProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Transforms/EntropyCropProcessor{TPixel}.cs index b74fbb0ab..2b900ee36 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/EntropyCropProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/EntropyCropProcessor{TPixel}.cs @@ -42,16 +42,16 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms Configuration configuration = this.Source.GetConfiguration(); // Detect the edges. - new SobelProcessor(false).Apply(temp, this.SourceRectangle); + new SobelProcessor(false).Execute(temp, this.SourceRectangle); // Apply threshold binarization filter. - new BinaryThresholdProcessor(this.definition.Threshold).Apply(temp, this.SourceRectangle); + new BinaryThresholdProcessor(this.definition.Threshold).Execute(temp, this.SourceRectangle); // Search for the first white pixels rectangle = ImageMaths.GetFilteredBoundingRectangle(temp.Frames.RootFrame, 0); } - new CropProcessor(rectangle, this.Source.Size()).Apply(this.Source, this.SourceRectangle); + new CropProcessor(rectangle, this.Source.Size()).Execute(this.Source, this.SourceRectangle); base.BeforeImageApply(); } diff --git a/src/ImageSharp/Processing/Processors/Transforms/ProjectiveTransformProcessor.cs b/src/ImageSharp/Processing/Processors/Transforms/ProjectiveTransformProcessor.cs index babdee593..d91db9a72 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/ProjectiveTransformProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/ProjectiveTransformProcessor.cs @@ -41,7 +41,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms public Size TargetDimensions { get; } /// - public override ICloningImageProcessor CreatePixelSpecificProcessor(Image source, Rectangle sourceRectangle) + public override ICloningImageProcessor CreatePixelSpecificCloningProcessor(Image source, Rectangle sourceRectangle) => new ProjectiveTransformProcessor(this, source, sourceRectangle); } } diff --git a/src/ImageSharp/Processing/Processors/Transforms/ProjectiveTransformProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Transforms/ProjectiveTransformProcessor{TPixel}.cs index 29dc8a070..68bfd817e 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/ProjectiveTransformProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/ProjectiveTransformProcessor{TPixel}.cs @@ -2,8 +2,6 @@ // Licensed under the Apache License, Version 2.0. using System; -using System.Collections.Generic; -using System.Linq; using System.Numerics; using SixLabors.ImageSharp.Advanced; @@ -20,7 +18,9 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms internal class ProjectiveTransformProcessor : TransformProcessor where TPixel : struct, IPixel { - private readonly ProjectiveTransformProcessor definition; + private Size targetSize; + private readonly IResampler resampler; + private Matrix4x4 transformMatrix; /// /// Initializes a new instance of the class. @@ -31,52 +31,37 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms public ProjectiveTransformProcessor(ProjectiveTransformProcessor definition, Image source, Rectangle sourceRectangle) : base(source, sourceRectangle) { - this.definition = definition; + this.targetSize = definition.TargetDimensions; + this.transformMatrix = definition.TransformMatrix; + this.resampler = definition.Sampler; } - private Size TargetDimensions => this.definition.TargetDimensions; - - /// - protected override Image CreateDestination() - { - // We will always be creating the clone even for mutate because we may need to resize the canvas - IEnumerable> frames = this.Source.Frames.Select, ImageFrame>( - x => new ImageFrame( - this.Source.GetConfiguration(), - this.TargetDimensions.Width, - this.TargetDimensions.Height, - x.Metadata.DeepClone())); - - // Use the overload to prevent an extra frame being added - return new Image(this.Source.GetConfiguration(), this.Source.Metadata.DeepClone(), frames); - } + protected override Size GetTargetSize() => this.targetSize; /// protected override void OnFrameApply(ImageFrame source, ImageFrame destination) { - Matrix4x4 transformMatrix = this.definition.TransformMatrix; - // Handle transforms that result in output identical to the original. - if (transformMatrix.Equals(default) || transformMatrix.Equals(Matrix4x4.Identity)) + if (this.transformMatrix.Equals(default) || this.transformMatrix.Equals(Matrix4x4.Identity)) { // The clone will be blank here copy all the pixel data over source.GetPixelSpan().CopyTo(destination.GetPixelSpan()); return; } - int width = this.TargetDimensions.Width; - var targetBounds = new Rectangle(Point.Empty, this.TargetDimensions); + int width = this.targetSize.Width; + Rectangle sourceBounds = this.SourceRectangle; + var targetBounds = new Rectangle(Point.Empty, this.targetSize); + Configuration configuration = this.Configuration; // Convert from screen to world space. - Matrix4x4.Invert(transformMatrix, out Matrix4x4 matrix); - - IResampler sampler = this.definition.Sampler; + Matrix4x4.Invert(this.transformMatrix, out Matrix4x4 matrix); - if (sampler is NearestNeighborResampler) + if (this.resampler is NearestNeighborResampler) { ParallelHelper.IterateRows( targetBounds, - this.Configuration, + configuration, rows => { for (int y = rows.Min; y < rows.Max; y++) @@ -89,7 +74,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms int px = (int)MathF.Round(point.X); int py = (int)MathF.Round(point.Y); - if (this.SourceRectangle.Contains(px, py)) + if (sourceBounds.Contains(px, py)) { destRow[x] = source[px, py]; } @@ -100,19 +85,20 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms return; } - var kernel = new TransformKernelMap(this.Configuration, source.Size(), destination.Size(), sampler); + var kernel = new TransformKernelMap(configuration, source.Size(), destination.Size(), this.resampler); + try { ParallelHelper.IterateRowsWithTempBuffer( targetBounds, - this.Configuration, + configuration, (rows, vectorBuffer) => { Span vectorSpan = vectorBuffer.Span; for (int y = rows.Min; y < rows.Max; y++) { Span targetRowSpan = destination.GetPixelRowSpan(y); - PixelOperations.Instance.ToVector4(this.Configuration, targetRowSpan, vectorSpan); + PixelOperations.Instance.ToVector4(configuration, targetRowSpan, vectorSpan); ref float ySpanRef = ref kernel.GetYStartReference(y); ref float xSpanRef = ref kernel.GetXStartReference(y); @@ -131,7 +117,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms } PixelOperations.Instance.FromVector4Destructive( - this.Configuration, + configuration, vectorSpan, targetRowSpan); } diff --git a/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor.cs b/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor.cs index 5390fa4a4..ccaa1ef9e 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor.cs @@ -55,7 +55,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms public bool Compand { get; } /// - public override ICloningImageProcessor CreatePixelSpecificProcessor(Image source, Rectangle sourceRectangle) + public override ICloningImageProcessor CreatePixelSpecificCloningProcessor(Image source, Rectangle sourceRectangle) => new ResizeProcessor(this, source, sourceRectangle); } } diff --git a/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor{TPixel}.cs index b85983a48..78e471ad6 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor{TPixel}.cs @@ -2,8 +2,6 @@ // Licensed under the Apache License, Version 2.0. using System; -using System.Collections.Generic; -using System.Linq; using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.Memory; @@ -24,74 +22,34 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms internal class ResizeProcessor : TransformProcessor where TPixel : struct, IPixel { - private readonly ResizeProcessor parameterSource; private bool isDisposed; + private readonly int targetWidth; + private readonly int targetHeight; + private readonly IResampler resampler; + private Rectangle targetRectangle; + private readonly bool compand; // The following fields are not immutable but are optionally created on demand. private ResizeKernelMap horizontalKernelMap; private ResizeKernelMap verticalKernelMap; - public ResizeProcessor(ResizeProcessor parameterSource, Image source, Rectangle sourceRectangle) + public ResizeProcessor(ResizeProcessor definition, Image source, Rectangle sourceRectangle) : base(source, sourceRectangle) { - this.parameterSource = parameterSource; + this.targetWidth = definition.TargetWidth; + this.targetHeight = definition.TargetHeight; + this.targetRectangle = definition.TargetRectangle; + this.resampler = definition.Sampler; + this.compand = definition.Compand; } - /// - /// Gets the sampler to perform the resize operation. - /// - public IResampler Sampler => this.parameterSource.Sampler; - - /// - /// Gets the target width. - /// - public int TargetWidth => this.parameterSource.TargetWidth; - - /// - /// Gets the target height. - /// - public int TargetHeight => this.parameterSource.TargetHeight; - - /// - /// Gets the target resize rectangle. - /// - public Rectangle TargetRectangle => this.parameterSource.TargetRectangle; - - /// - /// Gets a value indicating whether to compress or expand individual pixel color values on processing. - /// - public bool Compand => this.parameterSource.Compand; - - /// - /// This is a shim for tagging the CreateDestination virtual generic method for the AoT iOS compiler. - /// This method should never be referenced outside of the AotCompiler code. - /// - /// The result returned from . - internal Image AotCreateDestination() - => this.CreateDestination(); - /// - protected override Image CreateDestination() - { - Image source = this.Source; - Configuration configuration = this.Configuration; - - // We will always be creating the clone even for mutate because we may need to resize the canvas - IEnumerable> frames = source.Frames.Select, ImageFrame>( - x => new ImageFrame( - configuration, - this.TargetWidth, - this.TargetHeight, - x.Metadata.DeepClone())); - - // Use the overload to prevent an extra frame being added - return new Image(configuration, source.Metadata.DeepClone(), frames); - } + protected override Size GetTargetSize() => new Size(this.targetWidth, this.targetHeight); /// protected override void BeforeImageApply(Image destination) { - if (!(this.Sampler is NearestNeighborResampler)) + if (!(this.resampler is NearestNeighborResampler)) { Image source = this.Source; Rectangle sourceRectangle = this.SourceRectangle; @@ -99,14 +57,14 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms // 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.Sampler, - this.TargetRectangle.Width, + this.resampler, + this.targetRectangle.Width, sourceRectangle.Width, memoryAllocator); this.verticalKernelMap = ResizeKernelMap.Calculate( - this.Sampler, - this.TargetRectangle.Height, + this.resampler, + this.targetRectangle.Height, sourceRectangle.Height, memoryAllocator); } @@ -121,29 +79,29 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms Configuration configuration = this.Configuration; // Handle resize dimensions identical to the original - if (source.Width == destination.Width && source.Height == destination.Height && sourceRectangle == this.TargetRectangle) + 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.GetPixelSpan().CopyTo(destination.GetPixelSpan()); return; } - int width = this.TargetWidth; - int height = this.TargetHeight; + int width = this.targetWidth; + int height = this.targetHeight; int sourceX = sourceRectangle.X; int sourceY = sourceRectangle.Y; - int startY = this.TargetRectangle.Y; - int startX = this.TargetRectangle.X; + int startY = this.targetRectangle.Y; + int startX = this.targetRectangle.X; var targetWorkingRect = Rectangle.Intersect( - this.TargetRectangle, + this.targetRectangle, new Rectangle(0, 0, width, height)); - if (this.Sampler is NearestNeighborResampler) + if (this.resampler is NearestNeighborResampler) { // Scaling factors - float widthFactor = sourceRectangle.Width / (float)this.TargetRectangle.Width; - float heightFactor = sourceRectangle.Height / (float)this.TargetRectangle.Height; + float widthFactor = sourceRectangle.Width / (float)this.targetRectangle.Width; + float heightFactor = sourceRectangle.Height / (float)this.targetRectangle.Height; ParallelHelper.IterateRows( targetWorkingRect, @@ -153,8 +111,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms for (int y = rows.Min; y < rows.Max; y++) { // Y coordinates of source points - Span sourceRow = - source.GetPixelRowSpan((int)(((y - startY) * heightFactor) + sourceY)); + Span sourceRow = source.GetPixelRowSpan((int)(((y - startY) * heightFactor) + sourceY)); Span targetRow = destination.GetPixelRowSpan(y); for (int x = targetWorkingRect.Left; x < targetWorkingRect.Right; x++) @@ -169,7 +126,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms } PixelConversionModifiers conversionModifiers = - PixelConversionModifiers.Premultiply.ApplyCompanding(this.Compand); + PixelConversionModifiers.Premultiply.ApplyCompanding(this.compand); BufferArea sourceArea = source.PixelBuffer.GetArea(sourceRectangle); @@ -183,7 +140,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms this.verticalKernelMap, width, targetWorkingRect, - this.TargetRectangle.Location)) + this.targetRectangle.Location)) { worker.Initialize(); diff --git a/src/ImageSharp/Processing/Processors/Transforms/RotateProcessor.cs b/src/ImageSharp/Processing/Processors/Transforms/RotateProcessor.cs index 277cc05b2..7d6ec0e08 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/RotateProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/RotateProcessor.cs @@ -46,7 +46,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms public float Degrees { get; } /// - public override ICloningImageProcessor CreatePixelSpecificProcessor(Image source, Rectangle sourceRectangle) + public override ICloningImageProcessor CreatePixelSpecificCloningProcessor(Image source, Rectangle sourceRectangle) => new RotateProcessor(this, source, sourceRectangle); } } diff --git a/tests/ImageSharp.Tests/Drawing/FillRegionProcessorTests.cs b/tests/ImageSharp.Tests/Drawing/FillRegionProcessorTests.cs index 972f5cf4a..c0388ea2d 100644 --- a/tests/ImageSharp.Tests/Drawing/FillRegionProcessorTests.cs +++ b/tests/ImageSharp.Tests/Drawing/FillRegionProcessorTests.cs @@ -41,7 +41,7 @@ namespace SixLabors.ImageSharp.Tests.Drawing }; var processor = new FillRegionProcessor(brush.Object, region, options); var img = new Image(1, 1); - processor.Apply(img, bounds); + processor.Execute(img, bounds); Assert.Equal(4, region.ScanInvocationCounter); } @@ -54,7 +54,7 @@ namespace SixLabors.ImageSharp.Tests.Drawing var options = new GraphicsOptions(true); var processor = new FillRegionProcessor(brush.Object, new MockRegion1(), options); var img = new Image(10, 10); - processor.Apply(img, bounds); + processor.Execute(img, bounds); } [Fact] diff --git a/tests/ImageSharp.Tests/Processing/ImageProcessingContextTests.cs b/tests/ImageSharp.Tests/Processing/ImageProcessingContextTests.cs index afb2cbecd..9d16583cd 100644 --- a/tests/ImageSharp.Tests/Processing/ImageProcessingContextTests.cs +++ b/tests/ImageSharp.Tests/Processing/ImageProcessingContextTests.cs @@ -2,12 +2,10 @@ // Licensed under the Apache License, Version 2.0. using Moq; - using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Processing; using SixLabors.ImageSharp.Processing.Processors; using SixLabors.Primitives; - using Xunit; namespace SixLabors.ImageSharp.Tests.Processing @@ -21,7 +19,7 @@ namespace SixLabors.ImageSharp.Tests.Processing private readonly Mock processorDefinition; - private readonly Mock cloningProcessorDefinition; + private readonly Mock cloningProcessorDefinition; private readonly Mock> regularProcessorImpl; @@ -32,7 +30,7 @@ namespace SixLabors.ImageSharp.Tests.Processing public ImageProcessingContextTests() { this.processorDefinition = new Mock(); - this.cloningProcessorDefinition = new Mock(); + this.cloningProcessorDefinition = new Mock(); this.regularProcessorImpl = new Mock>(); this.cloningProcessorImpl = new Mock>(); } @@ -54,11 +52,11 @@ namespace SixLabors.ImageSharp.Tests.Processing if (throwException) { - Assert.Throws(() => this.MutateApply(useBounds)); + Assert.Throws(() => this.MutateRegularApply(useBounds)); } else { - this.MutateApply(useBounds); + this.MutateRegularApply(useBounds); } this.regularProcessorImpl.Verify(p => p.Execute(), Times.Once()); @@ -92,11 +90,11 @@ namespace SixLabors.ImageSharp.Tests.Processing if (throwException) { - Assert.Throws(() => this.MutateApply(useBounds)); + Assert.Throws(() => this.MutateCloneApply(useBounds)); } else { - this.MutateApply(useBounds); + this.MutateCloneApply(useBounds); } this.cloningProcessorImpl.Verify(p => p.Execute(), Times.Once()); @@ -122,7 +120,7 @@ namespace SixLabors.ImageSharp.Tests.Processing this.cloningProcessorImpl.Verify(p => p.Dispose(), Times.Once()); } - private void MutateApply(bool useBounds) + private void MutateRegularApply(bool useBounds) { if (useBounds) { @@ -134,6 +132,18 @@ namespace SixLabors.ImageSharp.Tests.Processing } } + private void MutateCloneApply(bool useBounds) + { + if (useBounds) + { + this.image.Mutate(c => c.ApplyProcessor(this.cloningProcessorDefinition.Object, Bounds)); + } + else + { + this.image.Mutate(c => c.ApplyProcessor(this.cloningProcessorDefinition.Object)); + } + } + private void CloneRegularApply(bool useBounds) { if (useBounds) @@ -178,8 +188,8 @@ namespace SixLabors.ImageSharp.Tests.Processing this.cloningProcessorImpl.Setup(p => p.CloneAndExecute()).Throws(new ImageProcessingException("Test")); } - this.processorDefinition - .Setup(p => p.CreatePixelSpecificProcessor(It.IsAny>(), It.IsAny())) + this.cloningProcessorDefinition + .Setup(p => p.CreatePixelSpecificCloningProcessor(It.IsAny>(), It.IsAny())) .Returns(this.cloningProcessorImpl.Object); this.cloningProcessorDefinition