Browse Source

Refactor cloning processors and tests

af/merge-core
James Jackson-South 6 years ago
parent
commit
40b69d7e95
  1. 36
      src/ImageSharp/Advanced/AotCompilerTools.cs
  2. 4
      src/ImageSharp/Processing/DefaultImageProcessorContext{TPixel}.cs
  3. 17
      src/ImageSharp/Processing/Processors/CloningImageProcessor.cs
  4. 35
      src/ImageSharp/Processing/Processors/CloningImageProcessor{TPixel}.cs
  5. 2
      src/ImageSharp/Processing/Processors/Convolution/EdgeDetector2DProcessor{TPixel}.cs
  6. 2
      src/ImageSharp/Processing/Processors/Convolution/EdgeDetectorCompassProcessor{TPixel}.cs
  7. 2
      src/ImageSharp/Processing/Processors/Convolution/EdgeDetectorProcessor{TPixel}.cs
  8. 2
      src/ImageSharp/Processing/Processors/Filters/LomographProcessor{TPixel}.cs
  9. 4
      src/ImageSharp/Processing/Processors/Filters/PolaroidProcessor{TPixel}.cs
  10. 27
      src/ImageSharp/Processing/Processors/ICloningImageProcessor.cs
  11. 2
      src/ImageSharp/Processing/Processors/ICloningImageProcessor{TPixel}.cs
  12. 17
      src/ImageSharp/Processing/Processors/ImageProcessorExtensions.cs
  13. 2
      src/ImageSharp/Processing/Processors/ImageProcessor{TPixel}.cs
  14. 2
      src/ImageSharp/Processing/Processors/Transforms/AffineTransformProcessor.cs
  15. 55
      src/ImageSharp/Processing/Processors/Transforms/AffineTransformProcessor{TPixel}.cs
  16. 18
      src/ImageSharp/Processing/Processors/Transforms/AutoOrientProcessor{TPixel}.cs
  17. 2
      src/ImageSharp/Processing/Processors/Transforms/CropProcessor.cs
  18. 46
      src/ImageSharp/Processing/Processors/Transforms/CropProcessor{TPixel}.cs
  19. 6
      src/ImageSharp/Processing/Processors/Transforms/EntropyCropProcessor{TPixel}.cs
  20. 2
      src/ImageSharp/Processing/Processors/Transforms/ProjectiveTransformProcessor.cs
  21. 56
      src/ImageSharp/Processing/Processors/Transforms/ProjectiveTransformProcessor{TPixel}.cs
  22. 2
      src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor.cs
  23. 101
      src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor{TPixel}.cs
  24. 2
      src/ImageSharp/Processing/Processors/Transforms/RotateProcessor.cs
  25. 4
      tests/ImageSharp.Tests/Drawing/FillRegionProcessorTests.cs
  26. 32
      tests/ImageSharp.Tests/Processing/ImageProcessingContextTests.cs

36
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<TPixel>();
AotCompileDithering<TPixel>();
AotCompilePixelOperations<TPixel>();
AotCompileResizeOperations<TPixel>();
System.Runtime.CompilerServices.Unsafe.SizeOf<TPixel>();
Unsafe.SizeOf<TPixel>();
AotCodec<TPixel>(new Formats.Png.PngDecoder(), new Formats.Png.PngEncoder());
AotCodec<TPixel>(new Formats.Bmp.BmpDecoder(), new Formats.Bmp.BmpEncoder());
@ -107,8 +105,10 @@ namespace SixLabors.ImageSharp.Advanced
private static void AotCompileOctreeQuantizer<TPixel>()
where TPixel : struct, IPixel<TPixel>
{
var test = new OctreeFrameQuantizer<TPixel>(new OctreeQuantizer(false));
test.AotGetPalette();
using (var test = new OctreeFrameQuantizer<TPixel>(new OctreeQuantizer(false)))
{
test.AotGetPalette();
}
}
/// <summary>
@ -118,9 +118,11 @@ namespace SixLabors.ImageSharp.Advanced
private static void AotCompileWuQuantizer<TPixel>()
where TPixel : struct, IPixel<TPixel>
{
var test = new WuFrameQuantizer<TPixel>(Configuration.Default.MemoryAllocator, new WuQuantizer(false));
test.QuantizeFrame(new ImageFrame<TPixel>(Configuration.Default, 1, 1));
test.AotGetPalette();
using (var test = new WuFrameQuantizer<TPixel>(Configuration.Default.MemoryAllocator, new WuQuantizer(false)))
{
test.QuantizeFrame(new ImageFrame<TPixel>(Configuration.Default, 1, 1));
test.AotGetPalette();
}
}
/// <summary>
@ -132,7 +134,10 @@ namespace SixLabors.ImageSharp.Advanced
{
var test = new FloydSteinbergDiffuser();
TPixel pixel = default;
test.Dither(new ImageFrame<TPixel>(Configuration.Default, 1, 1), pixel, pixel, 0, 0, 0, 0, 0, 0);
using (var image = new ImageFrame<TPixel>(Configuration.Default, 1, 1))
{
test.Dither(image, pixel, pixel, 0, 0, 0, 0, 0, 0);
}
}
/// <summary>
@ -171,16 +176,5 @@ namespace SixLabors.ImageSharp.Advanced
var pixelOp = new PixelOperations<TPixel>();
pixelOp.GetPixelBlender(PixelColorBlendingMode.Normal, PixelAlphaCompositionMode.Clear);
}
/// <summary>
/// This method pre-seeds the ResizeProcessor for the AoT compiler on iOS.
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
private static void AotCompileResizeOperations<TPixel>()
where TPixel : struct, IPixel<TPixel>
{
var genericResizeProcessor = (ResizeProcessor<TPixel>)new ResizeProcessor(new ResizeOptions(), default).CreatePixelSpecificProcessor(new Image<TPixel>(0, 0), default);
genericResizeProcessor.AotCreateDestination();
}
}
}

4
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<TPixel> pixelProcessor = cloningImageProcessor.CreatePixelSpecificProcessor(this.source, rectangle))
using (ICloningImageProcessor<TPixel> pixelProcessor = cloningImageProcessor.CreatePixelSpecificCloningProcessor(this.source, rectangle))
{
this.destination = pixelProcessor.CloneAndExecute();
return this;

17
src/ImageSharp/Processing/Processors/CloningImageProcessor.cs

@ -9,23 +9,14 @@ namespace SixLabors.ImageSharp.Processing.Processors
/// <summary>
/// The base class for all cloning image processors.
/// </summary>
public abstract class CloningImageProcessor : IImageProcessor
public abstract class CloningImageProcessor : ICloningImageProcessor
{
/// <summary>
/// Creates a pixel specific <see cref="ICloningImageProcessor{TPixel}"/> that is capable of executing
/// the processing algorithm on an <see cref="Image{TPixel}"/>.
/// </summary>
/// <typeparam name="TPixel">The pixel type.</typeparam>
/// <param name="source">The source image. Cannot be null.</param>
/// <param name="sourceRectangle">
/// The <see cref="Rectangle"/> structure that specifies the portion of the image object to draw.
/// </param>
/// <returns>The <see cref="ICloningImageProcessor{TPixel}"/></returns>
public abstract ICloningImageProcessor<TPixel> CreatePixelSpecificProcessor<TPixel>(Image<TPixel> source, Rectangle sourceRectangle)
/// <inheritdoc/>
public abstract ICloningImageProcessor<TPixel> CreatePixelSpecificCloningProcessor<TPixel>(Image<TPixel> source, Rectangle sourceRectangle)
where TPixel : struct, IPixel<TPixel>;
/// <inheritdoc/>
IImageProcessor<TPixel> IImageProcessor.CreatePixelSpecificProcessor<TPixel>(Image<TPixel> source, Rectangle sourceRectangle)
=> this.CreatePixelSpecificProcessor(source, sourceRectangle);
=> this.CreatePixelSpecificCloningProcessor(source, sourceRectangle);
}
}

35
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; }
/// <summary>
/// Gets the <see cref="ImageSharp.Configuration"/> instance to use when performing operations.
/// Gets the <see cref="Configuration"/> instance to use when performing operations.
/// </summary>
protected Configuration Configuration { get; }
/// <inheritdoc/>
public Image<TPixel> CloneAndExecute()
Image<TPixel> ICloningImageProcessor<TPixel>.CloneAndExecute()
{
try
{
Image<TPixel> clone = this.CreateDestination();
Image<TPixel> clone = this.CreateTarget();
this.CheckFrameCount(this.Source, clone);
Configuration configuration = this.Source.GetConfiguration();
@ -82,7 +84,7 @@ namespace SixLabors.ImageSharp.Processing.Processors
}
/// <inheritdoc/>
public void Execute()
void IImageProcessor<TPixel>.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<TPixel> clone = default;
try
{
clone = this.CloneAndExecute();
clone = ((ICloningImageProcessor<TPixel>)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
}
/// <summary>
/// Generates a deep clone of the source image that operations should be applied to.
/// Gets the size of the target image.
/// </summary>
/// <returns>The cloned image.</returns>
protected virtual Image<TPixel> CreateDestination() => this.Source.Clone();
/// <returns>The <see cref="Size"/>.</returns>
protected abstract Size GetTargetSize();
/// <summary>
/// This method is called before the process is applied to prepare the processor.
@ -166,6 +168,23 @@ namespace SixLabors.ImageSharp.Processing.Processors
{
}
private Image<TPixel> CreateTarget()
{
Image<TPixel> 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<ImageFrame<TPixel>> frames = source.Frames.Select<ImageFrame<TPixel>, ImageFrame<TPixel>>(
x => new ImageFrame<TPixel>(
source.GetConfiguration(),
targetSize.Width,
targetSize.Height,
x.Metadata.DeepClone()));
// Use the overload to prevent an extra frame being added
return new Image<TPixel>(this.Configuration, source.Metadata.DeepClone(), frames);
}
private void CheckFrameCount(Image<TPixel> a, Image<TPixel> b)
{
if (a.Frames.Count != b.Frames.Count)

2
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();

2
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();

2
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();

2
src/ImageSharp/Processing/Processors/Filters/LomographProcessor{TPixel}.cs

@ -29,7 +29,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Filters
/// <inheritdoc/>
protected override void AfterImageApply()
{
new VignetteProcessor(VeryDarkGreen).Apply(this.Source, this.SourceRectangle);
new VignetteProcessor(VeryDarkGreen).Execute(this.Source, this.SourceRectangle);
base.AfterImageApply();
}
}

4
src/ImageSharp/Processing/Processors/Filters/PolaroidProcessor{TPixel}.cs

@ -31,8 +31,8 @@ namespace SixLabors.ImageSharp.Processing.Processors.Filters
/// <inheritdoc/>
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();
}
}

27
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
{
/// <summary>
/// Defines an algorithm to alter the pixels of a cloned image.
/// </summary>
public interface ICloningImageProcessor : IImageProcessor
{
/// <summary>
/// Creates a pixel specific <see cref="ICloningImageProcessor{TPixel}"/> that is capable of executing
/// the processing algorithm on an <see cref="Image{TPixel}"/>.
/// </summary>
/// <typeparam name="TPixel">The pixel type.</typeparam>
/// <param name="source">The source image. Cannot be null.</param>
/// <param name="sourceRectangle">
/// The <see cref="Rectangle"/> structure that specifies the portion of the image object to draw.
/// </param>
/// <returns>The <see cref="ICloningImageProcessor{TPixel}"/></returns>
ICloningImageProcessor<TPixel> CreatePixelSpecificCloningProcessor<TPixel>(Image<TPixel> source, Rectangle sourceRectangle)
where TPixel : struct, IPixel<TPixel>;
}
}

2
src/ImageSharp/Processing/Processors/ICloningImageProcessor{TPixel}.cs

@ -6,7 +6,7 @@ using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp.Processing.Processors
{
/// <summary>
/// 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.
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
public interface ICloningImageProcessor<TPixel> : IImageProcessor<TPixel>

17
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));
}
/// <summary>
/// Executes the processor against the given source image and rectangle bounds.
/// </summary>
/// <param name="processor">The processor.</param>
/// <param name="source">The source image.</param>
/// <param name="sourceRectangle">The source bounds.</param>
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;

2
src/ImageSharp/Processing/Processors/ImageProcessor{TPixel}.cs

@ -44,7 +44,7 @@ namespace SixLabors.ImageSharp.Processing.Processors
protected Configuration Configuration { get; }
/// <inheritdoc/>
public void Execute()
void IImageProcessor<TPixel>.Execute()
{
try
{

2
src/ImageSharp/Processing/Processors/Transforms/AffineTransformProcessor.cs

@ -41,7 +41,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
public Size TargetDimensions { get; }
/// <inheritdoc/>
public override ICloningImageProcessor<TPixel> CreatePixelSpecificProcessor<TPixel>(Image<TPixel> source, Rectangle sourceRectangle)
public override ICloningImageProcessor<TPixel> CreatePixelSpecificCloningProcessor<TPixel>(Image<TPixel> source, Rectangle sourceRectangle)
=> new AffineTransformProcessor<TPixel>(this, source, sourceRectangle);
}
}

55
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<TPixel> : TransformProcessor<TPixel>
where TPixel : struct, IPixel<TPixel>
{
private Size targetSize;
private Matrix3x2 transformMatrix;
private readonly IResampler resampler;
/// <summary>
/// Initializes a new instance of the <see cref="AffineTransformProcessor{TPixel}"/> class.
/// </summary>
@ -29,50 +30,37 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
public AffineTransformProcessor(AffineTransformProcessor definition, Image<TPixel> 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;
/// <inheritdoc/>
protected override Image<TPixel> CreateDestination()
{
// We will always be creating the clone even for mutate because we may need to resize the canvas
IEnumerable<ImageFrame<TPixel>> frames = this.Source.Frames.Select<ImageFrame<TPixel>, ImageFrame<TPixel>>(
x => new ImageFrame<TPixel>(this.Configuration, this.TargetDimensions, x.Metadata.DeepClone()));
// Use the overload to prevent an extra frame being added
return new Image<TPixel>(this.Configuration, this.Source.Metadata.DeepClone(), frames);
}
protected override Size GetTargetSize() => this.targetSize;
/// <inheritdoc/>
protected override void OnFrameApply(ImageFrame<TPixel> source, ImageFrame<TPixel> 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<Vector4>(
targetBounds,
this.Configuration,
configuration,
(rows, vectorBuffer) =>
{
Span<Vector4> vectorSpan = vectorBuffer.Span;
for (int y = rows.Min; y < rows.Max; y++)
{
Span<TPixel> targetRowSpan = destination.GetPixelRowSpan(y);
PixelOperations<TPixel>.Instance.ToVector4(this.Configuration, targetRowSpan, vectorSpan);
PixelOperations<TPixel>.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<TPixel>.Instance.FromVector4Destructive(
this.Configuration,
configuration,
vectorSpan,
targetRowSpan);
}

18
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:

2
src/ImageSharp/Processing/Processors/Transforms/CropProcessor.cs

@ -32,7 +32,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
public Rectangle CropRectangle { get; }
/// <inheritdoc />
public override ICloningImageProcessor<TPixel> CreatePixelSpecificProcessor<TPixel>(Image<TPixel> source, Rectangle sourceRectangle)
public override ICloningImageProcessor<TPixel> CreatePixelSpecificCloningProcessor<TPixel>(Image<TPixel> source, Rectangle sourceRectangle)
=> new CropProcessor<TPixel>(this, source, sourceRectangle);
}
}

46
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<TPixel> : TransformProcessor<TPixel>
where TPixel : struct, IPixel<TPixel>
{
private readonly CropProcessor definition;
private Rectangle cropRectangle;
/// <summary>
/// Initializes a new instance of the <see cref="CropProcessor{TPixel}"/> class.
@ -29,53 +26,42 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
/// <param name="sourceRectangle">The source area to process for the current processor instance.</param>
public CropProcessor(CropProcessor definition, Image<TPixel> source, Rectangle sourceRectangle)
: base(source, sourceRectangle)
{
this.definition = definition;
}
private Rectangle CropRectangle => this.definition.CropRectangle;
=> this.cropRectangle = definition.CropRectangle;
/// <inheritdoc/>
protected override Image<TPixel> CreateDestination()
{
// We will always be creating the clone even for mutate because we may need to resize the canvas
IEnumerable<ImageFrame<TPixel>> frames = this.Source.Frames.Select<ImageFrame<TPixel>, ImageFrame<TPixel>>(
x => new ImageFrame<TPixel>(
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<TPixel>(this.Source.GetConfiguration(), this.Source.Metadata.DeepClone(), frames);
}
protected override Size GetTargetSize() => new Size(this.cropRectangle.Width, this.cropRectangle.Height);
/// <inheritdoc/>
protected override void OnFrameApply(ImageFrame<TPixel> source, ImageFrame<TPixel> 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<TPixel> sourceRow = source.GetPixelRowSpan(y).Slice(rect.Left);
Span<TPixel> targetRow = destination.GetPixelRowSpan(y - rect.Top);
sourceRow.Slice(0, rect.Width).CopyTo(targetRow);
Span<TPixel> sourceRow = source.GetPixelRowSpan(y).Slice(bounds.Left);
Span<TPixel> targetRow = destination.GetPixelRowSpan(y - bounds.Top);
sourceRow.Slice(0, bounds.Width).CopyTo(targetRow);
}
});
}

6
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();
}

2
src/ImageSharp/Processing/Processors/Transforms/ProjectiveTransformProcessor.cs

@ -41,7 +41,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
public Size TargetDimensions { get; }
/// <inheritdoc />
public override ICloningImageProcessor<TPixel> CreatePixelSpecificProcessor<TPixel>(Image<TPixel> source, Rectangle sourceRectangle)
public override ICloningImageProcessor<TPixel> CreatePixelSpecificCloningProcessor<TPixel>(Image<TPixel> source, Rectangle sourceRectangle)
=> new ProjectiveTransformProcessor<TPixel>(this, source, sourceRectangle);
}
}

56
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<TPixel> : TransformProcessor<TPixel>
where TPixel : struct, IPixel<TPixel>
{
private readonly ProjectiveTransformProcessor definition;
private Size targetSize;
private readonly IResampler resampler;
private Matrix4x4 transformMatrix;
/// <summary>
/// Initializes a new instance of the <see cref="ProjectiveTransformProcessor{TPixel}"/> class.
@ -31,52 +31,37 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
public ProjectiveTransformProcessor(ProjectiveTransformProcessor definition, Image<TPixel> 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;
/// <inheritdoc/>
protected override Image<TPixel> CreateDestination()
{
// We will always be creating the clone even for mutate because we may need to resize the canvas
IEnumerable<ImageFrame<TPixel>> frames = this.Source.Frames.Select<ImageFrame<TPixel>, ImageFrame<TPixel>>(
x => new ImageFrame<TPixel>(
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<TPixel>(this.Source.GetConfiguration(), this.Source.Metadata.DeepClone(), frames);
}
protected override Size GetTargetSize() => this.targetSize;
/// <inheritdoc/>
protected override void OnFrameApply(ImageFrame<TPixel> source, ImageFrame<TPixel> 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<Vector4>(
targetBounds,
this.Configuration,
configuration,
(rows, vectorBuffer) =>
{
Span<Vector4> vectorSpan = vectorBuffer.Span;
for (int y = rows.Min; y < rows.Max; y++)
{
Span<TPixel> targetRowSpan = destination.GetPixelRowSpan(y);
PixelOperations<TPixel>.Instance.ToVector4(this.Configuration, targetRowSpan, vectorSpan);
PixelOperations<TPixel>.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<TPixel>.Instance.FromVector4Destructive(
this.Configuration,
configuration,
vectorSpan,
targetRowSpan);
}

2
src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor.cs

@ -55,7 +55,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
public bool Compand { get; }
/// <inheritdoc />
public override ICloningImageProcessor<TPixel> CreatePixelSpecificProcessor<TPixel>(Image<TPixel> source, Rectangle sourceRectangle)
public override ICloningImageProcessor<TPixel> CreatePixelSpecificCloningProcessor<TPixel>(Image<TPixel> source, Rectangle sourceRectangle)
=> new ResizeProcessor<TPixel>(this, source, sourceRectangle);
}
}

101
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<TPixel> : TransformProcessor<TPixel>
where TPixel : struct, IPixel<TPixel>
{
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<TPixel> source, Rectangle sourceRectangle)
public ResizeProcessor(ResizeProcessor definition, Image<TPixel> 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;
}
/// <summary>
/// Gets the sampler to perform the resize operation.
/// </summary>
public IResampler Sampler => this.parameterSource.Sampler;
/// <summary>
/// Gets the target width.
/// </summary>
public int TargetWidth => this.parameterSource.TargetWidth;
/// <summary>
/// Gets the target height.
/// </summary>
public int TargetHeight => this.parameterSource.TargetHeight;
/// <summary>
/// Gets the target resize rectangle.
/// </summary>
public Rectangle TargetRectangle => this.parameterSource.TargetRectangle;
/// <summary>
/// Gets a value indicating whether to compress or expand individual pixel color values on processing.
/// </summary>
public bool Compand => this.parameterSource.Compand;
/// <summary>
/// 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.
/// </summary>
/// <returns>The result returned from <see cref="M:CreateDestination"/>.</returns>
internal Image<TPixel> AotCreateDestination()
=> this.CreateDestination();
/// <inheritdoc/>
protected override Image<TPixel> CreateDestination()
{
Image<TPixel> 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<ImageFrame<TPixel>> frames = source.Frames.Select<ImageFrame<TPixel>, ImageFrame<TPixel>>(
x => new ImageFrame<TPixel>(
configuration,
this.TargetWidth,
this.TargetHeight,
x.Metadata.DeepClone()));
// Use the overload to prevent an extra frame being added
return new Image<TPixel>(configuration, source.Metadata.DeepClone(), frames);
}
protected override Size GetTargetSize() => new Size(this.targetWidth, this.targetHeight);
/// <inheritdoc/>
protected override void BeforeImageApply(Image<TPixel> destination)
{
if (!(this.Sampler is NearestNeighborResampler))
if (!(this.resampler is NearestNeighborResampler))
{
Image<TPixel> 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<TPixel> sourceRow =
source.GetPixelRowSpan((int)(((y - startY) * heightFactor) + sourceY));
Span<TPixel> sourceRow = source.GetPixelRowSpan((int)(((y - startY) * heightFactor) + sourceY));
Span<TPixel> 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<TPixel> 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();

2
src/ImageSharp/Processing/Processors/Transforms/RotateProcessor.cs

@ -46,7 +46,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
public float Degrees { get; }
/// <inheritdoc />
public override ICloningImageProcessor<TPixel> CreatePixelSpecificProcessor<TPixel>(Image<TPixel> source, Rectangle sourceRectangle)
public override ICloningImageProcessor<TPixel> CreatePixelSpecificCloningProcessor<TPixel>(Image<TPixel> source, Rectangle sourceRectangle)
=> new RotateProcessor<TPixel>(this, source, sourceRectangle);
}
}

4
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<Rgba32>(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<Rgba32>(10, 10);
processor.Apply(img, bounds);
processor.Execute(img, bounds);
}
[Fact]

32
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<IImageProcessor> processorDefinition;
private readonly Mock<CloningImageProcessor> cloningProcessorDefinition;
private readonly Mock<ICloningImageProcessor> cloningProcessorDefinition;
private readonly Mock<IImageProcessor<Rgba32>> regularProcessorImpl;
@ -32,7 +30,7 @@ namespace SixLabors.ImageSharp.Tests.Processing
public ImageProcessingContextTests()
{
this.processorDefinition = new Mock<IImageProcessor>();
this.cloningProcessorDefinition = new Mock<CloningImageProcessor>();
this.cloningProcessorDefinition = new Mock<ICloningImageProcessor>();
this.regularProcessorImpl = new Mock<IImageProcessor<Rgba32>>();
this.cloningProcessorImpl = new Mock<ICloningImageProcessor<Rgba32>>();
}
@ -54,11 +52,11 @@ namespace SixLabors.ImageSharp.Tests.Processing
if (throwException)
{
Assert.Throws<ImageProcessingException>(() => this.MutateApply(useBounds));
Assert.Throws<ImageProcessingException>(() => 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<ImageProcessingException>(() => this.MutateApply(useBounds));
Assert.Throws<ImageProcessingException>(() => 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<Image<Rgba32>>(), It.IsAny<Rectangle>()))
this.cloningProcessorDefinition
.Setup(p => p.CreatePixelSpecificCloningProcessor(It.IsAny<Image<Rgba32>>(), It.IsAny<Rectangle>()))
.Returns(this.cloningProcessorImpl.Object);
this.cloningProcessorDefinition

Loading…
Cancel
Save