From d62fa6929beafa8a2d4c29cf5664fdf9c5d87b88 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Wed, 18 Sep 2019 00:06:40 +1000 Subject: [PATCH] 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); } } }